@appcorp/fusion-storybook 0.1.4

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 (131) hide show
  1. package/README.md +147 -0
  2. package/base-modules/admission/cache.d.ts +14 -0
  3. package/base-modules/admission/cache.js +31 -0
  4. package/base-modules/admission/constants.d.ts +32 -0
  5. package/base-modules/admission/constants.js +37 -0
  6. package/base-modules/admission/context.d.ts +369 -0
  7. package/base-modules/admission/context.js +755 -0
  8. package/base-modules/admission/filter.d.ts +1 -0
  9. package/base-modules/admission/filter.js +31 -0
  10. package/base-modules/admission/form.d.ts +1 -0
  11. package/base-modules/admission/form.js +86 -0
  12. package/base-modules/admission/more-actions.d.ts +1 -0
  13. package/base-modules/admission/more-actions.js +5 -0
  14. package/base-modules/admission/page.d.ts +16 -0
  15. package/base-modules/admission/page.js +128 -0
  16. package/base-modules/admission/validate.d.ts +38 -0
  17. package/base-modules/admission/validate.js +70 -0
  18. package/base-modules/admission/view.d.ts +1 -0
  19. package/base-modules/admission/view.js +40 -0
  20. package/base-modules/discount-code/cache.d.ts +27 -0
  21. package/base-modules/discount-code/cache.js +46 -0
  22. package/base-modules/discount-code/constants.d.ts +19 -0
  23. package/base-modules/discount-code/constants.js +26 -0
  24. package/base-modules/discount-code/context.d.ts +154 -0
  25. package/base-modules/discount-code/context.js +416 -0
  26. package/base-modules/discount-code/filter.d.ts +9 -0
  27. package/base-modules/discount-code/filter.js +14 -0
  28. package/base-modules/discount-code/form.d.ts +1 -0
  29. package/base-modules/discount-code/form.js +44 -0
  30. package/base-modules/discount-code/more-actions.d.ts +9 -0
  31. package/base-modules/discount-code/more-actions.js +13 -0
  32. package/base-modules/discount-code/page.d.ts +16 -0
  33. package/base-modules/discount-code/page.js +120 -0
  34. package/base-modules/discount-code/validate.d.ts +19 -0
  35. package/base-modules/discount-code/validate.js +38 -0
  36. package/base-modules/discount-code/view.d.ts +1 -0
  37. package/base-modules/discount-code/view.js +26 -0
  38. package/base-modules/family/cache.d.ts +14 -0
  39. package/base-modules/family/cache.js +31 -0
  40. package/base-modules/family/constants.d.ts +22 -0
  41. package/base-modules/family/constants.js +26 -0
  42. package/base-modules/family/context.d.ts +173 -0
  43. package/base-modules/family/context.js +441 -0
  44. package/base-modules/family/filter.d.ts +1 -0
  45. package/base-modules/family/filter.js +28 -0
  46. package/base-modules/family/form.d.ts +1 -0
  47. package/base-modules/family/form.js +12 -0
  48. package/base-modules/family/more-actions.d.ts +1 -0
  49. package/base-modules/family/more-actions.js +5 -0
  50. package/base-modules/family/page.d.ts +16 -0
  51. package/base-modules/family/page.js +120 -0
  52. package/base-modules/family/validate.d.ts +15 -0
  53. package/base-modules/family/validate.js +18 -0
  54. package/base-modules/family/view.d.ts +1 -0
  55. package/base-modules/family/view.js +18 -0
  56. package/base-modules/family-member/cache.d.ts +14 -0
  57. package/base-modules/family-member/cache.js +31 -0
  58. package/base-modules/family-member/constants.d.ts +22 -0
  59. package/base-modules/family-member/constants.js +26 -0
  60. package/base-modules/family-member/context.d.ts +215 -0
  61. package/base-modules/family-member/context.js +486 -0
  62. package/base-modules/family-member/filter.d.ts +1 -0
  63. package/base-modules/family-member/filter.js +28 -0
  64. package/base-modules/family-member/form.d.ts +1 -0
  65. package/base-modules/family-member/form.js +17 -0
  66. package/base-modules/family-member/more-actions.d.ts +1 -0
  67. package/base-modules/family-member/more-actions.js +5 -0
  68. package/base-modules/family-member/page.d.ts +16 -0
  69. package/base-modules/family-member/page.js +122 -0
  70. package/base-modules/family-member/validate.d.ts +24 -0
  71. package/base-modules/family-member/validate.js +27 -0
  72. package/base-modules/family-member/view.d.ts +1 -0
  73. package/base-modules/family-member/view.js +40 -0
  74. package/base-modules/student-profile/cache.d.ts +14 -0
  75. package/base-modules/student-profile/cache.js +31 -0
  76. package/base-modules/student-profile/constants.d.ts +105 -0
  77. package/base-modules/student-profile/constants.js +132 -0
  78. package/base-modules/student-profile/context.d.ts +290 -0
  79. package/base-modules/student-profile/context.js +583 -0
  80. package/base-modules/student-profile/filter.d.ts +1 -0
  81. package/base-modules/student-profile/filter.js +26 -0
  82. package/base-modules/student-profile/form.d.ts +1 -0
  83. package/base-modules/student-profile/form.js +19 -0
  84. package/base-modules/student-profile/more-actions.d.ts +1 -0
  85. package/base-modules/student-profile/more-actions.js +5 -0
  86. package/base-modules/student-profile/page.d.ts +9 -0
  87. package/base-modules/student-profile/page.js +86 -0
  88. package/base-modules/student-profile/validate.d.ts +23 -0
  89. package/base-modules/student-profile/validate.js +34 -0
  90. package/base-modules/student-profile/view.d.ts +1 -0
  91. package/base-modules/student-profile/view.js +29 -0
  92. package/base-modules/workspace/cache.d.ts +9 -0
  93. package/base-modules/workspace/cache.js +28 -0
  94. package/base-modules/workspace/constants.d.ts +10 -0
  95. package/base-modules/workspace/constants.js +18 -0
  96. package/base-modules/workspace/context.d.ts +187 -0
  97. package/base-modules/workspace/context.js +293 -0
  98. package/base-modules/workspace/drawer.d.ts +1 -0
  99. package/base-modules/workspace/drawer.js +24 -0
  100. package/base-modules/workspace/filter.d.ts +1 -0
  101. package/base-modules/workspace/filter.js +13 -0
  102. package/base-modules/workspace/form.d.ts +1 -0
  103. package/base-modules/workspace/form.js +40 -0
  104. package/base-modules/workspace/more-actions.d.ts +1 -0
  105. package/base-modules/workspace/more-actions.js +13 -0
  106. package/base-modules/workspace/page.d.ts +1 -0
  107. package/base-modules/workspace/page.js +31 -0
  108. package/base-modules/workspace/validate.d.ts +9 -0
  109. package/base-modules/workspace/validate.js +8 -0
  110. package/base-modules/workspace/view.d.ts +1 -0
  111. package/base-modules/workspace/view.js +52 -0
  112. package/components/rbac-no-access.d.ts +6 -0
  113. package/components/rbac-no-access.js +4 -0
  114. package/constants.d.ts +81 -0
  115. package/constants.js +81 -0
  116. package/lib/utils.d.ts +2 -0
  117. package/lib/utils.js +5 -0
  118. package/package.json +104 -0
  119. package/tsconfig.build.tsbuildinfo +1 -0
  120. package/type.d.ts +1141 -0
  121. package/type.js +427 -0
  122. package/utils/admission-pdf.d.ts +78 -0
  123. package/utils/admission-pdf.js +73 -0
  124. package/utils/clear-cache.d.ts +1 -0
  125. package/utils/clear-cache.js +8 -0
  126. package/utils/format-value.d.ts +1 -0
  127. package/utils/format-value.js +1 -0
  128. package/utils/pdf-generator.d.ts +41 -0
  129. package/utils/pdf-generator.js +107 -0
  130. package/utils/resolve-rbac-permissions.d.ts +11 -0
  131. package/utils/resolve-rbac-permissions.js +251 -0
@@ -0,0 +1,154 @@
1
+ import { type RowAction, type TableRow } from "@appcorp/shadcn/components/enhanced-table";
2
+ import { DiscountCodeBE } from "@/type";
3
+ export declare const DISCOUNT_CODE_DRAWER: {
4
+ readonly FILTER_DRAWER: string;
5
+ readonly FORM_DRAWER: string;
6
+ readonly MORE_ACTIONS_DRAWER: string;
7
+ readonly VIEW_DRAWER: string;
8
+ };
9
+ export declare const DISCOUNT_CODE_ACTION_TYPES: {
10
+ readonly RESET_FORM: "RESET_FORM";
11
+ readonly SET_CURRENT_PAGE: "SET_CURRENT_PAGE";
12
+ readonly SET_PAGE_LIMIT: "SET_PAGE_LIMIT";
13
+ readonly SET_SEARCH_QUERY: "SET_SEARCH_QUERY";
14
+ readonly SET_DRAWER: "SET_DRAWER";
15
+ readonly SET_ITEMS: "SET_ITEMS";
16
+ readonly SET_FORM_DATA: "SET_FORM_DATA";
17
+ readonly SET_DISABLE_SAVE_BUTTON: "SET_DISABLE_SAVE_BUTTON";
18
+ readonly SET_INPUT_FIELD: "SET_INPUT_FIELD";
19
+ readonly SET_ERRORS: "SET_ERRORS";
20
+ readonly SET_FILTERS: "SET_FILTERS";
21
+ }, discountCodeModuleConfig: import("@react-pakistan/util-functions/factory/generic-module-factory").ModuleConfig<{
22
+ items: DiscountCodeBE[];
23
+ count: number;
24
+ currentPage: number;
25
+ pageLimit: number;
26
+ searchQuery: string;
27
+ disableSaveButton: boolean;
28
+ drawer: null;
29
+ modal: null;
30
+ code: string;
31
+ description: string;
32
+ discountType: string;
33
+ discountValue: number;
34
+ enabled: boolean;
35
+ errors: Record<string, string>;
36
+ filterEnabled: boolean | undefined;
37
+ id: string;
38
+ }>, initialDiscountCodeState: {
39
+ items: DiscountCodeBE[];
40
+ count: number;
41
+ currentPage: number;
42
+ pageLimit: number;
43
+ searchQuery: string;
44
+ disableSaveButton: boolean;
45
+ drawer: null;
46
+ modal: null;
47
+ code: string;
48
+ description: string;
49
+ discountType: string;
50
+ discountValue: number;
51
+ enabled: boolean;
52
+ errors: Record<string, string>;
53
+ filterEnabled: boolean | undefined;
54
+ id: string;
55
+ }, DiscountCodeProvider: import("react").FC<{
56
+ children: React.ReactNode;
57
+ }>, discountCodeReducer: (state: {
58
+ items: DiscountCodeBE[];
59
+ count: number;
60
+ currentPage: number;
61
+ pageLimit: number;
62
+ searchQuery: string;
63
+ disableSaveButton: boolean;
64
+ drawer: null;
65
+ modal: null;
66
+ code: string;
67
+ description: string;
68
+ discountType: string;
69
+ discountValue: number;
70
+ enabled: boolean;
71
+ errors: Record<string, string>;
72
+ filterEnabled: boolean | undefined;
73
+ id: string;
74
+ }, action: any) => {
75
+ items: DiscountCodeBE[];
76
+ count: number;
77
+ currentPage: number;
78
+ pageLimit: number;
79
+ searchQuery: string;
80
+ disableSaveButton: boolean;
81
+ drawer: null;
82
+ modal: null;
83
+ code: string;
84
+ description: string;
85
+ discountType: string;
86
+ discountValue: number;
87
+ enabled: boolean;
88
+ errors: Record<string, string>;
89
+ filterEnabled: boolean | undefined;
90
+ id: string;
91
+ }, useDiscountCodeStateContext: () => import("@react-pakistan/util-functions/factory/generic-module-factory").GenericModuleContext<{
92
+ items: DiscountCodeBE[];
93
+ count: number;
94
+ currentPage: number;
95
+ pageLimit: number;
96
+ searchQuery: string;
97
+ disableSaveButton: boolean;
98
+ drawer: null;
99
+ modal: null;
100
+ code: string;
101
+ description: string;
102
+ discountType: string;
103
+ discountValue: number;
104
+ enabled: boolean;
105
+ errors: Record<string, string>;
106
+ filterEnabled: boolean | undefined;
107
+ id: string;
108
+ }>;
109
+ export declare const useDiscountCodeModule: () => {
110
+ applyFilters: () => void;
111
+ byIdLoading: boolean;
112
+ clearFilters: () => void;
113
+ deleteLoading: boolean;
114
+ handleChange: (field: string, value: string | number | boolean | undefined) => void;
115
+ handleCloseDrawer: () => void;
116
+ handleCreate: () => void;
117
+ handleDelete: (row?: TableRow) => void;
118
+ handleEdit: (row?: TableRow) => void;
119
+ handleFilters: () => void;
120
+ handlePageChange: (page: number) => void;
121
+ handlePageLimitChange: (limit: number) => void;
122
+ handleSearch: (query: string) => void;
123
+ handleSubmit: () => void;
124
+ handleView: (row?: TableRow) => void;
125
+ headerActions: {
126
+ enabled: boolean;
127
+ handleOnClick: () => void;
128
+ label: string;
129
+ order: number;
130
+ icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
131
+ }[];
132
+ listLoading: boolean;
133
+ rowActions: RowAction[];
134
+ updateLoading: boolean;
135
+ state: {
136
+ items: DiscountCodeBE[];
137
+ count: number;
138
+ currentPage: number;
139
+ pageLimit: number;
140
+ searchQuery: string;
141
+ disableSaveButton: boolean;
142
+ drawer: null;
143
+ modal: null;
144
+ code: string;
145
+ description: string;
146
+ discountType: string;
147
+ discountValue: number;
148
+ enabled: boolean;
149
+ errors: Record<string, string>;
150
+ filterEnabled: boolean | undefined;
151
+ id: string;
152
+ };
153
+ dispatch: React.Dispatch<any>;
154
+ };
@@ -0,0 +1,416 @@
1
+ "use client";
2
+ /**
3
+ * Discount Code Module — business logic + API integration
4
+ *
5
+ * Wires the generic module state (created by `createGenericModule`) to network
6
+ * actions using `useModuleEntityV2`. Contains domain-specific handlers for
7
+ * create/view/edit/delete operations.
8
+ *
9
+ * Key responsibilities:
10
+ * - expose `useDiscountCodeModule()` which UI components call for actions
11
+ * - keep module-specific `apiParams` and callbacks in one place
12
+ * - ensure cache invalidation and toast notifications on mutation
13
+ *
14
+ * Exported utilities:
15
+ * - `DiscountCodeProvider` — provider component used by the page
16
+ * - `useDiscountCodeModule()` — hook that returns state, dispatch, and handlers
17
+ */
18
+ import { useCallback, useEffect, useMemo } from "react";
19
+ import { isCreatedOrUpdated, validateForm, } from "@react-pakistan/util-functions";
20
+ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
21
+ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
22
+ import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
23
+ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
24
+ import { DISCOUNT_CODE_API_ROUTES, pageLimit } from "./constants";
25
+ import { getCachedDiscountCodes, invalidateDiscountCodeCache } from "./cache";
26
+ import { accountDiscountCodeFormValidation } from "./validate";
27
+ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
28
+ import { useTranslations } from "next-intl";
29
+ import { getCachedWorkspaceSync } from "../workspace/cache";
30
+ import { Filter, Plus } from "lucide-react";
31
+ // ============================================================================
32
+ // DRAWER TYPES
33
+ // ============================================================================
34
+ export const DISCOUNT_CODE_DRAWER = {
35
+ FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
36
+ FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
37
+ MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
38
+ VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
39
+ };
40
+ // ============================================================================
41
+ // CONFIGURATION
42
+ // ============================================================================
43
+ const discountCodeConfig = {
44
+ name: "DiscountCode",
45
+ displayName: "Discount Code",
46
+ initialState: {
47
+ // List Data
48
+ items: [],
49
+ count: 0,
50
+ // Search & Pagination
51
+ currentPage: 1,
52
+ pageLimit,
53
+ searchQuery: "",
54
+ // UI State
55
+ disableSaveButton: false,
56
+ drawer: null,
57
+ modal: null,
58
+ // Form State
59
+ code: "",
60
+ description: "",
61
+ discountType: "",
62
+ discountValue: 0,
63
+ enabled: true,
64
+ errors: {},
65
+ filterEnabled: undefined,
66
+ id: "",
67
+ },
68
+ drawerTypes: DRAWER_TYPES,
69
+ };
70
+ // ============================================================================
71
+ // CREATE DISCOUNT CODE MODULE
72
+ // ============================================================================
73
+ export const { actionTypes: DISCOUNT_CODE_ACTION_TYPES, config: discountCodeModuleConfig, initialState: initialDiscountCodeState, Provider: DiscountCodeProvider, reducer: discountCodeReducer, useContext: useDiscountCodeStateContext, } = createGenericModule(discountCodeConfig);
74
+ // ============================================================================
75
+ // ENHANCED DISCOUNT CODE HOOK WITH API INTEGRATION
76
+ // ============================================================================
77
+ export const useDiscountCodeModule = () => {
78
+ var _a;
79
+ const context = useDiscountCodeStateContext();
80
+ const t = useTranslations("discountCode");
81
+ const workspace = getCachedWorkspaceSync();
82
+ const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
83
+ const debouncedQuery = useDebounce(context.state.searchQuery, 800);
84
+ // ============================================================================
85
+ // API PARAMETERS
86
+ // ============================================================================
87
+ const listParams = useMemo(() => (Object.assign(Object.assign({ currentPage: context.state.currentPage, pageLimit: context.state.pageLimit, schoolId }, (debouncedQuery ? { searchQuery: debouncedQuery } : {})), (context.state.filterEnabled !== undefined
88
+ ? { filterEnabled: String(context.state.filterEnabled) }
89
+ : {}))), [
90
+ context.state.currentPage,
91
+ context.state.pageLimit,
92
+ context.state.filterEnabled,
93
+ debouncedQuery,
94
+ schoolId,
95
+ ]);
96
+ const updateParams = useMemo(() => ({
97
+ code: context.state.code,
98
+ description: context.state.description,
99
+ discountType: context.state.discountType,
100
+ discountValue: context.state.discountValue,
101
+ enabled: context.state.enabled,
102
+ id: context.state.id,
103
+ schoolId,
104
+ }), [
105
+ context.state.code,
106
+ context.state.description,
107
+ context.state.discountType,
108
+ context.state.discountValue,
109
+ context.state.enabled,
110
+ context.state.id,
111
+ schoolId,
112
+ ]);
113
+ const byIdParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
114
+ const deleteParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
115
+ // ============================================================================
116
+ // API CALLBACKS
117
+ // ============================================================================
118
+ const listCallback = ({ data, error }) => {
119
+ if (error) {
120
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
121
+ return;
122
+ }
123
+ if (data) {
124
+ context.dispatch({
125
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_ITEMS,
126
+ payload: { items: data.items || [], count: data.count || 0 },
127
+ });
128
+ }
129
+ };
130
+ const updateCallback = ({ data, error }) => {
131
+ const isCreated = isCreatedOrUpdated(data);
132
+ if (error) {
133
+ showToast(isCreated ? t("messagesCreateFailed") : t("messagesUpdateFailed"), TOAST_VARIANT.ERROR);
134
+ return;
135
+ }
136
+ if (data) {
137
+ showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
138
+ invalidateDiscountCodeCache();
139
+ listFetchNow();
140
+ resetFormAndCloseDrawer();
141
+ }
142
+ };
143
+ const byIdCallback = ({ data, error }) => {
144
+ if (error) {
145
+ showToast(t("messagesDetailsFetchFailed"), TOAST_VARIANT.ERROR);
146
+ return;
147
+ }
148
+ if (data) {
149
+ const discountCode = data;
150
+ // Batch field updates for better performance
151
+ const fieldMappings = {
152
+ id: discountCode.id,
153
+ code: discountCode.code,
154
+ description: discountCode.description,
155
+ discountType: discountCode.discountType,
156
+ discountValue: discountCode.discountValue,
157
+ enabled: discountCode.enabled,
158
+ };
159
+ Object.entries(fieldMappings).forEach(([key, value]) => {
160
+ setField(key, value !== null && value !== void 0 ? value : undefined);
161
+ });
162
+ }
163
+ };
164
+ const deleteCallback = ({ data, error }) => {
165
+ if (error) {
166
+ showToast(t("messagesDeleteFailed"), TOAST_VARIANT.ERROR);
167
+ return;
168
+ }
169
+ if (data) {
170
+ showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
171
+ invalidateDiscountCodeCache();
172
+ listFetchNow();
173
+ }
174
+ };
175
+ // ============================================================================
176
+ // API HOOKS
177
+ // ============================================================================
178
+ const { listFetchNow, listLoading, updateFetchNow, updateLoading, byIdFetchNow, deleteFetchNow, deleteLoading, byIdLoading, } = useModuleEntityV2({
179
+ byIdCallback,
180
+ byIdParams,
181
+ deleteCallback,
182
+ deleteParams,
183
+ listCallback,
184
+ listParams,
185
+ listUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
186
+ searchQuery: debouncedQuery,
187
+ unitByIdUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
188
+ unitUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
189
+ updateCallback,
190
+ updateParams,
191
+ headers: {
192
+ "Content-Type": "application/json",
193
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
194
+ },
195
+ });
196
+ // ============================================================================
197
+ // UTILITIES
198
+ // ============================================================================
199
+ const showToast = useCallback((message, variant) => {
200
+ generateThemeToast({ description: message, variant });
201
+ }, []);
202
+ const resetFormAndCloseDrawer = useCallback(() => {
203
+ context.dispatch({
204
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
205
+ payload: { errors: {} },
206
+ });
207
+ context.dispatch({
208
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
209
+ payload: { disabled: false },
210
+ });
211
+ context.dispatch({
212
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
213
+ payload: { drawer: null },
214
+ });
215
+ }, [context]);
216
+ const setField = useCallback((key, value) => {
217
+ context.dispatch({
218
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_INPUT_FIELD,
219
+ payload: { field: key, value },
220
+ });
221
+ }, [context]);
222
+ // ============================================================================
223
+ // HANDLERS
224
+ // ============================================================================
225
+ const handleChange = useCallback((field, value) => {
226
+ context.dispatch({
227
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
228
+ payload: { errors: {} },
229
+ });
230
+ context.dispatch({
231
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
232
+ payload: { disabled: false },
233
+ });
234
+ setField(field, value);
235
+ }, [setField, context]);
236
+ const handleCloseDrawer = useCallback(() => {
237
+ resetFormAndCloseDrawer();
238
+ }, [resetFormAndCloseDrawer]);
239
+ const handleCreate = useCallback(() => {
240
+ context.dispatch({
241
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
242
+ payload: { drawer: DISCOUNT_CODE_DRAWER.FORM_DRAWER },
243
+ });
244
+ }, [context]);
245
+ const handleView = useCallback((row) => {
246
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
247
+ context.dispatch({
248
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
249
+ payload: { drawer: DISCOUNT_CODE_DRAWER.VIEW_DRAWER },
250
+ });
251
+ }, [context, byIdFetchNow]);
252
+ const handleEdit = useCallback((row) => {
253
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
254
+ context.dispatch({
255
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
256
+ payload: { drawer: DISCOUNT_CODE_DRAWER.FORM_DRAWER },
257
+ });
258
+ }, [context, byIdFetchNow]);
259
+ const handleDelete = useCallback((row) => {
260
+ if (confirm(t("confirmDelete"))) {
261
+ deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
262
+ body: JSON.stringify({ id: row === null || row === void 0 ? void 0 : row.id }),
263
+ });
264
+ }
265
+ }, [t, deleteFetchNow]);
266
+ const handleFilters = useCallback(() => {
267
+ context.dispatch({
268
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
269
+ payload: { drawer: DISCOUNT_CODE_DRAWER.FILTER_DRAWER },
270
+ });
271
+ }, [context]);
272
+ const handlePageChange = useCallback((page) => {
273
+ context.dispatch({
274
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
275
+ payload: { currentPage: page },
276
+ });
277
+ }, [context]);
278
+ const handlePageLimitChange = useCallback((limit) => {
279
+ context.dispatch({
280
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_PAGE_LIMIT,
281
+ payload: { pageLimit: limit },
282
+ });
283
+ }, [context]);
284
+ const handleSearch = useCallback((query) => {
285
+ context.dispatch({
286
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_SEARCH_QUERY,
287
+ payload: { searchQuery: query },
288
+ });
289
+ }, [context]);
290
+ const clearFilters = useCallback(() => {
291
+ context.dispatch({
292
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_FILTERS,
293
+ payload: { filters: { filterEnabled: undefined } },
294
+ });
295
+ context.dispatch({
296
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
297
+ payload: { currentPage: 1 },
298
+ });
299
+ }, [context]);
300
+ const handleSubmit = useCallback(() => {
301
+ context.dispatch({
302
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
303
+ payload: { disabled: true },
304
+ });
305
+ validateForm({
306
+ params: updateParams,
307
+ schema: accountDiscountCodeFormValidation,
308
+ successCallback: () => {
309
+ updateFetchNow(undefined, {
310
+ body: JSON.stringify(updateParams),
311
+ });
312
+ },
313
+ errorCallback: (errors) => {
314
+ context.dispatch({
315
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
316
+ payload: { errors },
317
+ });
318
+ context.dispatch({
319
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
320
+ payload: { disabled: false },
321
+ });
322
+ showToast(t("messagesFormErrors"), TOAST_VARIANT.ERROR);
323
+ },
324
+ });
325
+ }, [context, updateParams, t, showToast, updateFetchNow]);
326
+ // ============================================================================
327
+ // HEADER & ROW ACTIONS
328
+ // ============================================================================
329
+ const headerActions = useMemo(() => [
330
+ {
331
+ enabled: true,
332
+ handleOnClick: handleFilters,
333
+ label: t("headerActionsFilters"),
334
+ order: 1,
335
+ icon: Filter,
336
+ },
337
+ {
338
+ enabled: true,
339
+ handleOnClick: handleCreate,
340
+ label: t("headerActionsAdd"),
341
+ order: 2,
342
+ icon: Plus,
343
+ },
344
+ ], [handleFilters, handleCreate, t]);
345
+ const rowActions = useMemo(() => [
346
+ {
347
+ enabled: true,
348
+ handleOnClick: handleView,
349
+ label: t("rowActionsView"),
350
+ order: 1,
351
+ },
352
+ {
353
+ enabled: true,
354
+ handleOnClick: handleEdit,
355
+ label: t("rowActionsEdit"),
356
+ order: 2,
357
+ },
358
+ {
359
+ enabled: true,
360
+ handleOnClick: handleDelete,
361
+ label: t("rowActionsDelete"),
362
+ order: 3,
363
+ },
364
+ ], [handleView, handleEdit, handleDelete, t]);
365
+ const applyFilters = useCallback(() => {
366
+ context.dispatch({
367
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
368
+ payload: { currentPage: 1 },
369
+ });
370
+ listFetchNow();
371
+ handleCloseDrawer();
372
+ }, [context, listFetchNow, handleCloseDrawer]);
373
+ // ============================================================================
374
+ // EFFECTS
375
+ // ============================================================================
376
+ useEffect(() => {
377
+ if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
378
+ return;
379
+ (async () => {
380
+ try {
381
+ const { count, items } = await getCachedDiscountCodes({
382
+ params: listParams,
383
+ });
384
+ context.dispatch({
385
+ type: DISCOUNT_CODE_ACTION_TYPES.SET_ITEMS,
386
+ payload: { items: items || [], count: count || 0 },
387
+ });
388
+ }
389
+ catch (_a) {
390
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
391
+ }
392
+ })();
393
+ }, [listParams, showToast, t, workspace === null || workspace === void 0 ? void 0 : workspace.id, context]);
394
+ // ============================================================================
395
+ // RETURN
396
+ // ============================================================================
397
+ return Object.assign(Object.assign({}, context), { applyFilters,
398
+ byIdLoading,
399
+ clearFilters,
400
+ deleteLoading,
401
+ handleChange,
402
+ handleCloseDrawer,
403
+ handleCreate,
404
+ handleDelete,
405
+ handleEdit,
406
+ handleFilters,
407
+ handlePageChange,
408
+ handlePageLimitChange,
409
+ handleSearch,
410
+ handleSubmit,
411
+ handleView,
412
+ headerActions,
413
+ listLoading,
414
+ rowActions,
415
+ updateLoading });
416
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * DiscountCodeFilter
3
+ *
4
+ * Lightweight filter UI for the discount-code drawer. The generic page
5
+ * will render this component inside the filter drawer slot. Keep filters
6
+ * minimal and explicit (enabled flag, type select) so caching remains
7
+ * predictable.
8
+ */
9
+ export declare const DiscountCodeFilter: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslations } from "next-intl";
3
+ /**
4
+ * DiscountCodeFilter
5
+ *
6
+ * Lightweight filter UI for the discount-code drawer. The generic page
7
+ * will render this component inside the filter drawer slot. Keep filters
8
+ * minimal and explicit (enabled flag, type select) so caching remains
9
+ * predictable.
10
+ */
11
+ export const DiscountCodeFilter = () => {
12
+ const t = useTranslations("discountCode");
13
+ return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "text-sm text-muted-foreground", children: t("filter") }) }));
14
+ };
@@ -0,0 +1 @@
1
+ export declare const DiscountCodeForm: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Discount Code Form
5
+ *
6
+ * Controlled form component used inside the generic module drawer.
7
+ * Fields in this form map directly to the `accountDiscountCodeFormValidation`
8
+ * schema in `validate.ts` and to the `update` payload sent to the API.
9
+ *
10
+ * Guidelines:
11
+ * - Use `handleChange(field, value)` from `useDiscountCodeModule()` to
12
+ * update the local module state.
13
+ * - Keep input `id` props stable to preserve accessibility and testing hooks.
14
+ */
15
+ import { useDiscountCodeModule } from "./context";
16
+ import { useTranslations } from "next-intl";
17
+ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
18
+ import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
19
+ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
20
+ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
21
+ import { Percent, DollarSign } from "lucide-react";
22
+ import { DISCOUNT_TYPE } from "@/type";
23
+ export const DiscountCodeForm = () => {
24
+ const { state, handleChange } = useDiscountCodeModule();
25
+ const t = useTranslations("discountCode");
26
+ const { code, description, discountType, discountValue, enabled, errors } = state;
27
+ const discountTypeOptions = [
28
+ {
29
+ label: t("formDiscountTypeOptionsPercentage"),
30
+ value: DISCOUNT_TYPE.PERCENTAGE,
31
+ icon: _jsx(Percent, { className: "h-4 w-4" }),
32
+ },
33
+ {
34
+ label: t("formDiscountTypeOptionsFixed"),
35
+ value: DISCOUNT_TYPE.FIXED,
36
+ icon: _jsx(DollarSign, { className: "h-4 w-4" }),
37
+ },
38
+ ];
39
+ return (_jsx("div", { className: "space-y-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.code, id: "code", info: t("formDiscountCodeInfo"), label: t("formDiscountCodeLabel"), onChange: (e) => handleChange("code", e.target.value.toUpperCase()), placeholder: t("formDiscountCodePlaceholder"), required: true, value: code }), _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(EnhancedCombobox, { error: errors.discountType, id: "discountType", info: t("formDiscountTypeInfo"), label: t("formDiscountTypeLabel"), onValueChange: (value) => handleChange("discountType", value), options: discountTypeOptions, placeholder: t("formDiscountTypePlaceholder"), required: true, value: discountType }), _jsx(EnhancedInput, { id: "discountValue", label: t("formDiscountValueLabel"), type: "number", value: (discountValue === null || discountValue === void 0 ? void 0 : discountValue.toString()) || "", onChange: (e) => handleChange("discountValue", parseFloat(e.target.value) || 0), error: errors.discountValue, placeholder: discountType === DISCOUNT_TYPE.PERCENTAGE
40
+ ? t("formDiscountValuePlaceholderPercentage")
41
+ : t("formDiscountValuePlaceholderFixed"), required: true, min: 0, max: discountType === DISCOUNT_TYPE.PERCENTAGE ? 100 : undefined, step: discountType === DISCOUNT_TYPE.PERCENTAGE ? 0.1 : 0.01, info: discountType === DISCOUNT_TYPE.PERCENTAGE
42
+ ? t("formDiscountValueInfoPercentage")
43
+ : t("formDiscountValueInfoFixed") }), _jsx(EnhancedCheckbox, { checked: enabled, id: "enabled", info: t("formEnabledInfo"), label: t("formEnabledLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }) }));
44
+ };
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ /**
3
+ * DiscountCodeMoreActions
4
+ *
5
+ * Placeholder slot rendered inside the generic page's "more actions"
6
+ * area. Keep this file small — add any contextual bulk-actions here
7
+ * (export/import, bulk enable/disable) as the product requires.
8
+ */
9
+ export declare const DiscountCodeMoreActions: React.FC;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslations } from "next-intl";
3
+ /**
4
+ * DiscountCodeMoreActions
5
+ *
6
+ * Placeholder slot rendered inside the generic page's "more actions"
7
+ * area. Keep this file small — add any contextual bulk-actions here
8
+ * (export/import, bulk enable/disable) as the product requires.
9
+ */
10
+ export const DiscountCodeMoreActions = () => {
11
+ const t = useTranslations("discountCode");
12
+ return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "text-sm text-muted-foreground", children: t("moreActions") }) }));
13
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Discount Code Page Component
3
+ *
4
+ * Thin wrapper around GenericModulePage. All handlers, header actions, and row
5
+ * actions live in context.tsx — this file only owns:
6
+ * - tableBodyCols (static, module-level constant)
7
+ * - discountCodeConfig (memoised on locale change only; size fixed so the
8
+ * component type produced by createGenericModulePage never changes at
9
+ * runtime, preventing the infinite-remount re-render loop)
10
+ * - permission guard
11
+ */
12
+ import { FC } from "react";
13
+ import { USER_ROLE } from "@/type";
14
+ export declare const DiscountCodePage: FC<{
15
+ userRole: USER_ROLE;
16
+ }>;