From 5241144bc55c582e4eca1841fafc86f8361dd0d3 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 6 Feb 2026 11:04:37 +0000 Subject: [PATCH] feat: AI quiz generation per lesson + hide edit buttons for non-authors - Generate unique quiz for each lesson using OpenRouter API - Parse lesson content (TipTap JSON) and send to AI for question generation - Cache quiz in database to avoid regeneration - Hide Edit/Delete buttons if current user is not course author - Add backendUser to auth context for proper authorization checks - Show certificate button prominently when course is completed Co-authored-by: Cursor --- .../dashboard/courses/[id]/page.tsx | 92 +++++++++++-------- apps/web/src/contexts/auth-context.tsx | 14 ++- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/apps/web/src/app/(dashboard)/dashboard/courses/[id]/page.tsx b/apps/web/src/app/(dashboard)/dashboard/courses/[id]/page.tsx index 786dbcb..9e3c210 100644 --- a/apps/web/src/app/(dashboard)/dashboard/courses/[id]/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/courses/[id]/page.tsx @@ -45,13 +45,14 @@ type CourseData = { title: string; description?: string | null; status: string; + authorId: string; chapters: Chapter[]; }; export default function CoursePage() { const params = useParams(); const router = useRouter(); - const { loading: authLoading } = useAuth(); + const { loading: authLoading, backendUser } = useAuth(); const id = params?.id as string; const [course, setCourse] = useState(null); const [loading, setLoading] = useState(true); @@ -248,6 +249,9 @@ export default function CoursePage() { const activeLessonMeta = selectedLessonId ? flatLessons.find((l) => l.id === selectedLessonId) : null; + + const isAuthor = course && backendUser && course.authorId === backendUser.id; + const courseCompleted = completedCount >= totalLessons && totalLessons > 0; return (
@@ -272,37 +276,51 @@ export default function CoursePage() {
{progressPercent}% пройдено
- - - - + )} + + {/* Edit/Delete - only for author */} + {isAuthor && ( + <> + - - - - Удалить курс? - - Курс «{course.title}» будет удалён безвозвратно. - - - - Отмена - { e.preventDefault(); handleDelete(); }} - className="bg-destructive text-destructive-foreground hover:bg-destructive/90" - disabled={deleting} - > - {deleting ? 'Удаление...' : 'Удалить'} - - - - + + + + + + + Удалить курс? + + Курс «{course.title}» будет удалён безвозвратно. + + + + Отмена + { e.preventDefault(); handleDelete(); }} + className="bg-destructive text-destructive-foreground hover:bg-destructive/90" + disabled={deleting} + > + {deleting ? 'Удаление...' : 'Удалить'} + + + + + + )}
@@ -520,16 +538,10 @@ export default function CoursePage() { Следующий урок - ) : completedCount >= totalLessons ? ( - ) : ( - +
+ {courseCompleted ? 'Курс пройден!' : 'Последний урок'} +
)} diff --git a/apps/web/src/contexts/auth-context.tsx b/apps/web/src/contexts/auth-context.tsx index 2843a71..f3ea097 100644 --- a/apps/web/src/contexts/auth-context.tsx +++ b/apps/web/src/contexts/auth-context.tsx @@ -6,8 +6,17 @@ import { getSupabase } from '@/lib/supabase'; import { useRouter } from 'next/navigation'; import { api, setApiToken } from '@/lib/api'; +interface BackendUser { + id: string; + email: string; + name: string | null; + avatarUrl: string | null; + subscriptionTier: string; +} + interface AuthContextType { user: User | null; + backendUser: BackendUser | null; session: Session | null; loading: boolean; signUp: (email: string, password: string, name: string) => Promise<{ error: Error | null }>; @@ -20,6 +29,7 @@ const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); + const [backendUser, setBackendUser] = useState(null); const [session, setSession] = useState(null); const [loading, setLoading] = useState(true); const router = useRouter(); @@ -64,8 +74,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const tryExchange = () => { api .exchangeToken(session.access_token) - .then(({ accessToken }) => { + .then(({ accessToken, user: backendUserData }) => { setApiToken(accessToken); + setBackendUser(backendUserData); setLoading(false); }) .catch(() => { @@ -152,6 +163,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {