@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
@@ -6,13 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * Create / update form for an expense record.
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
12
  import { EnhancedDropzone } from "@appcorp/shadcn/components/enhanced-dropzone";
13
- import { useState } from "react";
14
13
  import { useExpenseModule } from "./context";
15
- import { EXPENSE_CATEGORY_OPTIONS, EXPENSE_STATUS_OPTIONS, PAYMENT_METHOD_OPTIONS, } from "./constants";
14
+ import { EXPENSE_CATEGORY_OPTIONS, EXPENSE_STATUS_OPTIONS, PAYMENT_METHOD_OPTIONS, EXPENSE_API_ROUTES, } from "./constants";
16
15
  import { getCachedWorkspaceSync } from "../workspace/cache";
17
16
  import { useTranslations } from "next-intl";
18
17
  export const ExpenseForm = () => {
@@ -22,9 +21,86 @@ export const ExpenseForm = () => {
22
21
  const workspace = getCachedWorkspaceSync();
23
22
  const 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 : "";
24
23
  const t = useTranslations("expense");
25
- const [categorySearchQuery, setCategorySearchQuery] = useState("");
26
- const filteredCategoryOptions = EXPENSE_CATEGORY_OPTIONS.filter((opt) => opt.label.toLowerCase().includes(categorySearchQuery.toLowerCase()));
27
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.title, id: "title", info: t("formTitleInfo"), label: t("formTitleLabel"), onChange: (e) => handleChange("title", e.target.value), placeholder: t("formTitlePlaceholder"), required: true, type: "text", value: title || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoCategoryEmpty"), error: errors.category, id: "category", info: t("formCategoryInfo"), label: t("formCategoryLabel"), onSearchChange: setCategorySearchQuery, onValueChange: (value) => handleChange("category", value), options: filteredCategoryOptions, placeholder: t("formCategoryPlaceholder"), required: true, searchPlaceholder: t("formSearchCategoriesPlaceholder"), value: category || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoStatusEmpty"), error: errors.status, id: "status", info: t("formStatusInfo"), label: t("formOptionStatus"), onValueChange: (value) => handleChange("status", value), options: EXPENSE_STATUS_OPTIONS, placeholder: t("formStatusPlaceholder"), required: true, searchPlaceholder: t("formSearchStatusPlaceholder"), value: status || "" }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: `${t("formAmountInfo")} ${currency}`, label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedInput, { error: errors.expenseDate, id: "expenseDate", info: t("formExpenseDateInfo"), label: t("formExpenseDateLabel"), onChange: (e) => handleChange("expenseDate", e.target.value), required: true, type: "date", value: expenseDate || "" }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" }), _jsx(EnhancedInput, { error: errors.vendorName, id: "vendorName", info: t("formVendorNameInfo"), label: t("formVendorNameLabel"), onChange: (e) => handleChange("vendorName", e.target.value), placeholder: t("formVendorNamePlaceholder"), type: "text", value: vendorName || "" }), _jsx(EnhancedInput, { error: errors.vendorContact, id: "vendorContact", info: t("formVendorContactInfo"), label: t("formVendorContactLabel"), onChange: (e) => handleChange("vendorContact", e.target.value), placeholder: t("formVendorContactPlaceholder"), type: "text", value: vendorContact || "" }), _jsx(EnhancedInput, { error: errors.invoiceNumber, id: "invoiceNumber", info: t("formInvoiceNumberInfo"), label: t("formInvoiceNumberLabel"), onChange: (e) => handleChange("invoiceNumber", e.target.value), placeholder: t("formInvoiceNumberPlaceholder"), type: "text", value: invoiceNumber || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoPaymentMethodEmpty"), error: errors.paymentMethod, id: "paymentMethod", info: t("formPaymentMethodInfo"), label: t("formPaymentMethodLabel"), onValueChange: (value) => handleChange("paymentMethod", value), options: PAYMENT_METHOD_OPTIONS, placeholder: t("formPaymentMethodPlaceholder"), searchPlaceholder: t("formSearchPaymentMethodsPlaceholder"), value: paymentMethod || "" }), _jsx(EnhancedInput, { error: errors.paymentDate, id: "paymentDate", info: t("formPaymentDateInfo"), label: t("formPaymentDateLabel"), onChange: (e) => handleChange("paymentDate", e.target.value), type: "date", value: paymentDate || "" }), _jsx(EnhancedInput, { error: errors.receiptNumber, id: "receiptNumber", info: t("formReceiptNumberInfo"), label: t("formReceiptNumberLabel"), onChange: (e) => handleChange("receiptNumber", e.target.value), placeholder: t("formReceiptNumberPlaceholder"), type: "text", value: receiptNumber || "" }), _jsx(EnhancedInput, { error: errors.transactionId, id: "transactionId", info: t("formTransactionIdInfo"), label: t("formTransactionIdLabel"), onChange: (e) => handleChange("transactionId", e.target.value), placeholder: t("formTransactionIdPlaceholder"), type: "text", value: transactionId || "" }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", label: t("formRemarksLabel"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("formRemarksPlaceholder"), rows: 3, value: remarks || "" }), _jsx(EnhancedDropzone, { accept: ["image/*", "application/pdf"], error: errors.attachments, id: "attachments", info: t("formAttachmentsInfo"), label: t("formAttachmentsLabel"), maxFiles: 10, onChange: (files) => {
24
+ const categoryLabelMap = {
25
+ SALARY: t("formOptionSalary"),
26
+ UTILITIES: t("formOptionUtilities"),
27
+ MAINTENANCE: t("formOptionMaintenance"),
28
+ SUPPLIES: t("formOptionSupplies"),
29
+ TRANSPORT: t("formOptionTransport"),
30
+ MARKETING: t("formOptionMarketing"),
31
+ INSURANCE: t("formOptionInsurance"),
32
+ RENT: t("formOptionRent"),
33
+ TECHNOLOGY: t("formOptionTechnology"),
34
+ PROFESSIONAL: t("formOptionProfessional"),
35
+ TRAINING: t("formOptionTraining"),
36
+ EVENTS: t("formOptionEvents"),
37
+ FOOD: t("formOptionFood"),
38
+ MISCELLANEOUS: t("formOptionMiscellaneous"),
39
+ };
40
+ const statusLabelMap = {
41
+ PENDING: t("formOptionPending"),
42
+ APPROVED: t("formOptionApproved"),
43
+ PAID: t("formOptionPaid"),
44
+ REJECTED: t("formOptionRejected"),
45
+ CANCELLED: t("formOptionCancelled"),
46
+ };
47
+ const paymentMethodLabelMap = {
48
+ CASH: t("formOptionCash"),
49
+ BANK_TRANSFER: t("formOptionBankTransfer"),
50
+ CREDIT_CARD: t("formOptionCreditCard"),
51
+ DEBIT_CARD: t("formOptionDebitCard"),
52
+ CHEQUE: t("formOptionCheque"),
53
+ ONLINE: t("formOptionOnline"),
54
+ MOBILE_WALLET: t("formOptionMobileWallet"),
55
+ };
56
+ const { enhancedComboboxElement: categoryCombo } = useEnhancedCombobox({
57
+ emptyText: t("formNoCategoryEmpty"),
58
+ id: "category",
59
+ info: t("formCategoryInfo"),
60
+ label: t("formCategoryLabel"),
61
+ onValueChange: (value) => handleChange("category", value),
62
+ options: EXPENSE_CATEGORY_OPTIONS.map((opt) => ({
63
+ id: opt.value,
64
+ name: categoryLabelMap[opt.value] || opt.label,
65
+ })),
66
+ placeholder: t("formCategoryPlaceholder"),
67
+ required: true,
68
+ searchEndpoint: EXPENSE_API_ROUTES.UNIT,
69
+ searchPlaceholder: t("formSearchCategoriesPlaceholder"),
70
+ value: category || "",
71
+ });
72
+ const { enhancedComboboxElement: statusCombo } = useEnhancedCombobox({
73
+ emptyText: t("formNoStatusEmpty"),
74
+ id: "status",
75
+ info: t("formStatusInfo"),
76
+ label: t("formOptionStatus"),
77
+ onValueChange: (value) => handleChange("status", value),
78
+ options: EXPENSE_STATUS_OPTIONS.map((opt) => ({
79
+ id: opt.value,
80
+ name: statusLabelMap[opt.value] || opt.label,
81
+ })),
82
+ placeholder: t("formStatusPlaceholder"),
83
+ required: true,
84
+ searchEndpoint: EXPENSE_API_ROUTES.UNIT,
85
+ searchPlaceholder: t("formSearchStatusPlaceholder"),
86
+ value: status || "",
87
+ });
88
+ const { enhancedComboboxElement: paymentMethodCombo } = useEnhancedCombobox({
89
+ emptyText: t("formNoPaymentMethodEmpty"),
90
+ id: "paymentMethod",
91
+ info: t("formPaymentMethodInfo"),
92
+ label: t("formPaymentMethodLabel"),
93
+ onValueChange: (value) => handleChange("paymentMethod", value),
94
+ options: PAYMENT_METHOD_OPTIONS.map((opt) => ({
95
+ id: opt.value,
96
+ name: paymentMethodLabelMap[opt.value] || opt.label,
97
+ })),
98
+ placeholder: t("formPaymentMethodPlaceholder"),
99
+ searchEndpoint: EXPENSE_API_ROUTES.UNIT,
100
+ searchPlaceholder: t("formSearchPaymentMethodsPlaceholder"),
101
+ value: paymentMethod || "",
102
+ });
103
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.title, id: "title", info: t("formTitleInfo"), label: t("formTitleLabel"), onChange: (e) => handleChange("title", e.target.value), placeholder: t("formTitlePlaceholder"), required: true, type: "text", value: title || "" }), categoryCombo, statusCombo, _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: `${t("formAmountInfo")} ${currency}`, label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedInput, { error: errors.expenseDate, id: "expenseDate", info: t("formExpenseDateInfo"), label: t("formExpenseDateLabel"), onChange: (e) => handleChange("expenseDate", e.target.value), required: true, type: "date", value: expenseDate || "" }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" }), _jsx(EnhancedInput, { error: errors.vendorName, id: "vendorName", info: t("formVendorNameInfo"), label: t("formVendorNameLabel"), onChange: (e) => handleChange("vendorName", e.target.value), placeholder: t("formVendorNamePlaceholder"), type: "text", value: vendorName || "" }), _jsx(EnhancedInput, { error: errors.vendorContact, id: "vendorContact", info: t("formVendorContactInfo"), label: t("formVendorContactLabel"), onChange: (e) => handleChange("vendorContact", e.target.value), placeholder: t("formVendorContactPlaceholder"), type: "text", value: vendorContact || "" }), _jsx(EnhancedInput, { error: errors.invoiceNumber, id: "invoiceNumber", info: t("formInvoiceNumberInfo"), label: t("formInvoiceNumberLabel"), onChange: (e) => handleChange("invoiceNumber", e.target.value), placeholder: t("formInvoiceNumberPlaceholder"), type: "text", value: invoiceNumber || "" }), paymentMethodCombo, _jsx(EnhancedInput, { error: errors.paymentDate, id: "paymentDate", info: t("formPaymentDateInfo"), label: t("formPaymentDateLabel"), onChange: (e) => handleChange("paymentDate", e.target.value), type: "date", value: paymentDate || "" }), _jsx(EnhancedInput, { error: errors.receiptNumber, id: "receiptNumber", info: t("formReceiptNumberInfo"), label: t("formReceiptNumberLabel"), onChange: (e) => handleChange("receiptNumber", e.target.value), placeholder: t("formReceiptNumberPlaceholder"), type: "text", value: receiptNumber || "" }), _jsx(EnhancedInput, { error: errors.transactionId, id: "transactionId", info: t("formTransactionIdInfo"), label: t("formTransactionIdLabel"), onChange: (e) => handleChange("transactionId", e.target.value), placeholder: t("formTransactionIdPlaceholder"), type: "text", value: transactionId || "" }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", label: t("formRemarksLabel"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("formRemarksPlaceholder"), rows: 3, value: remarks || "" }), _jsx(EnhancedDropzone, { accept: ["image/*", "application/pdf"], error: errors.attachments, id: "attachments", info: t("formAttachmentsInfo"), label: t("formAttachmentsLabel"), maxFiles: 10, onChange: (files) => {
28
104
  const urls = files.map((f) => URL.createObjectURL(f));
29
105
  handleChange("attachments", urls);
30
106
  }, onRemoveRemote: (url) => {
@@ -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 { FAMILY_API_ROUTES, pageLimit } from "./constants";
28
- import { getCachedFamilies, invalidateFamiliesCache } from "./cache";
29
28
  import { familyFormValidation } from "./validate";
30
29
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
31
30
  import { useTranslations } from "next-intl";
@@ -133,10 +132,6 @@ export const useFamilyModule = () => {
133
132
  ]);
134
133
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
135
134
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
136
- const isDefaultListState = state.currentPage === 1 &&
137
- state.pageLimit === pageLimit &&
138
- !debouncedQuery &&
139
- state.filterEnabled === undefined;
140
135
  // ==========================================================================
141
136
  // 1.4.3 UTILITIES
142
137
  // ==========================================================================
@@ -192,7 +187,6 @@ export const useFamilyModule = () => {
192
187
  }
193
188
  if (data) {
194
189
  showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
195
- invalidateFamiliesCache();
196
190
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
197
191
  resetFormAndCloseDrawer();
198
192
  }
@@ -230,7 +224,6 @@ export const useFamilyModule = () => {
230
224
  }
231
225
  if (data) {
232
226
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
233
- invalidateFamiliesCache();
234
227
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
235
228
  }
236
229
  }, [showToast, t]);
@@ -293,7 +286,7 @@ export const useFamilyModule = () => {
293
286
  });
294
287
  }, [dispatch, byIdFetchNow]);
295
288
  const handleDelete = useCallback((row) => {
296
- if (!confirm(t("actionDeleteConfirmation")))
289
+ if (!confirm(t("messagesDeleteConfirmation")))
297
290
  return;
298
291
  deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
299
292
  body: JSON.stringify({ id: row === null || row === void 0 ? void 0 : row.id }),
@@ -394,21 +387,21 @@ export const useFamilyModule = () => {
394
387
  {
395
388
  enabled: false,
396
389
  handleOnClick: handleMoreActions,
397
- label: t("actionHeaderMoreActions"),
390
+ label: t("actionsButtonMoreActions"),
398
391
  order: 1,
399
392
  icon: MoreHorizontal,
400
393
  },
401
394
  {
402
395
  enabled: true,
403
396
  handleOnClick: handleFilters,
404
- label: t("actionHeaderFilters"),
397
+ label: t("actionsButtonFilters"),
405
398
  order: 2,
406
399
  icon: Filter,
407
400
  },
408
401
  {
409
402
  enabled: false,
410
403
  handleOnClick: handleCreate,
411
- label: t("actionHeaderAddFamily"),
404
+ label: t("actionsButtonAddFamily"),
412
405
  order: 3,
413
406
  icon: Plus,
414
407
  },
@@ -417,14 +410,14 @@ export const useFamilyModule = () => {
417
410
  {
418
411
  enabled: true,
419
412
  handleOnClick: handleView,
420
- label: t("actionRowView"),
413
+ label: t("actionsButtonView"),
421
414
  order: 1,
422
415
  icon: Eye,
423
416
  },
424
417
  {
425
418
  enabled: true,
426
419
  handleOnClick: handleEdit,
427
- label: t("actionRowEdit"),
420
+ label: t("actionsButtonEdit"),
428
421
  order: 2,
429
422
  icon: Pencil,
430
423
  },
@@ -436,32 +429,8 @@ export const useFamilyModule = () => {
436
429
  var _a, _b;
437
430
  if (!((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id))
438
431
  return;
439
- if (isDefaultListState) {
440
- (async () => {
441
- try {
442
- const { count, items } = await getCachedFamilies({
443
- params: listParams,
444
- });
445
- dispatch({
446
- type: FAMILY_ACTION_TYPES.SET_ITEMS,
447
- payload: { items: items || [], count: count || 0 },
448
- });
449
- }
450
- catch (_a) {
451
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
452
- }
453
- })();
454
- return;
455
- }
456
432
  (_b = listFetchNowRef.current) === null || _b === void 0 ? void 0 : _b.call(listFetchNowRef);
457
- }, [
458
- isDefaultListState,
459
- listParams,
460
- (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.id,
461
- dispatch,
462
- showToast,
463
- t,
464
- ]);
433
+ }, [listParams, (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.id]);
465
434
  useEffect(() => {
466
435
  listFetchNowRef.current = listFetchNow;
467
436
  }, [listFetchNow]);
@@ -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
  };