From 8a8d3099897b8bbfc6985f148c8687eb28d4351d Mon Sep 17 00:00:00 2001 From: nets7snipper <53284908+nets7snipper@users.noreply.github.com> Date: Sun, 6 Oct 2024 07:01:39 +0800 Subject: [PATCH] add dynamic navbar dependding on user login session (#97) Co-authored-by: benjaminneoh Co-authored-by: cloverluo112 <110218255+cloverluo112@users.noreply.github.com> --- blotztask-ui/.env.production | 1 - blotztask-ui/.gitignore | 3 + blotztask-ui/.vscode/settings.json | 8 - blotztask-ui/public/assets/images/logo.svg | 503 ++++++++++++++++++ .../src/app/api/auth/[...nextauth]/options.ts | 165 +++--- .../src/app/api/auth/[...nextauth]/route.ts | 9 +- blotztask-ui/src/app/layout.tsx | 24 +- blotztask-ui/src/app/navbar/main-nav.tsx | 116 ++-- blotztask-ui/src/app/page.tsx | 43 +- blotztask-ui/src/app/provider.tsx | 17 +- .../src/app/signin/LoginPage.module.css | 71 +++ blotztask-ui/src/app/signin/page.txt | 84 +++ .../src/app/signup/SignUpPage.module.css | 51 ++ blotztask-ui/src/app/signup/page.txt | 94 ++++ .../app/task-list/components/task-table.tsx | 52 +- blotztask-ui/src/app/task-list/mockdata.tsx | 78 +-- blotztask-ui/src/app/task-list/page.tsx | 13 +- blotztask-ui/src/app/tasks/page.tsx | 17 +- blotztask-ui/src/app/test-connection/page.tsx | 36 +- blotztask-ui/src/middleware.ts | 40 ++ blotztask-ui/src/services/authService.ts | 0 blotztask-ui/src/styles/globals.css | 164 +++++- blotztask-ui/tailwind.config.ts | 7 + 23 files changed, 1309 insertions(+), 287 deletions(-) delete mode 100644 blotztask-ui/.vscode/settings.json create mode 100644 blotztask-ui/public/assets/images/logo.svg create mode 100644 blotztask-ui/src/app/signin/LoginPage.module.css create mode 100644 blotztask-ui/src/app/signin/page.txt create mode 100644 blotztask-ui/src/app/signup/SignUpPage.module.css create mode 100644 blotztask-ui/src/app/signup/page.txt create mode 100644 blotztask-ui/src/services/authService.ts diff --git a/blotztask-ui/.env.production b/blotztask-ui/.env.production index 91d1037..8673974 100644 --- a/blotztask-ui/.env.production +++ b/blotztask-ui/.env.production @@ -1,3 +1,2 @@ NEXT_PUBLIC_API_BASE_URL=https://wapp-blotztaskapp.azurewebsites.net NEXT_PUBLIC_API_BASE_URL_WITH_API=https://wapp-blotztaskapp.azurewebsites.net/api - \ No newline at end of file diff --git a/blotztask-ui/.gitignore b/blotztask-ui/.gitignore index 75e9c1c..a636183 100644 --- a/blotztask-ui/.gitignore +++ b/blotztask-ui/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# vscode +.vscode diff --git a/blotztask-ui/.vscode/settings.json b/blotztask-ui/.vscode/settings.json deleted file mode 100644 index c93473f..0000000 --- a/blotztask-ui/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.organizeImports": true - } - } diff --git a/blotztask-ui/public/assets/images/logo.svg b/blotztask-ui/public/assets/images/logo.svg new file mode 100644 index 0000000..ce277f3 --- /dev/null +++ b/blotztask-ui/public/assets/images/logo.svg @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blotztask-ui/src/app/api/auth/[...nextauth]/options.ts b/blotztask-ui/src/app/api/auth/[...nextauth]/options.ts index 6801dcb..413bfd3 100644 --- a/blotztask-ui/src/app/api/auth/[...nextauth]/options.ts +++ b/blotztask-ui/src/app/api/auth/[...nextauth]/options.ts @@ -1,6 +1,6 @@ -import { NextAuthOptions } from "next-auth"; -import CredentialsProvider from "next-auth/providers/credentials"; -import { cookies } from "next/headers"; +import { NextAuthOptions } from 'next-auth'; +import CredentialsProvider from 'next-auth/providers/credentials'; +import { cookies } from 'next/headers'; interface Credentials { email: string; @@ -15,97 +15,94 @@ interface LoginApiResponse { } export const authOptions: NextAuthOptions = { - // Configure one or more authentication providers - providers: [ - CredentialsProvider({ - // The name to display on the sign in form (e.g. "Sign in with...") - name: "Credentials", - // `credentials` is used to generate a form on the sign in page. - // You can specify which fields should be submitted, by adding keys to the `credentials` object. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - email: { label: "Email", type: "text", placeholder: "your email" }, - password: { label: "Password", type: "password" } - }, - - async authorize(credentials: Credentials) { - - const { email, password } = credentials; - // console.log(email, password,req); - - try { - //TODO : Remove reject unauthorized set to false - if (process.env.NODE_ENV !== 'production') { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - } - //TODO :Also fix the fetch url for login in prod - const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/login/`, { + // Configure one or more authentication providers + providers: [ + CredentialsProvider({ + // The name to display on the sign in form (e.g. "Sign in with...") + name: 'Credentials', + // `credentials` is used to generate a form on the sign in page. + // You can specify which fields should be submitted, by adding keys to the `credentials` object. + // e.g. domain, username, password, 2FA token, etc. + // You can pass any HTML attribute to the tag through the object. + credentials: { + email: { label: 'Email', type: 'text', placeholder: 'your email' }, + password: { label: 'Password', type: 'password' }, + }, + + async authorize(credentials: Credentials) { + const { email, password } = credentials; + + try { + //TODO : Remove reject unauthorized set to false + if (process.env.NODE_ENV !== 'production') { + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + } + //TODO :Also fix the fetch url for login in prod + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_BASE_URL}/login/`, + { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body: JSON.stringify({ email, password }) - }); - - if (!response.ok) { - console.error('Failed to authenticate:', response); - return null; + body: JSON.stringify({ email, password }), } - - const data: LoginApiResponse = await response.json(); + ); - cookies().set('authToken', data.accessToken, { - secure: process.env.NODE_ENV === 'production', - sameSite: 'strict' - }); - - if (data.accessToken) { - return { - id: email || 'placeholder-id', - email: email, - accessToken: data.accessToken, // Include the access token here - refreshToken: data.refreshToken, - expiresIn: data.expiresIn, - }; - } else { - console.error('Access token not found in response'); - return null; - } - } catch (error) { - console.error('Unhandled error:', error); + if (!response.ok) { + console.error('Failed to authenticate:', response); return null; } - - } - }) - ], - secret: process.env.NEXTAUTH_SECRET, - callbacks: { - async signIn({ user, account }) { - if (user?.access_token) { - account.access_token = user?.access_token as string + const data: LoginApiResponse = await response.json(); + + cookies().set('authToken', data.accessToken, { + secure: process.env.NODE_ENV === 'production', + sameSite: 'strict', + }); + + if (data.accessToken) { + return { + id: email || 'placeholder-id', + email: email, + accessToken: data.accessToken, // Include the access token here + refreshToken: data.refreshToken, + expiresIn: data.expiresIn, + }; + } else { + console.error('Access token not found in response'); + return null; + } + } catch (error) { + console.error('Unhandled error:', error); + return null; } - if (user?.refresh_token) { - account.refresh_token = user?.refresh_token as string - } - - return true }, - async jwt({ token, session }) { - console.log('jwt callback', token, session); - return token; - }, - async session({ session, token, user }) { - console.log('session callback', session, token, user); - - - return session + }), + ], + secret: process.env.NEXTAUTH_SECRET, + callbacks: { + async signIn({ user, account }) { + if (user?.access_token) { + account.access_token = user?.access_token as string; } + if (user?.refresh_token) { + account.refresh_token = user?.refresh_token as string; + } + + return true; }, - session: { - strategy: "jwt" + async jwt({ token, session }) { + console.log('jwt callback', token, session); + return token; + }, + async session({ session, token, user }) { + console.log('session callback', session, token, user); + + return session; }, - - } \ No newline at end of file + }, + session: { + strategy: 'jwt', + }, +}; diff --git a/blotztask-ui/src/app/api/auth/[...nextauth]/route.ts b/blotztask-ui/src/app/api/auth/[...nextauth]/route.ts index 70b9923..a166338 100644 --- a/blotztask-ui/src/app/api/auth/[...nextauth]/route.ts +++ b/blotztask-ui/src/app/api/auth/[...nextauth]/route.ts @@ -1,6 +1,5 @@ -import NextAuth from "next-auth" -import { authOptions } from "./options" +import NextAuth from 'next-auth'; +import { authOptions } from './options'; - -const handler = NextAuth(authOptions) -export { handler as GET, handler as POST } \ No newline at end of file +const handler = NextAuth(authOptions); +export { handler as GET, handler as POST }; diff --git a/blotztask-ui/src/app/layout.tsx b/blotztask-ui/src/app/layout.tsx index 45a93f1..619747a 100644 --- a/blotztask-ui/src/app/layout.tsx +++ b/blotztask-ui/src/app/layout.tsx @@ -1,13 +1,10 @@ - +import { cn } from '@/lib/utils'; import type { Metadata } from 'next'; +import { ThemeProvider } from 'next-themes'; import { Inter as FontSans } from 'next/font/google'; import '../styles/globals.css'; -import { cn } from '@/lib/utils'; -import { ThemeProvider } from 'next-themes'; -import SessionProvider from './provider'; -import { getServerSession } from 'next-auth'; -import { authOptions } from './api/auth/[...nextauth]/options'; import { MainNav } from './navbar/main-nav'; +import Provider from './provider'; const fontSans = FontSans({ subsets: ['latin'], @@ -19,14 +16,11 @@ export const metadata: Metadata = { description: 'Efficiently organize and track users tasks', }; -export default async function RootLayout({ +export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - - const session = await getServerSession(authOptions); - return ( - + - + {/* TODO: Implement navbar to navigate between pages*/} -
{children}
+
+ {children} +
-
+ ); diff --git a/blotztask-ui/src/app/navbar/main-nav.tsx b/blotztask-ui/src/app/navbar/main-nav.tsx index eefb891..5265f1d 100644 --- a/blotztask-ui/src/app/navbar/main-nav.tsx +++ b/blotztask-ui/src/app/navbar/main-nav.tsx @@ -1,55 +1,97 @@ -import { authOptions } from "@/app/api/auth/[...nextauth]/options"; -import { getServerSession } from "next-auth"; -import { cn } from "@/lib/utils" -import Link from "next/link"; +'use client'; -export async function MainNav({ - className, - ...props -}: React.HTMLAttributes) { +import { + ClientSafeProvider, + getProviders, + signOut, + useSession, +} from 'next-auth/react'; +import Image from 'next/image'; +import Link from 'next/link'; +import { useEffect, useState } from 'react'; - const session = await getServerSession(authOptions); +export function MainNav({}: React.HTMLAttributes) { + const { data: session } = useSession(); + const [providers, setProviders] = useState | null>(null); + + // load the registered nextauth providers, in our case is the credential provider + useEffect(() => { + (async () => { + const res = await getProviders(); + setProviders(res); + })(); + }, []); return ( -
-
-
-
+ +
+ ) : ( +
+ {providers && + Object.values(providers).map((provider) => ( + + Sign in + + ))} +
+ )}
- - ) + + ); } diff --git a/blotztask-ui/src/app/page.tsx b/blotztask-ui/src/app/page.tsx index 73dd86b..39473e3 100644 --- a/blotztask-ui/src/app/page.tsx +++ b/blotztask-ui/src/app/page.tsx @@ -1,50 +1,19 @@ -"use client" -import { Button } from '@/components/ui/button'; +'use client'; import { H1, H3 } from '@/components/ui/heading-with-anchor'; -import { signOut } from 'next-auth/react'; -import Link from 'next/link'; export default function Home() { return (
-

Blotz Task

+

+ Blotz + Task +

+

Designed to help users efficiently organize and track their tasks providing functionality for task creation, management, and completion tracking.

-
- - - - - - - - - - - - - - - - - - -
); } diff --git a/blotztask-ui/src/app/provider.tsx b/blotztask-ui/src/app/provider.tsx index 4c2c744..a2dd450 100644 --- a/blotztask-ui/src/app/provider.tsx +++ b/blotztask-ui/src/app/provider.tsx @@ -1,4 +1,15 @@ -"use client"; +'use client'; -import { SessionProvider } from "next-auth/react"; -export default SessionProvider; \ No newline at end of file +import { Session } from 'next-auth'; +import { SessionProvider } from 'next-auth/react'; + +interface ProviderProps { + children: React.ReactNode; + session?: Session | null; +} + +const Provider: React.FC = ({ children, session }) => ( + {children} +); + +export default Provider; diff --git a/blotztask-ui/src/app/signin/LoginPage.module.css b/blotztask-ui/src/app/signin/LoginPage.module.css new file mode 100644 index 0000000..bbaaeea --- /dev/null +++ b/blotztask-ui/src/app/signin/LoginPage.module.css @@ -0,0 +1,71 @@ +.container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: white; /* Light background color */ +} + +.form_container { + background-color: white; /* White background for form */ + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* Soft shadow */ + width: 400px; /* Fixed width for form */ +} + +.title { + text-align: center; + margin-bottom: 20px; +} + +.input_group { + display: flex; + align-items: center; /* Align items vertically centered */ + margin-bottom: 15px; /* Space between input groups */ +} + +.label { + flex: 1; /* Take equal space for label */ + font-weight: bold; /* Make label bold */ +} + +.input { + flex: 2; /* Take more space for input field */ + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; /* Rounded corners */ +} + +.error { + color: red; /* Error message color */ + text-align: center; /* Center error message */ +} + +.submitButton { + width: 100%; /* Full width for button */ + padding: 10px; + background-color: #4caf50; /* Green background */ + color: white; /* White text */ + border: none; + border-radius: 4px; /* Rounded corners */ + cursor: pointer; /* Pointer cursor on hover */ +} + +.submitButton:hover { + background-color: #45a049; /* Darker green on hover */ +} + +.registerPrompt { + margin-top: 20px; + text-align: center; /* Center registration prompt */ +} + +.registerLink { + color: blue; /* Link color */ + text-decoration: underline; /* Underline for link */ +} + +.registerLink:hover { + text-decoration: none; /* Remove underline on hover */ +} diff --git a/blotztask-ui/src/app/signin/page.txt b/blotztask-ui/src/app/signin/page.txt new file mode 100644 index 0000000..6e0fb34 --- /dev/null +++ b/blotztask-ui/src/app/signin/page.txt @@ -0,0 +1,84 @@ +// //TODO : Will refactor register page later +//'use client'; + +// import { signIn } from 'next-auth/react'; +// import { useRouter, useSearchParams } from 'next/navigation'; +// import { useState } from 'react'; +// import styles from './LoginPage.module.css'; // Import CSS styles + +// const LoginPage = () => { +// const router = useRouter(); // Get the router instance +// const searchParams = useSearchParams(); // Get search parameters +// const callbackUrl = searchParams.get('callbackUrl') || '/'; // Get callbackUrl; use default if not provided + +// const [email, setEmail] = useState(''); // State for email input +// const [password, setPassword] = useState(''); // State for password input +// const [error, setError] = useState(null); // State for error message + +// const handleSubmit = async (event) => { +// event.preventDefault(); // Prevent default form submission + +// const result = await signIn('credentials', { +// redirect: false, +// email, +// password, +// callbackUrl, // Pass the callback URL +// }); + +// if (result.error) { +// setError(result.error); // Set error if login fails +// } else { +// router.push(callbackUrl); +// } +// }; + +// return ( +//
+//
+//

User Login

{' '} +// {/* Title of the login page */} +//
+//
+// +// setEmail(e.target.value)} +// required +// className={styles.input} +// placeholder="Enter your email" +// /> +//
+//
+// +// setPassword(e.target.value)} +// required +// className={styles.input} +// placeholder="Enter your password" +// /> +//
+// {error &&

{error}

}{' '} +// {/* Display error message if any */} +// +//
+//

+// Don’t have an account?{' '} +// +// Register here +// {' '} +// {/* Registration link */} +//

+//
+//
+// ); +// }; + +// export default LoginPage; diff --git a/blotztask-ui/src/app/signup/SignUpPage.module.css b/blotztask-ui/src/app/signup/SignUpPage.module.css new file mode 100644 index 0000000..a498291 --- /dev/null +++ b/blotztask-ui/src/app/signup/SignUpPage.module.css @@ -0,0 +1,51 @@ +.container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: white; /* Light grey background for contrast */ + padding: 20px; +} + +.form_container { + width: 100%; + max-width: 400px; /* Limit form width */ + padding: 30px; + border: 1px solid #ddd; + border-radius: 10px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + background-color: white; /* Form background */ +} + +.title { + text-align: center; + margin-bottom: 30px; + font-size: 24px; + color: #333; /* Darker color for contrast */ +} + +.input_group { + margin-bottom: 20px; /* Space between input groups */ + display: flex; + align-items: center; +} + +.label { + flex: 1; /* Allow label to take up space */ + margin-right: 15px; /* Space between label and input */ + color: #555; /* Softer color for labels */ + font-weight: bold; /* Bold labels */ +} + +.input { + flex: 2; /* Allow input to take up more space */ + padding: 12px; /* More padding for input */ + border: 1px solid #ccc; /* Light border */ + border-radius: 5px; /* Rounded input corners */ + transition: border-color 0.3s; /* Transition effect */ +} + +.input:focus { + border-color: #4caf50; /* Green border on focus */ + outline: none; /* Remove default outline */ +} diff --git a/blotztask-ui/src/app/signup/page.txt b/blotztask-ui/src/app/signup/page.txt new file mode 100644 index 0000000..4e75760 --- /dev/null +++ b/blotztask-ui/src/app/signup/page.txt @@ -0,0 +1,94 @@ +//TODO : Will refactor register page later +// 'use client'; + +// import { useRouter } from 'next/navigation'; // Import useRouter for navigation +// import { useState } from 'react'; +// import styles from './SignUpPage.module.css'; // Import CSS styles + +// const SignUpPage = () => { +// const router = useRouter(); // Initialize router for navigation +// const [email, setEmail] = useState(''); +// const [password, setPassword] = useState(''); +// const [confirmPassword, setConfirmPassword] = useState(''); +// const [error, setError] = useState(null); +// const [success, setSuccess] = useState(null); + +// const handleSubmit = async (event) => { +// event.preventDefault(); + +// if (password !== confirmPassword) { +// setError('Passwords do not match'); +// return; +// } + +// // Call the sign-up API +// const signUpResponse = await fetch( +// `${process.env.NEXT_PUBLIC_API_BASE_URL}/register`, +// { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify({ email, password }), +// } +// ); + +// if (signUpResponse.ok) { +// // Redirect to the login page after successful sign-up +// router.push('/signin'); // Use router to navigate to the login page +// } else { +// const errorData = await signUpResponse.json(); +// setError(errorData.error || 'An error occurred during sign-up.'); +// } +// }; + +// return ( +//
+//
+//

Sign Up

+//
+//
+// +// setEmail(e.target.value)} +// required +// className={styles.input} +// placeholder="Enter your email" +// /> +//
+//
+// +// setPassword(e.target.value)} +// required +// className={styles.input} +// placeholder="Enter your password" +// /> +//
+//
+// +// setConfirmPassword(e.target.value)} +// required +// className={styles.input} +// placeholder="Confirm your password" +// /> +//
+// {error &&

{error}

} +// {success &&

{success}

} +// +//
+//
+//
+// ); +// }; + +// export default SignUpPage; diff --git a/blotztask-ui/src/app/task-list/components/task-table.tsx b/blotztask-ui/src/app/task-list/components/task-table.tsx index 5dd8c71..af719ed 100644 --- a/blotztask-ui/src/app/task-list/components/task-table.tsx +++ b/blotztask-ui/src/app/task-list/components/task-table.tsx @@ -5,40 +5,42 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; +} from '@/components/ui/table'; -import { TaskItemDTO } from "@/model/task-Item-dto"; +import { TaskItemDTO } from '@/model/task-Item-dto'; interface TaskTableProps { tasks: TaskItemDTO[]; // tasks prop is an array of TaskItemDTO objects } - + export function TaskTable({ tasks }: TaskTableProps) { - return ( - - - - Task-id - Title - Isdone + return ( +
+ + + Task-id + Title + Isdone + + + + {tasks.map((task) => ( + + {task.id} + {task.title} + + {' '} + {task.isDone ? 'Yes' : 'No'} + - - - {tasks.map((task) => ( - - {task.id} - {task.title} - {task.isDone ? "Yes" : "No"} - - ))} - - {/* + ))} + + {/* Total $2,500.00 */} -
- ) - } - \ No newline at end of file + + ); +} diff --git a/blotztask-ui/src/app/task-list/mockdata.tsx b/blotztask-ui/src/app/task-list/mockdata.tsx index 6f1ed15..d619050 100644 --- a/blotztask-ui/src/app/task-list/mockdata.tsx +++ b/blotztask-ui/src/app/task-list/mockdata.tsx @@ -1,39 +1,39 @@ -import { TaskItemDTO } from "@/model/task-Item-dto"; - - export const mocktasks : TaskItemDTO[] = [ - { - id: 1, - title: "Complete project report", - isDone: false, - }, - { - id: 2, - title: "Review pull requests", - isDone: true, - }, - { - id: 3, - title: "Team meeting", - isDone: false, - }, - { - id: 4, - title: "Update documentation", - isDone: true, - }, - { - id: 5, - title: "Code review for module A", - isDone: false, - }, - { - id: 6, - title: "Deploy new version", - isDone: true, - }, - { - id: 7, - title: "Refactor authentication service", - isDone: false, - }, -]; \ No newline at end of file +import { TaskItemDTO } from '@/model/task-Item-dto'; + +export const mocktasks: TaskItemDTO[] = [ + { + id: 1, + title: 'Complete project report', + isDone: false, + }, + { + id: 2, + title: 'Review pull requests', + isDone: true, + }, + { + id: 3, + title: 'Team meeting', + isDone: false, + }, + { + id: 4, + title: 'Update documentation', + isDone: true, + }, + { + id: 5, + title: 'Code review for module A', + isDone: false, + }, + { + id: 6, + title: 'Deploy new version', + isDone: true, + }, + { + id: 7, + title: 'Refactor authentication service', + isDone: false, + }, +]; diff --git a/blotztask-ui/src/app/task-list/page.tsx b/blotztask-ui/src/app/task-list/page.tsx index 303a3a9..197706d 100644 --- a/blotztask-ui/src/app/task-list/page.tsx +++ b/blotztask-ui/src/app/task-list/page.tsx @@ -7,16 +7,17 @@ import { mocktasks } from './mockdata'; const page = () => { return (
-
- +
+ +
+
- - ) -} + ); +}; -export default page +export default page; diff --git a/blotztask-ui/src/app/tasks/page.tsx b/blotztask-ui/src/app/tasks/page.tsx index 8911c58..6e17834 100644 --- a/blotztask-ui/src/app/tasks/page.tsx +++ b/blotztask-ui/src/app/tasks/page.tsx @@ -1,4 +1,10 @@ -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; import { promises as fs } from 'fs'; import { Metadata } from 'next'; import path from 'path'; @@ -48,11 +54,14 @@ export default async function TaskPage() { */} - ℹ️Notice + + ℹ️Notice + - This page is a newer version of the Task List page with more features. Currently still under implementation. + This page is a newer version of the Task List page with more + features. Currently still under implementation. @@ -62,7 +71,7 @@ export default async function TaskPage() {

Task List

- Here's a list of your tasks for this month! + Here's a list of your tasks for this month!

{/* */}
diff --git a/blotztask-ui/src/app/test-connection/page.tsx b/blotztask-ui/src/app/test-connection/page.tsx index 9eaef1a..45baa62 100644 --- a/blotztask-ui/src/app/test-connection/page.tsx +++ b/blotztask-ui/src/app/test-connection/page.tsx @@ -6,7 +6,7 @@ import { TaskItemDTO } from '@/model/task-Item-dto'; import Link from 'next/link'; import { useState } from 'react'; import TaskList from './components/task-list'; -import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { fetchAllTaskItems } from '@/services/taskService'; export default function Home() { @@ -44,24 +44,26 @@ export default function Home() { {error && ( - - - ❌Error fetching todos - - -

{error}

-
-
+ + + ❌Error fetching todos + + +

{error}

+
+
)} {tasks.length > 0 && ( - - - ✅Successfully fetched todos - - - - - + + + + ✅Successfully fetched todos + + + + + + )} ); diff --git a/blotztask-ui/src/middleware.ts b/blotztask-ui/src/middleware.ts index cb8a4e7..e385048 100644 --- a/blotztask-ui/src/middleware.ts +++ b/blotztask-ui/src/middleware.ts @@ -1,2 +1,42 @@ export { default } from "next-auth/middleware" + +// middleware.js +// import { getToken } from "next-auth/jwt"; +// import { NextResponse } from "next/server"; + +// export async function middleware(req) { +// // auth middleware +// const response = await withAuth(req); +// if (response) { +// console.log(response) +// return response; +// } + +// return NextResponse.next(); +// } + +// // config path no need to check auth +// const excludeAuthPath = ["/", "/signin", "/signup"]; + +// // customize auth redirect strategy +// async function withAuth(req) { +// const { pathname } = req.nextUrl; + +// if (excludeAuthPath.includes(pathname)) { +// return null; +// } + +// const token = await getToken({ req }); +// if (!token) { +// return NextResponse.redirect(new URL('/signin?callbackUrl='+pathname, req.url)); +// } + +// return null; +// } + +// // match all pages +// export const config = { +// matcher: [ +// '/((?!api|_next/static|_next/image|assets|favicon.ico).*)', +// ],}; \ No newline at end of file diff --git a/blotztask-ui/src/services/authService.ts b/blotztask-ui/src/services/authService.ts new file mode 100644 index 0000000..e69de29 diff --git a/blotztask-ui/src/styles/globals.css b/blotztask-ui/src/styles/globals.css index 5188d6b..8bfaec9 100644 --- a/blotztask-ui/src/styles/globals.css +++ b/blotztask-ui/src/styles/globals.css @@ -1,27 +1,177 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"); + @tailwind base; @tailwind components; @tailwind utilities; -:root { +/* + Note: The styles for this gradient grid background is heavily inspired by the creator of this amazing site (https://dub.sh) – all credits go to them! +*/ + +/* This class is not in used +.gradient { + height: fit-content; + z-index: 3; + width: 100%; + max-width: 640px; + background-image: radial-gradient( + at 27% 37%, + hsla(215, 98%, 61%, 1) 0px, + transparent 0% + ), + radial-gradient(at 97% 21%, hsla(125, 98%, 72%, 1) 0px, transparent 50%), + radial-gradient(at 52% 99%, hsla(354, 98%, 61%, 1) 0px, transparent 50%), + radial-gradient(at 10% 29%, hsla(256, 96%, 67%, 1) 0px, transparent 50%), + radial-gradient(at 97% 96%, hsla(38, 60%, 74%, 1) 0px, transparent 50%), + radial-gradient(at 33% 50%, hsla(222, 67%, 73%, 1) 0px, transparent 50%), + radial-gradient(at 79% 53%, hsla(343, 68%, 79%, 1) 0px, transparent 50%); + position: absolute; + content: ""; + width: 100%; + height: 100%; + filter: blur(100px) saturate(150%); + top: 80px; + opacity: 0.15; +} */ + +/* Tailwind Styles */ + +/* .app { + @apply relative z-10 flex justify-center items-center flex-col max-w-7xl mx-auto sm:px-16 px-6; +} */ + +/* .black_btn { + @apply rounded-full border border-blue-200 bg-blue-200 py-2 px-5 text-white transition-all hover:bg-blue-500 hover:border-blue-500 text-center text-sm font-inter flex items-center justify-center; +} */ + +/* .outline_btn { + @apply rounded-full border border-blue-600 bg-transparent py-1.5 px-5 border-blue-600 transition-all hover:bg-blue-500 hover:text-white text-center text-sm font-inter flex items-center justify-center; +} */ + +.gradient_green_blue_btn { + @apply rounded-full border border-transparent py-2 px-5 text-white transition-all text-center text-sm font-inter flex items-center justify-center shadow-md; + background: linear-gradient(135deg, #4caf50, #2196f3); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; +} + +.gradient_green_blue_btn:hover { + background: linear-gradient(135deg, #2196f3, #4caf50); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); +} + +.inverse_gradient_green_blue_btn { + @apply rounded-full border border-transparent py-2 px-5 text-black transition-all text-center text-sm font-inter flex items-center justify-center shadow-md; + background: white; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; +} + +.inverse_gradient_green_blue_btn:hover { + background: linear-gradient(135deg, #4caf50, #2196f3); + color: white; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); +} + +/* .head_text { + @apply mt-5 text-5xl font-extrabold leading-[1.15] text-black sm:text-6xl; +} */ + +/* .orange_gradient { + @apply bg-gradient-to-r from-amber-500 via-orange-600 to-yellow-500 bg-clip-text text-transparent; +} */ + +.green_gradient { + @apply bg-gradient-to-r from-green-400 to-green-500 bg-clip-text text-transparent; +} + +.blue_gradient { + @apply bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent; +} + +/* .desc { + @apply mt-5 text-lg text-gray-600 sm:text-xl max-w-2xl; +} + +.search_input { + @apply block w-full rounded-md border border-gray-200 bg-white py-2.5 font-satoshi pl-5 pr-12 text-sm shadow-lg font-medium focus:border-black focus:outline-none focus:ring-0; +} + +.copy_btn { + @apply w-7 h-7 rounded-full bg-white/10 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur flex justify-center items-center cursor-pointer; +} + +.glassmorphism { + @apply rounded-xl border border-gray-200 bg-white/20 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur p-5; +} + +.prompt_layout { + @apply space-y-6 py-8 sm:columns-2 sm:gap-6 xl:columns-3; +} */ + +/* Form Component */ +/* .form_textarea { + @apply w-full flex rounded-lg h-[200px] mt-2 p-3 text-sm text-gray-500 outline-0; +} */ + +/* .form_input { + @apply w-full flex rounded-lg mt-2 p-3 text-sm text-gray-500 outline-0; +} */ + +/* Nav Component */ +.logo_text { + @apply max-sm:hidden font-satoshi font-semibold text-lg text-black tracking-wide; +} + +.dropdown { + @apply absolute right-0 top-full mt-3 w-full p-5 rounded-lg bg-white min-w-[210px] flex flex-col gap-2 justify-end items-end; +} + +.dropdown_link { + @apply text-sm font-inter text-gray-700 hover:text-gray-500 font-medium; +} + +/* PromptCard Component */ +.prompt_card { + @apply flex-1 break-inside-avoid rounded-lg border border-gray-300 bg-white/20 bg-clip-padding p-6 pb-4 backdrop-blur-lg backdrop-filter md:w-[360px] w-full h-fit; +} + +.flex-center { + @apply flex justify-center items-center; +} + +.flex-start { + @apply flex justify-start items-start; +} + +.flex-end { + @apply flex justify-end items-center; +} + +.flex-between { + @apply flex justify-between items-center; +} + +/* :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; -} +} */ -@media (prefers-color-scheme: dark) { +/* @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } -} - +} */ +/* body { color: rgb(var(--foreground-rgb)); -} +} */ @layer utilities { .text-balance { text-wrap: balance; } -} +} \ No newline at end of file diff --git a/blotztask-ui/tailwind.config.ts b/blotztask-ui/tailwind.config.ts index 58bc2c4..6f52638 100644 --- a/blotztask-ui/tailwind.config.ts +++ b/blotztask-ui/tailwind.config.ts @@ -17,6 +17,13 @@ const config: Config = { 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, + fontFamily: { + satoshi: ['Satoshi', 'sans-serif'], + inter: ['Inter', 'sans-serif'], + }, + colors: { + 'primary-orange': '#FF5722', + } }, }, plugins: [],