@thanh01.pmt/interactive-quiz-kit 1.0.44 → 1.0.45

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 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;
@@ -142549,7 +142551,7 @@ function z29(e3 = x) {
142549
142551
  }
142550
142552
  var B = { tooltip: "core-styles-module_tooltip__3vRRp", fixed: "core-styles-module_fixed__pcSol", arrow: "core-styles-module_arrow__cvMwQ", noArrow: "core-styles-module_noArrow__xock6", clickable: "core-styles-module_clickable__ZuTTB", show: "core-styles-module_show__Nt9eE", closing: "core-styles-module_closing__sGnxF" };
142551
142553
  var D2 = { tooltip: "styles-module_tooltip__mnnfp", arrow: "styles-module_arrow__K0L3T", dark: "styles-module_dark__xNqje", light: "styles-module_light__Z6W-X", success: "styles-module_success__A2AKt", warning: "styles-module_warning__SCK0X", error: "styles-module_error__JvumD", info: "styles-module_info__BWdHW" };
142552
- var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u2 = "dark", anchorId: d, anchorSelect: p2, place: v = "top", offset: m = 10, events: h3 = ["hover"], openOnClick: w2 = false, positionStrategy: b2 = "absolute", middlewares: S3, wrapper: g, delayShow: A3 = 0, delayHide: O2 = 0, float: T3 = false, hidden: x3 = false, noArrow: N2 = false, clickable: $3 = false, closeOnEsc: I = false, closeOnScroll: j = false, closeOnResize: q2 = false, openEvents: H2, closeEvents: M2, globalCloseEvents: W, imperativeModeOnly: P2, style: V, position: F, afterShow: K, afterHide: U2, disableTooltip: X2, content: Y, contentWrapperRef: G, isOpen: Z2, defaultIsOpen: J = false, setIsOpen: Q, activeAnchor: ee, setActiveAnchor: te, border: oe, opacity: le, arrowColor: re3, arrowSize: ne = 8, role: ie3 = "tooltip" }) => {
142554
+ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u2 = "dark", anchorId: d, anchorSelect: p2, place: v = "top", offset: m = 10, events: h3 = ["hover"], openOnClick: w2 = false, positionStrategy: b2 = "absolute", middlewares: S3, wrapper: g, delayShow: A3 = 0, delayHide: O2 = 0, float: T3 = false, hidden: x3 = false, noArrow: N2 = false, clickable: $3 = false, closeOnEsc: I = false, closeOnScroll: j = false, closeOnResize: q2 = false, openEvents: H2, closeEvents: M2, globalCloseEvents: W, imperativeModeOnly: P2, style: V, position: F, afterShow: K, afterHide: U2, disableTooltip: X3, content: Y, contentWrapperRef: G, isOpen: Z2, defaultIsOpen: J = false, setIsOpen: Q, activeAnchor: ee, setActiveAnchor: te, border: oe, opacity: le, arrowColor: re3, arrowSize: ne = 8, role: ie3 = "tooltip" }) => {
142553
142555
  var ce;
142554
142556
  const se = React163.useRef(null), ae = React163.useRef(null), ue = React163.useRef(null), de = React163.useRef(null), pe = React163.useRef(null), [ve, me] = React163.useState({ tooltipStyles: {}, tooltipArrowStyles: {}, place: v }), [fe, ye] = React163.useState(false), [he, we] = React163.useState(false), [be, Se] = React163.useState(null), ge = React163.useRef(false), Ee = React163.useRef(null), { anchorRefs: Ae, setActiveAnchor: _e } = z29(l2), Oe = React163.useRef(false), [ke, Te] = React163.useState([]), Le = React163.useRef(false), Ce = w2 || h3.includes("click"), Re = Ce || (null == H2 ? void 0 : H2.click) || (null == H2 ? void 0 : H2.dblclick) || (null == H2 ? void 0 : H2.mousedown), xe = H2 ? { ...H2 } : { mouseover: true, focus: true, mouseenter: false, click: false, dblclick: false, mousedown: false };
142555
142557
  !H2 && Ce && Object.assign(xe, { mouseenter: false, focus: false, mouseover: false, click: true });
@@ -142637,10 +142639,10 @@ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u
142637
142639
  var e3, t5;
142638
142640
  const o2 = new Set(Ae);
142639
142641
  ke.forEach((e4) => {
142640
- (null == X2 ? void 0 : X2(e4)) || o2.add({ current: e4 });
142642
+ (null == X3 ? void 0 : X3(e4)) || o2.add({ current: e4 });
142641
142643
  });
142642
142644
  const l3 = document.querySelector(`[id='${d}']`);
142643
- l3 && !(null == X2 ? void 0 : X2(l3)) && o2.add({ current: l3 });
142645
+ l3 && !(null == X3 ? void 0 : X3(l3)) && o2.add({ current: l3 });
142644
142646
  const r4 = () => {
142645
142647
  Ie(false);
142646
142648
  }, n2 = L2(ee), i3 = L2(se.current);
@@ -142756,7 +142758,7 @@ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u
142756
142758
  }, style: { ...V, ...ve.tooltipStyles, opacity: void 0 !== le && Ye ? le : void 0 }, ref: se }, Xe, React163__namespace.default.createElement(g, { className: (0, import_classnames.default)("react-tooltip-arrow", B.arrow, D2.arrow, c4, N2 && B.noArrow), style: { ...ve.tooltipArrowStyles, background: re3 ? `linear-gradient(to right bottom, transparent 50%, ${re3} 50%)` : void 0, "--rt-arrow-size": `${ne}px` }, ref: ae })) : null;
142757
142759
  };
142758
142760
  var H = ({ content: t4 }) => React163__namespace.default.createElement("span", { dangerouslySetInnerHTML: { __html: t4 } });
142759
- var M = React163__namespace.default.forwardRef(({ id: t4, anchorId: l2, anchorSelect: n2, content: i2, html: c4, render: a4, className: u2, classNameArrow: d, variant: p2 = "dark", place: v = "top", offset: m = 10, wrapper: f = "div", children: h3 = null, events: w2 = ["hover"], openOnClick: b2 = false, positionStrategy: S3 = "absolute", middlewares: g, delayShow: E2 = 0, delayHide: _2 = 0, float: O2 = false, hidden: k3 = false, noArrow: T3 = false, clickable: L3 = false, closeOnEsc: C3 = false, closeOnScroll: R3 = false, closeOnResize: x3 = false, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j = false, style: B2, position: D3, isOpen: M2, defaultIsOpen: W = false, disableStyleInjection: P2 = false, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X2, afterShow: Y, afterHide: G, disableTooltip: Z2, role: J = "tooltip" }, Q) => {
142761
+ var M = React163__namespace.default.forwardRef(({ id: t4, anchorId: l2, anchorSelect: n2, content: i2, html: c4, render: a4, className: u2, classNameArrow: d, variant: p2 = "dark", place: v = "top", offset: m = 10, wrapper: f = "div", children: h3 = null, events: w2 = ["hover"], openOnClick: b2 = false, positionStrategy: S3 = "absolute", middlewares: g, delayShow: E2 = 0, delayHide: _2 = 0, float: O2 = false, hidden: k3 = false, noArrow: T3 = false, clickable: L3 = false, closeOnEsc: C3 = false, closeOnScroll: R3 = false, closeOnResize: x3 = false, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j = false, style: B2, position: D3, isOpen: M2, defaultIsOpen: W = false, disableStyleInjection: P2 = false, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X3, afterShow: Y, afterHide: G, disableTooltip: Z2, role: J = "tooltip" }, Q) => {
142760
142762
  const [ee, te] = React163.useState(i2), [oe, le] = React163.useState(c4), [re3, ne] = React163.useState(v), [ie3, ce] = React163.useState(p2), [se, ae] = React163.useState(m), [ue, de] = React163.useState(E2), [pe, ve] = React163.useState(_2), [me, fe] = React163.useState(O2), [ye, he] = React163.useState(k3), [we, be] = React163.useState(f), [Se, ge] = React163.useState(w2), [Ee, Ae] = React163.useState(S3), [_e, Oe] = React163.useState(null), [ke, Te] = React163.useState(null), Le = React163.useRef(P2), { anchorRefs: Ce, activeAnchor: Re } = z29(t4), xe = (e3) => null == e3 ? void 0 : e3.getAttributeNames().reduce((t5, o2) => {
142761
142763
  var l3;
142762
142764
  if (o2.startsWith("data-tooltip-")) {
@@ -142863,7 +142865,7 @@ var M = React163__namespace.default.forwardRef(({ id: t4, anchorId: l2, anchorSe
142863
142865
  $e = t5 ? React163__namespace.default.createElement("div", { ref: Ie, className: "react-tooltip-content-wrapper" }, t5) : null;
142864
142866
  } else ee && ($e = ee);
142865
142867
  oe && ($e = React163__namespace.default.createElement(H, { content: oe }));
142866
- const ze = { forwardRef: Q, id: t4, anchorId: l2, anchorSelect: n2, className: (0, import_classnames.default)(u2, _e), classNameArrow: d, content: $e, contentWrapperRef: Ie, place: re3, variant: ie3, offset: se, wrapper: we, events: Se, openOnClick: b2, positionStrategy: Ee, middlewares: g, delayShow: ue, delayHide: pe, float: me, hidden: ye, noArrow: T3, clickable: L3, closeOnEsc: C3, closeOnScroll: R3, closeOnResize: x3, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j, style: B2, position: D3, isOpen: M2, defaultIsOpen: W, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X2, afterShow: Y, afterHide: G, disableTooltip: Z2, activeAnchor: ke, setActiveAnchor: (e3) => Te(e3), role: J };
142868
+ const ze = { forwardRef: Q, id: t4, anchorId: l2, anchorSelect: n2, className: (0, import_classnames.default)(u2, _e), classNameArrow: d, content: $e, contentWrapperRef: Ie, place: re3, variant: ie3, offset: se, wrapper: we, events: Se, openOnClick: b2, positionStrategy: Ee, middlewares: g, delayShow: ue, delayHide: pe, float: me, hidden: ye, noArrow: T3, clickable: L3, closeOnEsc: C3, closeOnScroll: R3, closeOnResize: x3, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j, style: B2, position: D3, isOpen: M2, defaultIsOpen: W, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X3, afterShow: Y, afterHide: G, disableTooltip: Z2, activeAnchor: ke, setActiveAnchor: (e3) => Te(e3), role: J };
142867
142869
  return React163__namespace.default.createElement(q, { ...ze });
142868
142870
  });
142869
142871
  "undefined" != typeof window && window.addEventListener("react-tooltip-inject-styles", (e3) => {
@@ -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(DashboardLayoutService.getLayout());
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(practiceHistory, practiceStats);
167256
+ const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
167257
+ practiceHistory,
167258
+ practiceStats
167259
+ );
167252
167260
  if (newlyUnlocked.length > 0) {
167253
- newlyUnlocked.forEach((ach) => toast2({ title: "\u{1F3C6} Th\xE0nh t\xEDch M\u1EDBi!", description: `B\u1EA1n \u0111\xE3 m\u1EDF kh\xF3a: ${t4(ach.nameKey)}` }));
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((a4, b2) => a4.averageScore - b2.averageScore)[0]?.name;
167276
- const aiQuote = await generateMotivationalQuote({
167277
- language: i18n.language === "vi" ? "Vietnamese" : "English",
167278
- userName: UserConfigService.getFullName() || void 0,
167279
- currentStreak: stats.currentStreak,
167280
- weakestTopic
167281
- }, apiKey);
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(() => setMotivationalQuote(QuoteService.getRandomQuote()), 10 * 60 * 1e3);
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) => ({ ...session.summary, id: session.id, timestamp: session.timestamp, quizTitle: session.quizConfig.title }));
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(() => router.push("/practice"), [router]);
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({ title: "AI Tutor \u0111ang suy ngh\u0129...", description: "\u0110ang t\u1EA1o l\u1ED9 tr\xECnh h\u1ECDc t\u1EADp \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a cho b\u1EA1n." });
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({ title: "API Key Missing", description: "Vui l\xF2ng c\u1EA5u h\xECnh Gemini API Key trong C\xE0i \u0111\u1EB7t.", variant: "destructive" });
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("No topic data found. Please import curriculum data in Settings first.");
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
- language: i18n.language === "vi" ? "Vietnamese" : "English",
167316
- userName: UserConfigService.getFullName() || void 0,
167317
- weeklyGoal: UserConfigService.getWeeklyGoal() || void 0,
167318
- unlockedAchievements: AchievementService.getAllAchievementsWithStatus().filter((a4) => a4.unlockedAt),
167319
- stats: PracticeHistoryService.getPracticeStats(),
167320
- history: PracticeHistoryService.getPracticeHistory(),
167321
- startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0],
167322
- endDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
167323
- allAvailableTopics
167324
- }, apiKey);
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({ title: "L\u1ED9 tr\xECnh M\u1EDBi \u0111\xE3 S\u1EB5n s\xE0ng!", description: "L\u1ED9 tr\xECnh h\u1ECDc t\u1EADp cho tu\u1EA7n n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt." });
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({ title: "T\u1EA1o L\u1ED9 tr\xECnh Th\u1EA5t b\u1EA1i", description: msg, variant: "destructive" });
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((c4) => c4.id === active.id) ? "column1" : "column2";
167358
- const overColKey = newLayout.column1.some((c4) => c4.id === over.id) ? "column1" : "column2";
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((c4) => c4.id === active.id);
167362
- const overIndex = overCol.findIndex((c4) => c4.id === over.id);
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(activeCol, activeIndex, overIndex);
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);
@@ -167369,15 +167431,6 @@ var PersonalPracticeDashboard = () => {
167369
167431
  return newLayout;
167370
167432
  });
167371
167433
  };
167372
- const handleSaveLayout = () => {
167373
- DashboardLayoutService.saveLayout(dashboardLayout);
167374
- setIsEditMode(false);
167375
- toast2({ title: "Layout \u0111\xE3 \u0111\u01B0\u1EE3c l\u01B0u", description: "Dashboard c\u1EE7a b\u1EA1n \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt." });
167376
- };
167377
- const handleCancelEdit = () => {
167378
- setDashboardLayout(DashboardLayoutService.getLayout());
167379
- setIsEditMode(false);
167380
- };
167381
167434
  const cardComponents = {
167382
167435
  roadmap: /* @__PURE__ */ React163__namespace.default.createElement(RoadmapChecklist, null),
167383
167436
  activity: /* @__PURE__ */ React163__namespace.default.createElement(ActivityCalendar, { stats }),
@@ -167393,7 +167446,179 @@ var PersonalPracticeDashboard = () => {
167393
167446
  const column2Items = dashboardLayout.column2.map((c4) => c4.id);
167394
167447
  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
167448
  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(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd }, /* @__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" }, !isEditMode ? /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "secondary", size: "sm", onClick: () => setIsEditMode(true) }, /* @__PURE__ */ React163__namespace.default.createElement(Settings, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "common.customize", fallback: "Customize" })) : /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "ghost", size: "sm", onClick: handleCancelEdit, className: "bg-background/20 hover:bg-background/40 text-white" }, /* @__PURE__ */ React163__namespace.default.createElement(X, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "common.cancel", fallback: "Cancel" })), /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "secondary", size: "sm", onClick: handleSaveLayout }, /* @__PURE__ */ React163__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "common.saveLayout", fallback: "Save Layout" })))), /* @__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(Button, { size: "lg", onClick: () => setIsUploadModalOpen(true), className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__namespace.default.createElement(FileUp, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "dashboard.actions.createFromDoc", fallback: "Create Quiz from Document" })), /* @__PURE__ */ React163__namespace.default.createElement(Button, { size: "lg", onClick: handleStartPractice, className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__namespace.default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "dashboard.actions.freestyleTopic", fallback: "Freestyle Topic" })), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__namespace.default.createElement(Button, { size: "lg", variant: "secondary", className: "px-8 py-6 text-base font-semibold", disabled: isGeneratingRoadmap }, /* @__PURE__ */ React163__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "dashboard.actions.suggestRoadmap", fallback: "Suggest Next Week's Roadmap" }))), /* @__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("dialogs.confirmRoadmap.description"))), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogAction2, { onClick: handleGenerateRoadmap }, t4("dialogs.confirmRoadmap.action"))))), /* @__PURE__ */ React163__namespace.default.createElement(Button, { size: "lg", onClick: () => setIsAnalysisDialogOpen(true), className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__namespace.default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__namespace.default.createElement(ClientTranslation, { tKey: "dashboard.actions.deepAnalysis", fallback: "Deep Analysis" })))), /* @__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(SortableContext, { items: column1Items, strategy: verticalListSortingStrategy }, /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(DraggableDashboardCard, { key: cardConfig.id, id: cardConfig.id, visible: cardConfig.visible, isEditMode, onToggleVisibility: handleToggleVisibility }, cardComponents[cardConfig.id])))), /* @__PURE__ */ React163__namespace.default.createElement(SortableContext, { items: column2Items, strategy: verticalListSortingStrategy }, /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(DraggableDashboardCard, { key: cardConfig.id, id: cardConfig.id, visible: cardConfig.visible, isEditMode, onToggleVisibility: handleToggleVisibility }, cardComponents[cardConfig.id]))))), /* @__PURE__ */ React163__namespace.default.createElement(AnalysisDialog, { isOpen: isAnalysisDialogOpen, onClose: () => setIsAnalysisDialogOpen(false) }), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__namespace.default.createElement(Button, { size: "icon", className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90", onClick: () => setIsChatbotOpen(true), "aria-label": "Open AI Tutor Chat" }, /* @__PURE__ */ React163__namespace.default.createElement(Bot, { className: "h-7 w-7" }))), /* @__PURE__ */ React163__namespace.default.createElement(ChatbotDialog, { isOpen: isChatbotOpen, onClose: () => setIsChatbotOpen(false) }), /* @__PURE__ */ React163__namespace.default.createElement(UploadResourceModal, { isOpen: isUploadModalOpen, onClose: () => setIsUploadModalOpen(false) })));
167449
+ return /* @__PURE__ */ React163__namespace.default.createElement(
167450
+ DndContext,
167451
+ {
167452
+ sensors,
167453
+ collisionDetection: closestCenter,
167454
+ onDragEnd: handleDragEnd
167455
+ },
167456
+ /* @__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" }, settingsPath ? (
167457
+ // If settingsPath is provided, always render a Link regardless of edit mode
167458
+ /* @__PURE__ */ React163__namespace.default.createElement(Button, { asChild: true, variant: "secondary", size: "sm" }, /* @__PURE__ */ React163__namespace.default.createElement(Link3__default.default, { href: settingsPath }, /* @__PURE__ */ React163__namespace.default.createElement(Settings, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__namespace.default.createElement(
167459
+ ClientTranslation,
167460
+ {
167461
+ tKey: "common.customize",
167462
+ fallback: "Customize"
167463
+ }
167464
+ )))
167465
+ ) : (
167466
+ // If no settingsPath, always open the modal regardless of edit mode
167467
+ /* @__PURE__ */ React163__namespace.default.createElement(
167468
+ Button,
167469
+ {
167470
+ variant: "secondary",
167471
+ size: "sm",
167472
+ onClick: () => setIsSettingsModalOpen(true)
167473
+ },
167474
+ /* @__PURE__ */ React163__namespace.default.createElement(Settings, { className: "mr-2 h-4 w-4" }),
167475
+ /* @__PURE__ */ React163__namespace.default.createElement(
167476
+ ClientTranslation,
167477
+ {
167478
+ tKey: "common.customize",
167479
+ fallback: "Customize"
167480
+ }
167481
+ )
167482
+ )
167483
+ )), /* @__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(
167484
+ Button,
167485
+ {
167486
+ size: "lg",
167487
+ onClick: () => setIsUploadModalOpen(true),
167488
+ className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6"
167489
+ },
167490
+ /* @__PURE__ */ React163__namespace.default.createElement(FileUp, { className: "mr-2 h-5 w-5" }),
167491
+ /* @__PURE__ */ React163__namespace.default.createElement(
167492
+ ClientTranslation,
167493
+ {
167494
+ tKey: "dashboard.actions.createFromDoc",
167495
+ fallback: "Create Quiz from Document"
167496
+ }
167497
+ )
167498
+ ), /* @__PURE__ */ React163__namespace.default.createElement(
167499
+ Button,
167500
+ {
167501
+ size: "lg",
167502
+ onClick: handleStartPractice,
167503
+ className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6"
167504
+ },
167505
+ /* @__PURE__ */ React163__namespace.default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }),
167506
+ /* @__PURE__ */ React163__namespace.default.createElement(
167507
+ ClientTranslation,
167508
+ {
167509
+ tKey: "dashboard.actions.freestyleTopic",
167510
+ fallback: "Freestyle Topic"
167511
+ }
167512
+ )
167513
+ ), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__namespace.default.createElement(
167514
+ Button,
167515
+ {
167516
+ size: "lg",
167517
+ variant: "secondary",
167518
+ className: "px-8 py-6 text-base font-semibold",
167519
+ disabled: isGeneratingRoadmap
167520
+ },
167521
+ /* @__PURE__ */ React163__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }),
167522
+ /* @__PURE__ */ React163__namespace.default.createElement(
167523
+ ClientTranslation,
167524
+ {
167525
+ tKey: "dashboard.actions.suggestRoadmap",
167526
+ fallback: "Suggest Next Week's Roadmap"
167527
+ }
167528
+ )
167529
+ )), /* @__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(
167530
+ "dialogs.confirmRoadmap.description"
167531
+ ))), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__namespace.default.createElement(
167532
+ AlertDialogAction2,
167533
+ {
167534
+ onClick: handleGenerateRoadmap
167535
+ },
167536
+ t4("dialogs.confirmRoadmap.action")
167537
+ )))), /* @__PURE__ */ React163__namespace.default.createElement(
167538
+ Button,
167539
+ {
167540
+ size: "lg",
167541
+ onClick: () => setIsAnalysisDialogOpen(true),
167542
+ className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6"
167543
+ },
167544
+ /* @__PURE__ */ React163__namespace.default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }),
167545
+ /* @__PURE__ */ React163__namespace.default.createElement(
167546
+ ClientTranslation,
167547
+ {
167548
+ tKey: "dashboard.actions.deepAnalysis",
167549
+ fallback: "Deep Analysis"
167550
+ }
167551
+ )
167552
+ ))), /* @__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(
167553
+ SortableContext,
167554
+ {
167555
+ items: column1Items,
167556
+ strategy: verticalListSortingStrategy
167557
+ },
167558
+ /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(
167559
+ DraggableDashboardCard,
167560
+ {
167561
+ key: cardConfig.id,
167562
+ id: cardConfig.id,
167563
+ visible: cardConfig.visible,
167564
+ isEditMode,
167565
+ onToggleVisibility: handleToggleVisibility
167566
+ },
167567
+ cardComponents[cardConfig.id]
167568
+ )))
167569
+ ), /* @__PURE__ */ React163__namespace.default.createElement(
167570
+ SortableContext,
167571
+ {
167572
+ items: column2Items,
167573
+ strategy: verticalListSortingStrategy
167574
+ },
167575
+ /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__namespace.default.createElement(
167576
+ DraggableDashboardCard,
167577
+ {
167578
+ key: cardConfig.id,
167579
+ id: cardConfig.id,
167580
+ visible: cardConfig.visible,
167581
+ isEditMode,
167582
+ onToggleVisibility: handleToggleVisibility
167583
+ },
167584
+ cardComponents[cardConfig.id]
167585
+ )))
167586
+ )), /* @__PURE__ */ React163__namespace.default.createElement(
167587
+ AnalysisDialog,
167588
+ {
167589
+ isOpen: isAnalysisDialogOpen,
167590
+ onClose: () => setIsAnalysisDialogOpen(false)
167591
+ }
167592
+ ), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__namespace.default.createElement(
167593
+ Button,
167594
+ {
167595
+ size: "icon",
167596
+ className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90",
167597
+ onClick: () => setIsChatbotOpen(true),
167598
+ "aria-label": "Open AI Tutor Chat"
167599
+ },
167600
+ /* @__PURE__ */ React163__namespace.default.createElement(Bot, { className: "h-7 w-7" })
167601
+ )), /* @__PURE__ */ React163__namespace.default.createElement(
167602
+ ChatbotDialog,
167603
+ {
167604
+ isOpen: isChatbotOpen,
167605
+ onClose: () => setIsChatbotOpen(false)
167606
+ }
167607
+ ), /* @__PURE__ */ React163__namespace.default.createElement(
167608
+ UploadResourceModal,
167609
+ {
167610
+ isOpen: isUploadModalOpen,
167611
+ onClose: () => setIsUploadModalOpen(false)
167612
+ }
167613
+ )),
167614
+ !settingsPath && /* @__PURE__ */ React163__namespace.default.createElement(
167615
+ SettingsModal,
167616
+ {
167617
+ isOpen: isSettingsModalOpen,
167618
+ onClose: () => setIsSettingsModalOpen(false)
167619
+ }
167620
+ )
167621
+ );
167397
167622
  };
167398
167623
 
167399
167624
  // src/react-ui/components/practice/PracticeModeController.tsx
@@ -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
- declare const PersonalPracticeDashboard: React__default.FC;
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 {
@@ -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
- declare const PersonalPracticeDashboard: React__default.FC;
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;
@@ -142523,7 +142524,7 @@ function z29(e3 = x) {
142523
142524
  }
142524
142525
  var B = { tooltip: "core-styles-module_tooltip__3vRRp", fixed: "core-styles-module_fixed__pcSol", arrow: "core-styles-module_arrow__cvMwQ", noArrow: "core-styles-module_noArrow__xock6", clickable: "core-styles-module_clickable__ZuTTB", show: "core-styles-module_show__Nt9eE", closing: "core-styles-module_closing__sGnxF" };
142525
142526
  var D2 = { tooltip: "styles-module_tooltip__mnnfp", arrow: "styles-module_arrow__K0L3T", dark: "styles-module_dark__xNqje", light: "styles-module_light__Z6W-X", success: "styles-module_success__A2AKt", warning: "styles-module_warning__SCK0X", error: "styles-module_error__JvumD", info: "styles-module_info__BWdHW" };
142526
- var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u2 = "dark", anchorId: d, anchorSelect: p2, place: v = "top", offset: m = 10, events: h3 = ["hover"], openOnClick: w2 = false, positionStrategy: b2 = "absolute", middlewares: S3, wrapper: g, delayShow: A3 = 0, delayHide: O2 = 0, float: T3 = false, hidden: x3 = false, noArrow: N2 = false, clickable: $3 = false, closeOnEsc: I = false, closeOnScroll: j = false, closeOnResize: q2 = false, openEvents: H2, closeEvents: M2, globalCloseEvents: W, imperativeModeOnly: P2, style: V, position: F, afterShow: K, afterHide: U2, disableTooltip: X2, content: Y, contentWrapperRef: G, isOpen: Z2, defaultIsOpen: J = false, setIsOpen: Q, activeAnchor: ee, setActiveAnchor: te, border: oe, opacity: le, arrowColor: re3, arrowSize: ne = 8, role: ie3 = "tooltip" }) => {
142527
+ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u2 = "dark", anchorId: d, anchorSelect: p2, place: v = "top", offset: m = 10, events: h3 = ["hover"], openOnClick: w2 = false, positionStrategy: b2 = "absolute", middlewares: S3, wrapper: g, delayShow: A3 = 0, delayHide: O2 = 0, float: T3 = false, hidden: x3 = false, noArrow: N2 = false, clickable: $3 = false, closeOnEsc: I = false, closeOnScroll: j = false, closeOnResize: q2 = false, openEvents: H2, closeEvents: M2, globalCloseEvents: W, imperativeModeOnly: P2, style: V, position: F, afterShow: K, afterHide: U2, disableTooltip: X3, content: Y, contentWrapperRef: G, isOpen: Z2, defaultIsOpen: J = false, setIsOpen: Q, activeAnchor: ee, setActiveAnchor: te, border: oe, opacity: le, arrowColor: re3, arrowSize: ne = 8, role: ie3 = "tooltip" }) => {
142527
142528
  var ce;
142528
142529
  const se = useRef(null), ae = useRef(null), ue = useRef(null), de = useRef(null), pe = useRef(null), [ve, me] = useState({ tooltipStyles: {}, tooltipArrowStyles: {}, place: v }), [fe, ye] = useState(false), [he, we] = useState(false), [be, Se] = useState(null), ge = useRef(false), Ee = useRef(null), { anchorRefs: Ae, setActiveAnchor: _e } = z29(l2), Oe = useRef(false), [ke, Te] = useState([]), Le = useRef(false), Ce = w2 || h3.includes("click"), Re = Ce || (null == H2 ? void 0 : H2.click) || (null == H2 ? void 0 : H2.dblclick) || (null == H2 ? void 0 : H2.mousedown), xe = H2 ? { ...H2 } : { mouseover: true, focus: true, mouseenter: false, click: false, dblclick: false, mousedown: false };
142529
142530
  !H2 && Ce && Object.assign(xe, { mouseenter: false, focus: false, mouseover: false, click: true });
@@ -142611,10 +142612,10 @@ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u
142611
142612
  var e3, t5;
142612
142613
  const o2 = new Set(Ae);
142613
142614
  ke.forEach((e4) => {
142614
- (null == X2 ? void 0 : X2(e4)) || o2.add({ current: e4 });
142615
+ (null == X3 ? void 0 : X3(e4)) || o2.add({ current: e4 });
142615
142616
  });
142616
142617
  const l3 = document.querySelector(`[id='${d}']`);
142617
- l3 && !(null == X2 ? void 0 : X2(l3)) && o2.add({ current: l3 });
142618
+ l3 && !(null == X3 ? void 0 : X3(l3)) && o2.add({ current: l3 });
142618
142619
  const r4 = () => {
142619
142620
  Ie(false);
142620
142621
  }, n2 = L2(ee), i3 = L2(se.current);
@@ -142730,7 +142731,7 @@ var q = ({ forwardRef: t4, id: l2, className: i2, classNameArrow: c4, variant: u
142730
142731
  }, style: { ...V, ...ve.tooltipStyles, opacity: void 0 !== le && Ye ? le : void 0 }, ref: se }, Xe, React163__default.createElement(g, { className: (0, import_classnames.default)("react-tooltip-arrow", B.arrow, D2.arrow, c4, N2 && B.noArrow), style: { ...ve.tooltipArrowStyles, background: re3 ? `linear-gradient(to right bottom, transparent 50%, ${re3} 50%)` : void 0, "--rt-arrow-size": `${ne}px` }, ref: ae })) : null;
142731
142732
  };
142732
142733
  var H = ({ content: t4 }) => React163__default.createElement("span", { dangerouslySetInnerHTML: { __html: t4 } });
142733
- var M = React163__default.forwardRef(({ id: t4, anchorId: l2, anchorSelect: n2, content: i2, html: c4, render: a4, className: u2, classNameArrow: d, variant: p2 = "dark", place: v = "top", offset: m = 10, wrapper: f = "div", children: h3 = null, events: w2 = ["hover"], openOnClick: b2 = false, positionStrategy: S3 = "absolute", middlewares: g, delayShow: E2 = 0, delayHide: _2 = 0, float: O2 = false, hidden: k3 = false, noArrow: T3 = false, clickable: L3 = false, closeOnEsc: C3 = false, closeOnScroll: R3 = false, closeOnResize: x3 = false, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j = false, style: B2, position: D3, isOpen: M2, defaultIsOpen: W = false, disableStyleInjection: P2 = false, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X2, afterShow: Y, afterHide: G, disableTooltip: Z2, role: J = "tooltip" }, Q) => {
142734
+ var M = React163__default.forwardRef(({ id: t4, anchorId: l2, anchorSelect: n2, content: i2, html: c4, render: a4, className: u2, classNameArrow: d, variant: p2 = "dark", place: v = "top", offset: m = 10, wrapper: f = "div", children: h3 = null, events: w2 = ["hover"], openOnClick: b2 = false, positionStrategy: S3 = "absolute", middlewares: g, delayShow: E2 = 0, delayHide: _2 = 0, float: O2 = false, hidden: k3 = false, noArrow: T3 = false, clickable: L3 = false, closeOnEsc: C3 = false, closeOnScroll: R3 = false, closeOnResize: x3 = false, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j = false, style: B2, position: D3, isOpen: M2, defaultIsOpen: W = false, disableStyleInjection: P2 = false, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X3, afterShow: Y, afterHide: G, disableTooltip: Z2, role: J = "tooltip" }, Q) => {
142734
142735
  const [ee, te] = useState(i2), [oe, le] = useState(c4), [re3, ne] = useState(v), [ie3, ce] = useState(p2), [se, ae] = useState(m), [ue, de] = useState(E2), [pe, ve] = useState(_2), [me, fe] = useState(O2), [ye, he] = useState(k3), [we, be] = useState(f), [Se, ge] = useState(w2), [Ee, Ae] = useState(S3), [_e, Oe] = useState(null), [ke, Te] = useState(null), Le = useRef(P2), { anchorRefs: Ce, activeAnchor: Re } = z29(t4), xe = (e3) => null == e3 ? void 0 : e3.getAttributeNames().reduce((t5, o2) => {
142735
142736
  var l3;
142736
142737
  if (o2.startsWith("data-tooltip-")) {
@@ -142837,7 +142838,7 @@ var M = React163__default.forwardRef(({ id: t4, anchorId: l2, anchorSelect: n2,
142837
142838
  $e = t5 ? React163__default.createElement("div", { ref: Ie, className: "react-tooltip-content-wrapper" }, t5) : null;
142838
142839
  } else ee && ($e = ee);
142839
142840
  oe && ($e = React163__default.createElement(H, { content: oe }));
142840
- const ze = { forwardRef: Q, id: t4, anchorId: l2, anchorSelect: n2, className: (0, import_classnames.default)(u2, _e), classNameArrow: d, content: $e, contentWrapperRef: Ie, place: re3, variant: ie3, offset: se, wrapper: we, events: Se, openOnClick: b2, positionStrategy: Ee, middlewares: g, delayShow: ue, delayHide: pe, float: me, hidden: ye, noArrow: T3, clickable: L3, closeOnEsc: C3, closeOnScroll: R3, closeOnResize: x3, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j, style: B2, position: D3, isOpen: M2, defaultIsOpen: W, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X2, afterShow: Y, afterHide: G, disableTooltip: Z2, activeAnchor: ke, setActiveAnchor: (e3) => Te(e3), role: J };
142841
+ const ze = { forwardRef: Q, id: t4, anchorId: l2, anchorSelect: n2, className: (0, import_classnames.default)(u2, _e), classNameArrow: d, content: $e, contentWrapperRef: Ie, place: re3, variant: ie3, offset: se, wrapper: we, events: Se, openOnClick: b2, positionStrategy: Ee, middlewares: g, delayShow: ue, delayHide: pe, float: me, hidden: ye, noArrow: T3, clickable: L3, closeOnEsc: C3, closeOnScroll: R3, closeOnResize: x3, openEvents: N2, closeEvents: $3, globalCloseEvents: I, imperativeModeOnly: j, style: B2, position: D3, isOpen: M2, defaultIsOpen: W, border: V, opacity: F, arrowColor: K, arrowSize: U2, setIsOpen: X3, afterShow: Y, afterHide: G, disableTooltip: Z2, activeAnchor: ke, setActiveAnchor: (e3) => Te(e3), role: J };
142841
142842
  return React163__default.createElement(q, { ...ze });
142842
142843
  });
142843
142844
  "undefined" != typeof window && window.addEventListener("react-tooltip-inject-styles", (e3) => {
@@ -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(DashboardLayoutService.getLayout());
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(practiceHistory, practiceStats);
167229
+ const newlyUnlocked = AchievementService.checkAndUnlockAchievements(
167230
+ practiceHistory,
167231
+ practiceStats
167232
+ );
167226
167233
  if (newlyUnlocked.length > 0) {
167227
- newlyUnlocked.forEach((ach) => toast2({ title: "\u{1F3C6} Th\xE0nh t\xEDch M\u1EDBi!", description: `B\u1EA1n \u0111\xE3 m\u1EDF kh\xF3a: ${t4(ach.nameKey)}` }));
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((a4, b2) => a4.averageScore - b2.averageScore)[0]?.name;
167250
- const aiQuote = await generateMotivationalQuote({
167251
- language: i18n.language === "vi" ? "Vietnamese" : "English",
167252
- userName: UserConfigService.getFullName() || void 0,
167253
- currentStreak: stats.currentStreak,
167254
- weakestTopic
167255
- }, apiKey);
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(() => setMotivationalQuote(QuoteService.getRandomQuote()), 10 * 60 * 1e3);
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) => ({ ...session.summary, id: session.id, timestamp: session.timestamp, quizTitle: session.quizConfig.title }));
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(() => router.push("/practice"), [router]);
167293
+ const handleStartPractice = useCallback(
167294
+ () => router.push("/practice"),
167295
+ [router]
167296
+ );
167269
167297
  const handleGenerateRoadmap = useCallback(async () => {
167270
167298
  setIsGeneratingRoadmap(true);
167271
- toast2({ title: "AI Tutor \u0111ang suy ngh\u0129...", description: "\u0110ang t\u1EA1o l\u1ED9 tr\xECnh h\u1ECDc t\u1EADp \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a cho b\u1EA1n." });
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({ title: "API Key Missing", description: "Vui l\xF2ng c\u1EA5u h\xECnh Gemini API Key trong C\xE0i \u0111\u1EB7t.", variant: "destructive" });
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("No topic data found. Please import curriculum data in Settings first.");
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
- language: i18n.language === "vi" ? "Vietnamese" : "English",
167290
- userName: UserConfigService.getFullName() || void 0,
167291
- weeklyGoal: UserConfigService.getWeeklyGoal() || void 0,
167292
- unlockedAchievements: AchievementService.getAllAchievementsWithStatus().filter((a4) => a4.unlockedAt),
167293
- stats: PracticeHistoryService.getPracticeStats(),
167294
- history: PracticeHistoryService.getPracticeHistory(),
167295
- startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0],
167296
- endDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
167297
- allAvailableTopics
167298
- }, apiKey);
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({ title: "L\u1ED9 tr\xECnh M\u1EDBi \u0111\xE3 S\u1EB5n s\xE0ng!", description: "L\u1ED9 tr\xECnh h\u1ECDc t\u1EADp cho tu\u1EA7n n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt." });
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({ title: "T\u1EA1o L\u1ED9 tr\xECnh Th\u1EA5t b\u1EA1i", description: msg, variant: "destructive" });
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((c4) => c4.id === active.id) ? "column1" : "column2";
167332
- const overColKey = newLayout.column1.some((c4) => c4.id === over.id) ? "column1" : "column2";
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((c4) => c4.id === active.id);
167336
- const overIndex = overCol.findIndex((c4) => c4.id === over.id);
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(activeCol, activeIndex, overIndex);
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);
@@ -167343,15 +167404,6 @@ var PersonalPracticeDashboard = () => {
167343
167404
  return newLayout;
167344
167405
  });
167345
167406
  };
167346
- const handleSaveLayout = () => {
167347
- DashboardLayoutService.saveLayout(dashboardLayout);
167348
- setIsEditMode(false);
167349
- toast2({ title: "Layout \u0111\xE3 \u0111\u01B0\u1EE3c l\u01B0u", description: "Dashboard c\u1EE7a b\u1EA1n \u0111\xE3 \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt." });
167350
- };
167351
- const handleCancelEdit = () => {
167352
- setDashboardLayout(DashboardLayoutService.getLayout());
167353
- setIsEditMode(false);
167354
- };
167355
167407
  const cardComponents = {
167356
167408
  roadmap: /* @__PURE__ */ React163__default.createElement(RoadmapChecklist, null),
167357
167409
  activity: /* @__PURE__ */ React163__default.createElement(ActivityCalendar, { stats }),
@@ -167367,7 +167419,179 @@ var PersonalPracticeDashboard = () => {
167367
167419
  const column2Items = dashboardLayout.column2.map((c4) => c4.id);
167368
167420
  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
167421
  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(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd }, /* @__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" }, !isEditMode ? /* @__PURE__ */ React163__default.createElement(Button, { variant: "secondary", size: "sm", onClick: () => setIsEditMode(true) }, /* @__PURE__ */ React163__default.createElement(Settings, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "common.customize", fallback: "Customize" })) : /* @__PURE__ */ React163__default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React163__default.createElement(Button, { variant: "ghost", size: "sm", onClick: handleCancelEdit, className: "bg-background/20 hover:bg-background/40 text-white" }, /* @__PURE__ */ React163__default.createElement(X, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "common.cancel", fallback: "Cancel" })), /* @__PURE__ */ React163__default.createElement(Button, { variant: "secondary", size: "sm", onClick: handleSaveLayout }, /* @__PURE__ */ React163__default.createElement(Save, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "common.saveLayout", fallback: "Save Layout" })))), /* @__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(Button, { size: "lg", onClick: () => setIsUploadModalOpen(true), className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__default.createElement(FileUp, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "dashboard.actions.createFromDoc", fallback: "Create Quiz from Document" })), /* @__PURE__ */ React163__default.createElement(Button, { size: "lg", onClick: handleStartPractice, className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "dashboard.actions.freestyleTopic", fallback: "Freestyle Topic" })), /* @__PURE__ */ React163__default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__default.createElement(Button, { size: "lg", variant: "secondary", className: "px-8 py-6 text-base font-semibold", disabled: isGeneratingRoadmap }, /* @__PURE__ */ React163__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "dashboard.actions.suggestRoadmap", fallback: "Suggest Next Week's Roadmap" }))), /* @__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("dialogs.confirmRoadmap.description"))), /* @__PURE__ */ React163__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__default.createElement(AlertDialogAction2, { onClick: handleGenerateRoadmap }, t4("dialogs.confirmRoadmap.action"))))), /* @__PURE__ */ React163__default.createElement(Button, { size: "lg", onClick: () => setIsAnalysisDialogOpen(true), className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6" }, /* @__PURE__ */ React163__default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }), /* @__PURE__ */ React163__default.createElement(ClientTranslation, { tKey: "dashboard.actions.deepAnalysis", fallback: "Deep Analysis" })))), /* @__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(SortableContext, { items: column1Items, strategy: verticalListSortingStrategy }, /* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(DraggableDashboardCard, { key: cardConfig.id, id: cardConfig.id, visible: cardConfig.visible, isEditMode, onToggleVisibility: handleToggleVisibility }, cardComponents[cardConfig.id])))), /* @__PURE__ */ React163__default.createElement(SortableContext, { items: column2Items, strategy: verticalListSortingStrategy }, /* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(DraggableDashboardCard, { key: cardConfig.id, id: cardConfig.id, visible: cardConfig.visible, isEditMode, onToggleVisibility: handleToggleVisibility }, cardComponents[cardConfig.id]))))), /* @__PURE__ */ React163__default.createElement(AnalysisDialog, { isOpen: isAnalysisDialogOpen, onClose: () => setIsAnalysisDialogOpen(false) }), /* @__PURE__ */ React163__default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__default.createElement(Button, { size: "icon", className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90", onClick: () => setIsChatbotOpen(true), "aria-label": "Open AI Tutor Chat" }, /* @__PURE__ */ React163__default.createElement(Bot, { className: "h-7 w-7" }))), /* @__PURE__ */ React163__default.createElement(ChatbotDialog, { isOpen: isChatbotOpen, onClose: () => setIsChatbotOpen(false) }), /* @__PURE__ */ React163__default.createElement(UploadResourceModal, { isOpen: isUploadModalOpen, onClose: () => setIsUploadModalOpen(false) })));
167422
+ return /* @__PURE__ */ React163__default.createElement(
167423
+ DndContext,
167424
+ {
167425
+ sensors,
167426
+ collisionDetection: closestCenter,
167427
+ onDragEnd: handleDragEnd
167428
+ },
167429
+ /* @__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" }, settingsPath ? (
167430
+ // If settingsPath is provided, always render a Link regardless of edit mode
167431
+ /* @__PURE__ */ React163__default.createElement(Button, { asChild: true, variant: "secondary", size: "sm" }, /* @__PURE__ */ React163__default.createElement(Link3, { href: settingsPath }, /* @__PURE__ */ React163__default.createElement(Settings, { className: "mr-2 h-4 w-4" }), /* @__PURE__ */ React163__default.createElement(
167432
+ ClientTranslation,
167433
+ {
167434
+ tKey: "common.customize",
167435
+ fallback: "Customize"
167436
+ }
167437
+ )))
167438
+ ) : (
167439
+ // If no settingsPath, always open the modal regardless of edit mode
167440
+ /* @__PURE__ */ React163__default.createElement(
167441
+ Button,
167442
+ {
167443
+ variant: "secondary",
167444
+ size: "sm",
167445
+ onClick: () => setIsSettingsModalOpen(true)
167446
+ },
167447
+ /* @__PURE__ */ React163__default.createElement(Settings, { className: "mr-2 h-4 w-4" }),
167448
+ /* @__PURE__ */ React163__default.createElement(
167449
+ ClientTranslation,
167450
+ {
167451
+ tKey: "common.customize",
167452
+ fallback: "Customize"
167453
+ }
167454
+ )
167455
+ )
167456
+ )), /* @__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(
167457
+ Button,
167458
+ {
167459
+ size: "lg",
167460
+ onClick: () => setIsUploadModalOpen(true),
167461
+ className: "bg-indigo-600 hover:bg-indigo-700 text-white text-base font-semibold px-8 py-6"
167462
+ },
167463
+ /* @__PURE__ */ React163__default.createElement(FileUp, { className: "mr-2 h-5 w-5" }),
167464
+ /* @__PURE__ */ React163__default.createElement(
167465
+ ClientTranslation,
167466
+ {
167467
+ tKey: "dashboard.actions.createFromDoc",
167468
+ fallback: "Create Quiz from Document"
167469
+ }
167470
+ )
167471
+ ), /* @__PURE__ */ React163__default.createElement(
167472
+ Button,
167473
+ {
167474
+ size: "lg",
167475
+ onClick: handleStartPractice,
167476
+ className: "bg-amber-400 hover:bg-amber-500 text-amber-950 text-base font-semibold px-8 py-6"
167477
+ },
167478
+ /* @__PURE__ */ React163__default.createElement(BrainCircuit, { className: "mr-2 h-5 w-5" }),
167479
+ /* @__PURE__ */ React163__default.createElement(
167480
+ ClientTranslation,
167481
+ {
167482
+ tKey: "dashboard.actions.freestyleTopic",
167483
+ fallback: "Freestyle Topic"
167484
+ }
167485
+ )
167486
+ ), /* @__PURE__ */ React163__default.createElement(AlertDialog2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React163__default.createElement(
167487
+ Button,
167488
+ {
167489
+ size: "lg",
167490
+ variant: "secondary",
167491
+ className: "px-8 py-6 text-base font-semibold",
167492
+ disabled: isGeneratingRoadmap
167493
+ },
167494
+ /* @__PURE__ */ React163__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5" }),
167495
+ /* @__PURE__ */ React163__default.createElement(
167496
+ ClientTranslation,
167497
+ {
167498
+ tKey: "dashboard.actions.suggestRoadmap",
167499
+ fallback: "Suggest Next Week's Roadmap"
167500
+ }
167501
+ )
167502
+ )), /* @__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(
167503
+ "dialogs.confirmRoadmap.description"
167504
+ ))), /* @__PURE__ */ React163__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React163__default.createElement(
167505
+ AlertDialogAction2,
167506
+ {
167507
+ onClick: handleGenerateRoadmap
167508
+ },
167509
+ t4("dialogs.confirmRoadmap.action")
167510
+ )))), /* @__PURE__ */ React163__default.createElement(
167511
+ Button,
167512
+ {
167513
+ size: "lg",
167514
+ onClick: () => setIsAnalysisDialogOpen(true),
167515
+ className: "bg-blue-600 hover:bg-blue-700 text-white text-base font-semibold px-8 py-6"
167516
+ },
167517
+ /* @__PURE__ */ React163__default.createElement(BarChart4, { className: "mr-2 h-5 w-5" }),
167518
+ /* @__PURE__ */ React163__default.createElement(
167519
+ ClientTranslation,
167520
+ {
167521
+ tKey: "dashboard.actions.deepAnalysis",
167522
+ fallback: "Deep Analysis"
167523
+ }
167524
+ )
167525
+ ))), /* @__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(
167526
+ SortableContext,
167527
+ {
167528
+ items: column1Items,
167529
+ strategy: verticalListSortingStrategy
167530
+ },
167531
+ /* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column1.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(
167532
+ DraggableDashboardCard,
167533
+ {
167534
+ key: cardConfig.id,
167535
+ id: cardConfig.id,
167536
+ visible: cardConfig.visible,
167537
+ isEditMode,
167538
+ onToggleVisibility: handleToggleVisibility
167539
+ },
167540
+ cardComponents[cardConfig.id]
167541
+ )))
167542
+ ), /* @__PURE__ */ React163__default.createElement(
167543
+ SortableContext,
167544
+ {
167545
+ items: column2Items,
167546
+ strategy: verticalListSortingStrategy
167547
+ },
167548
+ /* @__PURE__ */ React163__default.createElement("div", { className: "flex flex-col gap-8" }, dashboardLayout.column2.map((cardConfig) => /* @__PURE__ */ React163__default.createElement(
167549
+ DraggableDashboardCard,
167550
+ {
167551
+ key: cardConfig.id,
167552
+ id: cardConfig.id,
167553
+ visible: cardConfig.visible,
167554
+ isEditMode,
167555
+ onToggleVisibility: handleToggleVisibility
167556
+ },
167557
+ cardComponents[cardConfig.id]
167558
+ )))
167559
+ )), /* @__PURE__ */ React163__default.createElement(
167560
+ AnalysisDialog,
167561
+ {
167562
+ isOpen: isAnalysisDialogOpen,
167563
+ onClose: () => setIsAnalysisDialogOpen(false)
167564
+ }
167565
+ ), /* @__PURE__ */ React163__default.createElement("div", { className: "fixed bottom-6 right-6 z-50" }, /* @__PURE__ */ React163__default.createElement(
167566
+ Button,
167567
+ {
167568
+ size: "icon",
167569
+ className: "rounded-full h-14 w-14 shadow-lg bg-primary hover:bg-primary/90",
167570
+ onClick: () => setIsChatbotOpen(true),
167571
+ "aria-label": "Open AI Tutor Chat"
167572
+ },
167573
+ /* @__PURE__ */ React163__default.createElement(Bot, { className: "h-7 w-7" })
167574
+ )), /* @__PURE__ */ React163__default.createElement(
167575
+ ChatbotDialog,
167576
+ {
167577
+ isOpen: isChatbotOpen,
167578
+ onClose: () => setIsChatbotOpen(false)
167579
+ }
167580
+ ), /* @__PURE__ */ React163__default.createElement(
167581
+ UploadResourceModal,
167582
+ {
167583
+ isOpen: isUploadModalOpen,
167584
+ onClose: () => setIsUploadModalOpen(false)
167585
+ }
167586
+ )),
167587
+ !settingsPath && /* @__PURE__ */ React163__default.createElement(
167588
+ SettingsModal,
167589
+ {
167590
+ isOpen: isSettingsModalOpen,
167591
+ onClose: () => setIsSettingsModalOpen(false)
167592
+ }
167593
+ )
167594
+ );
167371
167595
  };
167372
167596
 
167373
167597
  // src/react-ui/components/practice/PracticeModeController.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanh01.pmt/interactive-quiz-kit",
3
- "version": "1.0.44",
3
+ "version": "1.0.45",
4
4
  "description": "A comprehensive library for creating, managing, and playing interactive quizzes, with AI generation and SCORM support.",
5
5
  "keywords": [
6
6
  "react",