@appcorp/fusion-storybook 0.1.21 → 0.1.23

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 (62) hide show
  1. package/base-modules/teacher/cache.d.ts +14 -0
  2. package/base-modules/teacher/cache.js +31 -0
  3. package/base-modules/teacher/constants.d.ts +23 -0
  4. package/base-modules/teacher/constants.js +27 -0
  5. package/base-modules/teacher/context.d.ts +245 -0
  6. package/base-modules/teacher/context.js +461 -0
  7. package/base-modules/teacher/filter.d.ts +1 -0
  8. package/base-modules/teacher/filter.js +29 -0
  9. package/base-modules/teacher/form.d.ts +1 -0
  10. package/base-modules/teacher/form.js +30 -0
  11. package/base-modules/teacher/more-actions.d.ts +1 -0
  12. package/base-modules/teacher/more-actions.js +50 -0
  13. package/base-modules/teacher/page.d.ts +32 -0
  14. package/base-modules/teacher/page.js +141 -0
  15. package/base-modules/teacher/validate.d.ts +26 -0
  16. package/base-modules/teacher/validate.js +37 -0
  17. package/base-modules/teacher/view.d.ts +1 -0
  18. package/base-modules/teacher/view.js +27 -0
  19. package/base-modules/user/cache.d.ts +14 -0
  20. package/base-modules/user/cache.js +31 -0
  21. package/base-modules/user/constants.d.ts +9 -0
  22. package/base-modules/user/constants.js +19 -0
  23. package/base-modules/user/context.d.ts +218 -0
  24. package/base-modules/user/context.js +585 -0
  25. package/base-modules/user/drawer.d.ts +1 -0
  26. package/base-modules/user/drawer.js +25 -0
  27. package/base-modules/user/filter.d.ts +1 -0
  28. package/base-modules/user/filter.js +21 -0
  29. package/base-modules/user/form.d.ts +1 -0
  30. package/base-modules/user/form.js +28 -0
  31. package/base-modules/user/more-actions.d.ts +1 -0
  32. package/base-modules/user/more-actions.js +48 -0
  33. package/base-modules/user/page.d.ts +30 -0
  34. package/base-modules/user/page.js +120 -0
  35. package/base-modules/user/validate.d.ts +16 -0
  36. package/base-modules/user/validate.js +29 -0
  37. package/base-modules/user/view.d.ts +1 -0
  38. package/base-modules/user/view.js +24 -0
  39. package/base-modules/workspace-user/cache.d.ts +14 -0
  40. package/base-modules/workspace-user/cache.js +31 -0
  41. package/base-modules/workspace-user/constants.d.ts +21 -0
  42. package/base-modules/workspace-user/constants.js +27 -0
  43. package/base-modules/workspace-user/context.d.ts +155 -0
  44. package/base-modules/workspace-user/context.js +382 -0
  45. package/base-modules/workspace-user/filter.d.ts +1 -0
  46. package/base-modules/workspace-user/filter.js +23 -0
  47. package/base-modules/workspace-user/form.d.ts +1 -0
  48. package/base-modules/workspace-user/form.js +12 -0
  49. package/base-modules/workspace-user/more-actions.d.ts +1 -0
  50. package/base-modules/workspace-user/more-actions.js +51 -0
  51. package/base-modules/workspace-user/page.d.ts +28 -0
  52. package/base-modules/workspace-user/page.js +106 -0
  53. package/base-modules/workspace-user/validate.d.ts +12 -0
  54. package/base-modules/workspace-user/validate.js +15 -0
  55. package/base-modules/workspace-user/view.d.ts +1 -0
  56. package/base-modules/workspace-user/view.js +20 -0
  57. package/components/timeline.d.ts +11 -0
  58. package/components/timeline.js +4 -0
  59. package/package.json +4 -2
  60. package/tsconfig.build.tsbuildinfo +1 -1
  61. package/utils/toast-network-error.d.ts +1 -0
  62. package/utils/toast-network-error.js +7 -0
@@ -0,0 +1,382 @@
1
+ "use client";
2
+ /**
3
+ * WorkspaceUser Module — business logic + API integration
4
+ *
5
+ * This module wires the generic module state (created by
6
+ * `createGenericModule`) to network actions using `useModuleEntityV2`.
7
+ * It contains the domain-specific handlers (create/view/edit/delete),
8
+ * local validation wiring, and optimistic UI helpers.
9
+ *
10
+ * Key responsibilities:
11
+ * - expose `useWorkspaceUserModule()` which UI components call for actions
12
+ * - keep the module-specific `apiParams` and callbacks in one place
13
+ * - ensure cache invalidation and toast notifications on mutation
14
+ *
15
+ * Exported utilities:
16
+ * - `WorkspaceUserProvider` — provider component used by the page
17
+ * - `WorkspaceUserStateContextProvider` — backward-compat alias for dashboard-providers.tsx
18
+ * - `useWorkspaceUserModule()` — hook that returns handlers and state
19
+ */
20
+ import { useCallback, useEffect, useMemo } from "react";
21
+ import { isCreatedOrUpdated, validateForm, } from "@react-pakistan/util-functions";
22
+ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
23
+ import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
24
+ import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
25
+ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
26
+ import { WORKSPACE_USER_API_ROUTES, pageLimit } from "./constants";
27
+ import { getCachedWorkspaceUsers, invalidateWorkspaceUsersCache, } from "./cache";
28
+ import { workspaceUserFormValidation } from "./validate";
29
+ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
30
+ import { useTranslations } from "next-intl";
31
+ import { getCachedWorkspaceSync } from "../workspace/cache";
32
+ import { Eye, Filter, Plus } from "lucide-react";
33
+ // ============================================================================
34
+ // DRAWER TYPES
35
+ // ============================================================================
36
+ export const WORKSPACE_USER_DRAWER = {
37
+ FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
38
+ FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
39
+ MORE_ACTIONS_DRAWER: "MORE_ACTIONS_DRAWER",
40
+ VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
41
+ };
42
+ // ============================================================================
43
+ // CONFIGURATION
44
+ // ============================================================================
45
+ const workspaceUserConfig = {
46
+ name: "WorkspaceUser",
47
+ displayName: "Workspace User",
48
+ initialState: {
49
+ // List Data
50
+ items: [],
51
+ count: 0,
52
+ // Search & Pagination
53
+ currentPage: 1,
54
+ pageLimit,
55
+ searchQuery: "",
56
+ // UI State
57
+ disableSaveButton: false,
58
+ drawer: null,
59
+ // Form State
60
+ enabled: true,
61
+ errors: {},
62
+ filterEnabled: undefined,
63
+ id: "",
64
+ roleId: null,
65
+ userId: "",
66
+ workspaceId: "",
67
+ // Relations (populated from byId fetch)
68
+ role: undefined,
69
+ workspace: undefined,
70
+ },
71
+ drawerTypes: DRAWER_TYPES,
72
+ };
73
+ // ============================================================================
74
+ // CREATE WORKSPACE USER MODULE
75
+ // ============================================================================
76
+ export const { actionTypes: WORKSPACE_USER_ACTION_TYPES, config: workspaceUserModuleConfig, initialState: initialWorkspaceUserState, Provider: WorkspaceUserProvider, reducer: workspaceUserReducer, useContext: useWorkspaceUserContext, } = createGenericModule(workspaceUserConfig);
77
+ // ============================================================================
78
+ // ENHANCED WORKSPACE USER HOOK WITH API INTEGRATION
79
+ // ============================================================================
80
+ export const useWorkspaceUserModule = () => {
81
+ var _a;
82
+ const context = useWorkspaceUserContext();
83
+ const { state, dispatch } = context;
84
+ const t = useTranslations("workspaceUser");
85
+ const debouncedQuery = useDebounce(state.searchQuery, 800);
86
+ const workspace = getCachedWorkspaceSync();
87
+ const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
88
+ // ============================================================================
89
+ // API PARAMETERS
90
+ // ============================================================================
91
+ const listParams = useMemo(() => (Object.assign(Object.assign({ currentPage: state.currentPage, pageLimit: state.pageLimit, schoolId }, (debouncedQuery ? { searchQuery: debouncedQuery } : {})), (state.filterEnabled !== undefined
92
+ ? { filterEnabled: String(state.filterEnabled) }
93
+ : {}))), [
94
+ state.currentPage,
95
+ state.filterEnabled,
96
+ state.pageLimit,
97
+ debouncedQuery,
98
+ schoolId,
99
+ ]);
100
+ const updateParams = useMemo(() => ({
101
+ enabled: state.enabled,
102
+ id: state.id,
103
+ roleId: state.roleId,
104
+ schoolId,
105
+ userId: state.userId,
106
+ workspaceId: state.workspaceId,
107
+ }), [state, schoolId]);
108
+ const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
109
+ const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
110
+ // ============================================================================
111
+ // UTILITIES
112
+ // ============================================================================
113
+ const showToast = useCallback((message, variant) => {
114
+ generateThemeToast({ description: message, variant });
115
+ }, []);
116
+ const resetFormAndCloseDrawer = useCallback(() => {
117
+ dispatch({
118
+ type: WORKSPACE_USER_ACTION_TYPES.SET_ERRORS,
119
+ payload: { errors: {} },
120
+ });
121
+ dispatch({
122
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
123
+ payload: { disabled: false },
124
+ });
125
+ dispatch({
126
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DRAWER,
127
+ payload: { drawer: null },
128
+ });
129
+ }, [dispatch]);
130
+ // ============================================================================
131
+ // API CALLBACKS
132
+ // ============================================================================
133
+ const listCallback = ({ data, error }) => {
134
+ var _a, _b;
135
+ if (error) {
136
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
137
+ return;
138
+ }
139
+ if (data) {
140
+ const response = data;
141
+ const items = (_a = response.items) !== null && _a !== void 0 ? _a : [];
142
+ const count = (_b = response.count) !== null && _b !== void 0 ? _b : 0;
143
+ context.dispatch({
144
+ type: WORKSPACE_USER_ACTION_TYPES.SET_ITEMS,
145
+ payload: { items, count },
146
+ });
147
+ }
148
+ };
149
+ const updateCallback = ({ data, error }) => {
150
+ const isCreated = isCreatedOrUpdated(data);
151
+ if (error) {
152
+ showToast(isCreated ? t("messagesCreateFailed") : t("messagesUpdateFailed"), TOAST_VARIANT.ERROR);
153
+ return;
154
+ }
155
+ if (data) {
156
+ showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
157
+ invalidateWorkspaceUsersCache();
158
+ listFetchNow();
159
+ resetFormAndCloseDrawer();
160
+ }
161
+ };
162
+ const byIdCallback = ({ data, error }) => {
163
+ if (error) {
164
+ showToast(t("messagesDetailsFetchFailed"), TOAST_VARIANT.ERROR);
165
+ return;
166
+ }
167
+ if (data) {
168
+ context.dispatch({
169
+ type: WORKSPACE_USER_ACTION_TYPES.SET_FORM_DATA,
170
+ payload: { form: Object.assign(Object.assign({}, data), { filterEnabled: undefined }) },
171
+ });
172
+ }
173
+ };
174
+ const deleteCallback = ({ data, error }) => {
175
+ if (error) {
176
+ showToast(t("messagesDeleteFailed"), TOAST_VARIANT.ERROR);
177
+ return;
178
+ }
179
+ if (data) {
180
+ showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
181
+ invalidateWorkspaceUsersCache();
182
+ listFetchNow();
183
+ }
184
+ };
185
+ // ============================================================================
186
+ // API HOOKS
187
+ // ============================================================================
188
+ const { listFetchNow, listLoading, updateFetchNow, updateLoading, deleteFetchNow, deleteLoading, byIdLoading, } = useModuleEntityV2({
189
+ byIdCallback,
190
+ byIdParams,
191
+ deleteCallback,
192
+ deleteParams,
193
+ listCallback,
194
+ listParams,
195
+ listUrl: WORKSPACE_USER_API_ROUTES.UNIT,
196
+ searchQuery: debouncedQuery,
197
+ unitByIdUrl: WORKSPACE_USER_API_ROUTES.UNIT,
198
+ unitUrl: WORKSPACE_USER_API_ROUTES.UNIT,
199
+ updateCallback,
200
+ updateParams,
201
+ headers: {
202
+ "Content-Type": "application/json",
203
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
204
+ },
205
+ });
206
+ // ============================================================================
207
+ // HANDLERS
208
+ // ============================================================================
209
+ const handleChange = useCallback((field, value) => {
210
+ dispatch({
211
+ type: WORKSPACE_USER_ACTION_TYPES.SET_INPUT_FIELD,
212
+ payload: { field, value },
213
+ });
214
+ }, [dispatch]);
215
+ const handleFilters = useCallback(() => {
216
+ dispatch({
217
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DRAWER,
218
+ payload: { drawer: WORKSPACE_USER_DRAWER.FILTER_DRAWER },
219
+ });
220
+ }, [dispatch]);
221
+ const handleMoreActions = useCallback(() => {
222
+ dispatch({
223
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DRAWER,
224
+ payload: { drawer: WORKSPACE_USER_DRAWER.FORM_DRAWER },
225
+ });
226
+ }, [dispatch]);
227
+ const handleView = useCallback(() => {
228
+ dispatch({
229
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DRAWER,
230
+ payload: { drawer: WORKSPACE_USER_DRAWER.VIEW_DRAWER },
231
+ });
232
+ }, [dispatch]);
233
+ const handleSearch = useCallback((query) => {
234
+ dispatch({
235
+ type: WORKSPACE_USER_ACTION_TYPES.SET_SEARCH_QUERY,
236
+ payload: { searchQuery: query },
237
+ });
238
+ }, [dispatch]);
239
+ const handleCloseDrawer = useCallback(() => {
240
+ dispatch({
241
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DRAWER,
242
+ payload: { drawer: null },
243
+ });
244
+ }, [dispatch]);
245
+ const applyFilters = useCallback(() => {
246
+ dispatch({
247
+ type: WORKSPACE_USER_ACTION_TYPES.SET_CURRENT_PAGE,
248
+ payload: { currentPage: 1 },
249
+ });
250
+ listFetchNow();
251
+ handleCloseDrawer();
252
+ }, [dispatch, listFetchNow, handleCloseDrawer]);
253
+ const clearFilters = useCallback(() => {
254
+ dispatch({
255
+ type: WORKSPACE_USER_ACTION_TYPES.SET_FILTERS,
256
+ payload: { filters: { filterEnabled: undefined } },
257
+ });
258
+ dispatch({
259
+ type: WORKSPACE_USER_ACTION_TYPES.SET_CURRENT_PAGE,
260
+ payload: { currentPage: 1 },
261
+ });
262
+ }, [dispatch]);
263
+ const handleDelete = useCallback((row) => {
264
+ if (row === null || row === void 0 ? void 0 : row.id) {
265
+ deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
266
+ body: JSON.stringify({ id: row.id }),
267
+ });
268
+ }
269
+ }, [deleteFetchNow]);
270
+ const handleCreate = useCallback(() => { }, []);
271
+ const handleEdit = useCallback(() => { }, []);
272
+ const handlePageChange = useCallback((page) => {
273
+ dispatch({
274
+ type: WORKSPACE_USER_ACTION_TYPES.SET_CURRENT_PAGE,
275
+ payload: { currentPage: page },
276
+ });
277
+ }, [dispatch]);
278
+ const handlePageLimitChange = useCallback((limit) => {
279
+ dispatch({
280
+ type: WORKSPACE_USER_ACTION_TYPES.SET_PAGE_LIMIT,
281
+ payload: { pageLimit: limit },
282
+ });
283
+ }, [dispatch]);
284
+ const handleSubmit = useCallback(() => {
285
+ dispatch({
286
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
287
+ payload: { disabled: true },
288
+ });
289
+ validateForm({
290
+ params: updateParams,
291
+ schema: workspaceUserFormValidation,
292
+ successCallback: () => {
293
+ updateFetchNow(undefined, {
294
+ body: JSON.stringify(updateParams),
295
+ });
296
+ },
297
+ errorCallback: (errors) => {
298
+ dispatch({
299
+ type: WORKSPACE_USER_ACTION_TYPES.SET_ERRORS,
300
+ payload: { errors },
301
+ });
302
+ dispatch({
303
+ type: WORKSPACE_USER_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
304
+ payload: { disabled: false },
305
+ });
306
+ showToast(t("messagesFormErrors"), TOAST_VARIANT.ERROR);
307
+ },
308
+ });
309
+ }, [dispatch, updateParams, updateFetchNow, t, showToast]);
310
+ // ============================================================================
311
+ // HEADER & ROW ACTIONS
312
+ // ============================================================================
313
+ const headerActions = useMemo(() => [
314
+ {
315
+ enabled: true,
316
+ handleOnClick: handleFilters,
317
+ label: t("headerActionsFilters"),
318
+ order: 1,
319
+ icon: Filter,
320
+ },
321
+ {
322
+ enabled: true,
323
+ handleOnClick: handleMoreActions,
324
+ label: t("headerActionsMoreActions"),
325
+ order: 2,
326
+ icon: Plus,
327
+ },
328
+ ], [handleFilters, handleMoreActions, t]);
329
+ const rowActions = useMemo(() => [
330
+ {
331
+ enabled: true,
332
+ handleOnClick: handleView,
333
+ label: t("rowActionsView"),
334
+ order: 1,
335
+ icon: Eye,
336
+ },
337
+ ], [handleView, t]);
338
+ // ============================================================================
339
+ // EFFECTS
340
+ // ============================================================================
341
+ useEffect(() => {
342
+ if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
343
+ return;
344
+ (async () => {
345
+ try {
346
+ const { count, items } = await getCachedWorkspaceUsers({
347
+ params: listParams,
348
+ });
349
+ context.dispatch({
350
+ type: WORKSPACE_USER_ACTION_TYPES.SET_ITEMS,
351
+ payload: { items: items || [], count: count || 0 },
352
+ });
353
+ }
354
+ catch (_a) {
355
+ showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
356
+ }
357
+ })();
358
+ }, [listParams, context, showToast, t, workspace === null || workspace === void 0 ? void 0 : workspace.id]);
359
+ // ============================================================================
360
+ // RETURN
361
+ // ============================================================================
362
+ return Object.assign(Object.assign({}, context), { applyFilters,
363
+ byIdLoading,
364
+ clearFilters,
365
+ deleteLoading,
366
+ handleChange,
367
+ handleCloseDrawer,
368
+ handleCreate,
369
+ handleDelete,
370
+ handleEdit,
371
+ handleFilters,
372
+ handleMoreActions,
373
+ handlePageChange,
374
+ handlePageLimitChange,
375
+ handleSearch,
376
+ handleSubmit,
377
+ handleView,
378
+ headerActions,
379
+ listLoading,
380
+ rowActions,
381
+ updateLoading });
382
+ };
@@ -0,0 +1 @@
1
+ export declare const WorkspaceUserFilter: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
4
+ import { useWorkspaceUserModule } from "./context";
5
+ import { useTranslations } from "next-intl";
6
+ export const WorkspaceUserFilter = () => {
7
+ const { state, handleChange } = useWorkspaceUserModule();
8
+ const { filterEnabled } = state;
9
+ const t = useTranslations("workspaceUser");
10
+ const filterEnabledValue = filterEnabled === undefined
11
+ ? "undefined"
12
+ : filterEnabled
13
+ ? "true"
14
+ : "false";
15
+ return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "grid grid-cols-1 gap-4", children: _jsx(EnhancedRadio, { label: t("enabled"), name: "filterEnabled", value: filterEnabledValue, options: [
16
+ { label: t("all"), value: "undefined" },
17
+ { label: t("enabled"), value: "true" },
18
+ { label: t("disabled"), value: "false" },
19
+ ], onValueChange: (next) => {
20
+ const parsed = next === "true" ? true : next === "false" ? false : undefined;
21
+ handleChange("filterEnabled", parsed);
22
+ } }) }) }));
23
+ };
@@ -0,0 +1 @@
1
+ export declare const WorkspaceUserForm: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
4
+ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
5
+ import { useWorkspaceUserModule } from "./context";
6
+ import { useTranslations } from "next-intl";
7
+ export const WorkspaceUserForm = () => {
8
+ const { state, handleChange } = useWorkspaceUserModule();
9
+ const { enabled, errors, roleId, userId, workspaceId } = state;
10
+ const t = useTranslations("workspaceUser");
11
+ return (_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("workspaceUserInformation") }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.workspaceId, id: "workspaceId", info: t("enterTheWorkspaceId"), label: t("workspaceId"), onChange: (e) => handleChange("workspaceId", e.target.value), placeholder: t("enterWorkspaceId"), required: true, value: workspaceId }), _jsx(EnhancedInput, { error: errors.userId, id: "userId", info: t("enterTheUserId"), label: t("userId"), onChange: (e) => handleChange("userId", e.target.value), placeholder: t("enterUserId"), required: true, value: userId }), _jsx(EnhancedInput, { error: errors.roleId, id: "roleId", info: t("enterTheRoleId"), label: t("roleId"), onChange: (e) => handleChange("roleId", e.target.value), placeholder: t("enterRoleId"), value: roleId || "" })] }), _jsx(EnhancedCheckbox, { defaultChecked: enabled, label: t("active"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }) }));
12
+ };
@@ -0,0 +1 @@
1
+ export declare const WorkspaceUserMoreActions: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { API_METHODS } from "@react-pakistan/util-functions";
4
+ import { getCachedWorkspaceSync } from "../workspace/cache";
5
+ import converter from "json-2-csv";
6
+ import { Timeline } from "../../components/timeline";
7
+ import { downloadFromUrl } from "@react-pakistan/util-functions";
8
+ import { useTranslations } from "next-intl";
9
+ export const WorkspaceUserMoreActions = () => {
10
+ const t = useTranslations("workspaceUser");
11
+ const workspace = getCachedWorkspaceSync();
12
+ const handleGetAllRecords = async (schoolId) => {
13
+ const response = await fetch(`/api/workspace-user?pageLimit=1000&currentPage=1&schoolId=${schoolId}`, { method: API_METHODS.GET });
14
+ const result = await response.json();
15
+ const csv = await converter.json2csv(result.items || [], {});
16
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
17
+ await downloadFromUrl(blob, "workspace-user.csv");
18
+ };
19
+ const create = [
20
+ {
21
+ id: "1",
22
+ title: t("downloadEmptyCsvTemplate"),
23
+ handleOnClick: async () => {
24
+ await downloadFromUrl("https://nwolvgylwmjuqxsngjxt.supabase.co/storage/v1/object/public/public-blob/common-assets/workspace-user.csv", "workspace-user-template.csv");
25
+ },
26
+ },
27
+ { id: "2", title: t("addYourDataToTheCsv") },
28
+ {
29
+ id: "3",
30
+ title: t("uploadTheCompletedCsvToTheSystem"),
31
+ handleOnClick: () => console.log("clicked"),
32
+ },
33
+ ];
34
+ const update = [
35
+ {
36
+ id: "1",
37
+ title: t("downloadPopulatedCsvTemplate"),
38
+ handleOnClick: async () => {
39
+ var _a;
40
+ await handleGetAllRecords(((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "");
41
+ },
42
+ },
43
+ { id: "2", title: t("updateYourDataInTheCsv") },
44
+ {
45
+ id: "3",
46
+ title: t("uploadTheCompletedCsvToTheSystem"),
47
+ handleOnClick: () => console.log("clicked"),
48
+ },
49
+ ];
50
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(Timeline, { events: create, heading: t("bulkCreate") }), _jsx(Timeline, { events: update, heading: t("bulkUpdate") })] }));
51
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * WorkspaceUser Page Component
3
+ *
4
+ * Thin wrapper around GenericModulePage. All handlers, header actions, and row
5
+ * actions live in context.tsx — this file only owns:
6
+ * - tableBodyCols (static, module-level constant)
7
+ * - workspaceUserConfig (memoised on locale change only)
8
+ * - permission guard
9
+ */
10
+ import { FC } from "react";
11
+ import { USER_ROLE } from "../../type";
12
+ interface Props {
13
+ cancelLabel: string;
14
+ drawerTitle: string;
15
+ saveLabel: string;
16
+ searchPlaceholder: string;
17
+ tableDescription: string;
18
+ tableTitle: string;
19
+ labelActions: string;
20
+ labelId: string;
21
+ labelRole: string;
22
+ labelSystemRole: string;
23
+ labelUser: string;
24
+ labelWorkspace: string;
25
+ userRole: USER_ROLE;
26
+ }
27
+ export declare const WorkspaceUserPage: FC<Props>;
28
+ export {};
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * WorkspaceUser Page Component
5
+ *
6
+ * Thin wrapper around GenericModulePage. All handlers, header actions, and row
7
+ * actions live in context.tsx — this file only owns:
8
+ * - tableBodyCols (static, module-level constant)
9
+ * - workspaceUserConfig (memoised on locale change only)
10
+ * - permission guard
11
+ */
12
+ import { useMemo } from "react";
13
+ import { COMPONENT_TYPE } from "@appcorp/shadcn/components/enhanced-table";
14
+ import { createGenericModulePage, } from "@react-pakistan/util-functions/factory/generic-component-factory";
15
+ import { useWorkspaceUserModule, WorkspaceUserProvider, WORKSPACE_USER_DRAWER, WORKSPACE_USER_ACTION_TYPES, } from "./context";
16
+ import { WorkspaceUserFilter } from "./filter";
17
+ import { WorkspaceUserForm } from "./form";
18
+ import { WorkspaceUserMoreActions } from "./more-actions";
19
+ import { WorkspaceUserView } from "./view";
20
+ import { resolveRbacPermissions } from "../../utils/resolve-rbac-permissions";
21
+ import { RbacNoAccess } from "../../components/rbac-no-access";
22
+ // ============================================================================
23
+ // TABLE COLUMN CONFIGURATION (static — no runtime deps)
24
+ // ============================================================================
25
+ const tableBodyCols = [
26
+ { componentType: COMPONENT_TYPE.ID, key: "id" },
27
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["user:name"] },
28
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["workspace:name"] },
29
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["role:userRole"] },
30
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["role:isSystem"] },
31
+ { componentType: COMPONENT_TYPE.ACTIONS },
32
+ ];
33
+ // ============================================================================
34
+ // GENERIC PAGE COMPONENT
35
+ // ============================================================================
36
+ const GenericWorkspaceUserPage = createGenericModulePage();
37
+ const createWorkspaceUserConfig = ({ cancelLabel, dispatch, drawer, drawerTitle, saveLabel, searchPlaceholder, tableDescription, tableTitle, labelActions, labelId, labelRole, labelSystemRole, labelUser, labelWorkspace, }) => {
38
+ return {
39
+ moduleName: "workspaceUser",
40
+ tableColumns: [
41
+ { label: labelId, width: "5%" },
42
+ { label: labelUser, width: "30%" },
43
+ { label: labelWorkspace, width: "20%" },
44
+ { label: labelRole, width: "15%" },
45
+ { label: labelSystemRole, width: "15%" },
46
+ { label: labelActions, width: "15%" },
47
+ ],
48
+ cancelLabel,
49
+ drawerTitle,
50
+ filterContent: _jsx(WorkspaceUserFilter, {}),
51
+ formContent: _jsx(WorkspaceUserForm, {}),
52
+ moreActionsContent: _jsx(WorkspaceUserMoreActions, {}),
53
+ saveLabel,
54
+ searchPlaceholder,
55
+ tableDescription,
56
+ tableTitle,
57
+ viewContent: _jsx(WorkspaceUserView, {}),
58
+ size: drawer === WORKSPACE_USER_DRAWER.FORM_DRAWER ? "full" : "small",
59
+ onClearFilters: () => {
60
+ dispatch({ type: WORKSPACE_USER_ACTION_TYPES.RESET_FORM });
61
+ },
62
+ };
63
+ };
64
+ const WorkspaceUserPageInner = (props) => {
65
+ const context = useWorkspaceUserModule();
66
+ const workspaceUserConfig = useMemo(() => createWorkspaceUserConfig({
67
+ dispatch: context.dispatch,
68
+ drawer: context.state.drawer,
69
+ cancelLabel: props.cancelLabel,
70
+ drawerTitle: props.drawerTitle,
71
+ saveLabel: props.saveLabel,
72
+ searchPlaceholder: props.searchPlaceholder,
73
+ tableDescription: props.tableDescription,
74
+ tableTitle: props.tableTitle,
75
+ labelActions: props.labelActions,
76
+ labelId: props.labelId,
77
+ labelRole: props.labelRole,
78
+ labelSystemRole: props.labelSystemRole,
79
+ labelUser: props.labelUser,
80
+ labelWorkspace: props.labelWorkspace,
81
+ }), [
82
+ context.dispatch,
83
+ context.state.drawer,
84
+ props.cancelLabel,
85
+ props.drawerTitle,
86
+ props.saveLabel,
87
+ props.searchPlaceholder,
88
+ props.tableDescription,
89
+ props.tableTitle,
90
+ props.labelActions,
91
+ props.labelId,
92
+ props.labelRole,
93
+ props.labelSystemRole,
94
+ props.labelUser,
95
+ props.labelWorkspace,
96
+ ]);
97
+ const hasPermission = resolveRbacPermissions({
98
+ userRole: props.userRole,
99
+ moduleName: "People Management",
100
+ });
101
+ if (!hasPermission) {
102
+ return _jsx(RbacNoAccess, { moduleName: "People Management" });
103
+ }
104
+ return (_jsx("div", { className: "p-4", children: _jsx(GenericWorkspaceUserPage, { overrideConfig: workspaceUserConfig, context: context, tableBodyCols: tableBodyCols }) }));
105
+ };
106
+ export const WorkspaceUserPage = (props) => (_jsx(WorkspaceUserProvider, { children: _jsx(WorkspaceUserPageInner, Object.assign({}, props)) }));
@@ -0,0 +1,12 @@
1
+ /**
2
+ * WorkspaceUser Validation Schema
3
+ *
4
+ * Zod validation schemas for workspace-user form data.
5
+ */
6
+ import { z } from "zod";
7
+ export declare const workspaceUserFormValidation: z.ZodObject<{
8
+ workspaceId: z.ZodString;
9
+ userId: z.ZodString;
10
+ roleId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
11
+ enabled: z.ZodOptional<z.ZodBoolean>;
12
+ }, z.core.$strip>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * WorkspaceUser Validation Schema
3
+ *
4
+ * Zod validation schemas for workspace-user form data.
5
+ */
6
+ import { z } from "zod";
7
+ // ============================================================================
8
+ // VALIDATION SCHEMA
9
+ // ============================================================================
10
+ export const workspaceUserFormValidation = z.object({
11
+ workspaceId: z.string().min(1, "workspaceIsRequired"),
12
+ userId: z.string().min(1, "userIsRequired"),
13
+ roleId: z.string().nullable().optional(),
14
+ enabled: z.boolean().optional(),
15
+ });
@@ -0,0 +1 @@
1
+ export declare const WorkspaceUserView: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * WorkspaceUser View
5
+ *
6
+ * Read-only details view for a workspace user record.
7
+ */
8
+ import { useWorkspaceUserModule } from "./context";
9
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@appcorp/shadcn/components/ui/card";
10
+ import { Badge } from "@appcorp/shadcn/components/ui/badge";
11
+ import { Separator } from "@appcorp/shadcn/components/ui/separator";
12
+ import { Users, Building2, Shield, CheckCircle2, XCircle } from "lucide-react";
13
+ import { useTranslations } from "next-intl";
14
+ import { formatValue } from "../../utils/format-value";
15
+ export const WorkspaceUserView = () => {
16
+ const { state } = useWorkspaceUserModule();
17
+ const { enabled, role, workspace, workspaceId } = state;
18
+ const t = useTranslations("workspaceUser");
19
+ 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(Users, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("workspaceUserInformation") })] }), _jsx(CardDescription, { children: t("workspaceUserAssignmentDetails") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Building2, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("workspace") })] }), _jsx("p", { className: "text-base", children: formatValue((workspace === null || workspace === void 0 ? void 0 : workspace.name) || workspaceId) }), (workspace === null || workspace === void 0 ? void 0 : workspace.subdomain) && (_jsxs("p", { className: "text-muted-foreground text-sm", children: [t("subdomain"), workspace.subdomain] }))] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("role") })] }), _jsx(Badge, { variant: "secondary", className: "gap-1", children: (role === null || role === void 0 ? void 0 : role.userRole) || t("noRoleAssigned") })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("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 ? t("active") : t("inactive")] })] })] }) })] }) }));
20
+ };