+ | #{u.rank} |
+ |
+
+ {u.name}
+ {isCurrentUser && (Bạn)}
|
-
-
+ |
+
Lv {u.level}
|
-
+ |
{(u.experience || 0).toLocaleString()}
- EXP
+ EXP
|
-
+ |
{badges.slice(0, 3).map((badge, idx) => (
diff --git a/frontend/src/components/user/dashboard/ProfileCard.tsx b/frontend/src/components/user/dashboard/ProfileCard.tsx
index 9c92ebdb..09b0078d 100644
--- a/frontend/src/components/user/dashboard/ProfileCard.tsx
+++ b/frontend/src/components/user/dashboard/ProfileCard.tsx
@@ -13,19 +13,19 @@ export const ProfileCard: React.FC<{ user: UserProfile }> = ({ user }) => {
return (
-
+
- {user.name}
- {user.email}
+ {user.name}
+ {user.email}
{/* Level Badge */}
diff --git a/frontend/src/components/user/dashboard/StatsGrid.tsx b/frontend/src/components/user/dashboard/StatsGrid.tsx
index 888ce8bb..9a844195 100644
--- a/frontend/src/components/user/dashboard/StatsGrid.tsx
+++ b/frontend/src/components/user/dashboard/StatsGrid.tsx
@@ -3,13 +3,6 @@ import { UserProfile } from './types';
interface StatDef { label: string; value: number | string; color: string; icon: React.ReactNode }
export const StatsGrid: React.FC<{ user: UserProfile }> = ({ user }) => {
- console.log('📊 StatsGrid received user:', {
- total_courses: user.total_courses,
- completed_courses: user.completed_courses,
- total_lessons: user.total_lessons,
- total_quizzes: user.total_quizzes,
- });
-
const totalCourses = user.total_courses || 0;
const completedCourses = user.completed_courses || 0;
const totalQuizzes = user.total_quizzes || 0;
@@ -28,15 +21,15 @@ export const StatsGrid: React.FC<{ user: UserProfile }> = ({ user }) => {
{ label: 'Tổng Số Người Dùng', value: totalUsers, color: 'bg-slate-100 text-slate-700', icon: ( ) },
];
return (
-
+
{stats.map(stat => (
-
-
+
+
{stat.icon}
-
- {stat.value}
- {stat.label}
+
+ {stat.value}
+ {stat.label}
))}
diff --git a/frontend/src/components/user/generation/GenerationCard.tsx b/frontend/src/components/user/generation/GenerationCard.tsx
index e5c9c8a9..8b7dfb25 100644
--- a/frontend/src/components/user/generation/GenerationCard.tsx
+++ b/frontend/src/components/user/generation/GenerationCard.tsx
@@ -11,21 +11,23 @@ export type GenerationItem = {
progress?: string;
title_ready?: boolean;
lessons_ready?: boolean;
+ tests_ready?: boolean;
final_ready?: boolean;
vectorized?: boolean;
lessons_count?: number;
lessons_planned?: number;
roadmap_count?: number;
+ tests_count?: number;
final_count?: number;
updated_at?: string;
};
function getGenerationStatus(item: GenerationItem) {
const steps = [
- { key: 'vectorized', label: 'Docs', done: item.vectorized, count: null },
- { key: 'title_ready', label: 'Plan', done: item.title_ready, count: item.roadmap_count },
- { key: 'lesson_planned', label: 'L.Plan', done: item.title_ready && item.lessons_planned, count: item.lessons_planned },
- { key: 'lessons_ready', label: 'Lessons', done: item.lessons_ready, count: item.lessons_count },
+ { key: 'vectorized', label: 'Vectorized', done: Boolean(item.vectorized), count: null },
+ { key: 'title_ready', label: 'Plan', done: Boolean(item.title_ready), count: item.roadmap_count },
+ { key: 'lessons_ready', label: 'Lessons', done: Boolean(item.lessons_ready), count: item.lessons_count },
+ { key: 'tests_ready', label: 'Tests', done: Boolean(item.tests_ready), count: item.tests_count },
];
const completedSteps = steps.filter(s => s.done).length;
@@ -104,15 +106,15 @@ export default function GenerationCard({
return (
-
+
{/* Header with Status Badge */}
-
+
{displayTitle}
@@ -198,17 +200,17 @@ export default function GenerationCard({
)}
{/* Todo List Style Steps */}
-
+
{status.steps.map((step) => (
-
-
+
- {step.done ? : }
+ {step.done ? : }
-
{step.label}
diff --git a/frontend/src/components/user/generation/GenerationDetailModal.tsx b/frontend/src/components/user/generation/GenerationDetailModal.tsx
index 6df10894..c0996d3e 100644
--- a/frontend/src/components/user/generation/GenerationDetailModal.tsx
+++ b/frontend/src/components/user/generation/GenerationDetailModal.tsx
@@ -15,30 +15,30 @@ function getDetailedStatus(item: GenerationItem) {
{
key: 'vectorized',
label: 'Phân tích tài liệu',
- done: item.vectorized,
+ done: Boolean(item.vectorized),
description: 'Vectorize và index tài liệu vào OpenSearch',
count: null
},
{
key: 'title_ready',
label: 'Tạo kế hoạch khóa học',
- done: item.title_ready,
+ done: Boolean(item.title_ready),
description: 'Sinh tiêu đề, mô tả và roadmap học tập',
count: item.roadmap_count
},
{
- key: 'lesson_planned',
+ key: 'lessons_ready',
label: 'Lập kế hoạch bài học',
- done: item.title_ready && item.lessons_planned,
- description: `Xác định số lượng bài học cần tạo${item.lessons_planned ? ` (${item.lessons_planned} bài)` : ''}`,
- count: item.lessons_planned
+ done: Boolean(item.lessons_ready),
+ description: `Xác định số lượng bài học cần tạo${item.lessons_count ? ` (${item.lessons_count} bài)` : ''}`,
+ count: item.lessons_count
},
{
- key: 'lessons_ready',
+ key: 'tests_ready',
label: 'Tạo nội dung bài học',
- done: item.lessons_ready,
- description: `Sinh nội dung chi tiết cho từng bài học${item.lessons_count ? ` (${item.lessons_count}/${item.lessons_planned || 0} bài hoàn thành)` : ''}`,
- count: item.lessons_count
+ done: Boolean(item.tests_ready),
+ description: `Sinh nội dung chi tiết cho từng bài học${item.lessons_count ? ` (${item.lessons_count} bài hoàn thành)` : ''}`,
+ count: null
},
];
diff --git a/frontend/src/components/user/quiz-generation/QuizGenerationCard.tsx b/frontend/src/components/user/quiz-generation/QuizGenerationCard.tsx
index 443a35ef..7dd38ff3 100644
--- a/frontend/src/components/user/quiz-generation/QuizGenerationCard.tsx
+++ b/frontend/src/components/user/quiz-generation/QuizGenerationCard.tsx
@@ -12,15 +12,21 @@ export interface QuizGenerationCardProps {
function getQuizStatus(item: QuizGenerationItem) {
const steps = [
{
- key: 'plan',
+ key: 'vectorized',
+ label: 'Vectorized',
+ done: Boolean(item.vectorized),
+ count: null
+ },
+ {
+ key: 'plan_ready',
label: 'Plan',
- done: item.plan_ready ?? item.title_ready ?? false,
+ done: Boolean(item.plan_ready),
count: item.ideas_count
},
{
- key: 'questions',
+ key: 'questions_ready',
label: 'Questions',
- done: item.questions_ready ?? item.cards_ready ?? false,
+ done: Boolean(item.questions_ready),
count: item.questions_count
},
];
@@ -31,7 +37,7 @@ function getQuizStatus(item: QuizGenerationItem) {
const progressPercent = item.progress_percentage ?? (completedSteps / steps.length) * 100;
let overallStatus: 'done' | 'processing' | 'error' = 'processing';
- if (isComplete) {
+ if (isComplete || item.final_ready === true) {
overallStatus = 'done';
} else if (item.status === 'error' || item.status === 'failed') {
overallStatus = 'error';
@@ -89,15 +95,15 @@ export default function QuizGenerationCard({ item, onClick, userNames }: {
return (
-
+
{/* Header with Status Badge */}
-
+
{displayTitle}
@@ -183,17 +189,17 @@ export default function QuizGenerationCard({ item, onClick, userNames }: {
)}
{/* Todo List Style Steps */}
-
+
{status.steps.map((step) => (
-
-
+
- {step.done ? : }
+ {step.done ? : }
-
{step.label}
diff --git a/frontend/src/components/user/quiz-generation/QuizGenerationDetailModal.tsx b/frontend/src/components/user/quiz-generation/QuizGenerationDetailModal.tsx
index 498ef683..07dfd30c 100644
--- a/frontend/src/components/user/quiz-generation/QuizGenerationDetailModal.tsx
+++ b/frontend/src/components/user/quiz-generation/QuizGenerationDetailModal.tsx
@@ -13,26 +13,26 @@ import { QuizGenerationItem } from "./QuizGenerationList";
function getDetailedStatus(item: QuizGenerationItem) {
const steps = [
{
- key: 'khoi_tao',
- label: 'Khởi tạo Quiz',
- done: item.plan_ready ?? item.title_ready ?? false,
- description: 'Tạo tiêu đề, tổng quan và lập kế hoạch quiz',
+ key: 'vectorized',
+ label: 'Vectorized',
+ done: Boolean(item.vectorized),
+ description: 'Vector hóa tài liệu để chuẩn bị dữ liệu cho AI',
+ count: null
+ },
+ {
+ key: 'plan_ready',
+ label: 'Lập kế hoạch Quiz',
+ done: Boolean(item.plan_ready),
+ description: `Tạo tiêu đề, tổng quan và lập kế hoạch quiz${item.ideas_count ? ` (${item.ideas_count} ý tưởng)` : ''}`,
count: item.ideas_count
},
{
- key: 'questions',
+ key: 'questions_ready',
label: 'Tạo câu hỏi',
- done: item.questions_ready ?? item.cards_ready ?? false,
+ done: Boolean(item.questions_ready),
description: `Sinh câu hỏi và đáp án chi tiết${item.questions_count ? ` (${item.questions_count} câu)` : ''}`,
count: item.questions_count
},
- {
- key: 'hoan_thien',
- label: 'Hoàn thiện và xuất bản',
- done: item.status === 'completed',
- description: 'Kiểm tra chất lượng và xuất bản quiz',
- count: null
- },
];
const completedSteps = steps.filter(s => s.done).length;
diff --git a/frontend/src/components/user/quiz-generation/QuizGenerationList.tsx b/frontend/src/components/user/quiz-generation/QuizGenerationList.tsx
index d12a194f..e46a0043 100644
--- a/frontend/src/components/user/quiz-generation/QuizGenerationList.tsx
+++ b/frontend/src/components/user/quiz-generation/QuizGenerationList.tsx
@@ -14,6 +14,7 @@ export interface QuizGenerationItem {
progress_percentage?: number;
current_step?: string;
current_step_detail?: string;
+ vectorized?: boolean;
plan_ready?: boolean;
questions_ready?: boolean;
title?: string;
diff --git a/frontend/src/components/user/quizzes/QuizCard.tsx b/frontend/src/components/user/quizzes/QuizCard.tsx
index 6fa2dc10..64603180 100644
--- a/frontend/src/components/user/quizzes/QuizCard.tsx
+++ b/frontend/src/components/user/quizzes/QuizCard.tsx
@@ -85,7 +85,7 @@ export function QuizCard({ quiz, onSelect, viewMode = 'grid' }: QuizCardProps) {
-
+
{quiz.title}
@@ -190,27 +190,37 @@ export function QuizCard({ quiz, onSelect, viewMode = 'grid' }: QuizCardProps) {
className="group hover:shadow-xl transition-all duration-200 cursor-pointer border-gray-200 hover:border-sky-300 flex flex-col h-full"
onClick={handleClick}
>
-
- |