@appcorp/fusion-storybook 0.2.40 → 0.2.44

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 (100) 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/filter.js +3 -1
  8. package/base-modules/attendance/form.js +26 -10
  9. package/base-modules/attendance/more-actions.js +34 -25
  10. package/base-modules/campus/context.js +7 -7
  11. package/base-modules/class/cache.js +0 -1
  12. package/base-modules/class/context.js +10 -48
  13. package/base-modules/class/more-actions.js +47 -24
  14. package/base-modules/course/context.js +3 -37
  15. package/base-modules/course/form.js +91 -292
  16. package/base-modules/discount-code/constants.d.ts +5 -0
  17. package/base-modules/discount-code/constants.js +5 -0
  18. package/base-modules/discount-code/context.d.ts +1 -0
  19. package/base-modules/discount-code/context.js +40 -39
  20. package/base-modules/discount-code/form.js +21 -15
  21. package/base-modules/discount-code/more-actions.js +1 -1
  22. package/base-modules/enrollment/context.js +3 -37
  23. package/base-modules/enrollment/form.js +38 -11
  24. package/base-modules/enrollment/more-actions.js +48 -25
  25. package/base-modules/expense/constants.js +1 -1
  26. package/base-modules/expense/context.js +5 -32
  27. package/base-modules/expense/filter.js +50 -3
  28. package/base-modules/expense/form.js +82 -6
  29. package/base-modules/family/context.js +7 -38
  30. package/base-modules/family-member/context.js +7 -39
  31. package/base-modules/fee-structure/context.js +1 -25
  32. package/base-modules/fee-structure/form.js +77 -89
  33. package/base-modules/fee-structure/more-actions.js +45 -22
  34. package/base-modules/rbac/context.d.ts +1 -0
  35. package/base-modules/rbac/context.js +24 -33
  36. package/base-modules/rbac/form.js +3 -1
  37. package/base-modules/school/context.js +1 -1
  38. package/base-modules/school/form.js +34 -14
  39. package/base-modules/section/context.d.ts +1 -0
  40. package/base-modules/section/context.js +40 -47
  41. package/base-modules/section/form.js +25 -80
  42. package/base-modules/section/more-actions.js +47 -24
  43. package/base-modules/section/view.js +9 -7
  44. package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
  45. package/base-modules/student-fee/context/use-student-fee-module.js +48 -32
  46. package/base-modules/student-fee/context.d.ts +1 -1
  47. package/base-modules/student-fee/context.js +1 -1
  48. package/base-modules/student-fee/filter.js +23 -3
  49. package/base-modules/student-fee/form.js +93 -174
  50. package/base-modules/student-fee/view.d.ts +7 -1
  51. package/base-modules/student-fee/view.js +17 -20
  52. package/base-modules/student-profile/constants.d.ts +0 -6
  53. package/base-modules/student-profile/constants.js +1 -3
  54. package/base-modules/student-profile/context/use-student-profile-module.d.ts +1 -0
  55. package/base-modules/student-profile/context/use-student-profile-module.js +62 -55
  56. package/base-modules/student-profile/context.d.ts +1 -1
  57. package/base-modules/student-profile/context.js +1 -1
  58. package/base-modules/student-profile/filter.js +24 -4
  59. package/base-modules/student-profile/form.js +35 -3
  60. package/base-modules/subject/context.d.ts +1 -0
  61. package/base-modules/subject/context.js +38 -47
  62. package/base-modules/subject/more-actions.js +47 -24
  63. package/base-modules/teacher/constants.d.ts +0 -6
  64. package/base-modules/teacher/constants.js +0 -2
  65. package/base-modules/teacher/context.d.ts +1 -0
  66. package/base-modules/teacher/context.js +58 -39
  67. package/base-modules/teacher/form.js +46 -11
  68. package/base-modules/teacher/more-actions.js +45 -22
  69. package/base-modules/user/context/use-user-module.d.ts +1 -0
  70. package/base-modules/user/context/use-user-module.js +36 -32
  71. package/base-modules/user/context.js +1 -1
  72. package/base-modules/user/filter.js +6 -4
  73. package/base-modules/user/form.js +29 -5
  74. package/base-modules/user/more-actions.js +9 -7
  75. package/base-modules/user/view.js +3 -1
  76. package/base-modules/workspace/form.js +18 -8
  77. package/base-modules/workspace-user/context.d.ts +2 -1
  78. package/base-modules/workspace-user/context.js +31 -29
  79. package/package.json +1 -1
  80. package/tsconfig.build.tsbuildinfo +1 -1
  81. package/base-modules/admission/cache.d.ts +0 -14
  82. package/base-modules/admission/cache.js +0 -31
  83. package/base-modules/attendance/cache.d.ts +0 -14
  84. package/base-modules/attendance/cache.js +0 -31
  85. package/base-modules/course/cache.d.ts +0 -14
  86. package/base-modules/course/cache.js +0 -31
  87. package/base-modules/enrollment/cache.d.ts +0 -14
  88. package/base-modules/enrollment/cache.js +0 -31
  89. package/base-modules/expense/cache.d.ts +0 -14
  90. package/base-modules/expense/cache.js +0 -31
  91. package/base-modules/family/cache.d.ts +0 -14
  92. package/base-modules/family/cache.js +0 -31
  93. package/base-modules/family-member/cache.d.ts +0 -14
  94. package/base-modules/family-member/cache.js +0 -31
  95. package/base-modules/rbac/cache.d.ts +0 -27
  96. package/base-modules/rbac/cache.js +0 -46
  97. package/base-modules/student-fee/cache.d.ts +0 -15
  98. package/base-modules/student-fee/cache.js +0 -21
  99. package/base-modules/workspace-user/cache.d.ts +0 -14
  100. package/base-modules/workspace-user/cache.js +0 -31
@@ -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
  };
@@ -1,5 +1,6 @@
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 { useUserModule } from "./context";
4
5
  import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@appcorp/shadcn/components/ui/card";
5
6
  import { Badge } from "@appcorp/shadcn/components/ui/badge";
@@ -19,7 +20,8 @@ const getInitials = (fullName) => {
19
20
  .slice(0, 2);
20
21
  };
21
22
  export const UserView = () => {
23
+ const t = useTranslations("user");
22
24
  const { state } = useUserModule();
23
25
  const { avatar, email, enabled, name, phone } = state;
24
- return (_jsx("div", { className: "space-y-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: "User Information" })] }), _jsx(CardDescription, { children: "User account details" })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: "Profile Picture" }), _jsxs(Avatar, { className: "h-20 w-20", children: [_jsx(AvatarImage, { src: avatar || undefined, alt: name || "User" }), _jsx(AvatarFallback, { className: "text-lg", children: getInitials(name) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: "Name" }), _jsx("p", { className: "text-base", children: formatValue(name) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: "Email Address" })] }), _jsx("p", { className: "text-base", children: formatValue(email) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Phone, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: "Phone Number" })] }), _jsx("p", { className: "text-base", children: formatPhoneDisplay(phone) || formatValue(phone) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: "Status" }), _jsxs(Badge, { variant: enabled ? "default" : "destructive", className: "gap-1", children: [enabled ? (_jsx(CheckCircle2, { className: "h-3 w-3" })) : (_jsx(XCircle, { className: "h-3 w-3" })), enabled ? "Active" : "Inactive"] })] })] }) })] }) }));
26
+ return (_jsx("div", { className: "space-y-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewFieldUserInformation") })] }), _jsx(CardDescription, { children: t("viewUserAccountDetails") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldProfilePicture") }), _jsxs(Avatar, { className: "h-20 w-20", children: [_jsx(AvatarImage, { src: avatar || undefined, alt: name || t("viewFieldName") }), _jsx(AvatarFallback, { className: "text-lg", children: getInitials(name) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldName") }), _jsx("p", { className: "text-base", children: formatValue(name) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldEmailAddress") })] }), _jsx("p", { className: "text-base", children: formatValue(email) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Phone, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPhoneNumber") })] }), _jsx("p", { className: "text-base", children: formatPhoneDisplay(phone) || formatValue(phone) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStatus") }), _jsxs(Badge, { variant: enabled ? "default" : "destructive", className: "gap-1", children: [enabled ? (_jsx(CheckCircle2, { className: "h-3 w-3" })) : (_jsx(XCircle, { className: "h-3 w-3" })), enabled ? t("badgeActive") : t("badgeInactive")] })] })] }) })] }) }));
25
27
  };
@@ -10,9 +10,9 @@ import { useWorkspaceModule } from "./context";
10
10
  import { useTranslations } from "next-intl";
11
11
  import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
12
12
  import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
13
- import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
13
+ import { useEnhancedCombobox } from "@appcorp/shadcn/hooks/use-enhanced-combobox";
14
14
  import { Shield, Eye, EyeOff } from "lucide-react";
15
- import { secretKeys } from "./constants";
15
+ import { secretKeys, WORKSPACE_API_ROUTES } from "./constants";
16
16
  const SecretsEditor = ({ secrets, errors, onChange, secretPlaceholder, }) => {
17
17
  const [visibleMap, setVisibleMap] = useState(() => Object.fromEntries(secretKeys.map((k) => [k, false])));
18
18
  const toggle = (key) => setVisibleMap((s) => (Object.assign(Object.assign({}, s), { [key]: !s[key] })));
@@ -31,10 +31,20 @@ export const WorkspaceForm = () => {
31
31
  const handleSecretChange = (key, value) => {
32
32
  handleChange("secrets", Object.assign(Object.assign({}, secrets), { [key]: value }));
33
33
  };
34
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(EnhancedInput, { id: "name", label: t("formNameLabel"), value: state.name, onChange: (e) => handleChange("name", e.target.value), error: state.errors.name, placeholder: t("formNamePlaceholder"), required: true, readOnly: true }), _jsx(EnhancedInput, { id: "subdomain", label: t("formSubdomainLabel"), value: state.subdomain, onChange: (e) => handleChange("subdomain", e.target.value), error: state.errors.subdomain, placeholder: t("formSubdomainPlaceholder"), required: true, info: t("formSubdomainUrlInfo", { subdomain: state.subdomain }), readOnly: true }), _jsx(EnhancedTextarea, { id: "description", label: t("formDescriptionLabel"), value: state.description || "", onChange: (e) => handleChange("description", e.target.value), error: state.errors.description, placeholder: t("formDescriptionPlaceholder"), rows: 3 }), _jsx(EnhancedInput, { id: "logo", label: t("formLogoLabel"), value: state.logo || "", onChange: (e) => handleChange("logo", e.target.value), error: state.errors.logo, placeholder: t("formLogoPlaceholder"), type: "url", readOnly: true }), _jsx(EnhancedCombobox, { id: "planId", label: t("formPlanLabel"), value: state.planId || "", onValueChange: (value) => handleChange("planId", value), options: plans
35
- .filter(({ name }) => name.match("PKR"))
36
- .map((plan) => ({
37
- label: plan.name,
38
- value: plan.id,
39
- })), error: state.errors.planId, placeholder: t("formPlanPlaceholder"), info: t("formPlanInfo") }), _jsxs("div", { className: "mt-6 border-t pt-6", children: [_jsxs("div", { className: "mb-4 flex items-center gap-2", children: [_jsx(Shield, { className: "text-primary h-5 w-5" }), _jsx("h3", { className: "text-lg font-semibold", children: t("formSecretsTitleLabel") })] }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: t("formSecretsDescriptionLabel") }), _jsx(SecretsEditor, { secrets: secrets, errors: state.errors, onChange: handleSecretChange, secretPlaceholder: t("formSecretPlaceholder") })] })] }));
34
+ const { enhancedComboboxElement: planIdCombo } = useEnhancedCombobox({
35
+ id: "planId",
36
+ label: t("formPlanLabel"),
37
+ value: state.planId || "",
38
+ onValueChange: (value) => handleChange("planId", value),
39
+ options: plans
40
+ .filter(({ name }) => name.match("PKR"))
41
+ .map((plan) => ({
42
+ id: plan.id,
43
+ name: plan.name,
44
+ })),
45
+ placeholder: t("formPlanPlaceholder"),
46
+ info: t("formPlanInfo"),
47
+ searchEndpoint: WORKSPACE_API_ROUTES.UNIT,
48
+ });
49
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(EnhancedInput, { id: "name", label: t("formNameLabel"), value: state.name, onChange: (e) => handleChange("name", e.target.value), error: state.errors.name, placeholder: t("formNamePlaceholder"), required: true, readOnly: true }), _jsx(EnhancedInput, { id: "subdomain", label: t("formSubdomainLabel"), value: state.subdomain, onChange: (e) => handleChange("subdomain", e.target.value), error: state.errors.subdomain, placeholder: t("formSubdomainPlaceholder"), required: true, info: t("formSubdomainUrlInfo", { subdomain: state.subdomain }), readOnly: true }), _jsx(EnhancedTextarea, { id: "description", label: t("formDescriptionLabel"), value: state.description || "", onChange: (e) => handleChange("description", e.target.value), error: state.errors.description, placeholder: t("formDescriptionPlaceholder"), rows: 3 }), _jsx(EnhancedInput, { id: "logo", label: t("formLogoLabel"), value: state.logo || "", onChange: (e) => handleChange("logo", e.target.value), error: state.errors.logo, placeholder: t("formLogoPlaceholder"), type: "url", readOnly: true }), planIdCombo, _jsxs("div", { className: "mt-6 border-t pt-6", children: [_jsxs("div", { className: "mb-4 flex items-center gap-2", children: [_jsx(Shield, { className: "text-primary h-5 w-5" }), _jsx("h3", { className: "text-lg font-semibold", children: t("formSecretsTitleLabel") })] }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: t("formSecretsDescriptionLabel") }), _jsx(SecretsEditor, { secrets: secrets, errors: state.errors, onChange: handleSecretChange, secretPlaceholder: t("formSecretPlaceholder") })] })] }));
40
50
  };
@@ -9,7 +9,7 @@
9
9
  * Key responsibilities:
10
10
  * - Expose `useWorkspaceUserModule()` 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
  * - `WorkspaceUserProvider` — provider component used by the page
@@ -150,6 +150,7 @@ export declare const useWorkspaceUserModule: () => {
150
150
  icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
151
151
  }[];
152
152
  listLoading: boolean;
153
+ resetRecordFormState: () => void;
153
154
  rowActions: RowAction[];
154
155
  updateLoading: boolean;
155
156
  state: {
@@ -9,7 +9,7 @@
9
9
  * Key responsibilities:
10
10
  * - Expose `useWorkspaceUserModule()` 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
  * - `WorkspaceUserProvider` — provider component used by the page
@@ -26,7 +26,6 @@ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
26
26
  import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
27
27
  import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
28
28
  import { WORKSPACE_USER_API_ROUTES, pageLimit } from "./constants";
29
- import { getCachedWorkspaceUsers, invalidateWorkspaceUsersCache, } from "./cache";
30
29
  import { workspaceUserFormValidation } from "./validate";
31
30
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
32
31
  import { useTranslations } from "next-intl";
@@ -111,10 +110,6 @@ export const useWorkspaceUserModule = () => {
111
110
  }), [state.enabled, state.id, state.roleId, state.userId, workspace === null || workspace === void 0 ? void 0 : workspace.id]);
112
111
  const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
113
112
  const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
114
- const isDefaultListState = state.currentPage === 1 &&
115
- state.pageLimit === pageLimit &&
116
- !debouncedQuery &&
117
- state.filterEnabled === undefined;
118
113
  // ============================================================================
119
114
  // 1.4.3 UTILITIES
120
115
  // ============================================================================
@@ -139,6 +134,31 @@ export const useWorkspaceUserModule = () => {
139
134
  payload: { drawer: null },
140
135
  });
141
136
  }, [dispatch]);
137
+ const resetRecordFormState = useCallback(() => {
138
+ dispatch({
139
+ type: WORKSPACE_USER_ACTION_TYPES.SET_ERRORS,
140
+ payload: { errors: {} },
141
+ });
142
+ dispatch({
143
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
144
+ payload: { disabled: false },
145
+ });
146
+ dispatch({
147
+ type: WORKSPACE_USER_ACTION_TYPES.SET_FORM_DATA,
148
+ payload: {
149
+ form: {
150
+ enabled: true,
151
+ filterEnabled: undefined,
152
+ id: "",
153
+ roleId: null,
154
+ userId: "",
155
+ workspaceId: "",
156
+ role: undefined,
157
+ workspace: undefined,
158
+ },
159
+ },
160
+ });
161
+ }, [dispatch]);
142
162
  // ============================================================================
143
163
  // 1.4.4 API CALLBACKS
144
164
  // ============================================================================
@@ -167,7 +187,6 @@ export const useWorkspaceUserModule = () => {
167
187
  }
168
188
  if (data) {
169
189
  showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
170
- invalidateWorkspaceUsersCache();
171
190
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
172
191
  resetFormAndCloseDrawer();
173
192
  }
@@ -192,7 +211,6 @@ export const useWorkspaceUserModule = () => {
192
211
  }
193
212
  if (data) {
194
213
  showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
195
- invalidateWorkspaceUsersCache();
196
214
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
197
215
  }
198
216
  }, [showToast, t]);
@@ -338,14 +356,14 @@ export const useWorkspaceUserModule = () => {
338
356
  {
339
357
  enabled: false,
340
358
  handleOnClick: handleFilters,
341
- label: t("actionHeaderFilters"),
359
+ label: t("actionsButtonFilters"),
342
360
  order: 1,
343
361
  icon: Filter,
344
362
  },
345
363
  {
346
364
  enabled: false,
347
365
  handleOnClick: handleMoreActions,
348
- label: t("actionHeaderMoreActions"),
366
+ label: t("actionsButtonMoreActions"),
349
367
  order: 2,
350
368
  icon: Plus,
351
369
  },
@@ -354,7 +372,7 @@ export const useWorkspaceUserModule = () => {
354
372
  {
355
373
  enabled: true,
356
374
  handleOnClick: handleView,
357
- label: t("actionRowView"),
375
+ label: t("actionsButtonView"),
358
376
  order: 1,
359
377
  icon: Eye,
360
378
  },
@@ -366,25 +384,8 @@ export const useWorkspaceUserModule = () => {
366
384
  var _a;
367
385
  if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
368
386
  return;
369
- if (isDefaultListState) {
370
- (async () => {
371
- try {
372
- const { count, items } = await getCachedWorkspaceUsers({
373
- params: listParams,
374
- });
375
- dispatch({
376
- type: WORKSPACE_USER_ACTION_TYPES.SET_ITEMS,
377
- payload: { items: items || [], count: count || 0 },
378
- });
379
- }
380
- catch (_a) {
381
- showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
382
- }
383
- })();
384
- return;
385
- }
386
387
  (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
387
- }, [dispatch, isDefaultListState, listParams, workspace === null || workspace === void 0 ? void 0 : workspace.id, showToast, t]);
388
+ }, [dispatch, listParams, workspace === null || workspace === void 0 ? void 0 : workspace.id, showToast, t]);
388
389
  // ============================================================================
389
390
  // 1.4.10 RETURN
390
391
  // ============================================================================
@@ -406,6 +407,7 @@ export const useWorkspaceUserModule = () => {
406
407
  handleView,
407
408
  headerActions,
408
409
  listLoading,
410
+ resetRecordFormState,
409
411
  rowActions,
410
412
  updateLoading });
411
413
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.2.40",
3
+ "version": "0.2.44",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",