@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,486 @@
1
+ "use client";
2
+ /**
3
+ * FamilyMember Module — business logic + API integration
4
+ *
5
+ * This module wires the generic module state (created by
6
+ * `createGenericModule`) to network actions using `useModuleEntityV2`.
7
+ * It contains the domain-specific handlers (create/view/edit/delete),
8
+ * local validation wiring, and optimistic UI helpers.
9
+ *
10
+ * Key responsibilities:
11
+ * - expose `useFamilyMemberModule()` which UI components call for actions
12
+ * - keep the module-specific `apiParams` and callbacks in one place
13
+ * - ensure cache invalidation and toast notifications on mutation
14
+ *
15
+ * Exported utilities:
16
+ * - `FamilyMemberStateContextProvider` — provider component used by the page
17
+ * - `useFamilyMemberModule()` — hook that returns handlers and state
18
+ */
19
+ import { useCallback, useEffect, useMemo } from "react";
20
+ import { isCreatedOrUpdated, validateForm, } from "@react-pakistan/util-functions";
21
+ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
22
+ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
23
+ import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
24
+ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
25
+ import { FAMILY_MEMBER_API_ROUTES, pageLimit } from "./constants";
26
+ import { getCachedFamilyMembers, invalidateFamilyMembersCache } from "./cache";
27
+ import { familyMemberFormValidation } from "./validate";
28
+ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
29
+ import { useTranslations } from "next-intl";
30
+ import { getCachedWorkspaceSync } from "../workspace/cache";
31
+ import { Eye, Filter, MoreHorizontal, Pencil, Plus } from "lucide-react";
32
+ // ============================================================================
33
+ // DRAWER TYPES
34
+ // ============================================================================
35
+ export const FAMILY_MEMBER_DRAWER = {
36
+ FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
37
+ FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
38
+ MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
39
+ VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
40
+ };
41
+ // ============================================================================
42
+ // CONFIGURATION
43
+ // ============================================================================
44
+ const familyMemberConfig = {
45
+ name: "FamilyMember",
46
+ displayName: "Family Member",
47
+ initialState: {
48
+ // List Data
49
+ items: [],
50
+ count: 0,
51
+ // Search & Pagination
52
+ currentPage: 1,
53
+ pageLimit,
54
+ searchQuery: "",
55
+ // Filters
56
+ familyId: "",
57
+ filterEnabled: undefined,
58
+ // UI State
59
+ disableSaveButton: false,
60
+ drawer: null,
61
+ // Form State
62
+ avatar: "",
63
+ bloodGroup: "",
64
+ dateOfBirth: "",
65
+ email: "",
66
+ emergencyPhone: "",
67
+ enabled: true,
68
+ errors: {},
69
+ firstName: "",
70
+ gender: "",
71
+ id: "",
72
+ idNumber: "",
73
+ isPrimary: false,
74
+ lastName: "",
75
+ occupation: "",
76
+ phone: "",
77
+ relationship: "",
78
+ role: "",
79
+ },
80
+ drawerTypes: DRAWER_TYPES,
81
+ };
82
+ // ============================================================================
83
+ // CREATE FAMILY MEMBER MODULE
84
+ // ============================================================================
85
+ export const { actionTypes: FAMILY_MEMBER_ACTION_TYPES, config: familyMemberModuleConfig, initialState: initialFamilyMemberState, Provider: FamilyMemberProvider, reducer: familyMemberReducer, useContext: useFamilyMemberContext, } = createGenericModule(familyMemberConfig);
86
+ // ============================================================================
87
+ // ENHANCED FAMILY MEMBER HOOK WITH API INTEGRATION
88
+ // ============================================================================
89
+ export const useFamilyMemberModule = () => {
90
+ var _a, _b;
91
+ const context = useFamilyMemberContext();
92
+ const t = useTranslations("familyMember");
93
+ const workspace = getCachedWorkspaceSync();
94
+ const debouncedQuery = useDebounce(context.state.searchQuery, 800);
95
+ // ============================================================================
96
+ // API PARAMETERS
97
+ // ============================================================================
98
+ const listParams = useMemo(() => {
99
+ var _a;
100
+ return (Object.assign(Object.assign(Object.assign({ currentPage: context.state.currentPage, pageLimit: context.state.pageLimit, schoolId: ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "" }, (debouncedQuery ? { searchQuery: debouncedQuery } : {})), (context.state.filterEnabled !== undefined
101
+ ? { filterEnabled: String(context.state.filterEnabled) }
102
+ : {})), (context.state.familyId ? { familyId: context.state.familyId } : {})));
103
+ }, [
104
+ context.state.currentPage,
105
+ context.state.familyId,
106
+ context.state.filterEnabled,
107
+ context.state.pageLimit,
108
+ debouncedQuery,
109
+ (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id,
110
+ ]);
111
+ const updateParams = useMemo(() => ({
112
+ id: context.state.id,
113
+ familyId: context.state.familyId,
114
+ role: context.state.role,
115
+ firstName: context.state.firstName,
116
+ lastName: context.state.lastName,
117
+ dateOfBirth: context.state.dateOfBirth,
118
+ gender: context.state.gender,
119
+ bloodGroup: context.state.bloodGroup,
120
+ phone: context.state.phone,
121
+ email: context.state.email,
122
+ relationship: context.state.relationship,
123
+ occupation: context.state.occupation,
124
+ emergencyPhone: context.state.emergencyPhone,
125
+ idNumber: context.state.idNumber,
126
+ avatar: context.state.avatar,
127
+ isPrimary: context.state.isPrimary,
128
+ enabled: context.state.enabled,
129
+ }), [
130
+ context.state.avatar,
131
+ context.state.bloodGroup,
132
+ context.state.dateOfBirth,
133
+ context.state.email,
134
+ context.state.emergencyPhone,
135
+ context.state.enabled,
136
+ context.state.familyId,
137
+ context.state.firstName,
138
+ context.state.gender,
139
+ context.state.id,
140
+ context.state.idNumber,
141
+ context.state.isPrimary,
142
+ context.state.lastName,
143
+ context.state.occupation,
144
+ context.state.phone,
145
+ context.state.relationship,
146
+ context.state.role,
147
+ ]);
148
+ const byIdParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
149
+ const deleteParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
150
+ // ============================================================================
151
+ // API CALLBACKS
152
+ // ============================================================================
153
+ const listCallback = ({ data, error }) => {
154
+ if (error) {
155
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
156
+ return;
157
+ }
158
+ if (data) {
159
+ context.dispatch({
160
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_ITEMS,
161
+ payload: { items: data.items || [], count: data.count || 0 },
162
+ });
163
+ }
164
+ };
165
+ const updateCallback = ({ data, error }) => {
166
+ const isCreated = isCreatedOrUpdated(data);
167
+ if (error) {
168
+ showToast(isCreated ? t("messagesCreateFailed") : t("messagesUpdateFailed"), TOAST_VARIANT.ERROR);
169
+ return;
170
+ }
171
+ if (data) {
172
+ showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
173
+ invalidateFamilyMembersCache();
174
+ listFetchNow();
175
+ resetFormAndCloseDrawer();
176
+ }
177
+ };
178
+ const byIdCallback = ({ data, error }) => {
179
+ if (error) {
180
+ showToast(t("messagesDetailsFetchFailed"), TOAST_VARIANT.ERROR);
181
+ return;
182
+ }
183
+ if (data) {
184
+ const familyMember = data;
185
+ // Batch field updates for better performance
186
+ const fieldMappings = {
187
+ id: familyMember.id,
188
+ familyId: familyMember.familyId,
189
+ role: familyMember.role,
190
+ firstName: familyMember.firstName,
191
+ lastName: familyMember.lastName,
192
+ dateOfBirth: familyMember.dateOfBirth,
193
+ gender: familyMember.gender,
194
+ bloodGroup: familyMember.bloodGroup,
195
+ phone: familyMember.phone,
196
+ email: familyMember.email,
197
+ relationship: familyMember.relationship,
198
+ occupation: familyMember.occupation,
199
+ emergencyPhone: familyMember.emergencyPhone,
200
+ idNumber: familyMember.idNumber,
201
+ avatar: familyMember.avatar,
202
+ isPrimary: familyMember.isPrimary,
203
+ enabled: familyMember.enabled,
204
+ };
205
+ Object.entries(fieldMappings).forEach(([key, value]) => {
206
+ setField(key, value !== null && value !== void 0 ? value : undefined);
207
+ });
208
+ }
209
+ };
210
+ const deleteCallback = ({ data, error }) => {
211
+ if (error) {
212
+ showToast(t("messagesDeleteFailed"), TOAST_VARIANT.ERROR);
213
+ return;
214
+ }
215
+ if (data) {
216
+ showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
217
+ invalidateFamilyMembersCache();
218
+ listFetchNow();
219
+ }
220
+ };
221
+ // ============================================================================
222
+ // API HOOKS
223
+ // ============================================================================
224
+ const { listFetchNow, listLoading, updateFetchNow, updateLoading, byIdFetchNow, deleteFetchNow, deleteLoading, byIdLoading, } = useModuleEntityV2({
225
+ byIdCallback,
226
+ byIdParams,
227
+ deleteCallback,
228
+ deleteParams,
229
+ listCallback,
230
+ listParams,
231
+ listUrl: FAMILY_MEMBER_API_ROUTES.UNIT,
232
+ searchQuery: debouncedQuery,
233
+ unitByIdUrl: FAMILY_MEMBER_API_ROUTES.UNIT,
234
+ unitUrl: FAMILY_MEMBER_API_ROUTES.UNIT,
235
+ updateCallback,
236
+ updateParams,
237
+ headers: {
238
+ "Content-Type": "application/json",
239
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
240
+ },
241
+ });
242
+ // ============================================================================
243
+ // UTILITIES
244
+ // ============================================================================
245
+ const showToast = useCallback((message, variant) => {
246
+ generateThemeToast({ description: message, variant });
247
+ }, []);
248
+ const resetFormAndCloseDrawer = useCallback(() => {
249
+ context.dispatch({
250
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_ERRORS,
251
+ payload: { errors: {} },
252
+ });
253
+ context.dispatch({
254
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
255
+ payload: { disabled: false },
256
+ });
257
+ context.dispatch({
258
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
259
+ payload: { drawer: null },
260
+ });
261
+ }, [context]);
262
+ const setField = useCallback((key, value) => {
263
+ let processedValue;
264
+ if (value === null) {
265
+ processedValue = undefined;
266
+ }
267
+ else if (value instanceof Date) {
268
+ processedValue = value.toISOString().slice(0, 10); // YYYY-MM-DD format
269
+ }
270
+ else {
271
+ processedValue = value;
272
+ }
273
+ context.dispatch({
274
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_INPUT_FIELD,
275
+ payload: { key, value: processedValue },
276
+ });
277
+ }, [context]);
278
+ // ============================================================================
279
+ // HANDLERS
280
+ // ============================================================================
281
+ const handleChange = useCallback((key, value) => {
282
+ context.dispatch({
283
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_ERRORS,
284
+ payload: { errors: {} },
285
+ });
286
+ context.dispatch({
287
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
288
+ payload: { disabled: false },
289
+ });
290
+ setField(key, value);
291
+ }, [context, setField]);
292
+ const handlePageChange = useCallback((page) => {
293
+ context.dispatch({
294
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_CURRENT_PAGE,
295
+ payload: { currentPage: page },
296
+ });
297
+ }, [context]);
298
+ const handlePageLimitChange = useCallback((limit) => {
299
+ context.dispatch({
300
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_PAGE_LIMIT,
301
+ payload: { pageLimit: limit },
302
+ });
303
+ }, [context]);
304
+ const handleCloseDrawer = useCallback(() => {
305
+ resetFormAndCloseDrawer();
306
+ }, [resetFormAndCloseDrawer]);
307
+ const handleCreate = useCallback(() => {
308
+ context.dispatch({
309
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
310
+ payload: { drawer: FAMILY_MEMBER_DRAWER.FORM_DRAWER },
311
+ });
312
+ }, [context]);
313
+ const handleView = useCallback((row) => {
314
+ const record = row;
315
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: record.id } });
316
+ context.dispatch({
317
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
318
+ payload: { drawer: FAMILY_MEMBER_DRAWER.VIEW_DRAWER },
319
+ });
320
+ }, [context, byIdFetchNow]);
321
+ const handleEdit = useCallback((row) => {
322
+ const record = row;
323
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: record.id } });
324
+ context.dispatch({
325
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
326
+ payload: { drawer: FAMILY_MEMBER_DRAWER.FORM_DRAWER },
327
+ });
328
+ }, [context, byIdFetchNow]);
329
+ const handleDelete = useCallback((row) => {
330
+ const record = row;
331
+ if (!confirm(t("areYouSureYouWantToDeleteThisFamilyMember")))
332
+ return;
333
+ deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
334
+ body: JSON.stringify({ id: record.id }),
335
+ });
336
+ }, [t, deleteFetchNow]);
337
+ const handleFilters = useCallback(() => {
338
+ context.dispatch({
339
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
340
+ payload: { drawer: FAMILY_MEMBER_DRAWER.FILTER_DRAWER },
341
+ });
342
+ }, [context]);
343
+ const handleMoreActions = useCallback(() => {
344
+ context.dispatch({
345
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DRAWER,
346
+ payload: { drawer: FAMILY_MEMBER_DRAWER.MORE_ACTIONS_DRAWER },
347
+ });
348
+ }, [context]);
349
+ const clearFilters = useCallback(() => {
350
+ context.dispatch({
351
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_FILTERS,
352
+ payload: { filters: { filterEnabled: undefined } },
353
+ });
354
+ context.dispatch({
355
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_CURRENT_PAGE,
356
+ payload: { currentPage: 1 },
357
+ });
358
+ }, [context]);
359
+ const handleSearch = useCallback((query) => {
360
+ context.dispatch({
361
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_SEARCH_QUERY,
362
+ payload: { searchQuery: query },
363
+ });
364
+ }, [context]);
365
+ // ============================================================================
366
+ // NETWORK ACTIONS
367
+ // ============================================================================
368
+ const handleSubmit = useCallback(() => {
369
+ context.dispatch({
370
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
371
+ payload: { disabled: true },
372
+ });
373
+ validateForm({
374
+ params: updateParams,
375
+ schema: familyMemberFormValidation,
376
+ successCallback: () => {
377
+ updateFetchNow(undefined, {
378
+ body: JSON.stringify(updateParams),
379
+ });
380
+ },
381
+ errorCallback: (errors) => {
382
+ context.dispatch({
383
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_ERRORS,
384
+ payload: { errors },
385
+ });
386
+ context.dispatch({
387
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
388
+ payload: { disabled: false },
389
+ });
390
+ showToast(t("messagesFormErrors"), TOAST_VARIANT.ERROR);
391
+ },
392
+ });
393
+ }, [context, updateParams, t, showToast, updateFetchNow]);
394
+ const applyFilters = useCallback(() => {
395
+ context.dispatch({
396
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_CURRENT_PAGE,
397
+ payload: { currentPage: 1 },
398
+ });
399
+ listFetchNow();
400
+ handleCloseDrawer();
401
+ }, [context, listFetchNow, handleCloseDrawer]);
402
+ const headerActions = useMemo(() => [
403
+ {
404
+ enabled: false,
405
+ handleOnClick: handleMoreActions,
406
+ label: t("headerActionsMoreActions"),
407
+ order: 1,
408
+ icon: MoreHorizontal,
409
+ },
410
+ {
411
+ enabled: true,
412
+ handleOnClick: handleFilters,
413
+ label: t("headerActionsFilters"),
414
+ order: 2,
415
+ icon: Filter,
416
+ },
417
+ {
418
+ enabled: false,
419
+ handleOnClick: handleCreate,
420
+ label: t("headerActionsAddFamilyMember"),
421
+ order: 3,
422
+ icon: Plus,
423
+ },
424
+ ], [handleFilters, handleMoreActions, handleCreate, t]);
425
+ const rowActions = useMemo(() => [
426
+ {
427
+ enabled: true,
428
+ handleOnClick: (row) => handleView(row),
429
+ label: t("rowActionsView"),
430
+ order: 1,
431
+ icon: Eye,
432
+ },
433
+ {
434
+ enabled: true,
435
+ handleOnClick: (row) => handleEdit(row),
436
+ label: t("rowActionsEdit"),
437
+ order: 2,
438
+ icon: Pencil,
439
+ },
440
+ ], [handleView, handleEdit, t]);
441
+ // ============================================================================
442
+ // EFFECTS
443
+ // ============================================================================
444
+ useEffect(() => {
445
+ var _a;
446
+ if (!((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id))
447
+ return;
448
+ (async () => {
449
+ try {
450
+ const { count, items } = await getCachedFamilyMembers({
451
+ params: listParams,
452
+ });
453
+ context.dispatch({
454
+ type: FAMILY_MEMBER_ACTION_TYPES.SET_ITEMS,
455
+ payload: { items: items || [], count: count || 0 },
456
+ });
457
+ }
458
+ catch (_a) {
459
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
460
+ }
461
+ })();
462
+ }, [listParams, context, showToast, t, (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.id]);
463
+ // ============================================================================
464
+ // RETURN
465
+ // ============================================================================
466
+ return Object.assign(Object.assign({}, context), { applyFilters,
467
+ byIdLoading,
468
+ clearFilters,
469
+ deleteLoading,
470
+ handleChange,
471
+ handleCloseDrawer,
472
+ handleCreate,
473
+ handleDelete,
474
+ handleEdit,
475
+ handleFilters,
476
+ handleMoreActions,
477
+ handlePageChange,
478
+ handlePageLimitChange,
479
+ handleSearch,
480
+ handleSubmit,
481
+ handleView,
482
+ headerActions,
483
+ listLoading,
484
+ rowActions,
485
+ updateLoading });
486
+ };
@@ -0,0 +1 @@
1
+ export declare const FamilyMemberFilter: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * FamilyMember Filter Component
5
+ *
6
+ * Filter component for family member records.
7
+ */
8
+ import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
9
+ import { useFamilyMemberModule } from "./context";
10
+ import { useTranslations } from "next-intl";
11
+ export const FamilyMemberFilter = () => {
12
+ const { handleChange, state } = useFamilyMemberModule();
13
+ const { filterEnabled } = state;
14
+ const t = useTranslations("familyMember");
15
+ const filterEnabledValue = filterEnabled === undefined
16
+ ? "undefined"
17
+ : filterEnabled
18
+ ? "true"
19
+ : "false";
20
+ return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "grid grid-cols-1 gap-4", children: _jsx(EnhancedRadio, { label: t("enabled"), name: "filterEnabled", value: filterEnabledValue, options: [
21
+ { label: t("all"), value: "undefined" },
22
+ { label: t("enabled"), value: "true" },
23
+ { label: t("disabled"), value: "false" },
24
+ ], onValueChange: (next) => {
25
+ const parsed = next === "true" ? true : next === "false" ? false : undefined;
26
+ handleChange("filterEnabled", parsed);
27
+ } }) }) }));
28
+ };
@@ -0,0 +1 @@
1
+ export declare const FamilyMemberForm: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * FamilyMember Form Component
5
+ *
6
+ * Create and edit form for family member records.
7
+ */
8
+ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
9
+ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
10
+ import { useFamilyMemberModule } from "./context";
11
+ import { useTranslations } from "next-intl";
12
+ export const FamilyMemberForm = () => {
13
+ const { handleChange, state } = useFamilyMemberModule();
14
+ const { bloodGroup, dateOfBirth, email, emergencyPhone, enabled, errors, firstName, gender, idNumber, isPrimary, lastName, occupation, phone, relationship, role, } = state;
15
+ const t = useTranslations("familyMember");
16
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("personalInformation") }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { error: errors.firstName, id: "firstName", info: t("firstNameInfo"), label: t("firstName"), onChange: (e) => handleChange("firstName", e.target.value), placeholder: t("firstNamePlaceholder"), required: true, value: firstName }), _jsx(EnhancedInput, { error: errors.lastName, id: "lastName", info: t("lastNameInfo"), label: t("lastName"), onChange: (e) => handleChange("lastName", e.target.value), placeholder: t("lastNamePlaceholder"), required: true, value: lastName })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { error: errors.role, id: "role", info: t("roleInfo"), label: t("role"), onChange: (e) => handleChange("role", e.target.value), placeholder: t("rolePlaceholder"), required: true, value: role }), _jsx(EnhancedInput, { error: errors.dateOfBirth, id: "dateOfBirth", info: t("dateOfBirthInfo"), label: t("dateOfBirth"), onChange: (e) => handleChange("dateOfBirth", e.target.value), placeholder: "YYYY-MM-DD", type: "date", value: dateOfBirth })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { error: errors.gender, id: "gender", info: t("genderInfo"), label: t("gender"), onChange: (e) => handleChange("gender", e.target.value), placeholder: t("genderPlaceholder"), value: gender }), _jsx(EnhancedInput, { error: errors.bloodGroup, id: "bloodGroup", info: t("bloodGroupInfo"), label: t("bloodGroup"), onChange: (e) => handleChange("bloodGroup", e.target.value), placeholder: t("bloodGroupPlaceholder"), value: bloodGroup })] }), _jsx(EnhancedInput, { error: errors.idNumber, id: "idNumber", info: t("idNumberInfo"), label: t("idNumber"), onChange: (e) => handleChange("idNumber", e.target.value), placeholder: t("idNumberPlaceholder"), value: idNumber })] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("contactInformation") }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(EnhancedInput, { error: errors.phone, id: "phone", info: t("phoneInfo"), label: t("phone"), onChange: (e) => handleChange("phone", e.target.value), placeholder: t("phonePlaceholder"), type: "tel", value: phone }), _jsx(EnhancedInput, { error: errors.email, id: "email", info: t("emailInfo"), label: t("email"), onChange: (e) => handleChange("email", e.target.value), placeholder: t("emailPlaceholder"), type: "email", value: email })] }), _jsx(EnhancedInput, { error: errors.emergencyPhone, id: "emergencyPhone", info: t("emergencyPhoneInfo"), label: t("emergencyPhone"), onChange: (e) => handleChange("emergencyPhone", e.target.value), placeholder: t("emergencyPhonePlaceholder"), type: "tel", value: emergencyPhone })] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("additionalInformation") }), _jsx(EnhancedInput, { error: errors.relationship, id: "relationship", info: t("relationshipInfo"), label: t("relationship"), onChange: (e) => handleChange("relationship", e.target.value), placeholder: t("relationshipPlaceholder"), value: relationship }), _jsx(EnhancedInput, { error: errors.occupation, id: "occupation", info: t("occupationInfo"), label: t("occupation"), onChange: (e) => handleChange("occupation", e.target.value), placeholder: t("occupationPlaceholder"), value: occupation })] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("status") }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(EnhancedCheckbox, { checked: isPrimary, id: "isPrimary", label: t("primaryContact"), onCheckedChange: (checked) => handleChange("isPrimary", checked === true) }), _jsx(EnhancedCheckbox, { checked: enabled, id: "enabled", label: t("active"), onCheckedChange: (checked) => handleChange("enabled", checked === true) })] })] })] }));
17
+ };
@@ -0,0 +1 @@
1
+ export declare const FamilyMemberMoreActions: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export const FamilyMemberMoreActions = () => {
4
+ return _jsx("div", { className: "space-y-4", children: "More actions for family members" });
5
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Family Member 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
+ * - familyMemberConfig (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 FamilyMemberPage: FC<{
15
+ userRole: USER_ROLE;
16
+ }>;