@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
@@ -8,7 +8,6 @@ import { Timeline } from "../../components/timeline";
8
8
  import { useTranslations } from "next-intl";
9
9
  import { feeStructureFormValidation } from "./validate";
10
10
  import { FEE_STRUCTURE_API_ROUTES, pageLimit } from "./constants";
11
- import { invalidateFeeStructuresCache } from "./cache";
12
11
  import { FEE_STRUCTURE_ACTION_TYPES, useFeeStructureContext } from "./context";
13
12
  import { useRef, useEffect, useCallback } from "react";
14
13
  const workspace = getCachedWorkspaceSync();
@@ -68,13 +67,15 @@ async function pollBulkJob(jobId, signal, onProgress) {
68
67
  }
69
68
  }
70
69
  }
71
- function formatErrorSummary(errors) {
70
+ function formatErrorSummary(t, errors) {
72
71
  if (!(errors === null || errors === void 0 ? void 0 : errors.length))
73
72
  return "";
74
- const lines = errors.slice(0, 5).map((e) => `Row ${e.row}: ${e.error}`);
73
+ const lines = errors
74
+ .slice(0, 5)
75
+ .map((e) => t("messagesBulkRowError", { row: e.row, error: e.error }));
75
76
  const remaining = errors.length - 5;
76
77
  if (remaining > 0)
77
- lines.push(`...and ${remaining} more`);
78
+ lines.push(t("messagesBulkMoreRows", { count: remaining }));
78
79
  return lines.join("\n");
79
80
  }
80
81
  export const FeeStructureMoreActions = () => {
@@ -104,7 +105,7 @@ export const FeeStructureMoreActions = () => {
104
105
  const text = await file.text();
105
106
  const records = converter.csv2json(text);
106
107
  if (!Array.isArray(records) || records.length === 0) {
107
- showErrorToast("CSV file is empty or invalid");
108
+ showErrorToast(t("messagesBulkCsvEmpty"));
108
109
  return;
109
110
  }
110
111
  const validationErrors = [];
@@ -119,7 +120,7 @@ export const FeeStructureMoreActions = () => {
119
120
  }
120
121
  else {
121
122
  if (!((_b = row.id) === null || _b === void 0 ? void 0 : _b.trim()))
122
- msgs.push("id is required for update");
123
+ msgs.push(t("validationRequiredIdForUpdate"));
123
124
  }
124
125
  if (msgs.length > 0) {
125
126
  validationErrors.push({ row: i + 1, messages: msgs });
@@ -128,40 +129,59 @@ export const FeeStructureMoreActions = () => {
128
129
  if (validationErrors.length > 0) {
129
130
  const summary = validationErrors
130
131
  .slice(0, 5)
131
- .map((e) => `Row ${e.row}: ${e.messages.join("; ")}`)
132
+ .map((e) => t("messagesBulkRowError", {
133
+ row: e.row,
134
+ error: e.messages.join("; "),
135
+ }))
132
136
  .join("\n");
133
- const remaining = validationErrors.length - 5;
134
- showErrorToast(`Validation failed for ${validationErrors.length} row(s).\n${summary}${remaining > 0 ? `\n...and ${remaining} more` : ""}`);
137
+ showErrorToast(t("messagesBulkValidationFailed", {
138
+ count: validationErrors.length,
139
+ errors: summary,
140
+ }));
135
141
  return;
136
142
  }
137
143
  try {
138
- showInfoToast(`Bulk ${label} job queued (${records.length} records). Processing...`);
144
+ showInfoToast(t("messagesBulkJobQueued", { action: label, count: records.length }));
139
145
  let jobId;
140
146
  try {
141
147
  jobId = await submitBulkJob(text, method, signal);
142
148
  }
143
149
  catch (submitError) {
144
- showErrorToast(`Failed to submit ${label} job: ${submitError instanceof Error ? submitError.message : "Unknown error"}`);
150
+ showErrorToast(t("messagesBulkJobSubmitFailed", {
151
+ action: label,
152
+ error: submitError instanceof Error
153
+ ? submitError.message
154
+ : t("unknownError"),
155
+ }));
145
156
  return;
146
157
  }
147
158
  const status = await pollBulkJob(jobId, signal, (processed, total) => {
148
- showInfoToast(`Processing ${processed}/${total} fee structures...`);
159
+ showInfoToast(t("messagesBulkProgress", { processed, total }));
149
160
  });
150
161
  if (signal.aborted)
151
162
  return;
152
163
  if (status.status === "completed") {
153
164
  const r = status.results;
154
165
  if (r && ((_c = r.errors) === null || _c === void 0 ? void 0 : _c.length) > 0) {
155
- const summary = formatErrorSummary(r.errors);
156
- showSuccessToast(`Created ${r.created} | Updated ${r.updated} | Skipped ${r.skipped}\n${summary}`);
166
+ const summary = formatErrorSummary(t, r.errors);
167
+ showSuccessToast(t("messagesBulkResults", {
168
+ created: r.created,
169
+ updated: r.updated,
170
+ skipped: r.skipped,
171
+ }) +
172
+ "\n" +
173
+ summary);
157
174
  }
158
175
  else if (r) {
159
- showSuccessToast(`Created ${r.created} | Updated ${r.updated} | Skipped ${r.skipped}`);
176
+ showSuccessToast(t("messagesBulkResults", {
177
+ created: r.created,
178
+ updated: r.updated,
179
+ skipped: r.skipped,
180
+ }));
160
181
  }
161
182
  else {
162
- showSuccessToast("Bulk operation completed successfully");
183
+ showSuccessToast(t("messagesBulkSuccess"));
163
184
  }
164
- invalidateFeeStructuresCache();
165
185
  const schoolId = ((_d = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _d === void 0 ? void 0 : _d.id) || "";
166
186
  fetch(`${FEE_STRUCTURE_API_ROUTES.LIST}?currentPage=1&pageLimit=${pageLimit}&schoolId=${schoolId}`, {
167
187
  headers: {
@@ -187,17 +207,20 @@ export const FeeStructureMoreActions = () => {
187
207
  else {
188
208
  const r = status.results;
189
209
  const detail = ((_e = r === null || r === void 0 ? void 0 : r.errors) === null || _e === void 0 ? void 0 : _e.length)
190
- ? formatErrorSummary(r.errors)
191
- : "Unknown error";
192
- showErrorToast(`Bulk ${label} failed.\n${detail}`);
210
+ ? formatErrorSummary(t, r.errors)
211
+ : t("unknownError");
212
+ showErrorToast(t("messagesBulkFailed", { action: label }) + "\n" + detail);
193
213
  }
194
214
  }
195
215
  catch (error) {
196
216
  if (error.message === "Polling cancelled")
197
217
  return;
198
- showErrorToast(`Bulk ${label} failed: ${error instanceof Error ? error.message : "Unknown error"}`);
218
+ showErrorToast(t("messagesBulkFailedDetail", {
219
+ action: label,
220
+ error: error instanceof Error ? error.message : t("unknownError"),
221
+ }));
199
222
  }
200
- }, [dispatch]);
223
+ }, [dispatch, t]);
201
224
  const handleBulkCreate = useCallback((files) => handleBulkFlow(files, "POST"), [handleBulkFlow]);
202
225
  const handleBulkUpdate = useCallback((files) => handleBulkFlow(files, "PUT"), [handleBulkFlow]);
203
226
  const create = [
@@ -120,6 +120,7 @@ export declare const useRbacModule: () => {
120
120
  closeDrawer: () => void;
121
121
  deleteLoading: boolean;
122
122
  handleChange: (key: string, value: string | number | boolean | undefined) => void;
123
+ resetRecordFormState: () => void;
123
124
  handleCreate: () => void;
124
125
  handleDelete: (row?: TableRow) => void;
125
126
  handleEdit: (row?: TableRow) => Promise<void>;
@@ -27,9 +27,7 @@ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
27
27
  import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
28
28
  import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
29
29
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
30
- import { USER_ROLE } from "../../type";
31
30
  import { RBAC_API_ROUTES, pageLimit } from "./constants";
32
- import { getCachedRoles, invalidateRolesCache } from "./cache";
33
31
  import { roleFormValidation } from "./validate";
34
32
  // ============================================================================
35
33
  // 1.1 DRAWER TYPES
@@ -105,7 +103,6 @@ export const useRbacModule = () => {
105
103
  }), [state.id, state.name, state.description]);
106
104
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
107
105
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
108
- const isDefaultListState = state.currentPage === 1 && state.pageLimit === pageLimit && !debouncedQuery;
109
106
  // ============================================================================
110
107
  // 1.4.3 UTILITIES
111
108
  // ============================================================================
@@ -154,7 +151,6 @@ export const useRbacModule = () => {
154
151
  }
155
152
  if (data) {
156
153
  const isCreated = isCreatedOrUpdated(data);
157
- invalidateRolesCache();
158
154
  showToast(isCreated ? t("messagesRoleCreated") : t("messagesRoleUpdated"), TOAST_VARIANT.SUCCESS);
159
155
  resetFormAndCloseDrawer();
160
156
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
@@ -179,7 +175,6 @@ export const useRbacModule = () => {
179
175
  return;
180
176
  }
181
177
  if (data) {
182
- invalidateRolesCache();
183
178
  showToast(t("messagesRoleDeleted"), TOAST_VARIANT.SUCCESS);
184
179
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
185
180
  }
@@ -255,15 +250,31 @@ export const useRbacModule = () => {
255
250
  payload: { currentPage: 1 },
256
251
  });
257
252
  }, [dispatch]);
253
+ const resetRecordFormState = useCallback(() => {
254
+ dispatch({
255
+ type: RBAC_ACTION_TYPES.SET_INPUT_FIELD,
256
+ payload: { key: "description", value: "" },
257
+ });
258
+ dispatch({
259
+ type: RBAC_ACTION_TYPES.SET_INPUT_FIELD,
260
+ payload: { key: "enabled", value: true },
261
+ });
262
+ dispatch({
263
+ type: RBAC_ACTION_TYPES.SET_INPUT_FIELD,
264
+ payload: { key: "name", value: "" },
265
+ });
266
+ }, [dispatch]);
258
267
  // row actions
259
268
  const handleView = useCallback((row) => {
269
+ resetRecordFormState();
260
270
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
261
271
  dispatch({
262
272
  type: RBAC_ACTION_TYPES.SET_DRAWER,
263
273
  payload: { drawer: RBAC_DRAWER.VIEW_DRAWER },
264
274
  });
265
- }, [byIdFetchNow, dispatch]);
275
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
266
276
  const handleEdit = useCallback(async (row) => {
277
+ resetRecordFormState();
267
278
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
268
279
  // loadAvailablePermissions is declared below in 1.4.7
269
280
  // await loadAvailablePermissions();
@@ -271,7 +282,7 @@ export const useRbacModule = () => {
271
282
  type: RBAC_ACTION_TYPES.SET_DRAWER,
272
283
  payload: { drawer: RBAC_DRAWER.FORM_DRAWER },
273
284
  });
274
- }, [byIdFetchNow, dispatch]);
285
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
275
286
  const handleDelete = useCallback((row) => {
276
287
  if (!confirm(t("messagesDeleteConfirmation")))
277
288
  return;
@@ -410,7 +421,6 @@ export const useRbacModule = () => {
410
421
  }
411
422
  // Refetch the role so rolePermissions in state reflects the change
412
423
  await (byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: state.id } }));
413
- invalidateRolesCache();
414
424
  }
415
425
  catch (_a) {
416
426
  showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
@@ -468,35 +478,15 @@ export const useRbacModule = () => {
468
478
  // ============================================================================
469
479
  // 1.4.9 EFFECTS
470
480
  // ============================================================================
471
- // Initial load + re-fetch on page/search change via cache
472
- useEffect(() => {
473
- var _a;
474
- if (isDefaultListState) {
475
- (async () => {
476
- try {
477
- const { count, items } = await getCachedRoles({
478
- params: listParams,
479
- });
480
- dispatch({
481
- type: RBAC_ACTION_TYPES.SET_ITEMS,
482
- payload: {
483
- items: items.filter((item) => item.userRole !== USER_ROLE.SUPER_ADMIN) || [],
484
- count: count || 0,
485
- },
486
- });
487
- }
488
- catch (_a) {
489
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
490
- }
491
- })();
492
- return;
493
- }
494
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
495
- }, [dispatch, isDefaultListState, listParams, showToast, t]);
496
481
  // Sync listFetchNow into ref so callbacks can trigger re-fetch
497
482
  useEffect(() => {
498
483
  listFetchNowRef.current = listFetchNow;
499
484
  }, [listFetchNow]);
485
+ // Fetch list on mount and on page/search change
486
+ useEffect(() => {
487
+ var _a;
488
+ (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
489
+ }, [dispatch, listParams, showToast, t]);
500
490
  // ============================================================================
501
491
  // 1.4.10 RETURN
502
492
  // ============================================================================
@@ -507,6 +497,7 @@ export const useRbacModule = () => {
507
497
  closeDrawer,
508
498
  deleteLoading,
509
499
  handleChange,
500
+ resetRecordFormState,
510
501
  handleCreate,
511
502
  handleDelete,
512
503
  handleEdit,
@@ -9,7 +9,9 @@ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
9
9
  import { Separator } from "@appcorp/shadcn/components/ui/separator";
10
10
  import { useRbacModule } from "./context";
11
11
  import { AssignPermissions } from "./assign-permissions";
12
+ import { useTranslations } from "next-intl";
12
13
  export const RbacForm = () => {
14
+ const t = useTranslations("rbac");
13
15
  const { state, handleChange } = useRbacModule();
14
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { id: "name", label: "Role Name", required: true, type: "text", value: state.name || "", onChange: (e) => handleChange("name", e.target.value), error: state.errors.name, placeholder: "e.g. Class Teacher, Admin, Accountant", info: "A unique, descriptive name for this role" }), _jsx(EnhancedInput, { id: "description", label: "Description", type: "text", value: state.description || "", onChange: (e) => handleChange("description", e.target.value || undefined), error: state.errors.description, placeholder: "Brief description of what this role can do", info: "Optional description to clarify the role's purpose" })] }), state.id && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: "Permissions" }), _jsx(AssignPermissions, {})] })] }))] }));
16
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { id: "name", label: t("formNameLabel"), required: true, type: "text", value: state.name || "", onChange: (e) => handleChange("name", e.target.value), error: state.errors.name, placeholder: t("formNamePlaceholder"), info: t("formNameInfo") }), _jsx(EnhancedInput, { id: "description", label: t("formDescriptionLabel"), type: "text", value: state.description || "", onChange: (e) => handleChange("description", e.target.value || undefined), error: state.errors.description, placeholder: t("formDescriptionPlaceholder"), info: t("formDescriptionInfo") })] }), state.id && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: t("viewSectionPermissions") }), _jsx(AssignPermissions, {})] })] }))] }));
15
17
  };
@@ -259,7 +259,7 @@ export const useSchoolModule = () => {
259
259
  {
260
260
  enabled: true,
261
261
  handleOnClick: handleEdit,
262
- label: t("actionHeaderEdit"),
262
+ label: t("actionsButtonEdit"),
263
263
  order: 0,
264
264
  },
265
265
  ], [handleEdit, t]);
@@ -7,24 +7,44 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  */
8
8
  import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
9
9
  import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
10
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
10
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
11
11
  import { useSchoolModule } from "./context";
12
- import { getCachedTeachersSync } from "../teacher/cache";
13
12
  import { CURRENCY } from "../../type";
13
+ import { SCHOOL_API_ROUTES } from "./constants";
14
14
  import { useTranslations } from "next-intl";
15
15
  export const SchoolForm = () => {
16
16
  const t = useTranslations("school");
17
17
  const { state, handleChange } = useSchoolModule();
18
- const { address, city, code, country, currency, email, enabled, errors, logo, name, phone, postalCode, principalId, state: stateField, website, } = state;
19
- // Currency options for combobox
20
- const currencyOptions = Object.values(CURRENCY).map((c) => ({
21
- label: c,
22
- value: c,
23
- }));
24
- return (_jsx("div", { className: "space-y-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { id: "name", label: t("formNameLabel"), value: name, onChange: (e) => handleChange("name", e.target.value), error: errors.name, placeholder: t("formNamePlaceholder"), required: true }), _jsx(EnhancedInput, { id: "code", label: t("formCodeLabel"), value: code, onChange: (e) => handleChange("code", e.target.value), error: errors.code, placeholder: t("formCodePlaceholder"), required: true, info: t("formCodeInfo") }), _jsx(EnhancedCombobox, { emptyText: t("formCurrencyEmptyText"), error: errors.currency, id: "currency", info: t("formCurrencyInfo"), label: t("formCurrencyLabel"), onValueChange: (value) => handleChange("currency", value), options: currencyOptions, placeholder: t("formCurrencyPlaceholder"), required: true, searchPlaceholder: t("formCurrencySearchPlaceholder"), value: currency }), _jsx(EnhancedInput, { id: "address", label: t("formAddressLabel"), value: address || "", onChange: (e) => handleChange("address", e.target.value), error: errors.address, placeholder: t("formAddressPlaceholder") }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { id: "city", label: t("formCityLabel"), value: city || "", onChange: (e) => handleChange("city", e.target.value), error: errors.city, placeholder: t("formCityPlaceholder") }), _jsx(EnhancedInput, { id: "state", label: t("formStateLabel"), value: stateField || "", onChange: (e) => handleChange("state", e.target.value), error: errors.state, placeholder: t("formStatePlaceholder") })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { id: "country", label: t("formCountryLabel"), value: country || "", onChange: (e) => handleChange("country", e.target.value), error: errors.country, placeholder: t("formCountryPlaceholder") }), _jsx(EnhancedInput, { id: "postalCode", label: t("formPostalCodeLabel"), value: postalCode || "", onChange: (e) => handleChange("postalCode", e.target.value), error: errors.postalCode, placeholder: t("formPostalCodePlaceholder") })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { id: "phone", label: t("formPhoneLabel"), value: phone || "", onChange: (e) => handleChange("phone", e.target.value), error: errors.phone, placeholder: t("formPhonePlaceholder"), type: "tel" }), _jsx(EnhancedInput, { id: "email", label: t("formEmailLabel"), value: email || "", onChange: (e) => handleChange("email", e.target.value), error: errors.email, placeholder: t("formEmailPlaceholder"), type: "email" })] }), _jsx(EnhancedInput, { id: "website", label: t("formWebsiteLabel"), value: website || "", onChange: (e) => handleChange("website", e.target.value), error: errors.website, placeholder: t("formWebsitePlaceholder"), type: "url" }), _jsx(EnhancedInput, { id: "logo", label: t("formLogoLabel"), value: logo || "", onChange: (e) => handleChange("logo", e.target.value), error: errors.logo, placeholder: t("formLogoPlaceholder"), type: "url" }), _jsx(EnhancedCombobox, { emptyText: t("formPrincipalEmptyText"), error: errors.principalId, id: "principalId", info: t("formPrincipalInfo"), label: t("formPrincipalLabel"), onValueChange: (value) => handleChange("principalId", value), options: getCachedTeachersSync().items.map((teacher) => ({
25
- label: `${teacher.firstName || ""} ${teacher.lastName || ""}`.trim() ||
26
- teacher.teacherCode ||
27
- teacher.id,
28
- value: teacher.id,
29
- })), placeholder: t("formPrincipalPlaceholder"), searchPlaceholder: t("formPrincipalSearchPlaceholder"), value: principalId || "" }), _jsx(EnhancedCheckbox, { id: "enabled", label: t("formEnabledLabel"), checked: enabled, error: errors.enabled, onCheckedChange: (checked) => handleChange("enabled", checked), info: t("formEnabledInfo") })] }) }));
18
+ const { address, city, code, country, currency, email, enabled, errors, logo, name, phone, postalCode,
19
+ // principalId,
20
+ state: stateField, website, } = state;
21
+ const { enhancedComboboxElement: currencyCombo } = useEnhancedCombobox({
22
+ emptyText: t("formCurrencyEmptyText"),
23
+ id: "currency",
24
+ info: t("formCurrencyInfo"),
25
+ label: t("formCurrencyLabel"),
26
+ onValueChange: (value) => handleChange("currency", value),
27
+ options: Object.values(CURRENCY).map((c) => ({ id: c, name: c })),
28
+ placeholder: t("formCurrencyPlaceholder"),
29
+ required: true,
30
+ searchEndpoint: SCHOOL_API_ROUTES.UNIT,
31
+ searchPlaceholder: t("formCurrencySearchPlaceholder"),
32
+ value: currency,
33
+ });
34
+ // const { enhancedComboboxElement: principalIdCombo } = useEnhancedCombobox({
35
+ // emptyText: t("formPrincipalEmptyText"),
36
+ // id: "principalId",
37
+ // info: t("formPrincipalInfo"),
38
+ // label: t("formPrincipalLabel"),
39
+ // onValueChange: (value) => handleChange("principalId", value),
40
+ // options: getCachedTeachersSync().items.map((teacher) => ({
41
+ // id: teacher.id as string,
42
+ // name: `${teacher.firstName || ""} ${teacher.lastName || ""}`.trim() || teacher.teacherCode || teacher.id,
43
+ // })),
44
+ // placeholder: t("formPrincipalPlaceholder"),
45
+ // searchEndpoint: SCHOOL_API_ROUTES.UNIT,
46
+ // searchPlaceholder: t("formPrincipalSearchPlaceholder"),
47
+ // value: principalId || "",
48
+ // });
49
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { id: "name", label: t("formNameLabel"), value: name, onChange: (e) => handleChange("name", e.target.value), error: errors.name, placeholder: t("formNamePlaceholder"), required: true }), _jsx(EnhancedInput, { id: "code", label: t("formCodeLabel"), value: code, onChange: (e) => handleChange("code", e.target.value), error: errors.code, placeholder: t("formCodePlaceholder"), required: true, info: t("formCodeInfo") }), currencyCombo, _jsx(EnhancedInput, { id: "address", label: t("formAddressLabel"), value: address || "", onChange: (e) => handleChange("address", e.target.value), error: errors.address, placeholder: t("formAddressPlaceholder") }), _jsx(EnhancedInput, { id: "city", label: t("formCityLabel"), value: city || "", onChange: (e) => handleChange("city", e.target.value), error: errors.city, placeholder: t("formCityPlaceholder") }), _jsx(EnhancedInput, { id: "state", label: t("formStateLabel"), value: stateField || "", onChange: (e) => handleChange("state", e.target.value), error: errors.state, placeholder: t("formStatePlaceholder") }), _jsx(EnhancedInput, { id: "country", label: t("formCountryLabel"), value: country || "", onChange: (e) => handleChange("country", e.target.value), error: errors.country, placeholder: t("formCountryPlaceholder") }), _jsx(EnhancedInput, { id: "postalCode", label: t("formPostalCodeLabel"), value: postalCode || "", onChange: (e) => handleChange("postalCode", e.target.value), error: errors.postalCode, placeholder: t("formPostalCodePlaceholder") }), _jsx(EnhancedInput, { id: "phone", label: t("formPhoneLabel"), value: phone || "", onChange: (e) => handleChange("phone", e.target.value), error: errors.phone, placeholder: t("formPhonePlaceholder"), type: "tel" }), _jsx(EnhancedInput, { id: "email", label: t("formEmailLabel"), value: email || "", onChange: (e) => handleChange("email", e.target.value), error: errors.email, placeholder: t("formEmailPlaceholder"), type: "email" })] }), _jsx(EnhancedInput, { id: "website", label: t("formWebsiteLabel"), value: website || "", onChange: (e) => handleChange("website", e.target.value), error: errors.website, placeholder: t("formWebsitePlaceholder"), type: "url" }), _jsx(EnhancedInput, { id: "logo", label: t("formLogoLabel"), value: logo || "", onChange: (e) => handleChange("logo", e.target.value), error: errors.logo, placeholder: t("formLogoPlaceholder"), type: "url" }), _jsx(EnhancedCheckbox, { id: "enabled", label: t("formEnabledLabel"), checked: enabled, error: errors.enabled, onCheckedChange: (checked) => handleChange("enabled", checked), info: t("formEnabledInfo") })] }));
30
50
  };
@@ -128,6 +128,7 @@ export declare const useSectionModule: () => {
128
128
  }[];
129
129
  listFetchNow: (url?: string, config?: import("@react-pakistan/util-functions/hooks/use-fetch").FetchConfig) => void;
130
130
  listLoading: boolean;
131
+ resetRecordFormState: () => void;
131
132
  rowActions: RowAction[];
132
133
  toggleStatus: (row?: TableRow) => void;
133
134
  updateLoading: boolean;
@@ -9,7 +9,7 @@
9
9
  * Key responsibilities:
10
10
  * - Expose `useSectionModule()` which UI components call for actions
11
11
  * - Keep module-specific `apiParams` and callbacks in one place
12
- * - Ensure cache invalidation and toast notifications on mutation
12
+ * - Ensure toast notifications on mutation
13
13
  *
14
14
  * Exported utilities:
15
15
  * - `SectionProvider` — provider component used by the page
@@ -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 { SECTION_API_ROUTES, pageLimit } from "./constants";
30
30
  import { sectionFormValidation } from "./validate";
31
- import { getCachedSections, invalidateSectionsCache } from "./cache";
32
31
  import { getCachedWorkspaceSync } from "../workspace/cache";
33
32
  // ============================================================================
34
33
  // 1.1 DRAWER TYPES
@@ -136,6 +135,30 @@ export const useSectionModule = () => {
136
135
  payload: { drawer: null },
137
136
  });
138
137
  }, [dispatch]);
138
+ const resetRecordFormState = useCallback(() => {
139
+ dispatch({
140
+ type: SECTION_ACTION_TYPES.SET_ERRORS,
141
+ payload: { errors: {} },
142
+ });
143
+ dispatch({
144
+ type: SECTION_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
145
+ payload: { disabled: false },
146
+ });
147
+ dispatch({
148
+ type: SECTION_ACTION_TYPES.SET_FORM_DATA,
149
+ payload: {
150
+ form: {
151
+ capacity: null,
152
+ classId: "",
153
+ enabled: true,
154
+ filterEnabled: undefined,
155
+ id: "",
156
+ name: "",
157
+ schoolId,
158
+ },
159
+ },
160
+ });
161
+ }, [dispatch, schoolId]);
139
162
  // ============================================================================
140
163
  // 1.4.4 API CALLBACKS
141
164
  // ============================================================================
@@ -161,7 +184,6 @@ export const useSectionModule = () => {
161
184
  }
162
185
  if (data) {
163
186
  const isCreated = isCreatedOrUpdated(data);
164
- invalidateSectionsCache();
165
187
  showToast(isCreated ? t("messagesCreateSuccess") : t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
166
188
  resetFormAndCloseDrawer();
167
189
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
@@ -186,7 +208,6 @@ export const useSectionModule = () => {
186
208
  return;
187
209
  }
188
210
  if (data) {
189
- invalidateSectionsCache();
190
211
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
191
212
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
192
213
  }
@@ -239,19 +260,21 @@ export const useSectionModule = () => {
239
260
  });
240
261
  }, [dispatch]);
241
262
  const handleView = useCallback((row) => {
263
+ resetRecordFormState();
242
264
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
243
265
  dispatch({
244
266
  type: SECTION_ACTION_TYPES.SET_DRAWER,
245
267
  payload: { drawer: SECTION_DRAWER.VIEW_DRAWER },
246
268
  });
247
- }, [byIdFetchNow, dispatch]);
269
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
248
270
  const handleEdit = useCallback((row) => {
271
+ resetRecordFormState();
249
272
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
250
273
  dispatch({
251
274
  type: SECTION_ACTION_TYPES.SET_DRAWER,
252
275
  payload: { drawer: SECTION_DRAWER.FORM_DRAWER },
253
276
  });
254
- }, [byIdFetchNow, dispatch]);
277
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
255
278
  const handleDelete = useCallback((row) => {
256
279
  if (!confirm(t("messagesDeleteConfirmation")))
257
280
  return;
@@ -372,19 +395,19 @@ export const useSectionModule = () => {
372
395
  {
373
396
  enabled: true,
374
397
  handleOnClick: handleMoreActions,
375
- label: t("actionHeaderMoreActions"),
398
+ label: t("actionsButtonMoreActions"),
376
399
  order: 0,
377
400
  },
378
401
  {
379
402
  enabled: true,
380
403
  handleOnClick: handleFilters,
381
- label: t("actionHeaderFilters"),
404
+ label: t("actionsButtonFilters"),
382
405
  order: 1,
383
406
  },
384
407
  {
385
408
  enabled: true,
386
409
  handleOnClick: handleCreate,
387
- label: t("actionHeaderAdd"),
410
+ label: t("actionsButtonAdd"),
388
411
  order: 2,
389
412
  },
390
413
  ], [handleCreate, handleFilters, handleMoreActions, t]);
@@ -392,70 +415,39 @@ export const useSectionModule = () => {
392
415
  {
393
416
  enabled: true,
394
417
  handleOnClick: handleView,
395
- label: t("actionRowView"),
418
+ label: t("actionsButtonView"),
396
419
  order: 1,
397
420
  },
398
421
  {
399
422
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === true,
400
423
  handleOnClick: handleEdit,
401
- label: t("actionRowEdit"),
424
+ label: t("actionsButtonEdit"),
402
425
  order: 2,
403
426
  },
404
427
  {
405
428
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === false,
406
429
  handleOnClick: handleDelete,
407
- label: t("actionRowDelete"),
430
+ label: t("actionsButtonDelete"),
408
431
  order: 3,
409
432
  variant: "destructive",
410
433
  },
411
434
  {
412
435
  enabled: false,
413
436
  handleOnClick: toggleStatus,
414
- label: t("actionRowToggleStatus"),
437
+ label: t("actionsButtonToggleStatus"),
415
438
  order: 4,
416
439
  },
417
440
  ], [handleDelete, handleEdit, handleView, t, toggleStatus]);
418
441
  // ============================================================================
419
442
  // 1.4.9 EFFECTS
420
443
  // ============================================================================
421
- // Keep the latest fetch function in a ref so the fetch effect can call it
422
- // without depending on listFetchNow's unstable identity.
423
444
  useEffect(() => {
424
445
  listFetchNowRef.current = listFetchNow;
425
- });
426
- // Initial load via cache; re-fetch directly from API on page/pageLimit/filter/search changes.
446
+ }, [listFetchNow]);
427
447
  useEffect(() => {
428
448
  var _a;
429
- if (!schoolId)
430
- return;
431
- const currentPage = Number(listParams.currentPage) || 1;
432
- const currentPageLimit = Number(listParams.pageLimit) || pageLimit;
433
- const isDefaultLoad = currentPage === 1 &&
434
- currentPageLimit === pageLimit &&
435
- !listParams.searchQuery &&
436
- listParams.filterEnabled === undefined;
437
- if (isDefaultLoad) {
438
- (async () => {
439
- try {
440
- const { count, items } = await getCachedSections({
441
- params: listParams,
442
- });
443
- dispatch({
444
- type: SECTION_ACTION_TYPES.SET_ITEMS,
445
- payload: { items: items || [], count: count || 0 },
446
- });
447
- }
448
- catch (_a) {
449
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
450
- }
451
- })();
452
- }
453
- else {
454
- // Bypass cache for pagination, pageLimit, filter and search changes.
455
- // Use ref to avoid the infinite-loop caused by listFetchNow's unstable identity.
456
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
457
- }
458
- }, [dispatch, listParams, schoolId, showToast, t]);
449
+ (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
450
+ }, [listParams]);
459
451
  // ============================================================================
460
452
  // 1.4.10 RETURN
461
453
  // ============================================================================
@@ -480,6 +472,7 @@ export const useSectionModule = () => {
480
472
  headerActions,
481
473
  listFetchNow,
482
474
  listLoading,
475
+ resetRecordFormState,
483
476
  rowActions,
484
477
  toggleStatus,
485
478
  updateLoading });