From a789e6876f0e226b61bf80a6d68f2c77f6fea3c7 Mon Sep 17 00:00:00 2001 From: Al-Ameen Ogundiran Date: Sat, 4 Oct 2025 02:27:25 +0100 Subject: [PATCH 01/13] fix migration issue with docker prestart, next js hydration error, runtime env url for internal docker communication --- .env.example | 3 ++- .../10368f38610b_fix_delete_document_error.py | 11 ----------- docker-compose.override.yml | 1 + frontend/src/app/layout.tsx | 9 +++++++-- frontend/src/components/theme-toggle.tsx | 4 ++-- frontend/src/runtime-config.ts | 7 ++++--- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index e1e9179..7856e55 100644 --- a/.env.example +++ b/.env.example @@ -50,4 +50,5 @@ PINECONE_API_KEY=changethis OPENAI_API_KEY=changethis -NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8000 \ No newline at end of file +NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8000 +NEXT_INTERNAL_BACKEND_BASE_URL=http://backend:8000 diff --git a/backend/app/alembic/versions/10368f38610b_fix_delete_document_error.py b/backend/app/alembic/versions/10368f38610b_fix_delete_document_error.py index a1e3163..e226874 100644 --- a/backend/app/alembic/versions/10368f38610b_fix_delete_document_error.py +++ b/backend/app/alembic/versions/10368f38610b_fix_delete_document_error.py @@ -19,16 +19,6 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('chat', - sa.Column('message', sqlmodel.sql.sqltypes.AutoString(length=1024), nullable=True), - sa.Column('is_system', sa.Boolean(), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.DateTime(), nullable=False), - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('course_id', sa.Uuid(), nullable=False), - sa.ForeignKeyConstraint(['course_id'], ['course.id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id') - ) op.drop_constraint(op.f('quizattempt_quiz_id_fkey'), 'quizattempt', type_='foreignkey') op.create_foreign_key(None, 'quizattempt', 'quiz', ['quiz_id'], ['id'], ondelete='CASCADE') # ### end Alembic commands ### @@ -38,5 +28,4 @@ def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_constraint(None, 'quizattempt', type_='foreignkey') op.create_foreign_key(op.f('quizattempt_quiz_id_fkey'), 'quizattempt', 'quiz', ['quiz_id'], ['id']) - op.drop_table('chat') # ### end Alembic commands ### diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 871d206..bf6ce3e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -96,6 +96,7 @@ services: - "3000:3000" environment: NODE_ENV: development + NEXT_INTERNAL_BACKEND_BASE_URL: http://backend:8000 NEXT_PUBLIC_BACKEND_BASE_URL: http://localhost:8000 depends_on: - backend diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 96b1dc1..44c65a5 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -28,9 +28,14 @@ export default async function RootLayout({ children: React.ReactNode }) { return ( - + - +
{children}
diff --git a/frontend/src/components/theme-toggle.tsx b/frontend/src/components/theme-toggle.tsx index ee21035..51e0dc5 100644 --- a/frontend/src/components/theme-toggle.tsx +++ b/frontend/src/components/theme-toggle.tsx @@ -15,11 +15,11 @@ export function ThemeToggle() { useEffect(() => { if (mounted) { const savedTheme = localStorage.getItem('theme') - if (savedTheme) { + if (savedTheme && savedTheme !== theme) { setTheme(savedTheme) } } - }, [mounted, setTheme]) + }, [mounted, setTheme, theme]) useEffect(() => { if (mounted && theme) { diff --git a/frontend/src/runtime-config.ts b/frontend/src/runtime-config.ts index 30734d6..45fbc3e 100644 --- a/frontend/src/runtime-config.ts +++ b/frontend/src/runtime-config.ts @@ -4,8 +4,9 @@ import type { CreateClientConfig } from './client/client.gen' export const createClientConfig: CreateClientConfig =(config) => { const isServer = typeof window === 'undefined' - const baseURL = - process.env.NEXT_PUBLIC_BACKEND_BASE_URL ?? 'http://localhost:8000' + const baseURL = isServer + ? process.env.NEXT_INTERNAL_BACKEND_BASE_URL ?? 'http://backend:8000' + : process.env.NEXT_PUBLIC_BACKEND_BASE_URL ?? 'http://localhost:8000' return { ...config, @@ -26,4 +27,4 @@ export const createClientConfig: CreateClientConfig =(config) => { return undefined }, } -} \ No newline at end of file +} From 8a964e5c50463b9878fee218052a0ebeef45189d Mon Sep 17 00:00:00 2001 From: Al-Ameen Ogundiran Date: Sat, 4 Oct 2025 03:00:04 +0100 Subject: [PATCH 02/13] make authentication pages - sign up, log in, reset password, closer to UI design --- .../src/app/(routes)/(auth)/login/page.tsx | 210 +++++++++---- .../(routes)/(auth)/recover-password/page.tsx | 226 ++++++++------ .../src/app/(routes)/(auth)/signup/page.tsx | 282 +++++++++++------- .../src/components/ui/auth/AuthBackground.tsx | 16 - 4 files changed, 457 insertions(+), 277 deletions(-) delete mode 100644 frontend/src/components/ui/auth/AuthBackground.tsx diff --git a/frontend/src/app/(routes)/(auth)/login/page.tsx b/frontend/src/app/(routes)/(auth)/login/page.tsx index b8e96e9..97d05c1 100644 --- a/frontend/src/app/(routes)/(auth)/login/page.tsx +++ b/frontend/src/app/(routes)/(auth)/login/page.tsx @@ -2,20 +2,15 @@ import {useActionState, useState} from 'react' import Link from 'next/link' -import dynamic from 'next/dynamic' import {authenticate} from '@/actions/auth' import {IAuthState} from '@/types/auth' import {validateField} from '@/lib/form' -import PasswordInput from '@/components/ui/auth/PasswordInput' - -const AuthBackground = dynamic( - () => import('@/components/ui/auth/AuthBackground'), - {ssr: true}, -) export default function Login() { const [errors, setErrors] = useState>({}) + const [rememberMe, setRememberMe] = useState(false) + const [showPassword, setShowPassword] = useState(false) const [state, formAction, isPending] = useActionState< IAuthState | undefined, @@ -32,76 +27,159 @@ export default function Login() { } return ( -
- {/* Left side - Form */} -
-
-

Welcome Back

-
-
- - - {errors.email && ( -

{errors.email}

- )} -
+
+ {/* Header */} +
+
+
+ ! +
+ StudyBuddy +
+ + Sign up + +
-
- - {state && !state?.ok && ( -
{state?.error?.message}
- )} + {/* Main Content */} +
+
+ {/* Login Card */} +
+ {/* Title and Subtitle */} +
+

+ Welcome back +

+

+ Sign in to continue to your study space. +

-
- - Forgot your password? - -
+ {/* Form */} + + {/* Email Field */} +
+ +
+
+ + + +
+ +
+ {errors.email && ( +

{errors.email}

+ )} +
- - -
- Or + {/* Password Field */} +
+ +
+
+ + + +
+ + +
+ {errors.password && ( +

{errors.password}

+ )} + {state && !state?.ok && ( +
{state?.error?.message}
+ )} +
+ + {/* Remember Me and Forgot Password */} +
+
+ setRememberMe(e.target.checked)} + className='h-4 w-4 text-[#2563EB] focus:ring-blue-500 border-gray-300 rounded' + /> + +
+ + Forgot your password? + +
+ + {/* Login Button */} + +
-
-

- Don’t have an account?{' '} - + + {/* Footer Link */} +

+

+ Don't have an account?{' '} + Sign up

- - {/* Right side - Image */} -
) } diff --git a/frontend/src/app/(routes)/(auth)/recover-password/page.tsx b/frontend/src/app/(routes)/(auth)/recover-password/page.tsx index 67f3cb0..4195b47 100644 --- a/frontend/src/app/(routes)/(auth)/recover-password/page.tsx +++ b/frontend/src/app/(routes)/(auth)/recover-password/page.tsx @@ -4,7 +4,6 @@ import {useState} from 'react' import Link from 'next/link' import {emailPattern} from '@/lib/auth' -import AuthBackground from '@/components/ui/auth/AuthBackground' export default function RecoverPasswordPage() { const [error, _setError] = useState(null) @@ -70,38 +69,56 @@ export default function RecoverPasswordPage() { if (isSubmitted) { return ( -
-
-
-
- - - +
+ {/* Header */} +
+
+
+ !
-

- Check your email -

-

- We've sent a password recovery link to your email address. -

+ StudyBuddy
-
- - Back to Sign in - + + Sign In + +
+ + {/* Main Content */} +
+
+ {/* Success Card */} +
+
+ + + +
+

+ Check your email +

+

+ We've sent a password recovery link to your email address. +

+ + Back to Sign In + +
@@ -109,68 +126,103 @@ export default function RecoverPasswordPage() { } return ( -
- {/* Left side - Form */} -
-
-

Password Recovery

-

- A password recovery email will be sent to the registered account. -

- -
-
- - - {emailError && ( -

{emailError}

- )} +
+ {/* Header */} +
+
+
+ ! +
+ StudyBuddy +
+ + Sign In + +
+ + {/* Main Content */} +
+
+ {/* Recovery Card */} +
+ {/* Title and Subtitle */} +
+

+ Password Recovery +

+

+ A password recovery email will be sent to the registered account. +

- {error && ( -
- {error} + {/* Form */} + + {/* Email Field */} +
+ +
+
+ + + +
+ +
+ {emailError && ( +

{emailError}

+ )}
- )} - - - - -
- - Back to Sign in - + + {error && ( +
+ {error} +
+ )} + + {/* Submit Button */} + + +
+ + {/* Footer Link */} +
+

+ Remember your password?{' '} + + Sign In + +

- - {/* Right side - Background */} -
) } diff --git a/frontend/src/app/(routes)/(auth)/signup/page.tsx b/frontend/src/app/(routes)/(auth)/signup/page.tsx index 1f1dc00..0ed270b 100644 --- a/frontend/src/app/(routes)/(auth)/signup/page.tsx +++ b/frontend/src/app/(routes)/(auth)/signup/page.tsx @@ -8,7 +8,6 @@ import Link from 'next/link' import {IAuthState} from '@/types/auth' import {register} from '@/actions/auth' -import AuthBackground from '@/components/ui/auth/AuthBackground' import { Form, FormField, @@ -18,12 +17,13 @@ import { FormMessage, PasswordFormInput, } from '@/components/ui/form' -import {Input} from '@/components/ui/input' import {signUpSchema, SignUpSchema} from '@/types/form' export default function SignUpPage() { const router = useRouter() const [isPending, setIsPending] = useState(false) + const [showPassword, setShowPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) const [state, setState] = useState({ error: undefined, ok: false, @@ -60,129 +60,195 @@ export default function SignUpPage() { } return ( -
-
-
-

Get Started Now

- - {/* ✅ Form with action-based submit */} -
- - {/* Full name */} - ( - - Name - - - - - - )} - /> - - {/* Email */} - ( - - Email address - - - - - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - {/* Terms */} - ( - -
+
+ {/* Header */} +
+
+
+ ! +
+ StudyBuddy +
+ + Log In + +
+ + {/* Main Content */} +
+
+ {/* Sign Up Card */} +
+ {/* Title and Subtitle */} +
+

+ Create your account +

+

+ Join our community of learners. +

+
+ + {/* Form */} + + + {/* Name */} + ( + + + Name + + + + + + + )} + /> + + {/* Email */} + ( + + + Email address + field.onChange(e.target.checked)} - className='h-4 w-4 text-cyan-600 focus:ring-cyan-500 border-slate-300 rounded' + {...field} + type='email' + placeholder='Enter your email' + className='w-full px-4 py-3 bg-[#4A5568] border-0 rounded-md text-white placeholder-[#A0AEC0] focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors duration-200' /> - - I agree to the terms & policy + + + )} + /> + + {/* Password */} + ( + + + Password + + +
+ + +
+
+ +
+ )} + /> + + {/* Confirm Password */} + ( + + + Confirm Password -
- - + +
+ + +
+
+ + + )} + /> + + {/* Server error */} + {state && !state.ok && ( +
{state.error?.message}
)} - /> - - {/* Server error */} - {state && !state.ok && ( -
{state.error?.message}
- )} - {/* Submit Button */} - - - - -
- Or + {/* Submit Button */} + + +
-
-

- Have an account?{' '} + {/* Footer Link */} +

+

+ Already have an account?{' '} - Sign In + Log in

- - {/* Right side - Background */} -
) } diff --git a/frontend/src/components/ui/auth/AuthBackground.tsx b/frontend/src/components/ui/auth/AuthBackground.tsx deleted file mode 100644 index 61d08c9..0000000 --- a/frontend/src/components/ui/auth/AuthBackground.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export default function AuthBackground() { - return ( -
-
-
-
-
StudyCompanion
-
-
- {/* Decorative elements */} -
-
-
-
- ) -} From 7a19daf6c41d5a141916046730bc9b65bfd5fbaf Mon Sep 17 00:00:00 2001 From: Al-Ameen Ogundiran Date: Sat, 4 Oct 2025 03:15:37 +0100 Subject: [PATCH 03/13] update dashboard foundation layout to design theme look --- .../dashboard/courses/[id]/page.tsx | 14 +++--- .../(routes)/(dashboard)/dashboard/layout.tsx | 22 +++++---- frontend/src/components/app-sidebar.tsx | 45 ++++++++++--------- frontend/src/components/ui/tabs.tsx | 3 +- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/frontend/src/app/(routes)/(dashboard)/dashboard/courses/[id]/page.tsx b/frontend/src/app/(routes)/(dashboard)/dashboard/courses/[id]/page.tsx index 3b56ed6..a5bdf50 100644 --- a/frontend/src/app/(routes)/(dashboard)/dashboard/courses/[id]/page.tsx +++ b/frontend/src/app/(routes)/(dashboard)/dashboard/courses/[id]/page.tsx @@ -32,28 +32,28 @@ export default async function Page(props: {params: Promise<{id: string}>}) { <> - + - + - + - + - -
+ +
Podcast content will be displayed here
diff --git a/frontend/src/app/(routes)/(dashboard)/dashboard/layout.tsx b/frontend/src/app/(routes)/(dashboard)/dashboard/layout.tsx index fe86467..99b3765 100644 --- a/frontend/src/app/(routes)/(dashboard)/dashboard/layout.tsx +++ b/frontend/src/app/(routes)/(dashboard)/dashboard/layout.tsx @@ -33,14 +33,18 @@ import { getMe } from '@/actions/users' } return ( - - - -
- -
- {children} -
-
+
+ + + +
+ +
+
+ {children} +
+
+
+
) } diff --git a/frontend/src/components/app-sidebar.tsx b/frontend/src/components/app-sidebar.tsx index 364ffb6..ea230c6 100644 --- a/frontend/src/components/app-sidebar.tsx +++ b/frontend/src/components/app-sidebar.tsx @@ -29,23 +29,23 @@ import Link from 'next/link' import { ThemeToggle } from './theme-toggle' export function AppSidebar({ displayName = 'User' }: { displayName?: string }) { return ( - - + +
-
- +
+ S
- Study Companion + StudyBuddy
@@ -55,25 +55,25 @@ export function AppSidebar({ displayName = 'User' }: { displayName?: string }) { - + - Menu + Courses - + - - Dashboard + + Dashboard - @@ -82,30 +82,31 @@ export function AppSidebar({ displayName = 'User' }: { displayName?: string }) { - + - - {displayName} - + + + {displayName} + - + - Account + Account
diff --git a/frontend/src/components/ui/tabs.tsx b/frontend/src/components/ui/tabs.tsx index 8ef0ba5..3baabfe 100644 --- a/frontend/src/components/ui/tabs.tsx +++ b/frontend/src/components/ui/tabs.tsx @@ -67,8 +67,7 @@ function StyledTabList({name}: {name: string}) { return ( {name} From cd2eb3058e5181ad70b1a80c84fe7119ccb9b2f0 Mon Sep 17 00:00:00 2001 From: Al-Ameen Ogundiran Date: Sat, 4 Oct 2025 03:23:16 +0100 Subject: [PATCH 04/13] update base courses relate component to design theme look --- .../dashboard/courses/create/page.tsx | 4 +-- frontend/src/components/courses-list.tsx | 32 +++++++++---------- .../components/create-course/course-form.tsx | 16 +++++----- .../create-course/create-course.tsx | 16 +++++----- .../create-course/upload-documents.tsx | 8 +++-- frontend/src/components/ui/ErrorBox.tsx | 10 +++--- frontend/src/components/ui/course-card.tsx | 12 +++---- frontend/src/components/ui/file-card.tsx | 18 +++++------ frontend/src/components/upload-component.tsx | 19 +++++------ 9 files changed, 70 insertions(+), 65 deletions(-) diff --git a/frontend/src/app/(routes)/(dashboard)/dashboard/courses/create/page.tsx b/frontend/src/app/(routes)/(dashboard)/dashboard/courses/create/page.tsx index 0cee8e1..0dd48e5 100644 --- a/frontend/src/app/(routes)/(dashboard)/dashboard/courses/create/page.tsx +++ b/frontend/src/app/(routes)/(dashboard)/dashboard/courses/create/page.tsx @@ -4,8 +4,8 @@ import CreateCourseForm from '@/components/create-course/create-course' export default function CreateCoursePage() { return ( -
-
+
+
diff --git a/frontend/src/components/courses-list.tsx b/frontend/src/components/courses-list.tsx index 9a7bb24..999acd2 100644 --- a/frontend/src/components/courses-list.tsx +++ b/frontend/src/components/courses-list.tsx @@ -47,17 +47,17 @@ export default function CoursesList({ return ( -
+
-

My Courses

+

My Courses

- + setQuery(e.target.value)} - className='pl-9' + className='pl-9 bg-[#4A5568] border-[#4A5568] text-white placeholder-[#A0AEC0] focus:ring-[#2563EB]' />
@@ -65,23 +65,23 @@ export default function CoursesList({ value={datePreset} onValueChange={(val) => setDatePreset(val as DatePreset)} > - + - - All dates - Today - Last 7 days - Last 30 days - This year - On… + + All dates + Today + Last 7 days + Last 30 days + This year + On…
{datePreset === 'on' && ( -
+
- Add Course + Add Course -

+

No courses found.

diff --git a/frontend/src/components/create-course/course-form.tsx b/frontend/src/components/create-course/course-form.tsx index 00061e3..1563b61 100644 --- a/frontend/src/components/create-course/course-form.tsx +++ b/frontend/src/components/create-course/course-form.tsx @@ -23,23 +23,23 @@ export function CourseForm() { return (
- +
- +