@appcorp/fusion-storybook 0.2.39 → 0.2.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base-modules/admission/constants.d.ts +5 -17
- package/base-modules/admission/constants.js +12 -7
- package/base-modules/admission/context/use-admission-module.js +11 -48
- package/base-modules/admission/filter.js +23 -3
- package/base-modules/admission/form.js +49 -19
- package/base-modules/attendance/context.js +3 -37
- package/base-modules/attendance/form.js +26 -10
- package/base-modules/attendance/more-actions.js +34 -25
- package/base-modules/campus/context.js +13 -44
- package/base-modules/class/cache.js +0 -1
- package/base-modules/class/context.js +10 -48
- package/base-modules/class/more-actions.js +0 -2
- package/base-modules/course/context.js +3 -37
- package/base-modules/course/form.js +91 -292
- package/base-modules/discount-code/constants.d.ts +5 -0
- package/base-modules/discount-code/constants.js +5 -0
- package/base-modules/discount-code/context.d.ts +1 -0
- package/base-modules/discount-code/context.js +40 -39
- package/base-modules/discount-code/form.js +21 -15
- package/base-modules/discount-code/more-actions.js +1 -1
- package/base-modules/enrollment/context.js +3 -37
- package/base-modules/enrollment/form.js +38 -11
- package/base-modules/enrollment/more-actions.js +0 -2
- package/base-modules/expense/constants.js +1 -1
- package/base-modules/expense/context.js +5 -32
- package/base-modules/expense/filter.js +50 -3
- package/base-modules/expense/form.js +82 -6
- package/base-modules/family/context.js +7 -38
- package/base-modules/family-member/context.js +7 -39
- package/base-modules/fee-structure/context.js +1 -25
- package/base-modules/fee-structure/form.js +77 -89
- package/base-modules/fee-structure/more-actions.js +0 -2
- package/base-modules/rbac/context.d.ts +1 -0
- package/base-modules/rbac/context.js +23 -32
- package/base-modules/school/context.js +1 -1
- package/base-modules/school/form.js +34 -14
- package/base-modules/section/context.d.ts +1 -0
- package/base-modules/section/context.js +40 -47
- package/base-modules/section/form.js +25 -80
- package/base-modules/section/more-actions.js +0 -2
- package/base-modules/section/view.js +9 -7
- package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
- package/base-modules/student-fee/context/use-student-fee-module.js +48 -32
- package/base-modules/student-fee/context.d.ts +1 -1
- package/base-modules/student-fee/context.js +1 -1
- package/base-modules/student-fee/filter.js +23 -3
- package/base-modules/student-fee/form.js +93 -174
- package/base-modules/student-fee/view.d.ts +7 -1
- package/base-modules/student-fee/view.js +17 -20
- package/base-modules/student-profile/constants.d.ts +0 -6
- package/base-modules/student-profile/constants.js +1 -3
- package/base-modules/student-profile/context/use-student-profile-module.d.ts +1 -0
- package/base-modules/student-profile/context/use-student-profile-module.js +62 -55
- package/base-modules/student-profile/context.d.ts +1 -1
- package/base-modules/student-profile/context.js +1 -1
- package/base-modules/student-profile/filter.js +23 -3
- package/base-modules/student-profile/form.js +35 -3
- package/base-modules/subject/context.d.ts +1 -0
- package/base-modules/subject/context.js +38 -47
- package/base-modules/subject/more-actions.js +0 -2
- package/base-modules/teacher/constants.d.ts +0 -6
- package/base-modules/teacher/constants.js +0 -2
- package/base-modules/teacher/context.d.ts +1 -0
- package/base-modules/teacher/context.js +58 -39
- package/base-modules/teacher/form.js +46 -11
- package/base-modules/teacher/more-actions.js +0 -2
- package/base-modules/user/context/use-user-module.d.ts +1 -0
- package/base-modules/user/context/use-user-module.js +36 -32
- package/base-modules/user/context.js +1 -1
- package/base-modules/user/filter.js +6 -4
- package/base-modules/user/form.js +29 -5
- package/base-modules/user/more-actions.js +9 -7
- package/base-modules/user/view.js +3 -1
- package/base-modules/workspace/form.js +18 -8
- package/base-modules/workspace-user/context.d.ts +2 -1
- package/base-modules/workspace-user/context.js +31 -29
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/base-modules/admission/cache.d.ts +0 -14
- package/base-modules/admission/cache.js +0 -31
- package/base-modules/attendance/cache.d.ts +0 -14
- package/base-modules/attendance/cache.js +0 -31
- package/base-modules/campus/cache.d.ts +0 -14
- package/base-modules/campus/cache.js +0 -31
- package/base-modules/course/cache.d.ts +0 -14
- package/base-modules/course/cache.js +0 -31
- package/base-modules/enrollment/cache.d.ts +0 -14
- package/base-modules/enrollment/cache.js +0 -31
- package/base-modules/expense/cache.d.ts +0 -14
- package/base-modules/expense/cache.js +0 -31
- package/base-modules/family/cache.d.ts +0 -14
- package/base-modules/family/cache.js +0 -31
- package/base-modules/family-member/cache.d.ts +0 -14
- package/base-modules/family-member/cache.js +0 -31
- package/base-modules/rbac/cache.d.ts +0 -27
- package/base-modules/rbac/cache.js +0 -46
- package/base-modules/student-fee/cache.d.ts +0 -15
- package/base-modules/student-fee/cache.js +0 -21
- package/base-modules/workspace-user/cache.d.ts +0 -14
- package/base-modules/workspace-user/cache.js +0 -31
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Key responsibilities:
|
|
10
10
|
* - Expose `useSectionModule()` which UI components call for actions
|
|
11
11
|
* - Keep module-specific `apiParams` and callbacks in one place
|
|
12
|
-
* - Ensure
|
|
12
|
+
* - Ensure toast notifications on mutation
|
|
13
13
|
*
|
|
14
14
|
* Exported utilities:
|
|
15
15
|
* - `SectionProvider` — provider component used by the page
|
|
@@ -28,7 +28,6 @@ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-com
|
|
|
28
28
|
import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
|
|
29
29
|
import { SECTION_API_ROUTES, pageLimit } from "./constants";
|
|
30
30
|
import { sectionFormValidation } from "./validate";
|
|
31
|
-
import { getCachedSections, invalidateSectionsCache } from "./cache";
|
|
32
31
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
33
32
|
// ============================================================================
|
|
34
33
|
// 1.1 DRAWER TYPES
|
|
@@ -136,6 +135,30 @@ export const useSectionModule = () => {
|
|
|
136
135
|
payload: { drawer: null },
|
|
137
136
|
});
|
|
138
137
|
}, [dispatch]);
|
|
138
|
+
const resetRecordFormState = useCallback(() => {
|
|
139
|
+
dispatch({
|
|
140
|
+
type: SECTION_ACTION_TYPES.SET_ERRORS,
|
|
141
|
+
payload: { errors: {} },
|
|
142
|
+
});
|
|
143
|
+
dispatch({
|
|
144
|
+
type: SECTION_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
145
|
+
payload: { disabled: false },
|
|
146
|
+
});
|
|
147
|
+
dispatch({
|
|
148
|
+
type: SECTION_ACTION_TYPES.SET_FORM_DATA,
|
|
149
|
+
payload: {
|
|
150
|
+
form: {
|
|
151
|
+
capacity: null,
|
|
152
|
+
classId: "",
|
|
153
|
+
enabled: true,
|
|
154
|
+
filterEnabled: undefined,
|
|
155
|
+
id: "",
|
|
156
|
+
name: "",
|
|
157
|
+
schoolId,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}, [dispatch, schoolId]);
|
|
139
162
|
// ============================================================================
|
|
140
163
|
// 1.4.4 API CALLBACKS
|
|
141
164
|
// ============================================================================
|
|
@@ -161,7 +184,6 @@ export const useSectionModule = () => {
|
|
|
161
184
|
}
|
|
162
185
|
if (data) {
|
|
163
186
|
const isCreated = isCreatedOrUpdated(data);
|
|
164
|
-
invalidateSectionsCache();
|
|
165
187
|
showToast(isCreated ? t("messagesCreateSuccess") : t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
|
|
166
188
|
resetFormAndCloseDrawer();
|
|
167
189
|
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
@@ -186,7 +208,6 @@ export const useSectionModule = () => {
|
|
|
186
208
|
return;
|
|
187
209
|
}
|
|
188
210
|
if (data) {
|
|
189
|
-
invalidateSectionsCache();
|
|
190
211
|
showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
|
|
191
212
|
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
192
213
|
}
|
|
@@ -239,19 +260,21 @@ export const useSectionModule = () => {
|
|
|
239
260
|
});
|
|
240
261
|
}, [dispatch]);
|
|
241
262
|
const handleView = useCallback((row) => {
|
|
263
|
+
resetRecordFormState();
|
|
242
264
|
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
243
265
|
dispatch({
|
|
244
266
|
type: SECTION_ACTION_TYPES.SET_DRAWER,
|
|
245
267
|
payload: { drawer: SECTION_DRAWER.VIEW_DRAWER },
|
|
246
268
|
});
|
|
247
|
-
}, [byIdFetchNow, dispatch]);
|
|
269
|
+
}, [byIdFetchNow, dispatch, resetRecordFormState]);
|
|
248
270
|
const handleEdit = useCallback((row) => {
|
|
271
|
+
resetRecordFormState();
|
|
249
272
|
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
250
273
|
dispatch({
|
|
251
274
|
type: SECTION_ACTION_TYPES.SET_DRAWER,
|
|
252
275
|
payload: { drawer: SECTION_DRAWER.FORM_DRAWER },
|
|
253
276
|
});
|
|
254
|
-
}, [byIdFetchNow, dispatch]);
|
|
277
|
+
}, [byIdFetchNow, dispatch, resetRecordFormState]);
|
|
255
278
|
const handleDelete = useCallback((row) => {
|
|
256
279
|
if (!confirm(t("messagesDeleteConfirmation")))
|
|
257
280
|
return;
|
|
@@ -372,19 +395,19 @@ export const useSectionModule = () => {
|
|
|
372
395
|
{
|
|
373
396
|
enabled: true,
|
|
374
397
|
handleOnClick: handleMoreActions,
|
|
375
|
-
label: t("
|
|
398
|
+
label: t("actionsButtonMoreActions"),
|
|
376
399
|
order: 0,
|
|
377
400
|
},
|
|
378
401
|
{
|
|
379
402
|
enabled: true,
|
|
380
403
|
handleOnClick: handleFilters,
|
|
381
|
-
label: t("
|
|
404
|
+
label: t("actionsButtonFilters"),
|
|
382
405
|
order: 1,
|
|
383
406
|
},
|
|
384
407
|
{
|
|
385
408
|
enabled: true,
|
|
386
409
|
handleOnClick: handleCreate,
|
|
387
|
-
label: t("
|
|
410
|
+
label: t("actionsButtonAdd"),
|
|
388
411
|
order: 2,
|
|
389
412
|
},
|
|
390
413
|
], [handleCreate, handleFilters, handleMoreActions, t]);
|
|
@@ -392,70 +415,39 @@ export const useSectionModule = () => {
|
|
|
392
415
|
{
|
|
393
416
|
enabled: true,
|
|
394
417
|
handleOnClick: handleView,
|
|
395
|
-
label: t("
|
|
418
|
+
label: t("actionsButtonView"),
|
|
396
419
|
order: 1,
|
|
397
420
|
},
|
|
398
421
|
{
|
|
399
422
|
enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === true,
|
|
400
423
|
handleOnClick: handleEdit,
|
|
401
|
-
label: t("
|
|
424
|
+
label: t("actionsButtonEdit"),
|
|
402
425
|
order: 2,
|
|
403
426
|
},
|
|
404
427
|
{
|
|
405
428
|
enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === false,
|
|
406
429
|
handleOnClick: handleDelete,
|
|
407
|
-
label: t("
|
|
430
|
+
label: t("actionsButtonDelete"),
|
|
408
431
|
order: 3,
|
|
409
432
|
variant: "destructive",
|
|
410
433
|
},
|
|
411
434
|
{
|
|
412
435
|
enabled: false,
|
|
413
436
|
handleOnClick: toggleStatus,
|
|
414
|
-
label: t("
|
|
437
|
+
label: t("actionsButtonToggleStatus"),
|
|
415
438
|
order: 4,
|
|
416
439
|
},
|
|
417
440
|
], [handleDelete, handleEdit, handleView, t, toggleStatus]);
|
|
418
441
|
// ============================================================================
|
|
419
442
|
// 1.4.9 EFFECTS
|
|
420
443
|
// ============================================================================
|
|
421
|
-
// Keep the latest fetch function in a ref so the fetch effect can call it
|
|
422
|
-
// without depending on listFetchNow's unstable identity.
|
|
423
444
|
useEffect(() => {
|
|
424
445
|
listFetchNowRef.current = listFetchNow;
|
|
425
|
-
});
|
|
426
|
-
// Initial load via cache; re-fetch directly from API on page/pageLimit/filter/search changes.
|
|
446
|
+
}, [listFetchNow]);
|
|
427
447
|
useEffect(() => {
|
|
428
448
|
var _a;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const currentPage = Number(listParams.currentPage) || 1;
|
|
432
|
-
const currentPageLimit = Number(listParams.pageLimit) || pageLimit;
|
|
433
|
-
const isDefaultLoad = currentPage === 1 &&
|
|
434
|
-
currentPageLimit === pageLimit &&
|
|
435
|
-
!listParams.searchQuery &&
|
|
436
|
-
listParams.filterEnabled === undefined;
|
|
437
|
-
if (isDefaultLoad) {
|
|
438
|
-
(async () => {
|
|
439
|
-
try {
|
|
440
|
-
const { count, items } = await getCachedSections({
|
|
441
|
-
params: listParams,
|
|
442
|
-
});
|
|
443
|
-
dispatch({
|
|
444
|
-
type: SECTION_ACTION_TYPES.SET_ITEMS,
|
|
445
|
-
payload: { items: items || [], count: count || 0 },
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
catch (_a) {
|
|
449
|
-
showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
|
|
450
|
-
}
|
|
451
|
-
})();
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
// Bypass cache for pagination, pageLimit, filter and search changes.
|
|
455
|
-
// Use ref to avoid the infinite-loop caused by listFetchNow's unstable identity.
|
|
456
|
-
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
457
|
-
}
|
|
458
|
-
}, [dispatch, listParams, schoolId, showToast, t]);
|
|
449
|
+
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
450
|
+
}, [listParams]);
|
|
459
451
|
// ============================================================================
|
|
460
452
|
// 1.4.10 RETURN
|
|
461
453
|
// ============================================================================
|
|
@@ -480,6 +472,7 @@ export const useSectionModule = () => {
|
|
|
480
472
|
headerActions,
|
|
481
473
|
listFetchNow,
|
|
482
474
|
listLoading,
|
|
475
|
+
resetRecordFormState,
|
|
483
476
|
rowActions,
|
|
484
477
|
toggleStatus,
|
|
485
478
|
updateLoading });
|
|
@@ -7,95 +7,40 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
* Uses a class combobox populated from cached class data.
|
|
8
8
|
*/
|
|
9
9
|
import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
10
|
-
import {
|
|
10
|
+
import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
|
|
11
11
|
import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
|
|
12
|
-
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
13
|
-
import { useEffect, useMemo, useState } from "react";
|
|
14
12
|
import { useSectionModule } from "./context";
|
|
15
13
|
import { useTranslations } from "next-intl";
|
|
16
|
-
import { getCachedClasses, getCachedClassesSync } from "../class/cache";
|
|
17
14
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
15
|
+
import { SECTION_API_ROUTES } from "./constants";
|
|
16
|
+
import { useFetch } from "@react-pakistan/util-functions/hooks/use-fetch";
|
|
17
|
+
import { API_METHODS } from "@react-pakistan/util-functions";
|
|
18
18
|
export const SectionForm = () => {
|
|
19
|
-
var _a;
|
|
20
19
|
const { state, handleChange } = useSectionModule();
|
|
21
20
|
const { capacity, classId, enabled, errors, name } = state;
|
|
22
21
|
const t = useTranslations("section");
|
|
23
22
|
const workspace = getCachedWorkspaceSync();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
: [];
|
|
48
|
-
}, [cachedClasses.items, classId]);
|
|
49
|
-
const displayedClassOptions = useMemo(() => {
|
|
50
|
-
const sourceOptions = trimmedClassSearchQuery
|
|
51
|
-
? remoteClassOptions
|
|
52
|
-
: cachedClassOptions;
|
|
53
|
-
const mergedOptions = [...selectedClassOption, ...sourceOptions];
|
|
54
|
-
const uniqueOptions = new Map(mergedOptions.map((option) => [option.value, option]));
|
|
55
|
-
return [...uniqueOptions.values()];
|
|
56
|
-
}, [
|
|
57
|
-
cachedClassOptions,
|
|
58
|
-
remoteClassOptions,
|
|
59
|
-
selectedClassOption,
|
|
60
|
-
trimmedClassSearchQuery,
|
|
61
|
-
]);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
if (!trimmedClassSearchQuery || !schoolId)
|
|
64
|
-
return;
|
|
65
|
-
let isActive = true;
|
|
66
|
-
const fetchClasses = async () => {
|
|
67
|
-
setClassOptionsLoading(true);
|
|
68
|
-
try {
|
|
69
|
-
const { items } = await getCachedClasses({
|
|
70
|
-
params: {
|
|
71
|
-
schoolId,
|
|
72
|
-
searchQuery: trimmedClassSearchQuery,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
if (!isActive)
|
|
76
|
-
return;
|
|
77
|
-
setRemoteClassOptions((items || []).map((cls) => ({
|
|
78
|
-
label: `${cls.name} (${cls.code})`,
|
|
79
|
-
value: cls.id,
|
|
80
|
-
})));
|
|
81
|
-
}
|
|
82
|
-
catch (_a) {
|
|
83
|
-
if (!isActive)
|
|
84
|
-
return;
|
|
85
|
-
setRemoteClassOptions([]);
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
if (isActive) {
|
|
89
|
-
setClassOptionsLoading(false);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
void fetchClasses();
|
|
94
|
-
return () => {
|
|
95
|
-
isActive = false;
|
|
96
|
-
};
|
|
97
|
-
}, [schoolId, trimmedClassSearchQuery]);
|
|
98
|
-
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCombobox, { emptyText: t("formNoClassEmpty"), error: errors.classId, id: "classId", info: t("formClassInfo"), label: t("formClassLabel"), loading: classOptionsLoading && Boolean(trimmedClassSearchQuery), onSearchChange: setClassSearchQuery, onValueChange: (value) => handleChange("classId", value), options: displayedClassOptions, required: true, searchPlaceholder: t("formSearchClassesPlaceholder"), value: classId }), _jsx(EnhancedInput, { error: errors.name, id: "name", info: t("sectionNameInfo"), label: t("sectionName"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("sectionNamePlaceholder"), required: true, value: name }), _jsx(EnhancedInput, { error: errors.capacity, id: "capacity", info: t("formCapacityInfo"), label: t("formCapacityLabel"), onChange: (e) => {
|
|
23
|
+
const { data: classes } = useFetch("/api/v1/class", {
|
|
24
|
+
params: { workspaceId: workspace === null || workspace === void 0 ? void 0 : workspace.id },
|
|
25
|
+
method: API_METHODS.GET,
|
|
26
|
+
});
|
|
27
|
+
const { enhancedComboboxElement: classIdCombo } = useEnhancedCombobox({
|
|
28
|
+
emptyText: t("formNoClassEmpty"),
|
|
29
|
+
id: "classId",
|
|
30
|
+
info: t("formClassInfo"),
|
|
31
|
+
label: t("formClassLabel"),
|
|
32
|
+
onValueChange: (value) => handleChange("classId", value),
|
|
33
|
+
options: (classes === null || classes === void 0 ? void 0 : classes.map((cls) => ({
|
|
34
|
+
id: cls.id,
|
|
35
|
+
name: `${cls.name} (${cls.code})`,
|
|
36
|
+
}))) || [],
|
|
37
|
+
placeholder: t("formClassPlaceholder"),
|
|
38
|
+
required: true,
|
|
39
|
+
searchEndpoint: SECTION_API_ROUTES.UNIT,
|
|
40
|
+
searchPlaceholder: t("formSearchClassesPlaceholder"),
|
|
41
|
+
value: classId,
|
|
42
|
+
});
|
|
43
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [classIdCombo, _jsx(EnhancedInput, { error: errors.name, id: "name", info: t("sectionNameInfo"), label: t("sectionName"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("sectionNamePlaceholder"), required: true, value: name }), _jsx(EnhancedInput, { error: errors.capacity, id: "capacity", info: t("formCapacityInfo"), label: t("formCapacityLabel"), onChange: (e) => {
|
|
99
44
|
const value = e.target.value;
|
|
100
45
|
handleChange("capacity", value ? Number(value) : 0);
|
|
101
46
|
}, placeholder: t("formCapacityPlaceholder"), type: "number", value: (capacity === null || capacity === void 0 ? void 0 : capacity.toString()) || "" }), _jsx(EnhancedCheckbox, { label: t("formActiveSectionLabel"), defaultChecked: enabled, onCheckedChange: (checked) => handleChange("enabled", checked), info: t("actionToggleActivateOrDeactivateSection") })] }));
|
|
@@ -7,7 +7,6 @@ import converter from "json-2-csv";
|
|
|
7
7
|
import { Timeline } from "../../components/timeline";
|
|
8
8
|
import { useTranslations } from "next-intl";
|
|
9
9
|
import { SECTION_API_ROUTES, pageLimit } from "./constants";
|
|
10
|
-
import { invalidateSectionsCache } from "./cache";
|
|
11
10
|
import { SECTION_ACTION_TYPES, useSectionContext } from "./context";
|
|
12
11
|
import { useRef, useEffect, useCallback } from "react";
|
|
13
12
|
const workspace = getCachedWorkspaceSync();
|
|
@@ -165,7 +164,6 @@ export const SectionMoreActions = () => {
|
|
|
165
164
|
else {
|
|
166
165
|
showSuccessToast("Bulk operation completed successfully");
|
|
167
166
|
}
|
|
168
|
-
invalidateSectionsCache();
|
|
169
167
|
const schoolId = ((_f = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _f === void 0 ? void 0 : _f.id) || "";
|
|
170
168
|
fetch(`${SECTION_API_ROUTES.LIST}?currentPage=1&pageLimit=${pageLimit}&schoolId=${schoolId}`, {
|
|
171
169
|
headers: {
|
|
@@ -12,17 +12,19 @@ import { GraduationCap, CheckCircle2, XCircle, Users } from "lucide-react";
|
|
|
12
12
|
import { useSectionModule } from "./context";
|
|
13
13
|
import { useTranslations } from "next-intl";
|
|
14
14
|
import { formatValue } from "@react-pakistan/util-functions/general/format-value";
|
|
15
|
-
import {
|
|
15
|
+
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
16
|
+
import { useFetch } from "@react-pakistan/util-functions/hooks/use-fetch";
|
|
17
|
+
import { API_METHODS } from "@react-pakistan/util-functions";
|
|
16
18
|
export const SectionView = () => {
|
|
17
19
|
const { state } = useSectionModule();
|
|
18
20
|
const { capacity, classId, enabled, name } = state;
|
|
21
|
+
const workspace = getCachedWorkspaceSync();
|
|
19
22
|
const t = useTranslations("section");
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-6", children: [_jsx("div", { className: "bg-primary/10 flex h-24 w-24 items-center justify-center rounded-full", children: _jsx(Users, { className: "text-primary h-12 w-12" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("h2", { className: "text-2xl font-bold", children: name }), enabled ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" }))] }), _jsxs("p", { className: "text-muted-foreground mt-1", children: [t("viewFieldClass"), ": ", className] })] })] }) }) }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(GraduationCap, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("sectionDetails") })] }), _jsx(CardDescription, { children: t("viewSectionCompleteSectionInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("sectionName") }), _jsx("p", { className: "text-base", children: formatValue(name) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldClass") }), _jsx("p", { className: "text-base", children: className })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCapacity") }), _jsxs("p", { className: "text-base", children: [formatValue(capacity), " ", capacity !== null ? t("viewFieldStudents") : ""] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStatus") }), _jsx(Badge, { variant: enabled ? "default" : "secondary", children: enabled
|
|
23
|
+
const { data: classData } = useFetch("/api/v1/class", {
|
|
24
|
+
params: { classId, workspaceId: workspace === null || workspace === void 0 ? void 0 : workspace.id },
|
|
25
|
+
method: API_METHODS.GET,
|
|
26
|
+
});
|
|
27
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-6", children: [_jsx("div", { className: "bg-primary/10 flex h-24 w-24 items-center justify-center rounded-full", children: _jsx(Users, { className: "text-primary h-12 w-12" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("h2", { className: "text-2xl font-bold", children: name }), enabled ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" }))] }), _jsxs("p", { className: "text-muted-foreground mt-1", children: [t("viewFieldClass"), ": ", (classData === null || classData === void 0 ? void 0 : classData.name) || "—"] })] })] }) }) }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(GraduationCap, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("sectionDetails") })] }), _jsx(CardDescription, { children: t("viewSectionCompleteSectionInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("sectionName") }), _jsx("p", { className: "text-base", children: formatValue(name) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldClass") }), _jsx("p", { className: "text-base", children: (classData === null || classData === void 0 ? void 0 : classData.name) || "—" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCapacity") }), _jsxs("p", { className: "text-base", children: [formatValue(capacity), " ", capacity !== null ? t("viewFieldStudents") : ""] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStatus") }), _jsx(Badge, { variant: enabled ? "default" : "secondary", children: enabled
|
|
26
28
|
? t("viewFieldStatusActive")
|
|
27
29
|
: t("viewFieldStatusInactive") })] })] }) })] })] }));
|
|
28
30
|
};
|
|
@@ -26,6 +26,7 @@ export declare const useStudentFeeModule: () => {
|
|
|
26
26
|
}[];
|
|
27
27
|
listFetchNow: (url?: string, config?: import("@react-pakistan/util-functions/hooks/use-fetch").FetchConfig) => void;
|
|
28
28
|
listLoading: boolean;
|
|
29
|
+
resetRecordFormState: () => void;
|
|
29
30
|
rowActions: RowAction[];
|
|
30
31
|
toggleStatus: (row?: TableRow) => void;
|
|
31
32
|
updateLoading: boolean;
|
|
@@ -7,9 +7,8 @@ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-mod
|
|
|
7
7
|
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
8
8
|
import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
|
|
9
9
|
import { PAYMENT_STATUS, DISCOUNT_TYPE, } from "../../../type";
|
|
10
|
-
import { STUDENT_FEE_API_ROUTES
|
|
10
|
+
import { STUDENT_FEE_API_ROUTES } from "../constants";
|
|
11
11
|
import { studentFeeFormValidation } from "../validate";
|
|
12
|
-
import { getCachedStudentFees, invalidateStudentFeesCache } from "../cache";
|
|
13
12
|
import { getCachedWorkspaceSync } from "../../workspace/cache";
|
|
14
13
|
import { getCachedFeeStructureById } from "../../fee-structure/cache";
|
|
15
14
|
import { getCachedStudentProfilesSync } from "../../student-profile/cache";
|
|
@@ -71,11 +70,6 @@ export const useStudentFeeModule = () => {
|
|
|
71
70
|
}), [state, schoolId]);
|
|
72
71
|
const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
|
|
73
72
|
const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
|
|
74
|
-
const isDefaultListState = state.currentPage === 1 &&
|
|
75
|
-
state.pageLimit === pageLimit &&
|
|
76
|
-
!debouncedQuery &&
|
|
77
|
-
state.filterEnabled === undefined &&
|
|
78
|
-
!state.filterStatus;
|
|
79
73
|
// ============================================================================
|
|
80
74
|
// 1.4.3 UTILITIES
|
|
81
75
|
// ============================================================================
|
|
@@ -101,6 +95,44 @@ export const useStudentFeeModule = () => {
|
|
|
101
95
|
payload: { drawer: null },
|
|
102
96
|
});
|
|
103
97
|
}, [dispatch]);
|
|
98
|
+
const resetRecordFormState = useCallback(() => {
|
|
99
|
+
dispatch({
|
|
100
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_ERRORS,
|
|
101
|
+
payload: { errors: {} },
|
|
102
|
+
});
|
|
103
|
+
dispatch({
|
|
104
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
105
|
+
payload: { disabled: false },
|
|
106
|
+
});
|
|
107
|
+
dispatch({
|
|
108
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_FORM_DATA,
|
|
109
|
+
payload: {
|
|
110
|
+
form: {
|
|
111
|
+
amount: 0,
|
|
112
|
+
amountDue: 0,
|
|
113
|
+
amountPaid: 0,
|
|
114
|
+
discountAmount: 0,
|
|
115
|
+
discountCodeId: null,
|
|
116
|
+
dueDate: new Date().toISOString().slice(0, 10),
|
|
117
|
+
enabled: true,
|
|
118
|
+
familyId: "",
|
|
119
|
+
feeStructureId: "",
|
|
120
|
+
filterEnabled: undefined,
|
|
121
|
+
filterStatus: "",
|
|
122
|
+
id: "",
|
|
123
|
+
lastRemindedAt: null,
|
|
124
|
+
nextFollowUpAt: null,
|
|
125
|
+
originalFee: 0,
|
|
126
|
+
reliabilityScore: null,
|
|
127
|
+
remarks: null,
|
|
128
|
+
riskLevel: null,
|
|
129
|
+
schoolId,
|
|
130
|
+
status: PAYMENT_STATUS.PENDING,
|
|
131
|
+
studentProfileId: "",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}, [dispatch, schoolId]);
|
|
104
136
|
// ============================================================================
|
|
105
137
|
// 1.4.4 API CALLBACKS
|
|
106
138
|
// ============================================================================
|
|
@@ -126,7 +158,6 @@ export const useStudentFeeModule = () => {
|
|
|
126
158
|
}
|
|
127
159
|
if (data) {
|
|
128
160
|
const isCreated = isCreatedOrUpdated(data);
|
|
129
|
-
invalidateStudentFeesCache();
|
|
130
161
|
showToast(isCreated
|
|
131
162
|
? t("messagesStudentFeeCreated")
|
|
132
163
|
: t("messagesStudentFeeUpdated"), TOAST_VARIANT.SUCCESS);
|
|
@@ -162,7 +193,6 @@ export const useStudentFeeModule = () => {
|
|
|
162
193
|
return;
|
|
163
194
|
}
|
|
164
195
|
if (data) {
|
|
165
|
-
invalidateStudentFeesCache();
|
|
166
196
|
showToast(t("messagesStudentFeeDeleted"), TOAST_VARIANT.SUCCESS);
|
|
167
197
|
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
168
198
|
}
|
|
@@ -424,19 +454,21 @@ export const useStudentFeeModule = () => {
|
|
|
424
454
|
});
|
|
425
455
|
}, [dispatch]);
|
|
426
456
|
const handleView = useCallback((row) => {
|
|
457
|
+
resetRecordFormState();
|
|
427
458
|
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
428
459
|
dispatch({
|
|
429
460
|
type: STUDENT_FEE_ACTION_TYPES.SET_DRAWER,
|
|
430
461
|
payload: { drawer: STUDENT_FEE_DRAWER.VIEW_DRAWER },
|
|
431
462
|
});
|
|
432
|
-
}, [dispatch,
|
|
463
|
+
}, [byIdFetchNow, dispatch, resetRecordFormState]);
|
|
433
464
|
const handleEdit = useCallback((row) => {
|
|
465
|
+
resetRecordFormState();
|
|
434
466
|
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
435
467
|
dispatch({
|
|
436
468
|
type: STUDENT_FEE_ACTION_TYPES.SET_DRAWER,
|
|
437
469
|
payload: { drawer: STUDENT_FEE_DRAWER.FORM_DRAWER },
|
|
438
470
|
});
|
|
439
|
-
}, [dispatch,
|
|
471
|
+
}, [byIdFetchNow, dispatch, resetRecordFormState]);
|
|
440
472
|
const handleDelete = useCallback((row) => {
|
|
441
473
|
if (!confirm(t("messagesDeleteConfirmation")))
|
|
442
474
|
return;
|
|
@@ -555,32 +587,15 @@ export const useStudentFeeModule = () => {
|
|
|
555
587
|
// ============================================================================
|
|
556
588
|
// 1.4.9 EFFECTS
|
|
557
589
|
// ============================================================================
|
|
590
|
+
useEffect(() => {
|
|
591
|
+
listFetchNowRef.current = listFetchNow;
|
|
592
|
+
}, [listFetchNow]);
|
|
558
593
|
useEffect(() => {
|
|
559
594
|
var _a;
|
|
560
595
|
if (!schoolId)
|
|
561
596
|
return;
|
|
562
|
-
if (isDefaultListState) {
|
|
563
|
-
(async () => {
|
|
564
|
-
try {
|
|
565
|
-
const { count, items } = await getCachedStudentFees({
|
|
566
|
-
params: listParams,
|
|
567
|
-
});
|
|
568
|
-
dispatch({
|
|
569
|
-
type: STUDENT_FEE_ACTION_TYPES.SET_ITEMS,
|
|
570
|
-
payload: { items: items || [], count: count || 0 },
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
catch (_a) {
|
|
574
|
-
showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
|
|
575
|
-
}
|
|
576
|
-
})();
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
597
|
(_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
|
|
580
|
-
}, [dispatch,
|
|
581
|
-
useEffect(() => {
|
|
582
|
-
listFetchNowRef.current = listFetchNow;
|
|
583
|
-
}, [listFetchNow]);
|
|
598
|
+
}, [dispatch, listParams, schoolId, showToast, t]);
|
|
584
599
|
// ============================================================================
|
|
585
600
|
// 1.4.10 RETURN
|
|
586
601
|
// ============================================================================
|
|
@@ -604,6 +619,7 @@ export const useStudentFeeModule = () => {
|
|
|
604
619
|
headerActions,
|
|
605
620
|
listFetchNow,
|
|
606
621
|
listLoading,
|
|
622
|
+
resetRecordFormState,
|
|
607
623
|
rowActions,
|
|
608
624
|
toggleStatus,
|
|
609
625
|
updateLoading });
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Key responsibilities:
|
|
9
9
|
* - expose `useStudentFeeModule()` which UI components call for actions
|
|
10
10
|
* - keep module-specific `apiParams` and callbacks in one place
|
|
11
|
-
* - ensure
|
|
11
|
+
* - ensure toast notifications on mutation
|
|
12
12
|
*
|
|
13
13
|
* Exported utilities:
|
|
14
14
|
* - `StudentFeeProvider` — provider component used by the page
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Key responsibilities:
|
|
10
10
|
* - expose `useStudentFeeModule()` which UI components call for actions
|
|
11
11
|
* - keep module-specific `apiParams` and callbacks in one place
|
|
12
|
-
* - ensure
|
|
12
|
+
* - ensure toast notifications on mutation
|
|
13
13
|
*
|
|
14
14
|
* Exported utilities:
|
|
15
15
|
* - `StudentFeeProvider` — provider component used by the page
|
|
@@ -7,20 +7,40 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
* filterStatus (payment status combobox).
|
|
8
8
|
*/
|
|
9
9
|
import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
|
|
10
|
-
import {
|
|
10
|
+
import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
|
|
11
11
|
import { useStudentFeeModule } from "./context";
|
|
12
|
-
import { PAYMENT_STATUS_OPTIONS } from "./constants";
|
|
12
|
+
import { PAYMENT_STATUS_OPTIONS, STUDENT_FEE_API_ROUTES } from "./constants";
|
|
13
13
|
import { useTranslations } from "next-intl";
|
|
14
14
|
export const StudentFeeFilter = () => {
|
|
15
15
|
const { state, handleChange } = useStudentFeeModule();
|
|
16
16
|
const { filterEnabled, filterStatus } = state;
|
|
17
17
|
const t = useTranslations("studentFee");
|
|
18
|
+
const paymentStatusLabelMap = {
|
|
19
|
+
PENDING: t("formOptionPending"),
|
|
20
|
+
PARTIAL: t("formOptionPartial"),
|
|
21
|
+
PAID: t("formOptionPaid"),
|
|
22
|
+
OVERDUE: t("formOptionOverdue"),
|
|
23
|
+
CANCELLED: t("formOptionCancelled"),
|
|
24
|
+
REFUNDED: t("formOptionRefunded"),
|
|
25
|
+
};
|
|
18
26
|
const filterEnabledValue = filterEnabled === undefined
|
|
19
27
|
? "undefined"
|
|
20
28
|
: filterEnabled
|
|
21
29
|
? "true"
|
|
22
30
|
: "false";
|
|
23
|
-
|
|
31
|
+
const { enhancedComboboxElement: filterStatusCombo } = useEnhancedCombobox({
|
|
32
|
+
emptyText: t("filterNoStatusFoundLabel"),
|
|
33
|
+
id: "filterStatus",
|
|
34
|
+
info: t("filterByPaymentStatus"),
|
|
35
|
+
label: t("filterPaymentStatusLabel"),
|
|
36
|
+
onValueChange: (value) => handleChange("filterStatus", value),
|
|
37
|
+
options: PAYMENT_STATUS_OPTIONS.map((opt) => ({ id: opt.value, name: paymentStatusLabelMap[opt.value] || opt.label })),
|
|
38
|
+
placeholder: t("filterAllStatusesLabel"),
|
|
39
|
+
searchEndpoint: STUDENT_FEE_API_ROUTES.UNIT,
|
|
40
|
+
searchPlaceholder: t("filterSearchStatusLabel"),
|
|
41
|
+
value: filterStatus || "",
|
|
42
|
+
});
|
|
43
|
+
return (_jsxs("div", { className: "space-y-4", children: [filterStatusCombo, _jsx(EnhancedRadio, { label: t("filterOptionEnabled"), name: "filterEnabled", value: filterEnabledValue, options: [
|
|
24
44
|
{ label: t("filterOptionAll"), value: "undefined" },
|
|
25
45
|
{ label: t("filterOptionEnabled"), value: "true" },
|
|
26
46
|
{ label: t("filterOptionDisabled"), value: "false" },
|