@appcorp/fusion-storybook 0.2.40 → 0.2.42

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 (98) 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/form.js +26 -10
  8. package/base-modules/attendance/more-actions.js +34 -25
  9. package/base-modules/campus/context.js +7 -7
  10. package/base-modules/class/cache.js +0 -1
  11. package/base-modules/class/context.js +10 -48
  12. package/base-modules/class/more-actions.js +0 -2
  13. package/base-modules/course/context.js +3 -37
  14. package/base-modules/course/form.js +91 -292
  15. package/base-modules/discount-code/constants.d.ts +5 -0
  16. package/base-modules/discount-code/constants.js +5 -0
  17. package/base-modules/discount-code/context.d.ts +1 -0
  18. package/base-modules/discount-code/context.js +40 -39
  19. package/base-modules/discount-code/form.js +21 -15
  20. package/base-modules/discount-code/more-actions.js +1 -1
  21. package/base-modules/enrollment/context.js +3 -37
  22. package/base-modules/enrollment/form.js +38 -11
  23. package/base-modules/enrollment/more-actions.js +0 -2
  24. package/base-modules/expense/constants.js +1 -1
  25. package/base-modules/expense/context.js +5 -32
  26. package/base-modules/expense/filter.js +50 -3
  27. package/base-modules/expense/form.js +82 -6
  28. package/base-modules/family/context.js +7 -38
  29. package/base-modules/family-member/context.js +7 -39
  30. package/base-modules/fee-structure/context.js +1 -25
  31. package/base-modules/fee-structure/form.js +77 -89
  32. package/base-modules/fee-structure/more-actions.js +0 -2
  33. package/base-modules/rbac/context.d.ts +1 -0
  34. package/base-modules/rbac/context.js +23 -32
  35. package/base-modules/school/context.js +1 -1
  36. package/base-modules/school/form.js +34 -14
  37. package/base-modules/section/context.d.ts +1 -0
  38. package/base-modules/section/context.js +40 -47
  39. package/base-modules/section/form.js +25 -80
  40. package/base-modules/section/more-actions.js +0 -2
  41. package/base-modules/section/view.js +9 -7
  42. package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
  43. package/base-modules/student-fee/context/use-student-fee-module.js +48 -32
  44. package/base-modules/student-fee/context.d.ts +1 -1
  45. package/base-modules/student-fee/context.js +1 -1
  46. package/base-modules/student-fee/filter.js +23 -3
  47. package/base-modules/student-fee/form.js +93 -174
  48. package/base-modules/student-fee/view.d.ts +7 -1
  49. package/base-modules/student-fee/view.js +17 -20
  50. package/base-modules/student-profile/constants.d.ts +0 -6
  51. package/base-modules/student-profile/constants.js +1 -3
  52. package/base-modules/student-profile/context/use-student-profile-module.d.ts +1 -0
  53. package/base-modules/student-profile/context/use-student-profile-module.js +62 -55
  54. package/base-modules/student-profile/context.d.ts +1 -1
  55. package/base-modules/student-profile/context.js +1 -1
  56. package/base-modules/student-profile/filter.js +23 -3
  57. package/base-modules/student-profile/form.js +35 -3
  58. package/base-modules/subject/context.d.ts +1 -0
  59. package/base-modules/subject/context.js +38 -47
  60. package/base-modules/subject/more-actions.js +0 -2
  61. package/base-modules/teacher/constants.d.ts +0 -6
  62. package/base-modules/teacher/constants.js +0 -2
  63. package/base-modules/teacher/context.d.ts +1 -0
  64. package/base-modules/teacher/context.js +58 -39
  65. package/base-modules/teacher/form.js +46 -11
  66. package/base-modules/teacher/more-actions.js +0 -2
  67. package/base-modules/user/context/use-user-module.d.ts +1 -0
  68. package/base-modules/user/context/use-user-module.js +36 -32
  69. package/base-modules/user/context.js +1 -1
  70. package/base-modules/user/filter.js +6 -4
  71. package/base-modules/user/form.js +29 -5
  72. package/base-modules/user/more-actions.js +9 -7
  73. package/base-modules/user/view.js +3 -1
  74. package/base-modules/workspace/form.js +18 -8
  75. package/base-modules/workspace-user/context.d.ts +2 -1
  76. package/base-modules/workspace-user/context.js +31 -29
  77. package/package.json +1 -1
  78. package/tsconfig.build.tsbuildinfo +1 -1
  79. package/base-modules/admission/cache.d.ts +0 -14
  80. package/base-modules/admission/cache.js +0 -31
  81. package/base-modules/attendance/cache.d.ts +0 -14
  82. package/base-modules/attendance/cache.js +0 -31
  83. package/base-modules/course/cache.d.ts +0 -14
  84. package/base-modules/course/cache.js +0 -31
  85. package/base-modules/enrollment/cache.d.ts +0 -14
  86. package/base-modules/enrollment/cache.js +0 -31
  87. package/base-modules/expense/cache.d.ts +0 -14
  88. package/base-modules/expense/cache.js +0 -31
  89. package/base-modules/family/cache.d.ts +0 -14
  90. package/base-modules/family/cache.js +0 -31
  91. package/base-modules/family-member/cache.d.ts +0 -14
  92. package/base-modules/family-member/cache.js +0 -31
  93. package/base-modules/rbac/cache.d.ts +0 -27
  94. package/base-modules/rbac/cache.js +0 -46
  95. package/base-modules/student-fee/cache.d.ts +0 -15
  96. package/base-modules/student-fee/cache.js +0 -21
  97. package/base-modules/workspace-user/cache.d.ts +0 -14
  98. package/base-modules/workspace-user/cache.js +0 -31
@@ -26,7 +26,6 @@ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
26
26
  import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
27
27
  import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
28
28
  import { FAMILY_MEMBER_API_ROUTES, pageLimit } from "./constants";
29
- import { getCachedFamilyMembers, invalidateFamilyMembersCache } from "./cache";
30
29
  import { familyMemberFormValidation } from "./validate";
31
30
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
32
31
  import { formatNumber } from "@react-pakistan/util-functions/general/format-number";
@@ -159,11 +158,6 @@ export const useFamilyMemberModule = () => {
159
158
  ]);
160
159
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
161
160
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
162
- const isDefaultListState = state.currentPage === 1 &&
163
- state.pageLimit === pageLimit &&
164
- !debouncedQuery &&
165
- state.filterEnabled === undefined &&
166
- !state.familyId;
167
161
  // ============================================================================
168
162
  // 1.4.3 UTILITIES
169
163
  // ============================================================================
@@ -229,7 +223,6 @@ export const useFamilyMemberModule = () => {
229
223
  }
230
224
  if (data) {
231
225
  showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
232
- invalidateFamilyMembersCache();
233
226
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
234
227
  resetFormAndCloseDrawer();
235
228
  }
@@ -273,7 +266,6 @@ export const useFamilyMemberModule = () => {
273
266
  }
274
267
  if (data) {
275
268
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
276
- invalidateFamilyMembersCache();
277
269
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
278
270
  }
279
271
  }, [showToast, t]);
@@ -354,7 +346,7 @@ export const useFamilyMemberModule = () => {
354
346
  });
355
347
  }, [dispatch, byIdFetchNow]);
356
348
  const handleDelete = useCallback((row) => {
357
- if (!confirm(t("actionDeleteConfirmation")))
349
+ if (!confirm(t("messagesDeleteConfirmation")))
358
350
  return;
359
351
  deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
360
352
  body: JSON.stringify({ id: row === null || row === void 0 ? void 0 : row.id }),
@@ -437,21 +429,21 @@ export const useFamilyMemberModule = () => {
437
429
  {
438
430
  enabled: false,
439
431
  handleOnClick: handleMoreActions,
440
- label: t("actionHeaderMoreActions"),
432
+ label: t("actionsButtonMoreActions"),
441
433
  order: 1,
442
434
  icon: MoreHorizontal,
443
435
  },
444
436
  {
445
437
  enabled: true,
446
438
  handleOnClick: handleFilters,
447
- label: t("actionHeaderFilters"),
439
+ label: t("actionsButtonFilters"),
448
440
  order: 2,
449
441
  icon: Filter,
450
442
  },
451
443
  {
452
444
  enabled: false,
453
445
  handleOnClick: handleCreate,
454
- label: t("actionHeaderAddFamilyMember"),
446
+ label: t("actionsButtonAddFamilyMember"),
455
447
  order: 3,
456
448
  icon: Plus,
457
449
  },
@@ -460,14 +452,14 @@ export const useFamilyMemberModule = () => {
460
452
  {
461
453
  enabled: true,
462
454
  handleOnClick: handleView,
463
- label: t("actionRowView"),
455
+ label: t("actionsButtonView"),
464
456
  order: 1,
465
457
  icon: Eye,
466
458
  },
467
459
  {
468
460
  enabled: true,
469
461
  handleOnClick: handleEdit,
470
- label: t("actionRowEdit"),
462
+ label: t("actionsButtonEdit"),
471
463
  order: 2,
472
464
  icon: Pencil,
473
465
  },
@@ -479,32 +471,8 @@ export const useFamilyMemberModule = () => {
479
471
  var _a, _b;
480
472
  if (!((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id))
481
473
  return;
482
- if (isDefaultListState) {
483
- (async () => {
484
- try {
485
- const { count, items } = await getCachedFamilyMembers({
486
- params: listParams,
487
- });
488
- dispatch({
489
- type: FAMILY_MEMBER_ACTION_TYPES.SET_ITEMS,
490
- payload: { items: items || [], count: count || 0 },
491
- });
492
- }
493
- catch (_a) {
494
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
495
- }
496
- })();
497
- return;
498
- }
499
474
  (_b = listFetchNowRef.current) === null || _b === void 0 ? void 0 : _b.call(listFetchNowRef);
500
- }, [
501
- dispatch,
502
- isDefaultListState,
503
- listParams,
504
- showToast,
505
- t,
506
- (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.id,
507
- ]);
475
+ }, [listParams, (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.id]);
508
476
  useEffect(() => {
509
477
  listFetchNowRef.current = listFetchNow;
510
478
  }, [listFetchNow]);
@@ -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 { FEE_STRUCTURE_API_ROUTES, pageLimit } from "./constants";
28
- import { getCachedFeeStructures, invalidateFeeStructuresCache } from "./cache";
29
28
  import { feeStructureFormValidation } from "./validate";
30
29
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
31
30
  import { FEE_TYPE } from "../../type";
@@ -104,10 +103,6 @@ export const useFeeStructureModule = () => {
104
103
  const updateParams = useMemo(() => (Object.assign(Object.assign(Object.assign({}, (state.id ? { id: state.id } : {})), (!state.id ? { schoolId } : {})), { amount: Number(state.amount), classId: state.classId || null, description: state.description || null, enabled: state.enabled, feeType: state.feeType, frequency: state.frequency || null, name: state.name })), [state, schoolId]);
105
104
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
106
105
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
107
- const isDefaultListState = state.currentPage === 1 &&
108
- state.pageLimit === pageLimit &&
109
- !debouncedQuery &&
110
- state.filterEnabled === undefined;
111
106
  // ============================================================================
112
107
  // 1.4.3 UTILITIES
113
108
  // ============================================================================
@@ -158,7 +153,6 @@ export const useFeeStructureModule = () => {
158
153
  }
159
154
  if (data) {
160
155
  const isCreated = isCreatedOrUpdated(data);
161
- invalidateFeeStructuresCache();
162
156
  showToast(isCreated
163
157
  ? t("messagesFeeStructureCreated")
164
158
  : t("messagesFeeStructureUpdated"), TOAST_VARIANT.SUCCESS);
@@ -185,7 +179,6 @@ export const useFeeStructureModule = () => {
185
179
  return;
186
180
  }
187
181
  if (data) {
188
- invalidateFeeStructuresCache();
189
182
  showToast(t("messagesFeeStructureDeleted"), TOAST_VARIANT.SUCCESS);
190
183
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
191
184
  }
@@ -408,25 +401,8 @@ export const useFeeStructureModule = () => {
408
401
  var _a;
409
402
  if (!schoolId)
410
403
  return;
411
- if (isDefaultListState) {
412
- (async () => {
413
- try {
414
- const { count, items } = await getCachedFeeStructures({
415
- params: listParams,
416
- });
417
- dispatch({
418
- type: FEE_STRUCTURE_ACTION_TYPES.SET_ITEMS,
419
- payload: { items: items || [], count: count || 0 },
420
- });
421
- }
422
- catch (_a) {
423
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
424
- }
425
- })();
426
- return;
427
- }
428
404
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
429
- }, [dispatch, isDefaultListState, listParams, showToast, t, schoolId]);
405
+ }, [dispatch, listParams, showToast, t, schoolId]);
430
406
  // Sync ref to always point at latest listFetchNow (avoids stale closure in callbacks)
431
407
  useEffect(() => {
432
408
  listFetchNowRef.current = listFetchNow;
@@ -6,103 +6,91 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * Form for creating/updating fee structures.
7
7
  */
8
8
  import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
9
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
9
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
10
10
  import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
11
11
  import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
12
- import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
13
- import { useEffect, useMemo, useState } from "react";
14
12
  import { useFeeStructureModule } from "./context";
15
- import { FEE_TYPE_OPTIONS, FREQUENCY_OPTIONS } from "./constants";
16
- import { getCachedClasses, getCachedClassesSync } from "../class/cache";
13
+ import { FEE_TYPE_OPTIONS, FREQUENCY_OPTIONS, FEE_STRUCTURE_API_ROUTES, } from "./constants";
17
14
  import { getCachedWorkspaceSync } from "../workspace/cache";
18
15
  import { useTranslations } from "next-intl";
16
+ import { useFetch } from "@react-pakistan/util-functions/hooks/use-fetch";
17
+ import { API_METHODS } from "@react-pakistan/util-functions";
19
18
  export const FeeStructureForm = () => {
20
- var _a, _b, _c;
19
+ var _a, _b;
21
20
  const { state, handleChange } = useFeeStructureModule();
22
21
  const { amount, classId, description, enabled, errors, feeType, frequency, name, } = state;
23
22
  const t = useTranslations("feeStructure");
23
+ const feeTypeLabelMap = {
24
+ TUITION: t("formOptionTuition"),
25
+ ADMISSION: t("formOptionAdmission"),
26
+ EXAM: t("formOptionExam"),
27
+ LIBRARY: t("formOptionLibrary"),
28
+ LABORATORY: t("formOptionLaboratory"),
29
+ TRANSPORT: t("formOptionTransport"),
30
+ SPORTS: t("formOptionSports"),
31
+ MISCELLANEOUS: t("formOptionMiscellaneous"),
32
+ LATE: t("formOptionLate"),
33
+ };
34
+ const frequencyLabelMap = {
35
+ Monthly: t("formOptionMonthly"),
36
+ Quarterly: t("formOptionQuarterly"),
37
+ "Semi-Annual": t("formOptionSemiAnnual"),
38
+ Annual: t("formOptionAnnual"),
39
+ "One-time": t("formOptionOneTime"),
40
+ };
24
41
  const workspace = getCachedWorkspaceSync();
25
- const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
26
- const cachedClasses = getCachedClassesSync();
27
- const [classSearchQuery, setClassSearchQuery] = useState("");
28
- const [remoteClassOptions, setRemoteClassOptions] = useState(() => cachedClasses.items.map((cls) => ({
29
- label: `${cls.name} (${cls.code})`,
30
- value: cls.id,
31
- })));
32
- const [classOptionsLoading, setClassOptionsLoading] = useState(false);
33
- const debouncedClassSearchQuery = useDebounce(classSearchQuery, 300);
34
- const trimmedClassSearchQuery = debouncedClassSearchQuery.trim();
35
- const cachedClassOptions = useMemo(() => cachedClasses.items.map((cls) => ({
36
- label: `${cls.name} (${cls.code})`,
37
- value: cls.id,
38
- })), [cachedClasses.items]);
39
- const selectedClassOption = useMemo(() => {
40
- const selectedClass = cachedClasses.items.find((item) => item.id === classId);
41
- return selectedClass
42
- ? [
43
- {
44
- label: `${selectedClass.name} (${selectedClass.code})`,
45
- value: selectedClass.id,
46
- },
47
- ]
48
- : [];
49
- }, [cachedClasses.items, classId]);
50
- const displayedClassOptions = useMemo(() => {
51
- const sourceOptions = trimmedClassSearchQuery
52
- ? remoteClassOptions
53
- : cachedClassOptions;
54
- const mergedOptions = [...selectedClassOption, ...sourceOptions];
55
- const uniqueOptions = new Map(mergedOptions.map((option) => [option.value, option]));
56
- return [...uniqueOptions.values()];
57
- }, [
58
- cachedClassOptions,
59
- remoteClassOptions,
60
- selectedClassOption,
61
- trimmedClassSearchQuery,
62
- ]);
63
- useEffect(() => {
64
- if (!trimmedClassSearchQuery || !schoolId)
65
- return;
66
- let isActive = true;
67
- const fetchClasses = async () => {
68
- setClassOptionsLoading(true);
69
- try {
70
- const { items } = await getCachedClasses({
71
- params: {
72
- schoolId,
73
- searchQuery: trimmedClassSearchQuery,
74
- },
75
- });
76
- if (!isActive)
77
- return;
78
- setRemoteClassOptions((items || []).map((cls) => ({
79
- label: `${cls.name} (${cls.code})`,
80
- value: cls.id,
81
- })));
82
- }
83
- catch (_a) {
84
- if (!isActive)
85
- return;
86
- setRemoteClassOptions([]);
87
- }
88
- finally {
89
- if (isActive) {
90
- setClassOptionsLoading(false);
91
- }
92
- }
93
- };
94
- void fetchClasses();
95
- return () => {
96
- isActive = false;
97
- };
98
- }, [schoolId, trimmedClassSearchQuery]);
99
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.name, id: "name", info: t("formNameInfo"), label: t("formNameLabel"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("formNamePlaceholder"), required: true, value: name || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoFeeTypeEmpty"), error: errors.feeType, id: "feeType", info: t("formFeeTypeInfo"), label: t("formOptionFeeType"), onValueChange: (value) => handleChange("feeType", value), options: FEE_TYPE_OPTIONS.map((opt) => ({
100
- label: t(`feeType${opt.value.charAt(0).toUpperCase() + opt.value.slice(1).toLowerCase()}`),
101
- value: opt.value,
102
- })), placeholder: t("formFeeTypePlaceholder"), required: true, searchPlaceholder: t("formSearchFeeTypePlaceholder"), value: feeType || "" }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: t(`amountInfo`, {
103
- currency: (_c = (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.currency) !== null && _c !== void 0 ? _c : "",
104
- }), label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: t("formAmountPlaceholder"), required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedCombobox, { emptyText: t("formNoFrequencyEmpty"), id: "frequency", info: t("formFrequencyInfo"), label: t("formOptionFrequency"), onValueChange: (value) => handleChange("frequency", value), options: FREQUENCY_OPTIONS.map((opt) => ({
105
- label: t(`frequency${opt.value.replace(/-/g, "")}`),
106
- value: opt.value,
107
- })), placeholder: t("formFrequencyPlaceholder"), searchPlaceholder: t("formSearchFrequencyPlaceholder"), value: frequency || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoClassEmpty"), id: "classId", info: t("formClassInfo"), label: t("formClassLabel"), loading: classOptionsLoading && Boolean(trimmedClassSearchQuery), onSearchChange: setClassSearchQuery, onValueChange: (value) => handleChange("classId", value || null), options: displayedClassOptions, placeholder: t("formClassPlaceholder"), searchPlaceholder: t("formSearchClassPlaceholder"), value: classId || "" }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", info: t("formDescriptionInfo"), label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" })] }), _jsx(EnhancedCheckbox, { defaultChecked: enabled, label: t("formActiveFeeStructureLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }));
42
+ const { data: classes } = useFetch("/api/v1/class", {
43
+ params: { workspaceId: workspace === null || workspace === void 0 ? void 0 : workspace.id },
44
+ method: API_METHODS.GET,
45
+ });
46
+ // const cachedClasses = getCachedClassesSync();
47
+ const { enhancedComboboxElement: feeTypeCombo } = useEnhancedCombobox({
48
+ emptyText: t("formNoFeeTypeEmpty"),
49
+ id: "feeType",
50
+ info: t("formFeeTypeInfo"),
51
+ label: t("formOptionFeeType"),
52
+ onValueChange: (value) => handleChange("feeType", value),
53
+ options: FEE_TYPE_OPTIONS.map((opt) => ({
54
+ id: opt.value,
55
+ name: feeTypeLabelMap[opt.value] || opt.label,
56
+ })),
57
+ placeholder: t("formFeeTypePlaceholder"),
58
+ required: true,
59
+ searchEndpoint: FEE_STRUCTURE_API_ROUTES.UNIT,
60
+ searchPlaceholder: t("formSearchFeeTypePlaceholder"),
61
+ value: feeType || "",
62
+ });
63
+ const { enhancedComboboxElement: frequencyCombo } = useEnhancedCombobox({
64
+ emptyText: t("formNoFrequencyEmpty"),
65
+ id: "frequency",
66
+ info: t("formFrequencyInfo"),
67
+ label: t("formOptionFrequency"),
68
+ onValueChange: (value) => handleChange("frequency", value),
69
+ options: FREQUENCY_OPTIONS.map((opt) => ({
70
+ id: opt.value,
71
+ name: frequencyLabelMap[opt.value] || opt.label,
72
+ })),
73
+ placeholder: t("formFrequencyPlaceholder"),
74
+ searchEndpoint: FEE_STRUCTURE_API_ROUTES.UNIT,
75
+ searchPlaceholder: t("formSearchFrequencyPlaceholder"),
76
+ value: frequency || "",
77
+ });
78
+ const { enhancedComboboxElement: classIdCombo } = useEnhancedCombobox({
79
+ emptyText: t("formNoClassEmpty"),
80
+ id: "classId",
81
+ info: t("formClassInfo"),
82
+ label: t("formClassLabel"),
83
+ onValueChange: (value) => handleChange("classId", value || null),
84
+ options: classes === null || classes === void 0 ? void 0 : classes.map((cls) => ({
85
+ id: cls.id,
86
+ name: `${cls.name} (${cls.code})`,
87
+ })),
88
+ placeholder: t("formClassPlaceholder"),
89
+ searchEndpoint: FEE_STRUCTURE_API_ROUTES.UNIT,
90
+ searchPlaceholder: t("formSearchClassPlaceholder"),
91
+ value: classId || "",
92
+ });
93
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.name, id: "name", info: t("formNameInfo"), label: t("formNameLabel"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("formNamePlaceholder"), required: true, value: name || "" }), feeTypeCombo, _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: t(`amountInfo`, {
94
+ currency: (_b = (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.currency) !== null && _b !== void 0 ? _b : "",
95
+ }), label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: t("formAmountPlaceholder"), required: true, step: "0.01", type: "number", value: amount }), frequencyCombo, classIdCombo, _jsx(EnhancedTextarea, { error: errors.description, id: "description", info: t("formDescriptionInfo"), label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" })] }), _jsx(EnhancedCheckbox, { defaultChecked: enabled, label: t("formActiveFeeStructureLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }));
108
96
  };
@@ -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();
@@ -161,7 +160,6 @@ export const FeeStructureMoreActions = () => {
161
160
  else {
162
161
  showSuccessToast("Bulk operation completed successfully");
163
162
  }
164
- invalidateFeeStructuresCache();
165
163
  const schoolId = ((_d = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _d === void 0 ? void 0 : _d.id) || "";
166
164
  fetch(`${FEE_STRUCTURE_API_ROUTES.LIST}?currentPage=1&pageLimit=${pageLimit}&schoolId=${schoolId}`, {
167
165
  headers: {
@@ -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();
@@ -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,
@@ -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;