@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,583 @@
1
+ "use client";
2
+ /**
3
+ * StudentProfile Module — business logic + API integration
4
+ *
5
+ * Migrated to the generic module factory pattern (same as workspace-user /
6
+ * family-member). The hand-rolled context / reducer / actions / type files
7
+ * have been collapsed here; the factory creates the action types, initial
8
+ * state, provider, reducer, and useContext automatically.
9
+ *
10
+ * Key responsibilities:
11
+ * - expose `useStudentProfileModule()` which UI components call for actions
12
+ * - keep module-specific apiParams and callbacks in one place
13
+ * - preserve domain-specific handlers: withdraw, re-admit, status changes
14
+ * - ensure cache invalidation and toast notifications on mutation
15
+ *
16
+ * Exported utilities:
17
+ * - `StudentProfileProvider` — provider component used by the page
18
+ * - `StudentProfileStateContextProvider` — backward-compatible alias
19
+ * - `useStudentProfileModule()` — primary hook for UI components
20
+ * - `useStudentProfileStateContext` — backward-compatible alias
21
+ */
22
+ import { useCallback, useEffect, useMemo } from "react";
23
+ import { isCreatedOrUpdated, validateForm, fetchData, API_METHODS, } from "@react-pakistan/util-functions";
24
+ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
25
+ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
26
+ import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
27
+ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
28
+ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
29
+ import { STUDENT_STATUS } from "@/type";
30
+ import { useTranslations } from "next-intl";
31
+ import { STUDENT_PROFILE_API_ROUTES, pageLimit } from "./constants";
32
+ import { studentProfileFormValidation } from "./validate";
33
+ import { getCachedStudentProfiles, invalidateStudentProfilesCache, } from "./cache";
34
+ import { getCachedWorkspaceSync } from "../workspace/cache";
35
+ import { invalidateFamiliesCache } from "../family/cache";
36
+ import { invalidateFamilyMembersCache } from "../family-member/cache";
37
+ import { invalidateAdmissionsCache } from "../admission/cache";
38
+ import { Eye, Filter, MoreHorizontal, Pencil, Plus } from "lucide-react";
39
+ // ============================================================================
40
+ // DRAWER TYPES
41
+ // ============================================================================
42
+ export const STUDENT_PROFILE_DRAWER = {
43
+ FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
44
+ FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
45
+ MORE_ACTIONS_DRAWER: "MORE_ACTIONS_DRAWER",
46
+ VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
47
+ };
48
+ // ============================================================================
49
+ // MODULE CONFIGURATION
50
+ // ============================================================================
51
+ const studentProfileConfig = {
52
+ name: "StudentProfile",
53
+ displayName: "Student Profile",
54
+ initialState: {
55
+ // List Data
56
+ items: [],
57
+ count: 0,
58
+ // Search & Pagination
59
+ currentPage: 1,
60
+ pageLimit,
61
+ searchQuery: "",
62
+ // Filters
63
+ filterStatus: undefined,
64
+ filterGender: undefined,
65
+ filterEnabled: undefined,
66
+ // UI State
67
+ disableSaveButton: false,
68
+ drawer: null,
69
+ // StudentProfile Form State
70
+ id: "",
71
+ familyMemberId: "",
72
+ studentCode: "",
73
+ status: STUDENT_STATUS.ACTIVE,
74
+ enabled: true,
75
+ remarks: "",
76
+ // FamilyMember fields (nested)
77
+ firstName: "",
78
+ lastName: "",
79
+ dateOfBirth: "",
80
+ gender: "",
81
+ bloodGroup: "",
82
+ phone: "",
83
+ email: "",
84
+ emergencyPhone: "",
85
+ avatar: "",
86
+ // Family fields (nested)
87
+ familyId: "",
88
+ address: "",
89
+ city: "",
90
+ state: "",
91
+ country: "",
92
+ postalCode: "",
93
+ errors: {},
94
+ },
95
+ drawerTypes: DRAWER_TYPES,
96
+ };
97
+ // ============================================================================
98
+ // CREATE STUDENT PROFILE MODULE
99
+ // ============================================================================
100
+ export const { actionTypes: STUDENT_PROFILE_ACTION_TYPES, config: studentProfileModuleConfig, initialState: initialStudentProfileState, Provider: StudentProfileProvider, reducer: studentProfileReducer, useContext: useStudentProfileContext, } = createGenericModule(studentProfileConfig);
101
+ // Backward-compatible aliases
102
+ export const StudentProfileStateContextProvider = StudentProfileProvider;
103
+ export const useStudentProfileStateContext = useStudentProfileContext;
104
+ // ============================================================================
105
+ // ENHANCED STUDENT PROFILE HOOK WITH API INTEGRATION
106
+ // ============================================================================
107
+ export const useStudentProfileModule = () => {
108
+ var _a;
109
+ const context = useStudentProfileContext();
110
+ const workspace = getCachedWorkspaceSync();
111
+ const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
112
+ const debouncedQuery = useDebounce(context.state.searchQuery, 800);
113
+ const t = useTranslations("studentProfile");
114
+ // ============================================================================
115
+ // API PARAMETERS
116
+ // ============================================================================
117
+ const listParams = useMemo(() => (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ currentPage: context.state.currentPage }, (context.state.filterEnabled !== undefined
118
+ ? { filterEnabled: String(context.state.filterEnabled) }
119
+ : {})), (context.state.filterGender
120
+ ? { filterGender: context.state.filterGender }
121
+ : {})), (context.state.filterStatus
122
+ ? { filterStatus: context.state.filterStatus }
123
+ : {})), { pageLimit: context.state.pageLimit, schoolId }), (debouncedQuery ? { searchQuery: debouncedQuery } : {}))), [
124
+ context.state.currentPage,
125
+ context.state.filterEnabled,
126
+ context.state.filterGender,
127
+ context.state.filterStatus,
128
+ context.state.pageLimit,
129
+ debouncedQuery,
130
+ schoolId,
131
+ ]);
132
+ const updateParams = useMemo(() => ({
133
+ avatar: context.state.avatar,
134
+ bloodGroup: context.state.bloodGroup,
135
+ dateOfBirth: context.state.dateOfBirth,
136
+ email: context.state.email,
137
+ emergencyPhone: context.state.emergencyPhone,
138
+ enabled: context.state.enabled,
139
+ familyMemberId: context.state.familyMemberId,
140
+ firstName: context.state.firstName,
141
+ gender: context.state.gender,
142
+ id: context.state.id,
143
+ lastName: context.state.lastName,
144
+ phone: context.state.phone,
145
+ status: context.state.status,
146
+ studentCode: context.state.studentCode,
147
+ }), [
148
+ context.state.avatar,
149
+ context.state.bloodGroup,
150
+ context.state.dateOfBirth,
151
+ context.state.email,
152
+ context.state.emergencyPhone,
153
+ context.state.enabled,
154
+ context.state.familyMemberId,
155
+ context.state.firstName,
156
+ context.state.gender,
157
+ context.state.id,
158
+ context.state.lastName,
159
+ context.state.phone,
160
+ context.state.status,
161
+ context.state.studentCode,
162
+ ]);
163
+ const byIdParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
164
+ const deleteParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
165
+ // ============================================================================
166
+ // UTILITIES
167
+ // ============================================================================
168
+ const showToast = useCallback((message, variant) => {
169
+ generateThemeToast({ description: message, variant });
170
+ }, []);
171
+ const resetFormAndCloseDrawer = useCallback(() => {
172
+ context.dispatch({
173
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_ERRORS,
174
+ payload: { errors: {} },
175
+ });
176
+ context.dispatch({
177
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
178
+ payload: { disabled: false },
179
+ });
180
+ context.dispatch({
181
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
182
+ payload: { drawer: null },
183
+ });
184
+ }, [context.dispatch]);
185
+ // ============================================================================
186
+ // API CALLBACKS
187
+ // ============================================================================
188
+ const listCallback = ({ data, error }) => {
189
+ var _a, _b;
190
+ if (error) {
191
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
192
+ return;
193
+ }
194
+ if (data) {
195
+ const response = data;
196
+ const items = (_b = (_a = response.items) !== null && _a !== void 0 ? _a : response.studentProfiles) !== null && _b !== void 0 ? _b : [];
197
+ const count = typeof response.count === "number" ? response.count : 0;
198
+ context.dispatch({
199
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_ITEMS,
200
+ payload: { items, count },
201
+ });
202
+ }
203
+ };
204
+ const updateCallback = ({ data, error }) => {
205
+ const isCreated = isCreatedOrUpdated(data);
206
+ if (error) {
207
+ showToast(isCreated ? t("messagesCreateFailed") : t("messagesUpdateFailed"), TOAST_VARIANT.ERROR);
208
+ return;
209
+ }
210
+ if (data) {
211
+ showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
212
+ invalidateStudentProfilesCache();
213
+ invalidateFamiliesCache();
214
+ invalidateFamilyMembersCache();
215
+ listFetchNow();
216
+ resetFormAndCloseDrawer();
217
+ }
218
+ };
219
+ const byIdCallback = ({ data, error }) => {
220
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
221
+ if (error) {
222
+ showToast(t("messagesDetailsFetchFailed"), TOAST_VARIANT.ERROR);
223
+ return;
224
+ }
225
+ if (data && typeof data === "object" && "id" in data) {
226
+ const sp = data;
227
+ const fm = sp.familyMember;
228
+ const family = fm === null || fm === void 0 ? void 0 : fm.family;
229
+ context.dispatch({
230
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_FORM_DATA,
231
+ payload: {
232
+ form: {
233
+ id: sp.id,
234
+ familyMemberId: sp.familyMemberId,
235
+ studentCode: sp.studentCode,
236
+ status: sp.status,
237
+ enabled: sp.enabled,
238
+ remarks: (_a = sp.remarks) !== null && _a !== void 0 ? _a : null,
239
+ firstName: (_b = fm === null || fm === void 0 ? void 0 : fm.firstName) !== null && _b !== void 0 ? _b : "",
240
+ lastName: (_c = fm === null || fm === void 0 ? void 0 : fm.lastName) !== null && _c !== void 0 ? _c : "",
241
+ dateOfBirth: (_d = fm === null || fm === void 0 ? void 0 : fm.dateOfBirth) !== null && _d !== void 0 ? _d : null,
242
+ gender: (_e = fm === null || fm === void 0 ? void 0 : fm.gender) !== null && _e !== void 0 ? _e : null,
243
+ bloodGroup: (_f = fm === null || fm === void 0 ? void 0 : fm.bloodGroup) !== null && _f !== void 0 ? _f : null,
244
+ phone: (_g = fm === null || fm === void 0 ? void 0 : fm.phone) !== null && _g !== void 0 ? _g : null,
245
+ email: (_h = fm === null || fm === void 0 ? void 0 : fm.email) !== null && _h !== void 0 ? _h : null,
246
+ emergencyPhone: (_j = fm === null || fm === void 0 ? void 0 : fm.emergencyPhone) !== null && _j !== void 0 ? _j : null,
247
+ avatar: (_k = fm === null || fm === void 0 ? void 0 : fm.avatar) !== null && _k !== void 0 ? _k : null,
248
+ familyId: (_l = fm === null || fm === void 0 ? void 0 : fm.familyId) !== null && _l !== void 0 ? _l : "",
249
+ address: (_m = family === null || family === void 0 ? void 0 : family.address) !== null && _m !== void 0 ? _m : null,
250
+ city: (_o = family === null || family === void 0 ? void 0 : family.city) !== null && _o !== void 0 ? _o : null,
251
+ state: (_p = family === null || family === void 0 ? void 0 : family.state) !== null && _p !== void 0 ? _p : null,
252
+ country: (_q = family === null || family === void 0 ? void 0 : family.country) !== null && _q !== void 0 ? _q : null,
253
+ postalCode: (_r = family === null || family === void 0 ? void 0 : family.postalCode) !== null && _r !== void 0 ? _r : null,
254
+ },
255
+ },
256
+ });
257
+ }
258
+ };
259
+ const deleteCallback = ({ data, error }) => {
260
+ if (error) {
261
+ showToast(t("messagesDeleteFailed"), TOAST_VARIANT.ERROR);
262
+ return;
263
+ }
264
+ if (data) {
265
+ showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
266
+ invalidateStudentProfilesCache();
267
+ listFetchNow();
268
+ }
269
+ };
270
+ // ============================================================================
271
+ // API HOOKS
272
+ // ============================================================================
273
+ const { byIdFetchNow, byIdLoading, deleteFetchNow, deleteLoading, listFetchNow, listLoading, updateFetchNow, updateLoading, } = useModuleEntityV2({
274
+ byIdCallback,
275
+ byIdParams,
276
+ deleteCallback,
277
+ deleteParams,
278
+ headers: {
279
+ "Content-Type": "application/json",
280
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
281
+ },
282
+ listCallback,
283
+ listParams,
284
+ listUrl: STUDENT_PROFILE_API_ROUTES.UNIT,
285
+ searchQuery: debouncedQuery,
286
+ unitByIdUrl: STUDENT_PROFILE_API_ROUTES.UNIT,
287
+ unitUrl: STUDENT_PROFILE_API_ROUTES.UNIT,
288
+ updateCallback,
289
+ updateParams,
290
+ });
291
+ // ============================================================================
292
+ // EFFECTS
293
+ // ============================================================================
294
+ useEffect(() => {
295
+ (async () => {
296
+ try {
297
+ const { count, items } = await getCachedStudentProfiles({
298
+ params: listParams,
299
+ });
300
+ context.dispatch({
301
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_ITEMS,
302
+ payload: { items: items || [], count: count || 0 },
303
+ });
304
+ }
305
+ catch (_a) {
306
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
307
+ }
308
+ })();
309
+ }, [listParams]);
310
+ // ============================================================================
311
+ // HANDLERS
312
+ // ============================================================================
313
+ const handleChange = useCallback((field, value) => {
314
+ context.dispatch({
315
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_ERRORS,
316
+ payload: { errors: {} },
317
+ });
318
+ context.dispatch({
319
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
320
+ payload: { disabled: false },
321
+ });
322
+ context.dispatch({
323
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_INPUT_FIELD,
324
+ payload: { key: field, value },
325
+ });
326
+ }, [context.dispatch]);
327
+ const handlePageChange = useCallback((page) => {
328
+ context.dispatch({
329
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_CURRENT_PAGE,
330
+ payload: { currentPage: page },
331
+ });
332
+ }, [context.dispatch]);
333
+ const handlePageLimitChange = useCallback((limit) => {
334
+ context.dispatch({
335
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_PAGE_LIMIT,
336
+ payload: { pageLimit: limit },
337
+ });
338
+ }, [context.dispatch]);
339
+ const handleCloseDrawer = useCallback(() => {
340
+ resetFormAndCloseDrawer();
341
+ }, [resetFormAndCloseDrawer]);
342
+ const handleCreate = useCallback(() => {
343
+ context.dispatch({
344
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
345
+ payload: { drawer: STUDENT_PROFILE_DRAWER.FORM_DRAWER },
346
+ });
347
+ }, [context.dispatch]);
348
+ const handleView = useCallback((row) => {
349
+ const record = row;
350
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: record.id } });
351
+ context.dispatch({
352
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
353
+ payload: { drawer: STUDENT_PROFILE_DRAWER.VIEW_DRAWER },
354
+ });
355
+ }, [context.dispatch, byIdFetchNow]);
356
+ const handleEdit = useCallback((row) => {
357
+ const record = row;
358
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: record.id } });
359
+ context.dispatch({
360
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
361
+ payload: { drawer: STUDENT_PROFILE_DRAWER.FORM_DRAWER },
362
+ });
363
+ }, [context.dispatch, byIdFetchNow]);
364
+ const handleDelete = useCallback((row) => {
365
+ const record = row;
366
+ if (!confirm(t("areYouSureYouWantToDeleteThisStudentProfile")))
367
+ return;
368
+ deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
369
+ body: JSON.stringify({ id: record.id }),
370
+ });
371
+ }, [t]);
372
+ const handleFilters = useCallback(() => {
373
+ context.dispatch({
374
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
375
+ payload: { drawer: STUDENT_PROFILE_DRAWER.FILTER_DRAWER },
376
+ });
377
+ }, [context.dispatch]);
378
+ const handleMoreActions = useCallback(() => {
379
+ context.dispatch({
380
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
381
+ payload: { drawer: STUDENT_PROFILE_DRAWER.MORE_ACTIONS_DRAWER },
382
+ });
383
+ }, [context.dispatch]);
384
+ const applyFilters = useCallback(() => {
385
+ context.dispatch({
386
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_CURRENT_PAGE,
387
+ payload: { currentPage: 1 },
388
+ });
389
+ listFetchNow();
390
+ handleCloseDrawer();
391
+ }, [context.dispatch, listFetchNow, handleCloseDrawer]);
392
+ const clearFilters = useCallback(() => {
393
+ context.dispatch({
394
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_FILTERS,
395
+ payload: {
396
+ filters: {
397
+ filterEnabled: undefined,
398
+ filterStatus: undefined,
399
+ filterGender: undefined,
400
+ },
401
+ },
402
+ });
403
+ context.dispatch({
404
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_CURRENT_PAGE,
405
+ payload: { currentPage: 1 },
406
+ });
407
+ }, [context.dispatch]);
408
+ const handleSearch = useCallback((query) => {
409
+ context.dispatch({
410
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_SEARCH_QUERY,
411
+ payload: { searchQuery: query },
412
+ });
413
+ }, [context.dispatch]);
414
+ const handleWithdrawStudent = useCallback(async (row) => {
415
+ const record = row;
416
+ if (confirm(t("areYouSureYouWantToWithdrawThisStudent"))) {
417
+ byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: record.id } });
418
+ context.dispatch({
419
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DRAWER,
420
+ payload: { drawer: STUDENT_PROFILE_DRAWER.FORM_DRAWER },
421
+ });
422
+ context.dispatch({
423
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_INPUT_FIELD,
424
+ payload: { key: "status", value: STUDENT_STATUS.INACTIVE },
425
+ });
426
+ context.dispatch({
427
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_INPUT_FIELD,
428
+ payload: { key: "enabled", value: false },
429
+ });
430
+ invalidateStudentProfilesCache();
431
+ invalidateFamiliesCache();
432
+ invalidateFamilyMembersCache();
433
+ invalidateAdmissionsCache();
434
+ }
435
+ }, [context.dispatch, t]);
436
+ const handleReAdmitStudent = useCallback(async (row) => {
437
+ const record = row;
438
+ if (confirm(t("areYouSureYouWantToReAdmitThisStudent"))) {
439
+ const { data, error } = await fetchData({
440
+ url: "/api/v1/readmit-student",
441
+ method: API_METHODS.POST,
442
+ headers: {
443
+ "Content-Type": "application/json",
444
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
445
+ },
446
+ body: JSON.stringify({ studentProfileId: record.id }),
447
+ });
448
+ listFetchNow();
449
+ invalidateStudentProfilesCache();
450
+ invalidateFamiliesCache();
451
+ invalidateFamilyMembersCache();
452
+ invalidateAdmissionsCache();
453
+ if (data) {
454
+ showToast(t("messagesReAdmitSuccess"), TOAST_VARIANT.SUCCESS);
455
+ }
456
+ if (error) {
457
+ showToast(t("messagesReAdmitFailed"), TOAST_VARIANT.ERROR);
458
+ }
459
+ }
460
+ }, [context.dispatch, showToast, t]);
461
+ // ============================================================================
462
+ // SUBMIT
463
+ // ============================================================================
464
+ const handleSubmit = useCallback(() => {
465
+ context.dispatch({
466
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
467
+ payload: { disabled: true },
468
+ });
469
+ validateForm({
470
+ params: updateParams,
471
+ schema: studentProfileFormValidation,
472
+ successCallback: () => {
473
+ updateFetchNow(undefined, {
474
+ body: JSON.stringify(updateParams),
475
+ });
476
+ },
477
+ errorCallback: (errors) => {
478
+ context.dispatch({
479
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_ERRORS,
480
+ payload: { errors },
481
+ });
482
+ context.dispatch({
483
+ type: STUDENT_PROFILE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
484
+ payload: { disabled: false },
485
+ });
486
+ showToast(t("messagesFormErrors"), TOAST_VARIANT.ERROR);
487
+ },
488
+ });
489
+ }, [context.dispatch, updateParams, t, showToast]);
490
+ // ============================================================================
491
+ // HEADER & ROW ACTIONS
492
+ // ============================================================================
493
+ const headerActions = useMemo(() => [
494
+ {
495
+ enabled: false,
496
+ handleOnClick: handleMoreActions,
497
+ label: t("headerActionsMoreActions"),
498
+ order: 1,
499
+ icon: MoreHorizontal,
500
+ },
501
+ {
502
+ enabled: true,
503
+ handleOnClick: handleFilters,
504
+ label: t("headerActionsFilters"),
505
+ order: 2,
506
+ icon: Filter,
507
+ },
508
+ {
509
+ enabled: false,
510
+ handleOnClick: handleCreate,
511
+ label: t("headerActionsAddStudentProfile"),
512
+ order: 3,
513
+ icon: Plus,
514
+ },
515
+ ], [handleFilters, handleMoreActions, handleCreate, t]);
516
+ const rowActions = useMemo(() => [
517
+ {
518
+ enabled: true,
519
+ handleOnClick: (row) => handleView(row),
520
+ label: t("rowActionsView"),
521
+ order: 1,
522
+ icon: Eye,
523
+ },
524
+ {
525
+ enabled: true,
526
+ handleOnClick: (row) => handleEdit(row),
527
+ label: t("rowActionsEdit"),
528
+ order: 2,
529
+ icon: Pencil,
530
+ },
531
+ {
532
+ enabled: false,
533
+ handleOnClick: (row) => handleDelete(row),
534
+ label: t("rowActionsDelete"),
535
+ order: 3,
536
+ },
537
+ {
538
+ enabled: (row) => (row === null || row === void 0 ? void 0 : row.status) === STUDENT_STATUS.ACTIVE,
539
+ handleOnClick: (row) => handleWithdrawStudent(row),
540
+ label: t("rowActionsWithdrawStudent"),
541
+ order: 4,
542
+ variant: "destructive",
543
+ },
544
+ {
545
+ enabled: (row) => (row === null || row === void 0 ? void 0 : row.status) === STUDENT_STATUS.INACTIVE,
546
+ handleOnClick: (row) => handleReAdmitStudent(row),
547
+ label: t("rowActionsReAdmitStudent"),
548
+ order: 5,
549
+ },
550
+ ], [
551
+ handleView,
552
+ handleEdit,
553
+ handleDelete,
554
+ handleWithdrawStudent,
555
+ handleReAdmitStudent,
556
+ t,
557
+ ]);
558
+ // ============================================================================
559
+ // RETURN
560
+ // ============================================================================
561
+ return Object.assign(Object.assign({}, context), { applyFilters,
562
+ byIdLoading,
563
+ clearFilters,
564
+ deleteLoading,
565
+ handleChange,
566
+ handleCloseDrawer,
567
+ handleCreate,
568
+ handleDelete,
569
+ handleEdit,
570
+ handleFilters,
571
+ handleMoreActions,
572
+ handlePageChange,
573
+ handlePageLimitChange,
574
+ handleReAdmitStudent,
575
+ handleSearch,
576
+ handleSubmit,
577
+ handleView,
578
+ handleWithdrawStudent,
579
+ headerActions,
580
+ listLoading,
581
+ rowActions,
582
+ updateLoading });
583
+ };
@@ -0,0 +1 @@
1
+ export declare const StudentProfileFilter: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
4
+ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
5
+ import { useTranslations } from "next-intl";
6
+ import { useStudentProfileModule } from "./context";
7
+ import { STATUS_OPTIONS } from "./constants";
8
+ export const StudentProfileFilter = () => {
9
+ const t = useTranslations("studentProfile");
10
+ const context = useStudentProfileModule();
11
+ const { filterStatus, filterEnabled } = context.state;
12
+ const { handleChange } = context;
13
+ const filterEnabledValue = filterEnabled === undefined
14
+ ? "undefined"
15
+ : filterEnabled
16
+ ? "true"
17
+ : "false";
18
+ return (_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCombobox, { id: "filterStatus", label: t("status"), info: t("filterByStudentStatus"), options: [...STATUS_OPTIONS], value: filterStatus, onValueChange: (value) => handleChange("filterStatus", value) }), _jsx(EnhancedRadio, { label: t("enabled"), name: "filterEnabled", value: filterEnabledValue, options: [
19
+ { label: t("all"), value: "undefined" },
20
+ { label: t("enabled"), value: "true" },
21
+ { label: t("disabled"), value: "false" },
22
+ ], onValueChange: (next) => {
23
+ const parsed = next === "true" ? true : next === "false" ? false : undefined;
24
+ handleChange("filterEnabled", parsed);
25
+ } })] }) }));
26
+ };
@@ -0,0 +1 @@
1
+ export declare const StudentProfileForm: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
4
+ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
5
+ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
6
+ import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
7
+ import { useTranslations } from "next-intl";
8
+ import { useStudentProfileModule } from "./context";
9
+ import { STATUS_OPTIONS, GENDER_OPTIONS } from "./constants";
10
+ export const StudentProfileForm = () => {
11
+ const t = useTranslations("studentProfile");
12
+ const context = useStudentProfileModule();
13
+ const { address, city, country, dateOfBirth, emergencyPhone, enabled, errors, firstName, gender, lastName, phone, postalCode, remarks, status, } = context.state;
14
+ const stateValue = context.state.state;
15
+ const { handleChange } = context;
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 gap-4", children: [_jsx(EnhancedInput, { error: errors.firstName, id: "firstName", info: t("enterStudentsFirstName"), 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("enterStudentsLastName"), label: t("lastName"), onChange: (e) => handleChange("lastName", e.target.value), placeholder: t("lastNamePlaceholder"), required: true, value: lastName }), _jsx(EnhancedInput, { error: errors.dateOfBirth, id: "dateOfBirth", info: t("selectDateOfBirth"), label: t("dateOfBirth"), onChange: (e) => handleChange("dateOfBirth", e.target.value), onClick: (e) => { var _a, _b; return (_b = (_a = e.currentTarget).showPicker) === null || _b === void 0 ? void 0 : _b.call(_a); }, type: "date", value: dateOfBirth
17
+ ? new Date(dateOfBirth).toISOString().split("T")[0]
18
+ : "" }), _jsx(EnhancedCombobox, { error: errors.gender, id: "gender", info: t("selectGender"), label: t("gender"), onValueChange: (value) => handleChange("gender", value), options: GENDER_OPTIONS.slice(1), value: gender })] })] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("contactInformation") }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.phone, id: "phone", info: t("enterStudentsPhoneNumber"), label: t("phone"), onChange: (e) => handleChange("phone", e.target.value), placeholder: t("phonePlaceholder"), type: "tel", value: phone || "" }), _jsx(EnhancedInput, { error: errors.emergencyPhone, id: "emergencyPhone", info: t("enterStudentsEmergencyPhoneNumber"), label: t("emergencyPhone"), onChange: (e) => handleChange("emergencyPhone", e.target.value), placeholder: t("emergencyPhonePlaceholder"), type: "tel", value: emergencyPhone || "" })] }), _jsx(EnhancedInput, { error: errors.address, id: "address", info: t("enterStudentsAddress"), label: t("address"), onChange: (e) => handleChange("address", e.target.value), placeholder: t("addressPlaceholder"), value: address || "" }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.city, id: "city", info: t("enterStudentsCity"), label: t("city"), onChange: (e) => handleChange("city", e.target.value), placeholder: t("cityPlaceholder"), value: city || "" }), _jsx(EnhancedInput, { error: errors.state, id: "state", info: t("enterStudentsStateOrProvince"), label: t("stateProvince"), onChange: (e) => handleChange("state", e.target.value), placeholder: t("statePlaceholder"), value: stateValue || "" }), _jsx(EnhancedInput, { error: errors.country, id: "country", info: t("enterStudentsCountry"), label: t("country"), onChange: (e) => handleChange("country", e.target.value), placeholder: t("countryPlaceholder"), value: country || "" }), _jsx(EnhancedInput, { error: errors.postalCode, id: "postalCode", info: t("enterStudentsPostalCode"), label: t("postalCode"), onChange: (e) => handleChange("postalCode", e.target.value), placeholder: t("postalCodePlaceholder"), value: postalCode || "" })] })] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("academicInformation") }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCombobox, { error: errors.status, id: "status", info: t("selectStudentStatus"), label: t("status"), onValueChange: (value) => handleChange("status", value), options: STATUS_OPTIONS.slice(1), required: true, value: status }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", info: t("enterAnyAdditionalRemarks"), label: t("remarks"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("remarksPlaceholder"), rows: 4, value: remarks || "" })] }), _jsx(EnhancedCheckbox, { checked: enabled, info: t("toggleToActivateOrDeactivate"), label: t("activeStudent"), onCheckedChange: (checked) => handleChange("enabled", checked) })] })] }));
19
+ };
@@ -0,0 +1 @@
1
+ export declare const StudentProfileMoreActions: () => 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 StudentProfileMoreActions = () => {
4
+ return _jsx("div", { className: "space-y-4", children: "More actions for student profiles" });
5
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * StudentProfile Page — generic page wrapper
3
+ *
4
+ * Uses `createGenericModulePage` to provide the table + drawer chrome while
5
+ * wiring the module-specific content (filter, form, view, more-actions)
6
+ * via the `ComponentConfig`.
7
+ */
8
+ import { FC } from "react";
9
+ export declare const StudentProfilePage: FC;