@appcorp/fusion-storybook 0.2.40 → 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.
Files changed (98) hide show
  1. package/base-modules/admission/constants.d.ts +5 -17
  2. package/base-modules/admission/constants.js +12 -7
  3. package/base-modules/admission/context/use-admission-module.js +11 -48
  4. package/base-modules/admission/filter.js +23 -3
  5. package/base-modules/admission/form.js +49 -19
  6. package/base-modules/attendance/context.js +3 -37
  7. package/base-modules/attendance/form.js +26 -10
  8. package/base-modules/attendance/more-actions.js +34 -25
  9. package/base-modules/campus/context.js +7 -7
  10. package/base-modules/class/cache.js +0 -1
  11. package/base-modules/class/context.js +10 -48
  12. package/base-modules/class/more-actions.js +0 -2
  13. package/base-modules/course/context.js +3 -37
  14. package/base-modules/course/form.js +91 -292
  15. package/base-modules/discount-code/constants.d.ts +5 -0
  16. package/base-modules/discount-code/constants.js +5 -0
  17. package/base-modules/discount-code/context.d.ts +1 -0
  18. package/base-modules/discount-code/context.js +40 -39
  19. package/base-modules/discount-code/form.js +21 -15
  20. package/base-modules/discount-code/more-actions.js +1 -1
  21. package/base-modules/enrollment/context.js +3 -37
  22. package/base-modules/enrollment/form.js +38 -11
  23. package/base-modules/enrollment/more-actions.js +0 -2
  24. package/base-modules/expense/constants.js +1 -1
  25. package/base-modules/expense/context.js +5 -32
  26. package/base-modules/expense/filter.js +50 -3
  27. package/base-modules/expense/form.js +82 -6
  28. package/base-modules/family/context.js +7 -38
  29. package/base-modules/family-member/context.js +7 -39
  30. package/base-modules/fee-structure/context.js +1 -25
  31. package/base-modules/fee-structure/form.js +77 -89
  32. package/base-modules/fee-structure/more-actions.js +0 -2
  33. package/base-modules/rbac/context.d.ts +1 -0
  34. package/base-modules/rbac/context.js +23 -32
  35. package/base-modules/school/context.js +1 -1
  36. package/base-modules/school/form.js +34 -14
  37. package/base-modules/section/context.d.ts +1 -0
  38. package/base-modules/section/context.js +40 -47
  39. package/base-modules/section/form.js +25 -80
  40. package/base-modules/section/more-actions.js +0 -2
  41. package/base-modules/section/view.js +9 -7
  42. package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
  43. package/base-modules/student-fee/context/use-student-fee-module.js +48 -32
  44. package/base-modules/student-fee/context.d.ts +1 -1
  45. package/base-modules/student-fee/context.js +1 -1
  46. package/base-modules/student-fee/filter.js +23 -3
  47. package/base-modules/student-fee/form.js +93 -174
  48. package/base-modules/student-fee/view.d.ts +7 -1
  49. package/base-modules/student-fee/view.js +17 -20
  50. package/base-modules/student-profile/constants.d.ts +0 -6
  51. package/base-modules/student-profile/constants.js +1 -3
  52. package/base-modules/student-profile/context/use-student-profile-module.d.ts +1 -0
  53. package/base-modules/student-profile/context/use-student-profile-module.js +62 -55
  54. package/base-modules/student-profile/context.d.ts +1 -1
  55. package/base-modules/student-profile/context.js +1 -1
  56. package/base-modules/student-profile/filter.js +23 -3
  57. package/base-modules/student-profile/form.js +35 -3
  58. package/base-modules/subject/context.d.ts +1 -0
  59. package/base-modules/subject/context.js +38 -47
  60. package/base-modules/subject/more-actions.js +0 -2
  61. package/base-modules/teacher/constants.d.ts +0 -6
  62. package/base-modules/teacher/constants.js +0 -2
  63. package/base-modules/teacher/context.d.ts +1 -0
  64. package/base-modules/teacher/context.js +58 -39
  65. package/base-modules/teacher/form.js +46 -11
  66. package/base-modules/teacher/more-actions.js +0 -2
  67. package/base-modules/user/context/use-user-module.d.ts +1 -0
  68. package/base-modules/user/context/use-user-module.js +36 -32
  69. package/base-modules/user/context.js +1 -1
  70. package/base-modules/user/filter.js +6 -4
  71. package/base-modules/user/form.js +29 -5
  72. package/base-modules/user/more-actions.js +9 -7
  73. package/base-modules/user/view.js +3 -1
  74. package/base-modules/workspace/form.js +18 -8
  75. package/base-modules/workspace-user/context.d.ts +2 -1
  76. package/base-modules/workspace-user/context.js +31 -29
  77. package/package.json +1 -1
  78. package/tsconfig.build.tsbuildinfo +1 -1
  79. package/base-modules/admission/cache.d.ts +0 -14
  80. package/base-modules/admission/cache.js +0 -31
  81. package/base-modules/attendance/cache.d.ts +0 -14
  82. package/base-modules/attendance/cache.js +0 -31
  83. package/base-modules/course/cache.d.ts +0 -14
  84. package/base-modules/course/cache.js +0 -31
  85. package/base-modules/enrollment/cache.d.ts +0 -14
  86. package/base-modules/enrollment/cache.js +0 -31
  87. package/base-modules/expense/cache.d.ts +0 -14
  88. package/base-modules/expense/cache.js +0 -31
  89. package/base-modules/family/cache.d.ts +0 -14
  90. package/base-modules/family/cache.js +0 -31
  91. package/base-modules/family-member/cache.d.ts +0 -14
  92. package/base-modules/family-member/cache.js +0 -31
  93. package/base-modules/rbac/cache.d.ts +0 -27
  94. package/base-modules/rbac/cache.js +0 -46
  95. package/base-modules/student-fee/cache.d.ts +0 -15
  96. package/base-modules/student-fee/cache.js +0 -21
  97. package/base-modules/workspace-user/cache.d.ts +0 -14
  98. package/base-modules/workspace-user/cache.js +0 -31
@@ -9,7 +9,7 @@
9
9
  * Key responsibilities:
10
10
  * - Expose `useTeacherModule()` which UI components call for actions
11
11
  * - Keep module-specific `apiParams` and callbacks in one place
12
- * - Ensure cache invalidation and toast notifications on mutation
12
+ * - Ensure toast notifications on mutation
13
13
  *
14
14
  * Exported utilities:
15
15
  * - `TeacherProvider` — provider component used by the page
@@ -29,7 +29,6 @@ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-ut
29
29
  import { GENDER } from "../../type";
30
30
  import { TEACHER_API_ROUTES, pageLimit } from "./constants";
31
31
  import { teacherFormValidation } from "./validate";
32
- import { getCachedTeachers, invalidateTeachersCache } from "./cache";
33
32
  import { getCachedWorkspaceSync } from "../workspace/cache";
34
33
  import { processAvatarFile } from "./avatar-upload";
35
34
  // ============================================================================
@@ -146,10 +145,6 @@ export const useTeacherModule = () => {
146
145
  }), [state, schoolId]);
147
146
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
148
147
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
149
- const isDefaultListState = state.currentPage === 1 &&
150
- state.pageLimit === pageLimit &&
151
- !debouncedQuery &&
152
- state.filterEnabled === undefined;
153
148
  // ============================================================================
154
149
  // 1.4.3 UTILITIES
155
150
  // ============================================================================
@@ -175,6 +170,46 @@ export const useTeacherModule = () => {
175
170
  payload: { drawer: null },
176
171
  });
177
172
  }, [dispatch]);
173
+ const resetRecordFormState = useCallback(() => {
174
+ dispatch({
175
+ type: TEACHER_ACTION_TYPES.SET_ERRORS,
176
+ payload: { errors: {} },
177
+ });
178
+ dispatch({
179
+ type: TEACHER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
180
+ payload: { disabled: false },
181
+ });
182
+ dispatch({
183
+ type: TEACHER_ACTION_TYPES.SET_FORM_DATA,
184
+ payload: {
185
+ form: {
186
+ address: null,
187
+ avatar: null,
188
+ bio: null,
189
+ city: null,
190
+ country: null,
191
+ dateOfBirth: null,
192
+ emergencyPhone: "",
193
+ enabled: true,
194
+ experience: null,
195
+ filterEnabled: undefined,
196
+ firstName: "",
197
+ gender: GENDER.MALE,
198
+ id: "",
199
+ joiningDate: new Date().toISOString().split("T")[0],
200
+ lastName: "",
201
+ phone: "",
202
+ postalCode: null,
203
+ qualification: null,
204
+ schoolId,
205
+ specialization: null,
206
+ state: null,
207
+ teacherCode: "",
208
+ userId: null,
209
+ },
210
+ },
211
+ });
212
+ }, [dispatch, schoolId]);
178
213
  // ============================================================================
179
214
  // 1.4.4 API CALLBACKS
180
215
  // ============================================================================
@@ -200,7 +235,6 @@ export const useTeacherModule = () => {
200
235
  }
201
236
  if (data) {
202
237
  const isCreated = isCreatedOrUpdated(data);
203
- invalidateTeachersCache();
204
238
  showToast(isCreated ? t("messagesCreateSuccess") : t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
205
239
  resetFormAndCloseDrawer();
206
240
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
@@ -228,7 +262,6 @@ export const useTeacherModule = () => {
228
262
  return;
229
263
  }
230
264
  if (data) {
231
- invalidateTeachersCache();
232
265
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
233
266
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
234
267
  }
@@ -281,21 +314,23 @@ export const useTeacherModule = () => {
281
314
  });
282
315
  }, [dispatch]);
283
316
  const handleView = useCallback((row) => {
317
+ resetRecordFormState();
284
318
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
285
319
  dispatch({
286
320
  type: TEACHER_ACTION_TYPES.SET_DRAWER,
287
321
  payload: { drawer: TEACHER_DRAWER.VIEW_DRAWER },
288
322
  });
289
- }, [byIdFetchNow, dispatch]);
323
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
290
324
  const handleEdit = useCallback((row) => {
325
+ resetRecordFormState();
291
326
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
292
327
  dispatch({
293
328
  type: TEACHER_ACTION_TYPES.SET_DRAWER,
294
329
  payload: { drawer: TEACHER_DRAWER.FORM_DRAWER },
295
330
  });
296
- }, [byIdFetchNow, dispatch]);
331
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
297
332
  const handleDelete = useCallback((row) => {
298
- if (!confirm(t("actionDeleteConfirmation")))
333
+ if (!confirm(t("messagesDeleteConfirmation")))
299
334
  return;
300
335
  deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
301
336
  body: JSON.stringify({ id: row === null || row === void 0 ? void 0 : row.id }),
@@ -479,19 +514,19 @@ export const useTeacherModule = () => {
479
514
  {
480
515
  enabled: true,
481
516
  handleOnClick: handleMoreActions,
482
- label: t("actionHeaderMoreActions"),
517
+ label: t("actionsButtonMoreActions"),
483
518
  order: 0,
484
519
  },
485
520
  {
486
521
  enabled: true,
487
522
  handleOnClick: handleFilters,
488
- label: t("actionHeaderFilters"),
523
+ label: t("actionsButtonFilters"),
489
524
  order: 1,
490
525
  },
491
526
  {
492
527
  enabled: true,
493
528
  handleOnClick: handleCreate,
494
- label: t("actionHeaderAdd"),
529
+ label: t("actionsButtonAdd"),
495
530
  order: 2,
496
531
  },
497
532
  ], [handleCreate, handleFilters, handleMoreActions, t]);
@@ -499,58 +534,41 @@ export const useTeacherModule = () => {
499
534
  {
500
535
  enabled: true,
501
536
  handleOnClick: handleView,
502
- label: t("actionRowView"),
537
+ label: t("actionsButtonView"),
503
538
  order: 1,
504
539
  },
505
540
  {
506
541
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === true,
507
542
  handleOnClick: handleEdit,
508
- label: t("actionRowEdit"),
543
+ label: t("actionsButtonEdit"),
509
544
  order: 2,
510
545
  },
511
546
  {
512
547
  enabled: (row) => (row === null || row === void 0 ? void 0 : row.enabled) === false,
513
548
  handleOnClick: handleDelete,
514
- label: t("actionRowDelete"),
549
+ label: t("actionsButtonDelete"),
515
550
  order: 3,
516
551
  variant: "destructive",
517
552
  },
518
553
  {
519
554
  enabled: false,
520
555
  handleOnClick: toggleStatus,
521
- label: t("actionRowToggleStatus"),
556
+ label: t("actionsButtonToggleStatus"),
522
557
  order: 4,
523
558
  },
524
559
  ], [handleDelete, handleEdit, handleView, t, toggleStatus]);
525
560
  // ============================================================================
526
561
  // 1.4.9 EFFECTS
527
562
  // ============================================================================
563
+ useEffect(() => {
564
+ listFetchNowRef.current = listFetchNow;
565
+ }, [listFetchNow]);
528
566
  useEffect(() => {
529
567
  var _a;
530
568
  if (!schoolId)
531
569
  return;
532
- if (isDefaultListState) {
533
- (async () => {
534
- try {
535
- const { count, items } = await getCachedTeachers({
536
- params: listParams,
537
- });
538
- dispatch({
539
- type: TEACHER_ACTION_TYPES.SET_ITEMS,
540
- payload: { items: items || [], count: count || 0 },
541
- });
542
- }
543
- catch (_a) {
544
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
545
- }
546
- })();
547
- return;
548
- }
549
570
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
550
- }, [dispatch, isDefaultListState, listParams, schoolId, showToast, t]);
551
- useEffect(() => {
552
- listFetchNowRef.current = listFetchNow;
553
- }, [listFetchNow]);
571
+ }, [dispatch, listParams, schoolId, showToast, t]);
554
572
  // ============================================================================
555
573
  // 1.4.10 RETURN
556
574
  // ============================================================================
@@ -576,6 +594,7 @@ export const useTeacherModule = () => {
576
594
  headerActions,
577
595
  listFetchNow,
578
596
  listLoading,
597
+ resetRecordFormState,
579
598
  rowActions,
580
599
  toggleStatus,
581
600
  updateLoading });
@@ -3,27 +3,62 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
4
4
  import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
5
5
  import { EnhancedDropzone } from "@appcorp/shadcn/components/enhanced-dropzone";
6
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
6
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
7
7
  import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
8
8
  import { useTranslations } from "next-intl";
9
- import { USER_ROLE } from "../../type";
10
- import { getCachedUsersSync } from "../user/cache";
11
9
  import { useTeacherModule } from "./context";
12
- import { GENDER_OPTIONS } from "./constants";
10
+ import { GENDER_OPTIONS, TEACHER_API_ROUTES } from "./constants";
13
11
  export const TeacherForm = () => {
14
- var _a;
15
12
  const t = useTranslations("teacher");
13
+ const genderLabelMap = {
14
+ MALE: t("formOptionMale"),
15
+ FEMALE: t("formOptionFemale"),
16
+ PREFER_NOT_TO_SAY: t("formOptionPreferNotToSay"),
17
+ };
16
18
  const context = useTeacherModule();
17
- const { address, avatar, bio, city, country, dateOfBirth, emergencyPhone, enabled, errors, experience, firstName, gender, joiningDate, lastName, phone, postalCode, qualification, specialization, userId, } = context.state;
19
+ const { address, avatar, bio, city, country, dateOfBirth, emergencyPhone, enabled, errors, experience, firstName, gender, joiningDate, lastName, phone, postalCode, qualification, specialization,
20
+ // userId,
21
+ } = context.state;
18
22
  const stateValue = context.state.state;
19
23
  const { handleChange, handleAvatarUpload } = context;
20
- const users = getCachedUsersSync();
24
+ // const users = getCachedUsersSync();
25
+ // const { enhancedComboboxElement: userIdCombo } = useEnhancedCombobox({
26
+ // emptyText: t("formNoTeacherUsersEmpty"),
27
+ // id: "userId",
28
+ // info: t("formLinkedSystemUserLabel"),
29
+ // label: t("formTeacherUserPlaceholder"),
30
+ // onValueChange: (value) => handleChange("userId", value || null),
31
+ // options: users.items
32
+ // ?.filter(
33
+ // ({ workspaces }) =>
34
+ // workspaces?.filter(({ role }) => role?.userRole === USER_ROLE.TEACHER)
35
+ // .length
36
+ // )
37
+ // .map((u) => ({
38
+ // id: u.id,
39
+ // name: u?.name || u?.email || u.id,
40
+ // })),
41
+ // placeholder: t("formSelectLinkedUserPlaceholder"),
42
+ // searchEndpoint: TEACHER_API_ROUTES.UNIT,
43
+ // searchPlaceholder: t("formSearchUsersPlaceholder"),
44
+ // value: userId || "",
45
+ // });
46
+ const { enhancedComboboxElement: genderCombo } = useEnhancedCombobox({
47
+ id: "gender",
48
+ info: t("formTeachersGenderPlaceholder"),
49
+ label: t("formGenderLabel"),
50
+ onValueChange: (value) => handleChange("gender", value || null),
51
+ options: GENDER_OPTIONS.map((opt) => ({
52
+ id: opt.value,
53
+ name: genderLabelMap[opt.value] || opt.label,
54
+ })),
55
+ placeholder: "",
56
+ searchEndpoint: TEACHER_API_ROUTES.UNIT,
57
+ value: gender || "",
58
+ });
21
59
  return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.firstName, id: "firstName", info: t("formTeachersFirstNameLabel"), label: t("formFirstNameLabel"), onChange: (e) => handleChange("firstName", e.target.value), placeholder: t("formFirstNamePlaceholder"), required: true, value: firstName }), _jsx(EnhancedInput, { error: errors.lastName, id: "lastName", info: t("formTeachersLastNameLabel"), label: t("formLastNameLabel"), onChange: (e) => handleChange("lastName", e.target.value), placeholder: t("formLastNamePlaceholder"), required: true, value: lastName }), _jsx(EnhancedInput, { error: errors.joiningDate, id: "joiningDate", info: t("formDateTeacherJoinedTheSchoolLabel"), label: t("formJoiningDateLabel"), onChange: (e) => handleChange("joiningDate", e.target.value), onClick: (e) => { var _a, _b; return (_b = (_a = e.currentTarget).showPicker) === null || _b === void 0 ? void 0 : _b.call(_a); }, required: true, type: "date", value: joiningDate
22
60
  ? new Date(joiningDate).toISOString().split("formTLabel")[0]
23
- : "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoTeacherUsersEmpty"), error: errors.userId, id: "userId", info: t("formLinkedSystemUserLabel"), label: t("formTeacherUserPlaceholder"), onValueChange: (value) => handleChange("userId", value || null), options: (_a = users.items) === null || _a === void 0 ? void 0 : _a.filter(({ workspaces }) => workspaces === null || workspaces === void 0 ? void 0 : workspaces.filter(({ role }) => (role === null || role === void 0 ? void 0 : role.userRole) === USER_ROLE.TEACHER).length).map((u) => ({
24
- label: (u === null || u === void 0 ? void 0 : u.name) || (u === null || u === void 0 ? void 0 : u.email) || u.id,
25
- value: u.id,
26
- })), placeholder: t("formSelectLinkedUserPlaceholder"), searchPlaceholder: t("formSearchUsersPlaceholder"), value: userId || "" }), _jsx(EnhancedCombobox, { error: errors.gender, id: "gender", info: t("formTeachersGenderPlaceholder"), label: t("formGenderLabel"), onValueChange: (value) => handleChange("gender", value || null), options: GENDER_OPTIONS.slice(1), value: gender || "" }), _jsx(EnhancedInput, { error: errors.dateOfBirth, id: "dateOfBirth", info: t("formTeachersDateOfBirthLabel"), label: t("formDateOfBirthLabel"), 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
61
+ : "" }), genderCombo, _jsx(EnhancedInput, { error: errors.dateOfBirth, id: "dateOfBirth", info: t("formTeachersDateOfBirthLabel"), label: t("formDateOfBirthLabel"), 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
27
62
  ? new Date(dateOfBirth).toISOString().split("formTLabel")[0]
28
63
  : "" }), _jsx(EnhancedInput, { error: errors.phone, id: "phone", info: t("formTeachersPhoneNumberLabel"), label: t("formPhoneLabel"), onChange: (e) => handleChange("phone", e.target.value), placeholder: t("formPhonePlaceholder"), type: "tel", value: phone || "" }), _jsx(EnhancedInput, { error: errors.emergencyPhone, id: "emergencyPhone", info: t("formEmergencyContactNumberLabel"), label: t("formEmergencyPhoneLabel"), onChange: (e) => handleChange("emergencyPhone", e.target.value), placeholder: t("formEmergencyPhonePlaceholder"), type: "tel", value: emergencyPhone || "" }), _jsx(EnhancedInput, { error: errors.qualification, id: "qualification", info: t("formTeachersEducationalQualificationLabel"), label: t("formQualificationLabel"), onChange: (e) => handleChange("qualification", e.target.value), placeholder: t("formQualificationPlaceholder"), value: qualification || "" }), _jsx(EnhancedInput, { error: errors.specialization, id: "specialization", info: t("formTeachersAreaOfSpecializationLabel"), label: t("formSpecializationLabel"), onChange: (e) => handleChange("specialization", e.target.value), placeholder: t("formSpecializationPlaceholder"), value: specialization || "" }), _jsx(EnhancedInput, { error: errors.experience, id: "experience", info: t("formYearsOfTeachingExperienceLabel"), label: t("formExperienceLabel"), onChange: (e) => handleChange("experience", parseInt(e.target.value) || null), placeholder: t("formExperiencePlaceholder"), type: "number", value: (experience === null || experience === void 0 ? void 0 : experience.toString()) || "" }), _jsx(EnhancedInput, { error: errors.address, id: "address", info: t("formStreetAddressLabel"), label: t("formAddressLabel"), onChange: (e) => handleChange("address", e.target.value), placeholder: t("formAddressPlaceholder"), value: address || "" }), _jsx(EnhancedInput, { error: errors.city, id: "city", info: t("formCityNameLabel"), label: t("formCityLabel"), onChange: (e) => handleChange("city", e.target.value), placeholder: t("formCityPlaceholder"), value: city || "" }), _jsx(EnhancedInput, { error: errors.state, id: "state", info: t("formStateOrProvinceLabel"), label: t("formStateProvinceLabel"), onChange: (e) => handleChange("state", e.target.value), placeholder: t("formStatePlaceholder"), value: stateValue || "" }), _jsx(EnhancedInput, { error: errors.country, id: "country", info: t("formCountryNameLabel"), label: t("formCountryLabel"), onChange: (e) => handleChange("country", e.target.value), placeholder: t("formCountryPlaceholder"), value: country || "" }), _jsx(EnhancedInput, { error: errors.postalCode, id: "postalCode", info: t("formPostalOrZipCodeLabel"), label: t("formPostalCodeLabel"), onChange: (e) => handleChange("postalCode", e.target.value), placeholder: t("formPostalCodePlaceholder"), value: postalCode || "" }), _jsx(EnhancedDropzone, { accept: ["image/*"], error: errors.avatar, id: "avatar", info: t("formUploadTeachersPhoto"), label: t("formAvatarLabel"), maxFiles: 1, maxSize: 100 * 1024, onChange: (files) => {
29
64
  if (files.length > 0) {
@@ -8,7 +8,6 @@ import { Timeline } from "../../components/timeline";
8
8
  import { useTranslations } from "next-intl";
9
9
  import { teacherFormValidation } from "./validate";
10
10
  import { TEACHER_API_ROUTES, pageLimit } from "./constants";
11
- import { invalidateTeachersCache } from "./cache";
12
11
  import { TEACHER_ACTION_TYPES, useTeacherContext } from "./context";
13
12
  import { useRef, useEffect, useCallback } from "react";
14
13
  const workspace = getCachedWorkspaceSync();
@@ -174,7 +173,6 @@ export const TeacherMoreActions = () => {
174
173
  else {
175
174
  showSuccessToast("Bulk operation completed successfully");
176
175
  }
177
- invalidateTeachersCache();
178
176
  const schoolId = ((_d = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _d === void 0 ? void 0 : _d.id) || "";
179
177
  fetch(`${TEACHER_API_ROUTES.LIST}?currentPage=1&pageLimit=${pageLimit}&schoolId=${schoolId}`, {
180
178
  headers: {
@@ -27,6 +27,7 @@ export declare const useUserModule: () => {
27
27
  }[];
28
28
  listFetchNow: (url?: string, config?: import("@react-pakistan/util-functions/hooks/use-fetch").FetchConfig) => void;
29
29
  listLoading: boolean;
30
+ resetRecordFormState: () => void;
30
31
  rowActions: RowAction[];
31
32
  toggleStatus: (row?: TableRow) => void;
32
33
  updateLoading: boolean;
@@ -6,11 +6,9 @@ import { isCreatedOrUpdated, validateForm, API_METHODS, fetchData, } from "@reac
6
6
  import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
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
- import { USER_API_ROUTES, pageLimit } from "../constants";
9
+ import { USER_API_ROUTES } from "../constants";
10
10
  import { userFormValidation } from "../validate";
11
- import { getCachedUsers, invalidateUsersCache } from "../cache";
12
11
  import { getCachedWorkspaceSync } from "../../workspace/cache";
13
- import { invalidateWorkspaceUsersCache } from "../../workspace-user/cache";
14
12
  import { blobToWebP } from "webp-converter-browser";
15
13
  import { supabasePublicStorageUrl } from "../../../constants";
16
14
  import { toastNetworkError } from "../../../utils/toast-network-error";
@@ -54,10 +52,6 @@ export const useUserModule = () => {
54
52
  }), [state, workspace === null || workspace === void 0 ? void 0 : workspace.id]);
55
53
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
56
54
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
57
- const isDefaultListState = state.currentPage === 1 &&
58
- state.pageLimit === pageLimit &&
59
- !debouncedQuery &&
60
- state.filterEnabled === undefined;
61
55
  // ============================================================================
62
56
  // 1.4.3 UTILITIES
63
57
  // ============================================================================
@@ -83,6 +77,32 @@ export const useUserModule = () => {
83
77
  payload: { drawer: null },
84
78
  });
85
79
  }, [dispatch]);
80
+ const resetRecordFormState = useCallback(() => {
81
+ dispatch({
82
+ type: USER_ACTION_TYPES.SET_ERRORS,
83
+ payload: { errors: {} },
84
+ });
85
+ dispatch({
86
+ type: USER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
87
+ payload: { disabled: false },
88
+ });
89
+ dispatch({
90
+ type: USER_ACTION_TYPES.SET_FORM_DATA,
91
+ payload: {
92
+ form: {
93
+ avatar: "",
94
+ email: "",
95
+ enabled: true,
96
+ filterEnabled: undefined,
97
+ id: "",
98
+ name: "",
99
+ password: "",
100
+ phone: "",
101
+ userRole: null,
102
+ },
103
+ },
104
+ });
105
+ }, [dispatch]);
86
106
  // ============================================================================
87
107
  // 1.4.4 API CALLBACKS
88
108
  // ============================================================================
@@ -108,8 +128,6 @@ export const useUserModule = () => {
108
128
  }
109
129
  if (data) {
110
130
  const isCreated = isCreatedOrUpdated(data);
111
- invalidateUsersCache();
112
- invalidateWorkspaceUsersCache();
113
131
  showToast(isCreated ? t("messagesUserCreated") : t("messagesUserUpdated"), TOAST_VARIANT.SUCCESS);
114
132
  resetFormAndCloseDrawer();
115
133
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
@@ -163,8 +181,6 @@ export const useUserModule = () => {
163
181
  return;
164
182
  }
165
183
  if (data) {
166
- invalidateUsersCache();
167
- invalidateWorkspaceUsersCache();
168
184
  showToast(t("messagesUserDeleted"), TOAST_VARIANT.SUCCESS);
169
185
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
170
186
  }
@@ -223,19 +239,21 @@ export const useUserModule = () => {
223
239
  });
224
240
  }, [dispatch]);
225
241
  const handleView = useCallback((row) => {
242
+ resetRecordFormState();
226
243
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
227
244
  dispatch({
228
245
  type: USER_ACTION_TYPES.SET_DRAWER,
229
246
  payload: { drawer: USER_DRAWER.VIEW_DRAWER },
230
247
  });
231
- }, [byIdFetchNow, dispatch]);
248
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
232
249
  const handleEdit = useCallback((row) => {
250
+ resetRecordFormState();
233
251
  byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
234
252
  dispatch({
235
253
  type: USER_ACTION_TYPES.SET_DRAWER,
236
254
  payload: { drawer: USER_DRAWER.FORM_DRAWER },
237
255
  });
238
- }, [byIdFetchNow, dispatch]);
256
+ }, [byIdFetchNow, dispatch, resetRecordFormState]);
239
257
  const handleDelete = useCallback((row) => {
240
258
  if (!confirm(t("messagesDeleteConfirmation")))
241
259
  return;
@@ -492,30 +510,15 @@ export const useUserModule = () => {
492
510
  // ============================================================================
493
511
  // 1.4.9 EFFECTS
494
512
  // ============================================================================
513
+ useEffect(() => {
514
+ listFetchNowRef.current = listFetchNow;
515
+ }, [listFetchNow]);
495
516
  useEffect(() => {
496
517
  var _a;
497
518
  if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
498
519
  return;
499
- if (isDefaultListState) {
500
- (async () => {
501
- try {
502
- const { count, items } = await getCachedUsers({ params: listParams });
503
- dispatch({
504
- type: USER_ACTION_TYPES.SET_ITEMS,
505
- payload: { items: items || [], count: count || 0 },
506
- });
507
- }
508
- catch (_a) {
509
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
510
- }
511
- })();
512
- return;
513
- }
514
520
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
515
- }, [dispatch, isDefaultListState, listParams, workspace === null || workspace === void 0 ? void 0 : workspace.id, showToast, t]);
516
- useEffect(() => {
517
- listFetchNowRef.current = listFetchNow;
518
- }, [listFetchNow]);
521
+ }, [dispatch, listParams, workspace === null || workspace === void 0 ? void 0 : workspace.id, showToast, t]);
519
522
  // ============================================================================
520
523
  // 1.4.10 RETURN
521
524
  // ============================================================================
@@ -540,6 +543,7 @@ export const useUserModule = () => {
540
543
  headerActions,
541
544
  listFetchNow,
542
545
  listLoading,
546
+ resetRecordFormState,
543
547
  rowActions,
544
548
  toggleStatus,
545
549
  updateLoading });
@@ -9,7 +9,7 @@
9
9
  * Key responsibilities:
10
10
  * - Expose `useUserModule()` which UI components call for actions
11
11
  * - Keep module-specific `apiParams` and callbacks in one place
12
- * - Ensure cache invalidation and toast notifications on mutation
12
+ * - Ensure toast notifications on mutation
13
13
  *
14
14
  * Exported utilities:
15
15
  * - `UserProvider` — provider component used by the page
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useTranslations } from "next-intl";
3
4
  import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
4
5
  import { useUserModule } from "./context";
5
6
  export const UserFilter = () => {
7
+ const t = useTranslations("user");
6
8
  const { handleChange, state } = useUserModule();
7
9
  const { filterEnabled } = state;
8
10
  const filterEnabledValue = filterEnabled === undefined
@@ -10,10 +12,10 @@ export const UserFilter = () => {
10
12
  : filterEnabled
11
13
  ? "true"
12
14
  : "false";
13
- return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "grid grid-cols-1 gap-4", children: _jsx(EnhancedRadio, { label: "Enabled", name: "filterEnabled", value: filterEnabledValue, options: [
14
- { label: "All", value: "undefined" },
15
- { label: "Enabled", value: "true" },
16
- { label: "Disabled", value: "false" },
15
+ return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "grid grid-cols-1 gap-4", children: _jsx(EnhancedRadio, { label: t("filterOptionEnabled"), name: "filterEnabled", value: filterEnabledValue, options: [
16
+ { label: t("filterOptionAll"), value: "undefined" },
17
+ { label: t("filterOptionEnabled"), value: "true" },
18
+ { label: t("filterOptionDisabled"), value: "false" },
17
19
  ], onValueChange: (next) => {
18
20
  const parsed = next === "true" ? true : next === "false" ? false : undefined;
19
21
  handleChange("filterEnabled", parsed);
@@ -1,28 +1,52 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslations } from "next-intl";
3
4
  import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
4
5
  import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
5
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
6
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
6
7
  import { EnhancedDropzone } from "@appcorp/shadcn/components/enhanced-dropzone";
7
8
  import { useUserModule } from "./context";
8
9
  import { USER_ROLE } from "../../type";
10
+ import { USER_API_ROUTES } from "./constants";
9
11
  import { formatPhone } from "@react-pakistan/util-functions/general/format-phone";
10
12
  import { useState } from "react";
11
13
  import { Eye, EyeOff } from "lucide-react";
12
14
  export const UserForm = () => {
15
+ const t = useTranslations("user");
13
16
  const { handleAvatar, handleChange, state } = useUserModule();
14
17
  const { avatar, email, enabled, errors, name, password, phone, userRole } = state;
18
+ const roleLabelMap = {
19
+ SCHOOL_ADMIN: t("formOptionAdmin"),
20
+ TEACHER: t("formOptionTeacher"),
21
+ STAFF: t("formOptionStaff"),
22
+ };
15
23
  const [passwordVisible, setPasswordVisible] = useState(false);
16
24
  const roleOptions = Object.values(USER_ROLE)
17
25
  .filter((roleOption) => roleOption !== USER_ROLE.SUPER_ADMIN &&
18
26
  roleOption !== USER_ROLE.STUDENT &&
19
27
  roleOption !== USER_ROLE.PARENT)
20
28
  .map((roleOption) => ({
21
- label: roleOption.replace(/_/g, " "),
22
- value: roleOption,
29
+ id: roleOption,
30
+ name: roleLabelMap[roleOption] || roleOption.replace(/_/g, " "),
23
31
  }));
24
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.name, id: "name", info: "Enter user's full name", label: "Name", onChange: (e) => handleChange("name", e.target.value), placeholder: "John Doe", required: true, value: name || "" }), _jsx(EnhancedInput, { error: errors.email, id: "email", info: "Enter user's email address", label: "Email Address", onChange: (e) => handleChange("email", e.target.value), placeholder: "john.doe@example.com", required: true, type: "email", value: email }), _jsxs("div", { className: "relative", children: [_jsx(EnhancedInput, { error: errors.password, id: "password", info: "Enter user's password (optional for updates)", label: "Password", onChange: (e) => handleChange("password", e.target.value), placeholder: "Enter password", type: passwordVisible ? "text" : "password", value: password || "" }), _jsx("button", { type: "button", "aria-label": passwordVisible ? "Hide password" : "Show password", onClick: () => setPasswordVisible(!passwordVisible), className: "text-muted-foreground hover:text-foreground absolute inset-y-0 top-1 right-3 flex items-center justify-center p-1 text-sm", children: passwordVisible ? (_jsx(EyeOff, { className: "h-4 w-4" })) : (_jsx(Eye, { className: "h-4 w-4" })) })] }), _jsx(EnhancedInput, { error: errors.phone, id: "phone", info: "Enter user's phone number, include country code to autoformat", label: "Phone Number", onChange: (e) => {
32
+ const { enhancedComboboxElement: userRoleCombo } = useEnhancedCombobox({
33
+ emptyText: t("formNoRoleEmpty"),
34
+ id: "userRole",
35
+ info: t("formRoleIdInfo"),
36
+ label: t("formRoleIdLabel"),
37
+ onValueChange: (value) => handleChange("userRole", value),
38
+ options: roleOptions.map((opt) => ({
39
+ id: opt.id,
40
+ name: opt.name,
41
+ })),
42
+ placeholder: t("formRoleIdPlaceholder"),
43
+ required: true,
44
+ searchEndpoint: USER_API_ROUTES.UNIT,
45
+ searchPlaceholder: t("formSearchRolePlaceholder"),
46
+ value: userRole !== null && userRole !== void 0 ? userRole : "",
47
+ });
48
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.name, id: "name", info: t("formNameInfo"), label: t("formNameLabel"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("formNamePlaceholder"), required: true, value: name || "" }), _jsx(EnhancedInput, { error: errors.email, id: "email", info: t("formEmailInfo"), label: t("formEmailLabel"), onChange: (e) => handleChange("email", e.target.value), placeholder: t("formEmailPlaceholder"), required: true, type: "email", value: email }), _jsxs("div", { className: "relative", children: [_jsx(EnhancedInput, { error: errors.password, id: "password", info: t("formPasswordInfo"), label: t("formPasswordLabel"), onChange: (e) => handleChange("password", e.target.value), placeholder: t("formPasswordPlaceholder"), type: passwordVisible ? "text" : "password", value: password || "" }), _jsx("button", { type: "button", "aria-label": passwordVisible ? t("hidePassword") : t("showPassword"), onClick: () => setPasswordVisible(!passwordVisible), className: "text-muted-foreground hover:text-foreground absolute inset-y-0 top-1 right-3 flex items-center justify-center p-1 text-sm", children: passwordVisible ? (_jsx(EyeOff, { className: "h-4 w-4" })) : (_jsx(Eye, { className: "h-4 w-4" })) })] }), _jsx(EnhancedInput, { error: errors.phone, id: "phone", info: t("formPhoneInfo"), label: t("formPhoneLabel"), onChange: (e) => {
25
49
  var _a;
26
50
  return handleChange("phone", ((_a = formatPhone(e.target.value)) === null || _a === void 0 ? void 0 : _a.international) || e.target.value);
27
- }, placeholder: "+1 (555) 123-4567", type: "tel", value: phone || "" }), _jsx(EnhancedDropzone, { accept: ["image/*"], error: errors.avatar, id: "avatar", info: "Upload an avatar image (JPG, PNG, GIF, etc.)", label: "Avatar", maxFiles: 1, maxSize: 0.25 * 1024 * 1024, onChange: handleAvatar, onRemoveRemote: () => handleChange("avatar", ""), resize: true, value: avatar ? [avatar] : [] }), _jsx(EnhancedCheckbox, { checked: enabled, error: errors.enabled, id: "enabled", info: "Toggle to enable or disable the user account", label: "Active User", onCheckedChange: (checked) => handleChange("enabled", checked) }), _jsx(EnhancedCombobox, { disabled: userRole === USER_ROLE.SCHOOL_ADMIN, emptyText: "No role found", error: errors.userRole, id: "userRole", info: "Select the user role", label: "Role", onValueChange: (value) => handleChange("userRole", value), options: roleOptions, placeholder: "Select role", required: true, searchPlaceholder: "Search role...", value: userRole !== null && userRole !== void 0 ? userRole : "" })] }));
51
+ }, placeholder: t("formPhonePlaceholder"), type: "tel", value: phone || "" }), _jsx(EnhancedDropzone, { accept: ["image/*"], error: errors.avatar, id: "avatar", info: t("formAvatarInfo"), label: t("formAvatarLabel"), maxFiles: 1, maxSize: 0.25 * 1024 * 1024, onChange: handleAvatar, onRemoveRemote: () => handleChange("avatar", ""), resize: true, value: avatar ? [avatar] : [] }), _jsx(EnhancedCheckbox, { checked: enabled, error: errors.enabled, id: "enabled", info: t("formEnabledInfo"), label: t("formActiveUserLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) }), userRoleCombo] }));
28
52
  };
@@ -1,10 +1,12 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslations } from "next-intl";
3
4
  import { downloadFromUrl, API_METHODS } from "@react-pakistan/util-functions";
4
5
  import { getCachedWorkspaceSync } from "../workspace/cache";
5
6
  import converter from "json-2-csv";
6
7
  import { Timeline } from "../../components/timeline";
7
8
  export const UserMoreActions = () => {
9
+ const t = useTranslations("user");
8
10
  const workspace = getCachedWorkspaceSync();
9
11
  const handleGetAllRecords = async (schoolId) => {
10
12
  const response = await fetch(`/api/user?pageLimit=1000&currentPage=1&schoolId=${schoolId}`, { method: API_METHODS.GET });
@@ -16,33 +18,33 @@ export const UserMoreActions = () => {
16
18
  const create = [
17
19
  {
18
20
  id: "1",
19
- title: "Download empty csv template",
21
+ title: t("moreActionsDownloadEmptyCsvTemplate"),
20
22
  handleOnClick: async () => {
21
23
  await downloadFromUrl("https://nwolvgylwmjuqxsngjxt.supabase.co/storage/v1/object/public/public-blob/common-assets/user.csv", "user-template.csv");
22
24
  },
23
25
  },
24
- { id: "2", title: "Add your data to the csv" },
26
+ { id: "2", title: t("moreActionsAddYourDataToTheCsv") },
25
27
  {
26
28
  id: "3",
27
- title: "Upload the completed csv to the system",
29
+ title: t("moreActionsUploadTheCompletedCsvToTheSystem"),
28
30
  handleOnClick: () => { },
29
31
  },
30
32
  ];
31
33
  const update = [
32
34
  {
33
35
  id: "1",
34
- title: "Download populated csv template",
36
+ title: t("moreActionsDownloadPopulatedCsvTemplate"),
35
37
  handleOnClick: async () => {
36
38
  var _a;
37
39
  await handleGetAllRecords(((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "");
38
40
  },
39
41
  },
40
- { id: "2", title: "Update your data to the csv" },
42
+ { id: "2", title: t("moreActionsUpdateYourDataToTheCsv") },
41
43
  {
42
44
  id: "3",
43
- title: "Upload the completed csv to the system",
45
+ title: t("moreActionsUploadTheCompletedCsvToTheSystem"),
44
46
  handleOnClick: () => { },
45
47
  },
46
48
  ];
47
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Timeline, { events: create, heading: "Bulk Create" }), _jsx(Timeline, { events: update, heading: "Bulk Update" })] }));
49
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(Timeline, { events: create, heading: t("moreActionsBulkCreate") }), _jsx(Timeline, { events: update, heading: t("moreActionsBulkUpdate") })] }));
48
50
  };