@appcorp/fusion-storybook 0.2.40 → 0.2.44

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.
Files changed (100) hide show
  1. package/base-modules/admission/constants.d.ts +5 -17
  2. package/base-modules/admission/constants.js +12 -7
  3. package/base-modules/admission/context/use-admission-module.js +11 -48
  4. package/base-modules/admission/filter.js +23 -3
  5. package/base-modules/admission/form.js +49 -19
  6. package/base-modules/attendance/context.js +3 -37
  7. package/base-modules/attendance/filter.js +3 -1
  8. package/base-modules/attendance/form.js +26 -10
  9. package/base-modules/attendance/more-actions.js +34 -25
  10. package/base-modules/campus/context.js +7 -7
  11. package/base-modules/class/cache.js +0 -1
  12. package/base-modules/class/context.js +10 -48
  13. package/base-modules/class/more-actions.js +47 -24
  14. package/base-modules/course/context.js +3 -37
  15. package/base-modules/course/form.js +91 -292
  16. package/base-modules/discount-code/constants.d.ts +5 -0
  17. package/base-modules/discount-code/constants.js +5 -0
  18. package/base-modules/discount-code/context.d.ts +1 -0
  19. package/base-modules/discount-code/context.js +40 -39
  20. package/base-modules/discount-code/form.js +21 -15
  21. package/base-modules/discount-code/more-actions.js +1 -1
  22. package/base-modules/enrollment/context.js +3 -37
  23. package/base-modules/enrollment/form.js +38 -11
  24. package/base-modules/enrollment/more-actions.js +48 -25
  25. package/base-modules/expense/constants.js +1 -1
  26. package/base-modules/expense/context.js +5 -32
  27. package/base-modules/expense/filter.js +50 -3
  28. package/base-modules/expense/form.js +82 -6
  29. package/base-modules/family/context.js +7 -38
  30. package/base-modules/family-member/context.js +7 -39
  31. package/base-modules/fee-structure/context.js +1 -25
  32. package/base-modules/fee-structure/form.js +77 -89
  33. package/base-modules/fee-structure/more-actions.js +45 -22
  34. package/base-modules/rbac/context.d.ts +1 -0
  35. package/base-modules/rbac/context.js +24 -33
  36. package/base-modules/rbac/form.js +3 -1
  37. package/base-modules/school/context.js +1 -1
  38. package/base-modules/school/form.js +34 -14
  39. package/base-modules/section/context.d.ts +1 -0
  40. package/base-modules/section/context.js +40 -47
  41. package/base-modules/section/form.js +25 -80
  42. package/base-modules/section/more-actions.js +47 -24
  43. package/base-modules/section/view.js +9 -7
  44. package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
  45. package/base-modules/student-fee/context/use-student-fee-module.js +48 -32
  46. package/base-modules/student-fee/context.d.ts +1 -1
  47. package/base-modules/student-fee/context.js +1 -1
  48. package/base-modules/student-fee/filter.js +23 -3
  49. package/base-modules/student-fee/form.js +93 -174
  50. package/base-modules/student-fee/view.d.ts +7 -1
  51. package/base-modules/student-fee/view.js +17 -20
  52. package/base-modules/student-profile/constants.d.ts +0 -6
  53. package/base-modules/student-profile/constants.js +1 -3
  54. package/base-modules/student-profile/context/use-student-profile-module.d.ts +1 -0
  55. package/base-modules/student-profile/context/use-student-profile-module.js +62 -55
  56. package/base-modules/student-profile/context.d.ts +1 -1
  57. package/base-modules/student-profile/context.js +1 -1
  58. package/base-modules/student-profile/filter.js +24 -4
  59. package/base-modules/student-profile/form.js +35 -3
  60. package/base-modules/subject/context.d.ts +1 -0
  61. package/base-modules/subject/context.js +38 -47
  62. package/base-modules/subject/more-actions.js +47 -24
  63. package/base-modules/teacher/constants.d.ts +0 -6
  64. package/base-modules/teacher/constants.js +0 -2
  65. package/base-modules/teacher/context.d.ts +1 -0
  66. package/base-modules/teacher/context.js +58 -39
  67. package/base-modules/teacher/form.js +46 -11
  68. package/base-modules/teacher/more-actions.js +45 -22
  69. package/base-modules/user/context/use-user-module.d.ts +1 -0
  70. package/base-modules/user/context/use-user-module.js +36 -32
  71. package/base-modules/user/context.js +1 -1
  72. package/base-modules/user/filter.js +6 -4
  73. package/base-modules/user/form.js +29 -5
  74. package/base-modules/user/more-actions.js +9 -7
  75. package/base-modules/user/view.js +3 -1
  76. package/base-modules/workspace/form.js +18 -8
  77. package/base-modules/workspace-user/context.d.ts +2 -1
  78. package/base-modules/workspace-user/context.js +31 -29
  79. package/package.json +1 -1
  80. package/tsconfig.build.tsbuildinfo +1 -1
  81. package/base-modules/admission/cache.d.ts +0 -14
  82. package/base-modules/admission/cache.js +0 -31
  83. package/base-modules/attendance/cache.d.ts +0 -14
  84. package/base-modules/attendance/cache.js +0 -31
  85. package/base-modules/course/cache.d.ts +0 -14
  86. package/base-modules/course/cache.js +0 -31
  87. package/base-modules/enrollment/cache.d.ts +0 -14
  88. package/base-modules/enrollment/cache.js +0 -31
  89. package/base-modules/expense/cache.d.ts +0 -14
  90. package/base-modules/expense/cache.js +0 -31
  91. package/base-modules/family/cache.d.ts +0 -14
  92. package/base-modules/family/cache.js +0 -31
  93. package/base-modules/family-member/cache.d.ts +0 -14
  94. package/base-modules/family-member/cache.js +0 -31
  95. package/base-modules/rbac/cache.d.ts +0 -27
  96. package/base-modules/rbac/cache.js +0 -46
  97. package/base-modules/student-fee/cache.d.ts +0 -15
  98. package/base-modules/student-fee/cache.js +0 -21
  99. package/base-modules/workspace-user/cache.d.ts +0 -14
  100. package/base-modules/workspace-user/cache.js +0 -31
@@ -1,38 +1,47 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
3
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
4
4
  import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
5
5
  import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
6
- import { getCachedCoursesSync } from "../course/cache";
7
- import { getCachedEnrollmentsSync } from "../enrollment/cache";
8
- import { getCachedStudentProfilesSync } from "../student-profile/cache";
9
- import { useMemo, useState } from "react";
6
+ import { useState } from "react";
7
+ import { ATTENDANCE_API_ROUTES } from "./constants";
10
8
  export const AttendanceMoreActions = ({ labelAttendanceStatus, labelCourse, labelCourseInfo, labelCoursePlaceholder, labelNoCourseFound, labelRemarks, labelRemarksInfo, labelRemarksPlaceholder, labelSearchCourses, labelStatusAbsent, labelStatusExcused, labelStatusHalfDay, labelStatusLate, labelStatusPresent, }) => {
11
- const enrollment = getCachedEnrollmentsSync().items;
12
- const studentProfiles = getCachedStudentProfilesSync().items;
13
- const courses = getCachedCoursesSync().items;
9
+ // const enrollment = getCachedEnrollmentsSync().items;
10
+ // const studentProfiles = getCachedStudentProfilesSync().items;
11
+ // const courses = getCachedCoursesSync().items;
14
12
  const [courseId, setCourseId] = useState("");
15
13
  const [remarks, setRemarks] = useState({});
16
14
  const [attendance, setAttendance] = useState({});
17
15
  const [errors] = useState({});
16
+ const { enhancedComboboxElement: courseIdCombo } = useEnhancedCombobox({
17
+ emptyText: labelNoCourseFound,
18
+ id: "courseId",
19
+ info: labelCourseInfo,
20
+ label: labelCourse,
21
+ onValueChange: (value) => setCourseId(value),
22
+ options: [{ id: "", code: "" }].map((c) => ({
23
+ id: c.id,
24
+ name: c.code.trim(),
25
+ })),
26
+ placeholder: labelCoursePlaceholder,
27
+ required: true,
28
+ searchEndpoint: ATTENDANCE_API_ROUTES.UNIT,
29
+ searchPlaceholder: labelSearchCourses,
30
+ value: courseId || "",
31
+ });
18
32
  // Find the section for the selected course, then get students enrolled in that section
19
- const studentsInCourse = useMemo(() => {
20
- const courseObj = courses.find((c) => c.id === courseId);
21
- if (!courseObj)
22
- return [];
23
- return enrollment
24
- .filter((e) => e.sectionId === courseObj.sectionId)
25
- .map((e) => e.studentProfileId);
26
- }, [enrollment, courseId, courses]);
27
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedCombobox, { emptyText: labelNoCourseFound, error: errors.courseId, id: "courseId", info: labelCourseInfo, label: labelCourse, onValueChange: (value) => setCourseId(value), options: getCachedCoursesSync().items.map((c) => ({
28
- label: c.code.trim(),
29
- value: c.id,
30
- })), placeholder: labelCoursePlaceholder, required: true, searchPlaceholder: labelSearchCourses, value: courseId || "" }), courseId &&
31
- studentsInCourse.length > 0 &&
32
- studentsInCourse.map((s) => {
33
- var _a, _b, _c, _d;
34
- const student = studentProfiles.find((sp) => sp.id === s);
35
- return (_jsxs("div", { children: [_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { className: "flex flex-col items-center justify-center", children: [_jsxs("p", { children: [(_a = student === null || student === void 0 ? void 0 : student.familyMember) === null || _a === void 0 ? void 0 : _a.firstName, " ", (_b = student === null || student === void 0 ? void 0 : student.familyMember) === null || _b === void 0 ? void 0 : _b.lastName] }), _jsx("img", { src: (_c = student === null || student === void 0 ? void 0 : student.familyMember) === null || _c === void 0 ? void 0 : _c.avatar, alt: (_d = student === null || student === void 0 ? void 0 : student.familyMember) === null || _d === void 0 ? void 0 : _d.firstName, width: 100, height: 100, className: "rounded-full" })] }), _jsx("div", { children: _jsx(EnhancedRadio, { label: labelAttendanceStatus, name: "attendanceStatus", value: attendance[s] || "PRESENT", options: [
33
+ // const studentsInCourse = useMemo(() => {
34
+ // const courseObj = courses.find((c) => c.id === courseId);
35
+ // if (!courseObj) return [];
36
+ // return enrollment
37
+ // .filter((e) => e.sectionId === courseObj.sectionId)
38
+ // .map((e) => e.studentProfileId);
39
+ // }, [enrollment, courseId, courses]);
40
+ return (_jsxs("div", { className: "space-y-4", children: [courseIdCombo, courseId &&
41
+ [].length > 0 &&
42
+ [].map((s) => {
43
+ // const student = studentProfiles.find((sp) => sp.id === s);
44
+ return (_jsxs("div", { children: [_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { className: "flex flex-col items-center justify-center", children: [_jsx("p", { children: " " }), _jsx("img", { src: "student?.familyMember?.avatar", alt: "student?.familyMember?.firstName", width: 100, height: 100, className: "rounded-full" })] }), _jsx("div", { children: _jsx(EnhancedRadio, { label: labelAttendanceStatus, name: "attendanceStatus", value: attendance[s] || "PRESENT", options: [
36
45
  { label: labelStatusPresent, value: "PRESENT" },
37
46
  { label: labelStatusAbsent, value: "ABSENT" },
38
47
  { label: labelStatusLate, value: "LATE" },
@@ -397,19 +397,19 @@ export const useCampusModule = () => {
397
397
  {
398
398
  enabled: false,
399
399
  handleOnClick: handleMoreActions,
400
- label: t("actionHeaderMoreActions"),
400
+ label: t("actionsButtonMoreActions"),
401
401
  order: 0,
402
402
  },
403
403
  {
404
404
  enabled: true,
405
405
  handleOnClick: handleFilters,
406
- label: t("actionHeaderFilters"),
406
+ label: t("actionsButtonFilters"),
407
407
  order: 1,
408
408
  },
409
409
  {
410
410
  enabled: true,
411
411
  handleOnClick: handleCreate,
412
- label: t("actionHeaderAdd"),
412
+ label: t("actionsButtonAdd"),
413
413
  order: 2,
414
414
  },
415
415
  ], [handleCreate, handleFilters, handleMoreActions, t]);
@@ -417,26 +417,26 @@ export const useCampusModule = () => {
417
417
  {
418
418
  enabled: true,
419
419
  handleOnClick: handleView,
420
- label: t("actionRowView"),
420
+ label: t("actionsButtonView"),
421
421
  order: 1,
422
422
  },
423
423
  {
424
424
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === true,
425
425
  handleOnClick: handleEdit,
426
- label: t("actionRowEdit"),
426
+ label: t("actionsButtonEdit"),
427
427
  order: 2,
428
428
  },
429
429
  {
430
430
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === false,
431
431
  handleOnClick: handleDelete,
432
- label: t("actionRowDelete"),
432
+ label: t("actionsButtonDelete"),
433
433
  order: 3,
434
434
  variant: "destructive",
435
435
  },
436
436
  {
437
437
  enabled: false,
438
438
  handleOnClick: toggleStatus,
439
- label: t("actionRowToggleStatus"),
439
+ label: t("actionsButtonToggleStatus"),
440
440
  order: 4,
441
441
  },
442
442
  ], [handleDelete, handleEdit, handleView, t, toggleStatus]);
@@ -22,7 +22,6 @@ export const getCachedClasses = ({ params, }) => getCachedData({
22
22
  params,
23
23
  headers: {
24
24
  "Content-Type": "application/json",
25
- // "x-api-token": process.env.NEXT_PUBLIC_API_KEY!,
26
25
  },
27
26
  });
28
27
  export const getCachedClassById = (classId) => getCachedItemById(LS_KEYS.CLASSES, classId);
@@ -28,7 +28,6 @@ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-com
28
28
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
29
29
  import { CLASS_API_ROUTES, pageLimit } from "./constants";
30
30
  import { classFormValidation } from "./validate";
31
- import { getCachedClasses, invalidateClassesCache } from "./cache";
32
31
  import { getCachedWorkspaceSync } from "../workspace/cache";
33
32
  // ============================================================================
34
33
  // 1.1 DRAWER TYPES
@@ -159,7 +158,6 @@ export const useClassModule = () => {
159
158
  }
160
159
  if (data) {
161
160
  const isCreated = isCreatedOrUpdated(data);
162
- invalidateClassesCache();
163
161
  showToast(isCreated ? t("messagesCreateSuccess") : t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
164
162
  resetFormAndCloseDrawer();
165
163
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
@@ -184,7 +182,6 @@ export const useClassModule = () => {
184
182
  return;
185
183
  }
186
184
  if (data) {
187
- invalidateClassesCache();
188
185
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
189
186
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
190
187
  }
@@ -378,19 +375,19 @@ export const useClassModule = () => {
378
375
  {
379
376
  enabled: true,
380
377
  handleOnClick: handleMoreActions,
381
- label: t("actionHeaderMoreActions"),
378
+ label: t("actionsButtonMoreActions"),
382
379
  order: 0,
383
380
  },
384
381
  {
385
382
  enabled: true,
386
383
  handleOnClick: handleFilters,
387
- label: t("actionHeaderFilters"),
384
+ label: t("actionsButtonFilters"),
388
385
  order: 1,
389
386
  },
390
387
  {
391
388
  enabled: true,
392
389
  handleOnClick: handleCreate,
393
- label: t("actionHeaderAdd"),
390
+ label: t("actionsButtonAdd"),
394
391
  order: 2,
395
392
  },
396
393
  ], [handleCreate, handleFilters, handleMoreActions, t]);
@@ -398,74 +395,39 @@ export const useClassModule = () => {
398
395
  {
399
396
  enabled: true,
400
397
  handleOnClick: handleView,
401
- label: t("actionRowView"),
398
+ label: t("actionsButtonView"),
402
399
  order: 1,
403
400
  },
404
401
  {
405
402
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === true,
406
403
  handleOnClick: handleEdit,
407
- label: t("actionRowEdit"),
404
+ label: t("actionsButtonEdit"),
408
405
  order: 2,
409
406
  },
410
407
  {
411
408
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === false,
412
409
  handleOnClick: handleDelete,
413
- label: t("actionRowDelete"),
410
+ label: t("actionsButtonDelete"),
414
411
  order: 3,
415
412
  variant: "destructive",
416
413
  },
417
414
  {
418
415
  enabled: false,
419
416
  handleOnClick: toggleStatus,
420
- label: t("actionRowToggleStatus"),
417
+ label: t("actionsButtonToggleStatus"),
421
418
  order: 4,
422
419
  },
423
420
  ], [handleDelete, handleEdit, handleView, t, toggleStatus]);
424
421
  // ============================================================================
425
422
  // 1.4.9 EFFECTS
426
423
  // ============================================================================
427
- // Always keep the ref current so the main effect can call the latest listFetchNow
428
- // without including it in the dependency array (listFetchNow changes identity every
429
- // render because useModuleEntityV2 passes an inline headers object to useFetch).
430
- // Declared first so the ref is populated before the fetch effect runs.
431
424
  useEffect(() => {
432
425
  listFetchNowRef.current = listFetchNow;
433
- });
434
- // Initial load via cache; re-fetch directly from API on page/pageLimit/filter/search changes.
435
- // listFetchNow is intentionally excluded from deps — it changes every render due to the
436
- // inline headers object in useModuleEntityV2. We call it via the stable ref instead.
426
+ }, [listFetchNow]);
437
427
  useEffect(() => {
438
428
  var _a;
439
- if (!schoolId)
440
- return;
441
- const currentPage = Number(listParams.currentPage) || 1;
442
- const currentPageLimit = Number(listParams.pageLimit) || pageLimit;
443
- const isDefaultLoad = currentPage === 1 &&
444
- currentPageLimit === pageLimit &&
445
- !listParams.searchQuery &&
446
- listParams.filterEnabled === undefined;
447
- if (isDefaultLoad) {
448
- (async () => {
449
- try {
450
- const { count, items } = await getCachedClasses({
451
- params: listParams,
452
- });
453
- dispatch({
454
- type: CLASS_ACTION_TYPES.SET_ITEMS,
455
- payload: { items: items || [], count: count || 0 },
456
- });
457
- }
458
- catch (_a) {
459
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
460
- }
461
- })();
462
- }
463
- else {
464
- // Bypass cache for pagination, pageLimit, filter and search changes.
465
- // Use ref to avoid the infinite-loop caused by listFetchNow's unstable identity.
466
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
467
- }
468
- }, [dispatch, listParams, schoolId, showToast, t]);
429
+ (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
430
+ }, [listParams]);
469
431
  // ============================================================================
470
432
  // 1.4.10 RETURN
471
433
  // ============================================================================
@@ -7,7 +7,6 @@ import converter from "json-2-csv";
7
7
  import { Timeline } from "../../components/timeline";
8
8
  import { useTranslations } from "next-intl";
9
9
  import { CLASS_API_ROUTES, pageLimit } from "./constants";
10
- import { invalidateClassesCache } from "./cache";
11
10
  import { CLASS_ACTION_TYPES, useClassContext } from "./context";
12
11
  import { useRef, useEffect, useCallback } from "react";
13
12
  const workspace = getCachedWorkspaceSync();
@@ -68,13 +67,17 @@ async function pollBulkJob(jobId, signal, onProgress) {
68
67
  }
69
68
  }
70
69
  }
71
- function formatErrorSummary(errors) {
70
+ function formatErrorSummary(
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ t, errors) {
72
73
  if (!(errors === null || errors === void 0 ? void 0 : errors.length))
73
74
  return "";
74
- const lines = errors.slice(0, 5).map((e) => `Row ${e.row}: ${e.error}`);
75
+ const lines = errors
76
+ .slice(0, 5)
77
+ .map((e) => t("messagesBulkRowError", { row: e.row, error: e.error }));
75
78
  const remaining = errors.length - 5;
76
79
  if (remaining > 0)
77
- lines.push(`...and ${remaining} more`);
80
+ lines.push(t("messagesBulkMoreRows", { count: remaining }));
78
81
  return lines.join("\n");
79
82
  }
80
83
  export const ClassMoreActions = () => {
@@ -107,7 +110,7 @@ export const ClassMoreActions = () => {
107
110
  const text = await file.text();
108
111
  const records = converter.csv2json(text);
109
112
  if (!Array.isArray(records) || records.length === 0) {
110
- showErrorToast("CSV file is empty or invalid");
113
+ showErrorToast(t("messagesBulkCsvEmpty"));
111
114
  return;
112
115
  }
113
116
  // Client-side validation — basic required field check
@@ -117,55 +120,72 @@ export const ClassMoreActions = () => {
117
120
  const msgs = [];
118
121
  if (method === "POST") {
119
122
  if (!((_b = row.code) === null || _b === void 0 ? void 0 : _b.trim()))
120
- msgs.push("code is required");
123
+ msgs.push(t("validationRequiredCode"));
121
124
  if (!((_c = row.name) === null || _c === void 0 ? void 0 : _c.trim()))
122
- msgs.push("name is required");
125
+ msgs.push(t("validationRequiredName"));
123
126
  }
124
127
  else {
125
128
  if (!((_d = row.id) === null || _d === void 0 ? void 0 : _d.trim()))
126
- msgs.push("id is required for update");
129
+ msgs.push(t("validationRequiredIdForUpdate"));
127
130
  }
128
131
  if (msgs.length > 0) {
129
132
  validationErrors.push({ row: i + 1, messages: msgs });
130
133
  }
131
134
  }
132
135
  if (validationErrors.length > 0) {
133
- const summary = validationErrors
136
+ const errorsList = validationErrors
134
137
  .slice(0, 5)
135
- .map((e) => `Row ${e.row}: ${e.messages.join("; ")}`)
138
+ .map((e) => t("messagesBulkRowError", {
139
+ row: e.row,
140
+ error: e.messages.join("; "),
141
+ }))
136
142
  .join("\n");
137
143
  const remaining = validationErrors.length - 5;
138
- showErrorToast(`Validation failed for ${validationErrors.length} row(s).\n${summary}${remaining > 0 ? `\n...and ${remaining} more` : ""}`);
144
+ const errorsStr = remaining > 0
145
+ ? `${errorsList}\n${t("messagesBulkMoreRows", { count: remaining })}`
146
+ : errorsList;
147
+ showErrorToast(t("messagesBulkValidationFailed", {
148
+ count: validationErrors.length,
149
+ errors: errorsStr,
150
+ }));
139
151
  return;
140
152
  }
141
153
  try {
142
- showInfoToast(`Bulk ${label} job queued (${records.length} records). Processing...`);
154
+ showInfoToast(t("messagesBulkJobQueued", { action: label, count: records.length }));
143
155
  let jobId;
144
156
  try {
145
157
  jobId = await submitBulkJob(text, method, signal);
146
158
  }
147
159
  catch (submitError) {
148
- showErrorToast(`Failed to submit ${label} job: ${submitError instanceof Error ? submitError.message : "Unknown error"}`);
160
+ showErrorToast(t("messagesBulkJobSubmitFailed", {
161
+ action: label,
162
+ error: submitError instanceof Error
163
+ ? submitError.message
164
+ : t("unknownError"),
165
+ }));
149
166
  return;
150
167
  }
151
168
  const status = await pollBulkJob(jobId, signal, (processed, total) => {
152
- showInfoToast(`Processing ${processed}/${total} classes...`);
169
+ showInfoToast(t("messagesBulkProgress", { processed, total }));
153
170
  });
154
171
  if (signal.aborted)
155
172
  return;
156
173
  if (status.status === "completed") {
157
174
  const r = status.results;
158
175
  if (r && ((_e = r.errors) === null || _e === void 0 ? void 0 : _e.length) > 0) {
159
- const summary = formatErrorSummary(r.errors);
160
- showSuccessToast(`Created ${r.created} | Updated ${r.updated} | Skipped ${r.skipped}\n${summary}`);
176
+ const summary = formatErrorSummary(t, r.errors);
177
+ showSuccessToast(`${t("messagesBulkResults", { created: r.created, updated: r.updated, skipped: r.skipped })}\n${summary}`);
161
178
  }
162
179
  else if (r) {
163
- showSuccessToast(`Created ${r.created} | Updated ${r.updated} | Skipped ${r.skipped}`);
180
+ showSuccessToast(t("messagesBulkResults", {
181
+ created: r.created,
182
+ updated: r.updated,
183
+ skipped: r.skipped,
184
+ }));
164
185
  }
165
186
  else {
166
- showSuccessToast("Bulk operation completed successfully");
187
+ showSuccessToast(t("messagesBulkSuccess"));
167
188
  }
168
- invalidateClassesCache();
169
189
  const schoolId = ((_f = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _f === void 0 ? void 0 : _f.id) || "";
170
190
  fetch(`${CLASS_API_ROUTES.LIST}?currentPage=1&pageLimit=${pageLimit}&schoolId=${schoolId}`, {
171
191
  headers: {
@@ -191,17 +211,20 @@ export const ClassMoreActions = () => {
191
211
  else {
192
212
  const r = status.results;
193
213
  const detail = ((_g = r === null || r === void 0 ? void 0 : r.errors) === null || _g === void 0 ? void 0 : _g.length)
194
- ? formatErrorSummary(r.errors)
195
- : "Unknown error";
196
- showErrorToast(`Bulk ${label} failed.\n${detail}`);
214
+ ? formatErrorSummary(t, r.errors)
215
+ : t("unknownError");
216
+ showErrorToast(`${t("messagesBulkFailed", { action: label })}\n${detail}`);
197
217
  }
198
218
  }
199
219
  catch (error) {
200
220
  if (error.message === "Polling cancelled")
201
221
  return;
202
- showErrorToast(`Bulk ${label} failed: ${error instanceof Error ? error.message : "Unknown error"}`);
222
+ showErrorToast(t("messagesBulkFailedDetail", {
223
+ action: label,
224
+ error: error instanceof Error ? error.message : t("unknownError"),
225
+ }));
203
226
  }
204
- }, [dispatch]);
227
+ }, [dispatch, t]);
205
228
  const handleBulkCreate = useCallback((files) => handleBulkFlow(files, "POST"), [handleBulkFlow]);
206
229
  const handleBulkUpdate = useCallback((files) => handleBulkFlow(files, "PUT"), [handleBulkFlow]);
207
230
  const create = [
@@ -25,7 +25,6 @@ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
25
25
  import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
26
26
  import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
27
27
  import { COURSE_API_ROUTES, pageLimit } from "./constants";
28
- import { getCachedCourses, invalidateCoursesCache } from "./cache";
29
28
  import { courseFormValidation } from "./validate";
30
29
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
31
30
  import { getCachedWorkspaceSync } from "../workspace/cache";
@@ -167,7 +166,6 @@ export const useCourseModule = () => {
167
166
  showToast(isCreatedOrUpdated(data)
168
167
  ? t("messagesCourseCreated")
169
168
  : t("messagesCourseUpdated"), TOAST_VARIANT.SUCCESS);
170
- invalidateCoursesCache();
171
169
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
172
170
  resetFormAndCloseDrawer();
173
171
  }
@@ -247,7 +245,6 @@ export const useCourseModule = () => {
247
245
  }
248
246
  if (data) {
249
247
  showToast(t("messagesCourseDeleted"), TOAST_VARIANT.SUCCESS);
250
- invalidateCoursesCache();
251
248
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
252
249
  }
253
250
  }, [showToast, t]);
@@ -456,44 +453,13 @@ export const useCourseModule = () => {
456
453
  // ==========================================================================
457
454
  // 1.4.9 EFFECTS
458
455
  // ==========================================================================
459
- // Keep the latest fetch function in a ref so the fetch effect can call it
460
- // without depending on listFetchNow's unstable identity.
461
456
  useEffect(() => {
462
457
  listFetchNowRef.current = listFetchNow;
463
- });
464
- // Initial load via cache; re-fetch directly from API on page/pageLimit/filter/search changes.
458
+ }, [listFetchNow]);
465
459
  useEffect(() => {
466
460
  var _a;
467
- if (!schoolId)
468
- return;
469
- const currentPage = Number(listParams.currentPage) || 1;
470
- const currentPageLimit = Number(listParams.pageLimit) || pageLimit;
471
- const isDefaultLoad = currentPage === 1 &&
472
- currentPageLimit === pageLimit &&
473
- !listParams.searchQuery &&
474
- listParams.filterEnabled === undefined;
475
- if (isDefaultLoad) {
476
- (async () => {
477
- try {
478
- const { count, items } = await getCachedCourses({
479
- params: listParams,
480
- });
481
- dispatch({
482
- type: COURSE_ACTION_TYPES.SET_ITEMS,
483
- payload: { items: items || [], count: count || 0 },
484
- });
485
- }
486
- catch (_a) {
487
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
488
- }
489
- })();
490
- }
491
- else {
492
- // Bypass cache for pagination, pageLimit, filter and search changes.
493
- // Use ref to avoid the infinite-loop caused by listFetchNow's unstable identity.
494
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
495
- }
496
- }, [listParams, dispatch, showToast, schoolId, t]);
461
+ (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
462
+ }, [listParams]);
497
463
  // ==========================================================================
498
464
  // 1.4.10 RETURN
499
465
  // ==========================================================================