@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.
- package/base-modules/teacher/cache.d.ts +14 -0
- package/base-modules/teacher/cache.js +31 -0
- package/base-modules/teacher/constants.d.ts +23 -0
- package/base-modules/teacher/constants.js +27 -0
- package/base-modules/teacher/context.d.ts +245 -0
- package/base-modules/teacher/context.js +461 -0
- package/base-modules/teacher/filter.d.ts +1 -0
- package/base-modules/teacher/filter.js +29 -0
- package/base-modules/teacher/form.d.ts +1 -0
- package/base-modules/teacher/form.js +30 -0
- package/base-modules/teacher/more-actions.d.ts +1 -0
- package/base-modules/teacher/more-actions.js +50 -0
- package/base-modules/teacher/page.d.ts +32 -0
- package/base-modules/teacher/page.js +141 -0
- package/base-modules/teacher/validate.d.ts +26 -0
- package/base-modules/teacher/validate.js +37 -0
- package/base-modules/teacher/view.d.ts +1 -0
- package/base-modules/teacher/view.js +27 -0
- package/base-modules/user/cache.d.ts +14 -0
- package/base-modules/user/cache.js +31 -0
- package/base-modules/user/constants.d.ts +9 -0
- package/base-modules/user/constants.js +19 -0
- package/base-modules/user/context.d.ts +218 -0
- package/base-modules/user/context.js +585 -0
- package/base-modules/user/drawer.d.ts +1 -0
- package/base-modules/user/drawer.js +25 -0
- package/base-modules/user/filter.d.ts +1 -0
- package/base-modules/user/filter.js +21 -0
- package/base-modules/user/form.d.ts +1 -0
- package/base-modules/user/form.js +28 -0
- package/base-modules/user/more-actions.d.ts +1 -0
- package/base-modules/user/more-actions.js +48 -0
- package/base-modules/user/page.d.ts +30 -0
- package/base-modules/user/page.js +120 -0
- package/base-modules/user/validate.d.ts +16 -0
- package/base-modules/user/validate.js +29 -0
- package/base-modules/user/view.d.ts +1 -0
- package/base-modules/user/view.js +24 -0
- package/base-modules/workspace-user/cache.d.ts +14 -0
- package/base-modules/workspace-user/cache.js +31 -0
- package/base-modules/workspace-user/constants.d.ts +21 -0
- package/base-modules/workspace-user/constants.js +27 -0
- package/base-modules/workspace-user/context.d.ts +155 -0
- package/base-modules/workspace-user/context.js +382 -0
- package/base-modules/workspace-user/filter.d.ts +1 -0
- package/base-modules/workspace-user/filter.js +23 -0
- package/base-modules/workspace-user/form.d.ts +1 -0
- package/base-modules/workspace-user/form.js +12 -0
- package/base-modules/workspace-user/more-actions.d.ts +1 -0
- package/base-modules/workspace-user/more-actions.js +51 -0
- package/base-modules/workspace-user/page.d.ts +28 -0
- package/base-modules/workspace-user/page.js +106 -0
- package/base-modules/workspace-user/validate.d.ts +12 -0
- package/base-modules/workspace-user/validate.js +15 -0
- package/base-modules/workspace-user/view.d.ts +1 -0
- package/base-modules/workspace-user/view.js +20 -0
- package/components/timeline.d.ts +11 -0
- package/components/timeline.js +4 -0
- package/package.json +4 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/utils/toast-network-error.d.ts +1 -0
- 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¤tPage=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
|
+
};
|