@thanh01.pmt/interactive-quiz-kit 1.0.44 → 1.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react-ui.cjs +313 -37
- package/dist/react-ui.d.cts +4 -1
- package/dist/react-ui.d.ts +4 -1
- package/dist/react-ui.mjs +312 -37
- package/package.json +1 -1
package/dist/react-ui.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var default3 = require('process');
|
|
|
10
10
|
var url = require('url');
|
|
11
11
|
var JSZip = require('jszip');
|
|
12
12
|
var navigation = require('next/navigation');
|
|
13
|
+
var Link3 = require('next/link');
|
|
13
14
|
|
|
14
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
16
|
|
|
@@ -36,6 +37,7 @@ var ReactDOM4__namespace = /*#__PURE__*/_interopNamespace(ReactDOM4);
|
|
|
36
37
|
var default2__default = /*#__PURE__*/_interopDefault(default2);
|
|
37
38
|
var default3__default = /*#__PURE__*/_interopDefault(default3);
|
|
38
39
|
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
40
|
+
var Link3__default = /*#__PURE__*/_interopDefault(Link3);
|
|
39
41
|
|
|
40
42
|
var __create = Object.create;
|
|
41
43
|
var __defProp = Object.defineProperty;
|
|
@@ -167223,7 +167225,7 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
|
|
|
167223
167225
|
};
|
|
167224
167226
|
|
|
167225
167227
|
// src/react-ui/components/app/PersonalPracticeDashboard.tsx
|
|
167226
|
-
var PersonalPracticeDashboard = () => {
|
|
167228
|
+
var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
167227
167229
|
const router = navigation.useRouter();
|
|
167228
167230
|
const { toast: toast2 } = useToast();
|
|
167229
167231
|
const { t: t4, i18n } = useTranslation();
|
|
@@ -167240,7 +167242,10 @@ var PersonalPracticeDashboard = () => {
|
|
|
167240
167242
|
const [isChatbotOpen, setIsChatbotOpen] = React163.useState(false);
|
|
167241
167243
|
const [isUploadModalOpen, setIsUploadModalOpen] = React163.useState(false);
|
|
167242
167244
|
const [isEditMode, setIsEditMode] = React163.useState(false);
|
|
167243
|
-
const [dashboardLayout, setDashboardLayout] = React163.useState(
|
|
167245
|
+
const [dashboardLayout, setDashboardLayout] = React163.useState(
|
|
167246
|
+
DashboardLayoutService.getLayout()
|
|
167247
|
+
);
|
|
167248
|
+
const [isSettingsModalOpen, setIsSettingsModalOpen] = React163.useState(false);
|
|
167244
167249
|
const loadDashboardData = React163.useCallback(() => {
|
|
167245
167250
|
setIsLoading(true);
|
|
167246
167251
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
@@ -167248,9 +167253,17 @@ var PersonalPracticeDashboard = () => {
|
|
|
167248
167253
|
const practiceStats = PracticeHistoryService.getPracticeStats();
|
|
167249
167254
|
const name3 = UserConfigService.getFullName();
|
|
167250
167255
|
if (practiceStats && practiceHistory.length > 0) {
|
|
167251
|
-
const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
|
|
167256
|
+
const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
|
|
167257
|
+
practiceHistory,
|
|
167258
|
+
practiceStats
|
|
167259
|
+
);
|
|
167252
167260
|
if (newlyUnlocked.length > 0) {
|
|
167253
|
-
newlyUnlocked.forEach(
|
|
167261
|
+
newlyUnlocked.forEach(
|
|
167262
|
+
(ach) => toast2({
|
|
167263
|
+
title: "\u{1F3C6} Th\xE0nh t\xEDch M\u1EDBi!",
|
|
167264
|
+
description: `B\u1EA1n \u0111\xE3 m\u1EDF kh\xF3a: ${t4(ach.nameKey)}`
|
|
167265
|
+
})
|
|
167266
|
+
);
|
|
167254
167267
|
}
|
|
167255
167268
|
}
|
|
167256
167269
|
const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
|
|
@@ -167272,32 +167285,55 @@ var PersonalPracticeDashboard = () => {
|
|
|
167272
167285
|
const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
|
|
167273
167286
|
if (!apiKey || !stats) return;
|
|
167274
167287
|
try {
|
|
167275
|
-
const weakestTopic = stats.performanceByTopic.sort(
|
|
167276
|
-
|
|
167277
|
-
|
|
167278
|
-
|
|
167279
|
-
|
|
167280
|
-
|
|
167281
|
-
|
|
167288
|
+
const weakestTopic = stats.performanceByTopic.sort(
|
|
167289
|
+
(a4, b2) => a4.averageScore - b2.averageScore
|
|
167290
|
+
)[0]?.name;
|
|
167291
|
+
const aiQuote = await generateMotivationalQuote(
|
|
167292
|
+
{
|
|
167293
|
+
language: i18n.language === "vi" ? "Vietnamese" : "English",
|
|
167294
|
+
userName: UserConfigService.getFullName() || void 0,
|
|
167295
|
+
currentStreak: stats.currentStreak,
|
|
167296
|
+
weakestTopic
|
|
167297
|
+
},
|
|
167298
|
+
apiKey
|
|
167299
|
+
);
|
|
167282
167300
|
if (aiQuote) setMotivationalQuote(aiQuote);
|
|
167283
167301
|
} catch (e3) {
|
|
167284
167302
|
console.warn("Could not fetch AI-powered quote.", e3);
|
|
167285
167303
|
}
|
|
167286
167304
|
};
|
|
167287
167305
|
if (stats) fetchAIQuote();
|
|
167288
|
-
const intervalId = setInterval(
|
|
167306
|
+
const intervalId = setInterval(
|
|
167307
|
+
() => setMotivationalQuote(QuoteService.getRandomQuote()),
|
|
167308
|
+
10 * 60 * 1e3
|
|
167309
|
+
);
|
|
167289
167310
|
return () => clearInterval(intervalId);
|
|
167290
167311
|
}, [stats, i18n.language]);
|
|
167291
167312
|
const historySummaryForTable = React163.useMemo(() => {
|
|
167292
|
-
return history2.map((session) => ({
|
|
167313
|
+
return history2.map((session) => ({
|
|
167314
|
+
...session.summary,
|
|
167315
|
+
id: session.id,
|
|
167316
|
+
timestamp: session.timestamp,
|
|
167317
|
+
quizTitle: session.quizConfig.title
|
|
167318
|
+
}));
|
|
167293
167319
|
}, [history2]);
|
|
167294
|
-
const handleStartPractice = React163.useCallback(
|
|
167320
|
+
const handleStartPractice = React163.useCallback(
|
|
167321
|
+
() => router.push("/practice"),
|
|
167322
|
+
[router]
|
|
167323
|
+
);
|
|
167295
167324
|
const handleGenerateRoadmap = React163.useCallback(async () => {
|
|
167296
167325
|
setIsGeneratingRoadmap(true);
|
|
167297
|
-
toast2({
|
|
167326
|
+
toast2({
|
|
167327
|
+
title: "AI Tutor \u0111ang suy ngh\u0129...",
|
|
167328
|
+
description: "\u0110ang t\u1EA1o l\u1ED9 tr\xECnh h\u1ECDc t\u1EADp \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a cho b\u1EA1n."
|
|
167329
|
+
});
|
|
167298
167330
|
const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
|
|
167299
167331
|
if (!apiKey) {
|
|
167300
|
-
toast2({
|
|
167332
|
+
toast2({
|
|
167333
|
+
title: "API Key Missing",
|
|
167334
|
+
description: "Vui l\xF2ng c\u1EA5u h\xECnh Gemini API Key trong C\xE0i \u0111\u1EB7t.",
|
|
167335
|
+
variant: "destructive"
|
|
167336
|
+
});
|
|
167301
167337
|
setIsGeneratingRoadmap(false);
|
|
167302
167338
|
return;
|
|
167303
167339
|
}
|
|
@@ -167309,28 +167345,42 @@ var PersonalPracticeDashboard = () => {
|
|
|
167309
167345
|
topic: lo.topic
|
|
167310
167346
|
}));
|
|
167311
167347
|
if (allAvailableTopics.length === 0) {
|
|
167312
|
-
throw new Error(
|
|
167348
|
+
throw new Error(
|
|
167349
|
+
"No topic data found. Please import curriculum data in Settings first."
|
|
167350
|
+
);
|
|
167313
167351
|
}
|
|
167314
|
-
const result = await generateLearningAnalysis(
|
|
167315
|
-
|
|
167316
|
-
|
|
167317
|
-
|
|
167318
|
-
|
|
167319
|
-
|
|
167320
|
-
|
|
167321
|
-
|
|
167322
|
-
|
|
167323
|
-
|
|
167324
|
-
|
|
167352
|
+
const result = await generateLearningAnalysis(
|
|
167353
|
+
{
|
|
167354
|
+
language: i18n.language === "vi" ? "Vietnamese" : "English",
|
|
167355
|
+
userName: UserConfigService.getFullName() || void 0,
|
|
167356
|
+
weeklyGoal: UserConfigService.getWeeklyGoal() || void 0,
|
|
167357
|
+
unlockedAchievements: AchievementService.getAllAchievementsWithStatus().filter(
|
|
167358
|
+
(a4) => a4.unlockedAt
|
|
167359
|
+
),
|
|
167360
|
+
stats: PracticeHistoryService.getPracticeStats(),
|
|
167361
|
+
history: PracticeHistoryService.getPracticeHistory(),
|
|
167362
|
+
startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0],
|
|
167363
|
+
endDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
167364
|
+
allAvailableTopics
|
|
167365
|
+
},
|
|
167366
|
+
apiKey
|
|
167367
|
+
);
|
|
167325
167368
|
if (result.weeklyRoadmap) {
|
|
167326
167369
|
RoadmapService.saveRoadmap(result.weeklyRoadmap);
|
|
167327
|
-
toast2({
|
|
167370
|
+
toast2({
|
|
167371
|
+
title: "L\u1ED9 tr\xECnh M\u1EDBi \u0111\xE3 S\u1EB5n s\xE0ng!",
|
|
167372
|
+
description: "L\u1ED9 tr\xECnh h\u1ECDc t\u1EADp cho tu\u1EA7n n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt."
|
|
167373
|
+
});
|
|
167328
167374
|
} else {
|
|
167329
167375
|
throw new Error("AI \u0111\xE3 kh\xF4ng tr\u1EA3 v\u1EC1 m\u1ED9t l\u1ED9 tr\xECnh h\u1EE3p l\u1EC7.");
|
|
167330
167376
|
}
|
|
167331
167377
|
} catch (error) {
|
|
167332
167378
|
const msg = error instanceof Error ? error.message : "\u0110\xE3 x\u1EA3y ra l\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh.";
|
|
167333
|
-
toast2({
|
|
167379
|
+
toast2({
|
|
167380
|
+
title: "T\u1EA1o L\u1ED9 tr\xECnh Th\u1EA5t b\u1EA1i",
|
|
167381
|
+
description: msg,
|
|
167382
|
+
variant: "destructive"
|
|
167383
|
+
});
|
|
167334
167384
|
} finally {
|
|
167335
167385
|
setIsGeneratingRoadmap(false);
|
|
167336
167386
|
}
|
|
@@ -167354,14 +167404,26 @@ var PersonalPracticeDashboard = () => {
|
|
|
167354
167404
|
if (!over || active.id === over.id) return;
|
|
167355
167405
|
setDashboardLayout((prev) => {
|
|
167356
167406
|
const newLayout = JSON.parse(JSON.stringify(prev));
|
|
167357
|
-
const activeColKey = newLayout.column1.some(
|
|
167358
|
-
|
|
167407
|
+
const activeColKey = newLayout.column1.some(
|
|
167408
|
+
(c4) => c4.id === active.id
|
|
167409
|
+
) ? "column1" : "column2";
|
|
167410
|
+
const overColKey = newLayout.column1.some(
|
|
167411
|
+
(c4) => c4.id === over.id
|
|
167412
|
+
) ? "column1" : "column2";
|
|
167359
167413
|
const activeCol = newLayout[activeColKey];
|
|
167360
167414
|
const overCol = newLayout[overColKey];
|
|
167361
|
-
const activeIndex = activeCol.findIndex(
|
|
167362
|
-
|
|
167415
|
+
const activeIndex = activeCol.findIndex(
|
|
167416
|
+
(c4) => c4.id === active.id
|
|
167417
|
+
);
|
|
167418
|
+
const overIndex = overCol.findIndex(
|
|
167419
|
+
(c4) => c4.id === over.id
|
|
167420
|
+
);
|
|
167363
167421
|
if (activeColKey === overColKey) {
|
|
167364
|
-
newLayout[activeColKey] = arrayMove(
|
|
167422
|
+
newLayout[activeColKey] = arrayMove(
|
|
167423
|
+
activeCol,
|
|
167424
|
+
activeIndex,
|
|
167425
|
+
overIndex
|
|
167426
|
+
);
|
|
167365
167427
|
} else {
|
|
167366
167428
|
const [movedItem] = activeCol.splice(activeIndex, 1);
|
|
167367
167429
|
overCol.splice(overIndex, 0, movedItem);
|
|
@@ -167372,7 +167434,10 @@ var PersonalPracticeDashboard = () => {
|
|
|
167372
167434
|
const handleSaveLayout = () => {
|
|
167373
167435
|
DashboardLayoutService.saveLayout(dashboardLayout);
|
|
167374
167436
|
setIsEditMode(false);
|
|
167375
|
-
toast2({
|
|
167437
|
+
toast2({
|
|
167438
|
+
title: "Layout \u0111\xE3 \u0111\u01B0\u1EE3c l\u01B0u",
|
|
167439
|
+
description: "Dashboard c\u1EE7a b\u1EA1n \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt."
|
|
167440
|
+
});
|
|
167376
167441
|
};
|
|
167377
167442
|
const handleCancelEdit = () => {
|
|
167378
167443
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
@@ -167393,7 +167458,218 @@ var PersonalPracticeDashboard = () => {
|
|
|
167393
167458
|
const column2Items = dashboardLayout.column2.map((c4) => c4.id);
|
|
167394
167459
|
const quoteText = isMounted ? motivationalQuote ? `"${motivationalQuote.text}" \u2014 ${motivationalQuote.author}` : t4("dashboard.motivationalQuoteFallback") : '"The best way to predict the future is to create it." \u2014 Peter Drucker';
|
|
167395
167460
|
const welcomeMessage = isMounted ? userName ? t4("dashboard.welcomeMessage", { name: userName }) : t4("dashboard.welcomeMessageGuest") : userName ? `Welcome back, ${userName}!` : "Welcome to your Practice Dashboard";
|
|
167396
|
-
return /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167461
|
+
return /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167462
|
+
DndContext,
|
|
167463
|
+
{
|
|
167464
|
+
sensors,
|
|
167465
|
+
collisionDetection: closestCenter,
|
|
167466
|
+
onDragEnd: handleDragEnd
|
|
167467
|
+
},
|
|
167468
|
+
/* @__PURE__ */ React163__namespace.default.createElement("div", { className: "space-y-8" }, /* @__PURE__ */ React163__namespace.default.createElement(Card, { className: "text-center shadow-xl bg-gradient-to-br from-primary to-primary/80 text-primary-foreground border-none" }, /* @__PURE__ */ React163__namespace.default.createElement(CardHeader, { className: "p-8 relative" }, /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "absolute top-4 right-4 flex items-center gap-2" }, !isEditMode ? /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167469
|
+
Button,
|
|
167470
|
+
{
|
|
167471
|
+
variant: "secondary",
|
|
167472
|
+
size: "sm",
|
|
167473
|
+
onClick: () => setIsEditMode(true)
|
|
167474
|
+
},
|
|
167475
|
+
/* @__PURE__ */ React163__namespace.default.createElement(LayoutDashboard, { className: "mr-2 h-4 w-4" }),
|
|
167476
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167477
|
+
ClientTranslation,
|
|
167478
|
+
{
|
|
167479
|
+
tKey: "common.customizeLayout",
|
|
167480
|
+
fallback: "Customize Layout"
|
|
167481
|
+
}
|
|
167482
|
+
)
|
|
167483
|
+
) : /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167484
|
+
Button,
|
|
167485
|
+
{
|
|
167486
|
+
variant: "ghost",
|
|
167487
|
+
size: "sm",
|
|
167488
|
+
onClick: handleCancelEdit,
|
|
167489
|
+
className: "bg-background/20 hover:bg-background/40 text-white"
|
|
167490
|
+
},
|
|
167491
|
+
/* @__PURE__ */ React163__namespace.default.createElement(X, { className: "mr-2 h-4 w-4" }),
|
|
167492
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167493
|
+
ClientTranslation,
|
|
167494
|
+
{
|
|
167495
|
+
tKey: "common.cancel",
|
|
167496
|
+
fallback: "Cancel"
|
|
167497
|
+
}
|
|
167498
|
+
)
|
|
167499
|
+
), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167500
|
+
Button,
|
|
167501
|
+
{
|
|
167502
|
+
variant: "secondary",
|
|
167503
|
+
size: "sm",
|
|
167504
|
+
onClick: handleSaveLayout
|
|
167505
|
+
},
|
|
167506
|
+
/* @__PURE__ */ React163__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }),
|
|
167507
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167508
|
+
ClientTranslation,
|
|
167509
|
+
{
|
|
167510
|
+
tKey: "common.saveLayout",
|
|
167511
|
+
fallback: "Save Layout"
|
|
167512
|
+
}
|
|
167513
|
+
)
|
|
167514
|
+
)), settingsPath ? /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167515
|
+
Button,
|
|
167516
|
+
{
|
|
167517
|
+
asChild: true,
|
|
167518
|
+
variant: "ghost",
|
|
167519
|
+
size: "icon",
|
|
167520
|
+
className: "text-white hover:bg-background/20",
|
|
167521
|
+
title: "Settings"
|
|
167522
|
+
},
|
|
167523
|
+
/* @__PURE__ */ React163__namespace.default.createElement(Link3__default.default, { href: settingsPath }, /* @__PURE__ */ React163__namespace.default.createElement(Settings, { className: "h-5 w-5" }))
|
|
167524
|
+
) : /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167525
|
+
Button,
|
|
167526
|
+
{
|
|
167527
|
+
variant: "ghost",
|
|
167528
|
+
size: "icon",
|
|
167529
|
+
className: "text-white hover:bg-background/20",
|
|
167530
|
+
title: "Settings",
|
|
167531
|
+
onClick: () => setIsSettingsModalOpen(true)
|
|
167532
|
+
},
|
|
167533
|
+
/* @__PURE__ */ React163__namespace.default.createElement(Settings, { className: "h-5 w-5" })
|
|
167534
|
+
)), /* @__PURE__ */ React163__namespace.default.createElement(CardTitle, { className: "text-3xl md:text-4xl font-bold tracking-tight" }, welcomeMessage), /* @__PURE__ */ React163__namespace.default.createElement(CardDescription, { className: "text-lg md:text-xl text-primary-foreground/80 max-w-3xl mx-auto mt-2 h-14 flex items-center justify-center" }, quoteText)), /* @__PURE__ */ React163__namespace.default.createElement(CardContent, { className: "pb-8 flex flex-wrap justify-center items-center gap-4" }, /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167535
|
+
Button,
|
|
167536
|
+
{
|
|
167537
|
+
size: "lg",
|
|
167538
|
+
onClick: () => setIsUploadModalOpen(true),
|
|
167539
|
+
className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6"
|
|
167540
|
+
},
|
|
167541
|
+
/* @__PURE__ */ React163__namespace.default.createElement(FileUp, { className: "mr-2 h-5 w-5" }),
|
|
167542
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167543
|
+
ClientTranslation,
|
|
167544
|
+
{
|
|
167545
|
+
tKey: "dashboard.actions.createFromDoc",
|
|
167546
|
+
fallback: "Create Quiz from Document"
|
|
167547
|
+
}
|
|
167548
|
+
)
|
|
167549
|
+
), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167550
|
+
Button,
|
|
167551
|
+
{
|
|
167552
|
+
size: "lg",
|
|
167553
|
+
onClick: handleStartPractice,
|
|
167554
|
+
className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6"
|
|
167555
|
+
},
|
|
167556
|
+
/* @__PURE__ */ React163__namespace.default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }),
|
|
167557
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167558
|
+
ClientTranslation,
|
|
167559
|
+
{
|
|
167560
|
+
tKey: "dashboard.actions.freestyleTopic",
|
|
167561
|
+
fallback: "Freestyle Topic"
|
|
167562
|
+
}
|
|
167563
|
+
)
|
|
167564
|
+
), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167565
|
+
Button,
|
|
167566
|
+
{
|
|
167567
|
+
size: "lg",
|
|
167568
|
+
variant: "secondary",
|
|
167569
|
+
className: "px-8 py-6 text-base font-semibold",
|
|
167570
|
+
disabled: isGeneratingRoadmap
|
|
167571
|
+
},
|
|
167572
|
+
/* @__PURE__ */ React163__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }),
|
|
167573
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167574
|
+
ClientTranslation,
|
|
167575
|
+
{
|
|
167576
|
+
tKey: "dashboard.actions.suggestRoadmap",
|
|
167577
|
+
fallback: "Suggest Next Week's Roadmap"
|
|
167578
|
+
}
|
|
167579
|
+
)
|
|
167580
|
+
)), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTitle2, null, t4("dialogs.confirmRoadmap.title")), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogDescription2, null, t4(
|
|
167581
|
+
"dialogs.confirmRoadmap.description"
|
|
167582
|
+
))), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167583
|
+
AlertDialogAction2,
|
|
167584
|
+
{
|
|
167585
|
+
onClick: handleGenerateRoadmap
|
|
167586
|
+
},
|
|
167587
|
+
t4("dialogs.confirmRoadmap.action")
|
|
167588
|
+
)))), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167589
|
+
Button,
|
|
167590
|
+
{
|
|
167591
|
+
size: "lg",
|
|
167592
|
+
onClick: () => setIsAnalysisDialogOpen(true),
|
|
167593
|
+
className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6"
|
|
167594
|
+
},
|
|
167595
|
+
/* @__PURE__ */ React163__namespace.default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }),
|
|
167596
|
+
/* @__PURE__ */ React163__namespace.default.createElement(
|
|
167597
|
+
ClientTranslation,
|
|
167598
|
+
{
|
|
167599
|
+
tKey: "dashboard.actions.deepAnalysis",
|
|
167600
|
+
fallback: "Deep Analysis"
|
|
167601
|
+
}
|
|
167602
|
+
)
|
|
167603
|
+
))), /* @__PURE__ */ React163__namespace.default.createElement(PerformanceSnapshot, { stats, isLoading }), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-8 items-start" }, /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167604
|
+
SortableContext,
|
|
167605
|
+
{
|
|
167606
|
+
items: column1Items,
|
|
167607
|
+
strategy: verticalListSortingStrategy
|
|
167608
|
+
},
|
|
167609
|
+
/* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167610
|
+
DraggableDashboardCard,
|
|
167611
|
+
{
|
|
167612
|
+
key: cardConfig.id,
|
|
167613
|
+
id: cardConfig.id,
|
|
167614
|
+
visible: cardConfig.visible,
|
|
167615
|
+
isEditMode,
|
|
167616
|
+
onToggleVisibility: handleToggleVisibility
|
|
167617
|
+
},
|
|
167618
|
+
cardComponents[cardConfig.id]
|
|
167619
|
+
)))
|
|
167620
|
+
), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167621
|
+
SortableContext,
|
|
167622
|
+
{
|
|
167623
|
+
items: column2Items,
|
|
167624
|
+
strategy: verticalListSortingStrategy
|
|
167625
|
+
},
|
|
167626
|
+
/* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167627
|
+
DraggableDashboardCard,
|
|
167628
|
+
{
|
|
167629
|
+
key: cardConfig.id,
|
|
167630
|
+
id: cardConfig.id,
|
|
167631
|
+
visible: cardConfig.visible,
|
|
167632
|
+
isEditMode,
|
|
167633
|
+
onToggleVisibility: handleToggleVisibility
|
|
167634
|
+
},
|
|
167635
|
+
cardComponents[cardConfig.id]
|
|
167636
|
+
)))
|
|
167637
|
+
)), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167638
|
+
AnalysisDialog,
|
|
167639
|
+
{
|
|
167640
|
+
isOpen: isAnalysisDialogOpen,
|
|
167641
|
+
onClose: () => setIsAnalysisDialogOpen(false)
|
|
167642
|
+
}
|
|
167643
|
+
), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167644
|
+
Button,
|
|
167645
|
+
{
|
|
167646
|
+
size: "icon",
|
|
167647
|
+
className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90",
|
|
167648
|
+
onClick: () => setIsChatbotOpen(true),
|
|
167649
|
+
"aria-label": "Open AI Tutor Chat"
|
|
167650
|
+
},
|
|
167651
|
+
/* @__PURE__ */ React163__namespace.default.createElement(Bot, { className: "h-7 w-7" })
|
|
167652
|
+
)), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167653
|
+
ChatbotDialog,
|
|
167654
|
+
{
|
|
167655
|
+
isOpen: isChatbotOpen,
|
|
167656
|
+
onClose: () => setIsChatbotOpen(false)
|
|
167657
|
+
}
|
|
167658
|
+
), /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167659
|
+
UploadResourceModal,
|
|
167660
|
+
{
|
|
167661
|
+
isOpen: isUploadModalOpen,
|
|
167662
|
+
onClose: () => setIsUploadModalOpen(false)
|
|
167663
|
+
}
|
|
167664
|
+
)),
|
|
167665
|
+
!settingsPath && /* @__PURE__ */ React163__namespace.default.createElement(
|
|
167666
|
+
SettingsModal,
|
|
167667
|
+
{
|
|
167668
|
+
isOpen: isSettingsModalOpen,
|
|
167669
|
+
onClose: () => setIsSettingsModalOpen(false)
|
|
167670
|
+
}
|
|
167671
|
+
)
|
|
167672
|
+
);
|
|
167397
167673
|
};
|
|
167398
167674
|
|
|
167399
167675
|
// src/react-ui/components/practice/PracticeModeController.tsx
|
package/dist/react-ui.d.cts
CHANGED
|
@@ -70,7 +70,10 @@ interface QuestionRendererProps {
|
|
|
70
70
|
type ProgrammingQuestionUIRef = BlocklyProgrammingQuestionUIRef | ScratchProgrammingQuestionUIRef;
|
|
71
71
|
declare const QuestionRenderer: React__default.ForwardRefExoticComponent<QuestionRendererProps & React__default.RefAttributes<ProgrammingQuestionUIRef>>;
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
interface PersonalPracticeDashboardProps {
|
|
74
|
+
settingsPath?: string;
|
|
75
|
+
}
|
|
76
|
+
declare const PersonalPracticeDashboard: React__default.FC<PersonalPracticeDashboardProps>;
|
|
74
77
|
|
|
75
78
|
type SettingsTab = 'personal' | 'topics' | 'imageContexts' | 'layout' | 'apiKeys';
|
|
76
79
|
interface SettingsModalProps {
|
package/dist/react-ui.d.ts
CHANGED
|
@@ -70,7 +70,10 @@ interface QuestionRendererProps {
|
|
|
70
70
|
type ProgrammingQuestionUIRef = BlocklyProgrammingQuestionUIRef | ScratchProgrammingQuestionUIRef;
|
|
71
71
|
declare const QuestionRenderer: React__default.ForwardRefExoticComponent<QuestionRendererProps & React__default.RefAttributes<ProgrammingQuestionUIRef>>;
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
interface PersonalPracticeDashboardProps {
|
|
74
|
+
settingsPath?: string;
|
|
75
|
+
}
|
|
76
|
+
declare const PersonalPracticeDashboard: React__default.FC<PersonalPracticeDashboardProps>;
|
|
74
77
|
|
|
75
78
|
type SettingsTab = 'personal' | 'topics' | 'imageContexts' | 'layout' | 'apiKeys';
|
|
76
79
|
interface SettingsModalProps {
|
package/dist/react-ui.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import default3 from 'process';
|
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import JSZip from 'jszip';
|
|
12
12
|
import { useRouter } from 'next/navigation';
|
|
13
|
+
import Link3 from 'next/link';
|
|
13
14
|
|
|
14
15
|
var __create = Object.create;
|
|
15
16
|
var __defProp = Object.defineProperty;
|
|
@@ -167197,7 +167198,7 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
|
|
|
167197
167198
|
};
|
|
167198
167199
|
|
|
167199
167200
|
// src/react-ui/components/app/PersonalPracticeDashboard.tsx
|
|
167200
|
-
var PersonalPracticeDashboard = () => {
|
|
167201
|
+
var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
167201
167202
|
const router = useRouter();
|
|
167202
167203
|
const { toast: toast2 } = useToast();
|
|
167203
167204
|
const { t: t4, i18n } = useTranslation();
|
|
@@ -167214,7 +167215,10 @@ var PersonalPracticeDashboard = () => {
|
|
|
167214
167215
|
const [isChatbotOpen, setIsChatbotOpen] = useState(false);
|
|
167215
167216
|
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
|
167216
167217
|
const [isEditMode, setIsEditMode] = useState(false);
|
|
167217
|
-
const [dashboardLayout, setDashboardLayout] = useState(
|
|
167218
|
+
const [dashboardLayout, setDashboardLayout] = useState(
|
|
167219
|
+
DashboardLayoutService.getLayout()
|
|
167220
|
+
);
|
|
167221
|
+
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
|
167218
167222
|
const loadDashboardData = useCallback(() => {
|
|
167219
167223
|
setIsLoading(true);
|
|
167220
167224
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
@@ -167222,9 +167226,17 @@ var PersonalPracticeDashboard = () => {
|
|
|
167222
167226
|
const practiceStats = PracticeHistoryService.getPracticeStats();
|
|
167223
167227
|
const name3 = UserConfigService.getFullName();
|
|
167224
167228
|
if (practiceStats && practiceHistory.length > 0) {
|
|
167225
|
-
const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
|
|
167229
|
+
const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
|
|
167230
|
+
practiceHistory,
|
|
167231
|
+
practiceStats
|
|
167232
|
+
);
|
|
167226
167233
|
if (newlyUnlocked.length > 0) {
|
|
167227
|
-
newlyUnlocked.forEach(
|
|
167234
|
+
newlyUnlocked.forEach(
|
|
167235
|
+
(ach) => toast2({
|
|
167236
|
+
title: "\u{1F3C6} Th\xE0nh t\xEDch M\u1EDBi!",
|
|
167237
|
+
description: `B\u1EA1n \u0111\xE3 m\u1EDF kh\xF3a: ${t4(ach.nameKey)}`
|
|
167238
|
+
})
|
|
167239
|
+
);
|
|
167228
167240
|
}
|
|
167229
167241
|
}
|
|
167230
167242
|
const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
|
|
@@ -167246,32 +167258,55 @@ var PersonalPracticeDashboard = () => {
|
|
|
167246
167258
|
const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
|
|
167247
167259
|
if (!apiKey || !stats) return;
|
|
167248
167260
|
try {
|
|
167249
|
-
const weakestTopic = stats.performanceByTopic.sort(
|
|
167250
|
-
|
|
167251
|
-
|
|
167252
|
-
|
|
167253
|
-
|
|
167254
|
-
|
|
167255
|
-
|
|
167261
|
+
const weakestTopic = stats.performanceByTopic.sort(
|
|
167262
|
+
(a4, b2) => a4.averageScore - b2.averageScore
|
|
167263
|
+
)[0]?.name;
|
|
167264
|
+
const aiQuote = await generateMotivationalQuote(
|
|
167265
|
+
{
|
|
167266
|
+
language: i18n.language === "vi" ? "Vietnamese" : "English",
|
|
167267
|
+
userName: UserConfigService.getFullName() || void 0,
|
|
167268
|
+
currentStreak: stats.currentStreak,
|
|
167269
|
+
weakestTopic
|
|
167270
|
+
},
|
|
167271
|
+
apiKey
|
|
167272
|
+
);
|
|
167256
167273
|
if (aiQuote) setMotivationalQuote(aiQuote);
|
|
167257
167274
|
} catch (e3) {
|
|
167258
167275
|
console.warn("Could not fetch AI-powered quote.", e3);
|
|
167259
167276
|
}
|
|
167260
167277
|
};
|
|
167261
167278
|
if (stats) fetchAIQuote();
|
|
167262
|
-
const intervalId = setInterval(
|
|
167279
|
+
const intervalId = setInterval(
|
|
167280
|
+
() => setMotivationalQuote(QuoteService.getRandomQuote()),
|
|
167281
|
+
10 * 60 * 1e3
|
|
167282
|
+
);
|
|
167263
167283
|
return () => clearInterval(intervalId);
|
|
167264
167284
|
}, [stats, i18n.language]);
|
|
167265
167285
|
const historySummaryForTable = useMemo(() => {
|
|
167266
|
-
return history2.map((session) => ({
|
|
167286
|
+
return history2.map((session) => ({
|
|
167287
|
+
...session.summary,
|
|
167288
|
+
id: session.id,
|
|
167289
|
+
timestamp: session.timestamp,
|
|
167290
|
+
quizTitle: session.quizConfig.title
|
|
167291
|
+
}));
|
|
167267
167292
|
}, [history2]);
|
|
167268
|
-
const handleStartPractice = useCallback(
|
|
167293
|
+
const handleStartPractice = useCallback(
|
|
167294
|
+
() => router.push("/practice"),
|
|
167295
|
+
[router]
|
|
167296
|
+
);
|
|
167269
167297
|
const handleGenerateRoadmap = useCallback(async () => {
|
|
167270
167298
|
setIsGeneratingRoadmap(true);
|
|
167271
|
-
toast2({
|
|
167299
|
+
toast2({
|
|
167300
|
+
title: "AI Tutor \u0111ang suy ngh\u0129...",
|
|
167301
|
+
description: "\u0110ang t\u1EA1o l\u1ED9 tr\xECnh h\u1ECDc t\u1EADp \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a cho b\u1EA1n."
|
|
167302
|
+
});
|
|
167272
167303
|
const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
|
|
167273
167304
|
if (!apiKey) {
|
|
167274
|
-
toast2({
|
|
167305
|
+
toast2({
|
|
167306
|
+
title: "API Key Missing",
|
|
167307
|
+
description: "Vui l\xF2ng c\u1EA5u h\xECnh Gemini API Key trong C\xE0i \u0111\u1EB7t.",
|
|
167308
|
+
variant: "destructive"
|
|
167309
|
+
});
|
|
167275
167310
|
setIsGeneratingRoadmap(false);
|
|
167276
167311
|
return;
|
|
167277
167312
|
}
|
|
@@ -167283,28 +167318,42 @@ var PersonalPracticeDashboard = () => {
|
|
|
167283
167318
|
topic: lo.topic
|
|
167284
167319
|
}));
|
|
167285
167320
|
if (allAvailableTopics.length === 0) {
|
|
167286
|
-
throw new Error(
|
|
167321
|
+
throw new Error(
|
|
167322
|
+
"No topic data found. Please import curriculum data in Settings first."
|
|
167323
|
+
);
|
|
167287
167324
|
}
|
|
167288
|
-
const result = await generateLearningAnalysis(
|
|
167289
|
-
|
|
167290
|
-
|
|
167291
|
-
|
|
167292
|
-
|
|
167293
|
-
|
|
167294
|
-
|
|
167295
|
-
|
|
167296
|
-
|
|
167297
|
-
|
|
167298
|
-
|
|
167325
|
+
const result = await generateLearningAnalysis(
|
|
167326
|
+
{
|
|
167327
|
+
language: i18n.language === "vi" ? "Vietnamese" : "English",
|
|
167328
|
+
userName: UserConfigService.getFullName() || void 0,
|
|
167329
|
+
weeklyGoal: UserConfigService.getWeeklyGoal() || void 0,
|
|
167330
|
+
unlockedAchievements: AchievementService.getAllAchievementsWithStatus().filter(
|
|
167331
|
+
(a4) => a4.unlockedAt
|
|
167332
|
+
),
|
|
167333
|
+
stats: PracticeHistoryService.getPracticeStats(),
|
|
167334
|
+
history: PracticeHistoryService.getPracticeHistory(),
|
|
167335
|
+
startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0],
|
|
167336
|
+
endDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
167337
|
+
allAvailableTopics
|
|
167338
|
+
},
|
|
167339
|
+
apiKey
|
|
167340
|
+
);
|
|
167299
167341
|
if (result.weeklyRoadmap) {
|
|
167300
167342
|
RoadmapService.saveRoadmap(result.weeklyRoadmap);
|
|
167301
|
-
toast2({
|
|
167343
|
+
toast2({
|
|
167344
|
+
title: "L\u1ED9 tr\xECnh M\u1EDBi \u0111\xE3 S\u1EB5n s\xE0ng!",
|
|
167345
|
+
description: "L\u1ED9 tr\xECnh h\u1ECDc t\u1EADp cho tu\u1EA7n n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt."
|
|
167346
|
+
});
|
|
167302
167347
|
} else {
|
|
167303
167348
|
throw new Error("AI \u0111\xE3 kh\xF4ng tr\u1EA3 v\u1EC1 m\u1ED9t l\u1ED9 tr\xECnh h\u1EE3p l\u1EC7.");
|
|
167304
167349
|
}
|
|
167305
167350
|
} catch (error) {
|
|
167306
167351
|
const msg = error instanceof Error ? error.message : "\u0110\xE3 x\u1EA3y ra l\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh.";
|
|
167307
|
-
toast2({
|
|
167352
|
+
toast2({
|
|
167353
|
+
title: "T\u1EA1o L\u1ED9 tr\xECnh Th\u1EA5t b\u1EA1i",
|
|
167354
|
+
description: msg,
|
|
167355
|
+
variant: "destructive"
|
|
167356
|
+
});
|
|
167308
167357
|
} finally {
|
|
167309
167358
|
setIsGeneratingRoadmap(false);
|
|
167310
167359
|
}
|
|
@@ -167328,14 +167377,26 @@ var PersonalPracticeDashboard = () => {
|
|
|
167328
167377
|
if (!over || active.id === over.id) return;
|
|
167329
167378
|
setDashboardLayout((prev) => {
|
|
167330
167379
|
const newLayout = JSON.parse(JSON.stringify(prev));
|
|
167331
|
-
const activeColKey = newLayout.column1.some(
|
|
167332
|
-
|
|
167380
|
+
const activeColKey = newLayout.column1.some(
|
|
167381
|
+
(c4) => c4.id === active.id
|
|
167382
|
+
) ? "column1" : "column2";
|
|
167383
|
+
const overColKey = newLayout.column1.some(
|
|
167384
|
+
(c4) => c4.id === over.id
|
|
167385
|
+
) ? "column1" : "column2";
|
|
167333
167386
|
const activeCol = newLayout[activeColKey];
|
|
167334
167387
|
const overCol = newLayout[overColKey];
|
|
167335
|
-
const activeIndex = activeCol.findIndex(
|
|
167336
|
-
|
|
167388
|
+
const activeIndex = activeCol.findIndex(
|
|
167389
|
+
(c4) => c4.id === active.id
|
|
167390
|
+
);
|
|
167391
|
+
const overIndex = overCol.findIndex(
|
|
167392
|
+
(c4) => c4.id === over.id
|
|
167393
|
+
);
|
|
167337
167394
|
if (activeColKey === overColKey) {
|
|
167338
|
-
newLayout[activeColKey] = arrayMove(
|
|
167395
|
+
newLayout[activeColKey] = arrayMove(
|
|
167396
|
+
activeCol,
|
|
167397
|
+
activeIndex,
|
|
167398
|
+
overIndex
|
|
167399
|
+
);
|
|
167339
167400
|
} else {
|
|
167340
167401
|
const [movedItem] = activeCol.splice(activeIndex, 1);
|
|
167341
167402
|
overCol.splice(overIndex, 0, movedItem);
|
|
@@ -167346,7 +167407,10 @@ var PersonalPracticeDashboard = () => {
|
|
|
167346
167407
|
const handleSaveLayout = () => {
|
|
167347
167408
|
DashboardLayoutService.saveLayout(dashboardLayout);
|
|
167348
167409
|
setIsEditMode(false);
|
|
167349
|
-
toast2({
|
|
167410
|
+
toast2({
|
|
167411
|
+
title: "Layout \u0111\xE3 \u0111\u01B0\u1EE3c l\u01B0u",
|
|
167412
|
+
description: "Dashboard c\u1EE7a b\u1EA1n \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt."
|
|
167413
|
+
});
|
|
167350
167414
|
};
|
|
167351
167415
|
const handleCancelEdit = () => {
|
|
167352
167416
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
@@ -167367,7 +167431,218 @@ var PersonalPracticeDashboard = () => {
|
|
|
167367
167431
|
const column2Items = dashboardLayout.column2.map((c4) => c4.id);
|
|
167368
167432
|
const quoteText = isMounted ? motivationalQuote ? `"${motivationalQuote.text}" \u2014 ${motivationalQuote.author}` : t4("dashboard.motivationalQuoteFallback") : '"The best way to predict the future is to create it." \u2014 Peter Drucker';
|
|
167369
167433
|
const welcomeMessage = isMounted ? userName ? t4("dashboard.welcomeMessage", { name: userName }) : t4("dashboard.welcomeMessageGuest") : userName ? `Welcome back, ${userName}!` : "Welcome to your Practice Dashboard";
|
|
167370
|
-
return /* @__PURE__ */ React163__default.createElement(
|
|
167434
|
+
return /* @__PURE__ */ React163__default.createElement(
|
|
167435
|
+
DndContext,
|
|
167436
|
+
{
|
|
167437
|
+
sensors,
|
|
167438
|
+
collisionDetection: closestCenter,
|
|
167439
|
+
onDragEnd: handleDragEnd
|
|
167440
|
+
},
|
|
167441
|
+
/* @__PURE__ */ React163__default.createElement("div", { className: "space-y-8" }, /* @__PURE__ */ React163__default.createElement(Card, { className: "text-center shadow-xl bg-gradient-to-br from-primary to-primary/80 text-primary-foreground border-none" }, /* @__PURE__ */ React163__default.createElement(CardHeader, { className: "p-8 relative" }, /* @__PURE__ */ React163__default.createElement("div", { className: "absolute top-4 right-4 flex items-center gap-2" }, !isEditMode ? /* @__PURE__ */ React163__default.createElement(
|
|
167442
|
+
Button,
|
|
167443
|
+
{
|
|
167444
|
+
variant: "secondary",
|
|
167445
|
+
size: "sm",
|
|
167446
|
+
onClick: () => setIsEditMode(true)
|
|
167447
|
+
},
|
|
167448
|
+
/* @__PURE__ */ React163__default.createElement(LayoutDashboard, { className: "mr-2 h-4 w-4" }),
|
|
167449
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167450
|
+
ClientTranslation,
|
|
167451
|
+
{
|
|
167452
|
+
tKey: "common.customizeLayout",
|
|
167453
|
+
fallback: "Customize Layout"
|
|
167454
|
+
}
|
|
167455
|
+
)
|
|
167456
|
+
) : /* @__PURE__ */ React163__default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React163__default.createElement(
|
|
167457
|
+
Button,
|
|
167458
|
+
{
|
|
167459
|
+
variant: "ghost",
|
|
167460
|
+
size: "sm",
|
|
167461
|
+
onClick: handleCancelEdit,
|
|
167462
|
+
className: "bg-background/20 hover:bg-background/40 text-white"
|
|
167463
|
+
},
|
|
167464
|
+
/* @__PURE__ */ React163__default.createElement(X, { className: "mr-2 h-4 w-4" }),
|
|
167465
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167466
|
+
ClientTranslation,
|
|
167467
|
+
{
|
|
167468
|
+
tKey: "common.cancel",
|
|
167469
|
+
fallback: "Cancel"
|
|
167470
|
+
}
|
|
167471
|
+
)
|
|
167472
|
+
), /* @__PURE__ */ React163__default.createElement(
|
|
167473
|
+
Button,
|
|
167474
|
+
{
|
|
167475
|
+
variant: "secondary",
|
|
167476
|
+
size: "sm",
|
|
167477
|
+
onClick: handleSaveLayout
|
|
167478
|
+
},
|
|
167479
|
+
/* @__PURE__ */ React163__default.createElement(Save, { className: "mr-2 h-4 w-4" }),
|
|
167480
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167481
|
+
ClientTranslation,
|
|
167482
|
+
{
|
|
167483
|
+
tKey: "common.saveLayout",
|
|
167484
|
+
fallback: "Save Layout"
|
|
167485
|
+
}
|
|
167486
|
+
)
|
|
167487
|
+
)), settingsPath ? /* @__PURE__ */ React163__default.createElement(
|
|
167488
|
+
Button,
|
|
167489
|
+
{
|
|
167490
|
+
asChild: true,
|
|
167491
|
+
variant: "ghost",
|
|
167492
|
+
size: "icon",
|
|
167493
|
+
className: "text-white hover:bg-background/20",
|
|
167494
|
+
title: "Settings"
|
|
167495
|
+
},
|
|
167496
|
+
/* @__PURE__ */ React163__default.createElement(Link3, { href: settingsPath }, /* @__PURE__ */ React163__default.createElement(Settings, { className: "h-5 w-5" }))
|
|
167497
|
+
) : /* @__PURE__ */ React163__default.createElement(
|
|
167498
|
+
Button,
|
|
167499
|
+
{
|
|
167500
|
+
variant: "ghost",
|
|
167501
|
+
size: "icon",
|
|
167502
|
+
className: "text-white hover:bg-background/20",
|
|
167503
|
+
title: "Settings",
|
|
167504
|
+
onClick: () => setIsSettingsModalOpen(true)
|
|
167505
|
+
},
|
|
167506
|
+
/* @__PURE__ */ React163__default.createElement(Settings, { className: "h-5 w-5" })
|
|
167507
|
+
)), /* @__PURE__ */ React163__default.createElement(CardTitle, { className: "text-3xl md:text-4xl font-bold tracking-tight" }, welcomeMessage), /* @__PURE__ */ React163__default.createElement(CardDescription, { className: "text-lg md:text-xl text-primary-foreground/80 max-w-3xl mx-auto mt-2 h-14 flex items-center justify-center" }, quoteText)), /* @__PURE__ */ React163__default.createElement(CardContent, { className: "pb-8 flex flex-wrap justify-center items-center gap-4" }, /* @__PURE__ */ React163__default.createElement(
|
|
167508
|
+
Button,
|
|
167509
|
+
{
|
|
167510
|
+
size: "lg",
|
|
167511
|
+
onClick: () => setIsUploadModalOpen(true),
|
|
167512
|
+
className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6"
|
|
167513
|
+
},
|
|
167514
|
+
/* @__PURE__ */ React163__default.createElement(FileUp, { className: "mr-2 h-5 w-5" }),
|
|
167515
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167516
|
+
ClientTranslation,
|
|
167517
|
+
{
|
|
167518
|
+
tKey: "dashboard.actions.createFromDoc",
|
|
167519
|
+
fallback: "Create Quiz from Document"
|
|
167520
|
+
}
|
|
167521
|
+
)
|
|
167522
|
+
), /* @__PURE__ */ React163__default.createElement(
|
|
167523
|
+
Button,
|
|
167524
|
+
{
|
|
167525
|
+
size: "lg",
|
|
167526
|
+
onClick: handleStartPractice,
|
|
167527
|
+
className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6"
|
|
167528
|
+
},
|
|
167529
|
+
/* @__PURE__ */ React163__default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }),
|
|
167530
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167531
|
+
ClientTranslation,
|
|
167532
|
+
{
|
|
167533
|
+
tKey: "dashboard.actions.freestyleTopic",
|
|
167534
|
+
fallback: "Freestyle Topic"
|
|
167535
|
+
}
|
|
167536
|
+
)
|
|
167537
|
+
), /* @__PURE__ */ React163__default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__default.createElement(
|
|
167538
|
+
Button,
|
|
167539
|
+
{
|
|
167540
|
+
size: "lg",
|
|
167541
|
+
variant: "secondary",
|
|
167542
|
+
className: "px-8 py-6 text-base font-semibold",
|
|
167543
|
+
disabled: isGeneratingRoadmap
|
|
167544
|
+
},
|
|
167545
|
+
/* @__PURE__ */ React163__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }),
|
|
167546
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167547
|
+
ClientTranslation,
|
|
167548
|
+
{
|
|
167549
|
+
tKey: "dashboard.actions.suggestRoadmap",
|
|
167550
|
+
fallback: "Suggest Next Week's Roadmap"
|
|
167551
|
+
}
|
|
167552
|
+
)
|
|
167553
|
+
)), /* @__PURE__ */ React163__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTitle2, null, t4("dialogs.confirmRoadmap.title")), /* @__PURE__ */ React163__default.createElement(AlertDialogDescription2, null, t4(
|
|
167554
|
+
"dialogs.confirmRoadmap.description"
|
|
167555
|
+
))), /* @__PURE__ */ React163__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__default.createElement(
|
|
167556
|
+
AlertDialogAction2,
|
|
167557
|
+
{
|
|
167558
|
+
onClick: handleGenerateRoadmap
|
|
167559
|
+
},
|
|
167560
|
+
t4("dialogs.confirmRoadmap.action")
|
|
167561
|
+
)))), /* @__PURE__ */ React163__default.createElement(
|
|
167562
|
+
Button,
|
|
167563
|
+
{
|
|
167564
|
+
size: "lg",
|
|
167565
|
+
onClick: () => setIsAnalysisDialogOpen(true),
|
|
167566
|
+
className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6"
|
|
167567
|
+
},
|
|
167568
|
+
/* @__PURE__ */ React163__default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }),
|
|
167569
|
+
/* @__PURE__ */ React163__default.createElement(
|
|
167570
|
+
ClientTranslation,
|
|
167571
|
+
{
|
|
167572
|
+
tKey: "dashboard.actions.deepAnalysis",
|
|
167573
|
+
fallback: "Deep Analysis"
|
|
167574
|
+
}
|
|
167575
|
+
)
|
|
167576
|
+
))), /* @__PURE__ */ React163__default.createElement(PerformanceSnapshot, { stats, isLoading }), /* @__PURE__ */ React163__default.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-8 items-start" }, /* @__PURE__ */ React163__default.createElement(
|
|
167577
|
+
SortableContext,
|
|
167578
|
+
{
|
|
167579
|
+
items: column1Items,
|
|
167580
|
+
strategy: verticalListSortingStrategy
|
|
167581
|
+
},
|
|
167582
|
+
/* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(
|
|
167583
|
+
DraggableDashboardCard,
|
|
167584
|
+
{
|
|
167585
|
+
key: cardConfig.id,
|
|
167586
|
+
id: cardConfig.id,
|
|
167587
|
+
visible: cardConfig.visible,
|
|
167588
|
+
isEditMode,
|
|
167589
|
+
onToggleVisibility: handleToggleVisibility
|
|
167590
|
+
},
|
|
167591
|
+
cardComponents[cardConfig.id]
|
|
167592
|
+
)))
|
|
167593
|
+
), /* @__PURE__ */ React163__default.createElement(
|
|
167594
|
+
SortableContext,
|
|
167595
|
+
{
|
|
167596
|
+
items: column2Items,
|
|
167597
|
+
strategy: verticalListSortingStrategy
|
|
167598
|
+
},
|
|
167599
|
+
/* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(
|
|
167600
|
+
DraggableDashboardCard,
|
|
167601
|
+
{
|
|
167602
|
+
key: cardConfig.id,
|
|
167603
|
+
id: cardConfig.id,
|
|
167604
|
+
visible: cardConfig.visible,
|
|
167605
|
+
isEditMode,
|
|
167606
|
+
onToggleVisibility: handleToggleVisibility
|
|
167607
|
+
},
|
|
167608
|
+
cardComponents[cardConfig.id]
|
|
167609
|
+
)))
|
|
167610
|
+
)), /* @__PURE__ */ React163__default.createElement(
|
|
167611
|
+
AnalysisDialog,
|
|
167612
|
+
{
|
|
167613
|
+
isOpen: isAnalysisDialogOpen,
|
|
167614
|
+
onClose: () => setIsAnalysisDialogOpen(false)
|
|
167615
|
+
}
|
|
167616
|
+
), /* @__PURE__ */ React163__default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__default.createElement(
|
|
167617
|
+
Button,
|
|
167618
|
+
{
|
|
167619
|
+
size: "icon",
|
|
167620
|
+
className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90",
|
|
167621
|
+
onClick: () => setIsChatbotOpen(true),
|
|
167622
|
+
"aria-label": "Open AI Tutor Chat"
|
|
167623
|
+
},
|
|
167624
|
+
/* @__PURE__ */ React163__default.createElement(Bot, { className: "h-7 w-7" })
|
|
167625
|
+
)), /* @__PURE__ */ React163__default.createElement(
|
|
167626
|
+
ChatbotDialog,
|
|
167627
|
+
{
|
|
167628
|
+
isOpen: isChatbotOpen,
|
|
167629
|
+
onClose: () => setIsChatbotOpen(false)
|
|
167630
|
+
}
|
|
167631
|
+
), /* @__PURE__ */ React163__default.createElement(
|
|
167632
|
+
UploadResourceModal,
|
|
167633
|
+
{
|
|
167634
|
+
isOpen: isUploadModalOpen,
|
|
167635
|
+
onClose: () => setIsUploadModalOpen(false)
|
|
167636
|
+
}
|
|
167637
|
+
)),
|
|
167638
|
+
!settingsPath && /* @__PURE__ */ React163__default.createElement(
|
|
167639
|
+
SettingsModal,
|
|
167640
|
+
{
|
|
167641
|
+
isOpen: isSettingsModalOpen,
|
|
167642
|
+
onClose: () => setIsSettingsModalOpen(false)
|
|
167643
|
+
}
|
|
167644
|
+
)
|
|
167645
|
+
);
|
|
167371
167646
|
};
|
|
167372
167647
|
|
|
167373
167648
|
// src/react-ui/components/practice/PracticeModeController.tsx
|
package/package.json
CHANGED