@appcorp/fusion-storybook 0.5.1 → 0.5.3

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.
@@ -2,8 +2,17 @@
2
2
  * Workspace Cache
3
3
  *
4
4
  * Cache utilities for workspace data using generic cache system.
5
+ *
6
+ * The cache entry shape is `{ item: WorkspaceBE, cachedAt: ISOString }` — that
7
+ * is what `getCachedSingleItem` writes, and what `getCachedSingleItemSync`
8
+ * reads. Keep any writer (including this module) on the same shape.
5
9
  */
6
10
  import { WorkspaceBE } from "../../type";
7
- export declare const getCachedWorkspace: (id: string) => Promise<WorkspaceBE | null>;
11
+ export declare const getCachedWorkspace: (subdomain: string) => Promise<WorkspaceBE | null>;
8
12
  export declare const getCachedWorkspaceSync: () => WorkspaceBE | null;
13
+ /**
14
+ * Persist a freshly fetched/updated workspace so every other module reading
15
+ * `getCachedWorkspaceSync()` sees the new values without a reload.
16
+ */
17
+ export declare const setCachedWorkspace: (workspace: WorkspaceBE) => void;
9
18
  export declare const invalidateWorkspaceCache: () => void;
@@ -2,28 +2,63 @@
2
2
  * Workspace Cache
3
3
  *
4
4
  * Cache utilities for workspace data using generic cache system.
5
+ *
6
+ * The cache entry shape is `{ item: WorkspaceBE, cachedAt: ISOString }` — that
7
+ * is what `getCachedSingleItem` writes, and what `getCachedSingleItemSync`
8
+ * reads. Keep any writer (including this module) on the same shape.
5
9
  */
6
10
  import { getCachedSingleItem, getCachedSingleItemSync, invalidateCache, } from "@react-pakistan/util-functions/general/generic-cache";
7
11
  import { ONE_YEAR_IN_MS } from "@react-pakistan/util-functions/constants/cache-time";
8
12
  import { WORKSPACE_API_ROUTES } from "../../constants";
9
13
  import { LS_KEYS } from "../../constants";
14
+ import { setStorageValue } from "@react-pakistan/util-functions/local-storage/set-storage-value";
10
15
  // ============================================================================
11
16
  // CACHE CONFIGURATION
12
17
  // ============================================================================
13
18
  const CACHE_CONFIG = {
14
19
  cacheKey: LS_KEYS.WORKSPACE,
15
20
  apiUrl: WORKSPACE_API_ROUTES.UNIT,
21
+ expirationMs: ONE_YEAR_IN_MS,
16
22
  responseKey: "item",
17
23
  };
18
24
  // ============================================================================
19
25
  // CACHE WRAPPERS
20
26
  // ============================================================================
21
- export const getCachedWorkspace = (id) => {
22
- return getCachedSingleItem(CACHE_CONFIG, { id });
27
+ export const getCachedWorkspace = async (subdomain) => {
28
+ const item = await getCachedSingleItem(CACHE_CONFIG, {
29
+ subdomain,
30
+ });
31
+ // The generic cache ignores request params and caches whatever body came
32
+ // back (including a 404 `{ error }`). Drop anything that is not a workspace
33
+ // for the requested tenant so we never render another tenant's data.
34
+ if (!(item === null || item === void 0 ? void 0 : item.id)) {
35
+ invalidateCache(LS_KEYS.WORKSPACE);
36
+ return null;
37
+ }
38
+ if (subdomain && item.subdomain !== subdomain) {
39
+ invalidateCache(LS_KEYS.WORKSPACE);
40
+ const fresh = await getCachedSingleItem(CACHE_CONFIG, {
41
+ subdomain,
42
+ });
43
+ return (fresh === null || fresh === void 0 ? void 0 : fresh.id) && fresh.subdomain === subdomain ? fresh : null;
44
+ }
45
+ return item;
23
46
  };
24
47
  export const getCachedWorkspaceSync = () => {
25
48
  return getCachedSingleItemSync(LS_KEYS.WORKSPACE, ONE_YEAR_IN_MS);
26
49
  };
50
+ /**
51
+ * Persist a freshly fetched/updated workspace so every other module reading
52
+ * `getCachedWorkspaceSync()` sees the new values without a reload.
53
+ */
54
+ export const setCachedWorkspace = (workspace) => {
55
+ if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
56
+ return;
57
+ setStorageValue(LS_KEYS.WORKSPACE, {
58
+ item: workspace,
59
+ cachedAt: new Date().toISOString(),
60
+ });
61
+ };
27
62
  export const invalidateWorkspaceCache = () => {
28
63
  invalidateCache(LS_KEYS.WORKSPACE);
29
64
  };
@@ -1,3 +1,4 @@
1
+ import type { WorkspaceBE } from "../../type";
1
2
  export declare const WORKSPACE_DRAWER: {
2
3
  readonly FILTER_DRAWER: string;
3
4
  readonly FORM_DRAWER: string;
@@ -30,6 +31,11 @@ export declare const WORKSPACE_ACTION_TYPES: {
30
31
  id: string;
31
32
  logo: string;
32
33
  name: string;
34
+ plan: {
35
+ currency?: string;
36
+ id: string;
37
+ name: string;
38
+ } | null;
33
39
  planEndDate: null;
34
40
  planId: string;
35
41
  planStartDate: null;
@@ -56,6 +62,11 @@ export declare const WORKSPACE_ACTION_TYPES: {
56
62
  id: string;
57
63
  logo: string;
58
64
  name: string;
65
+ plan: {
66
+ currency?: string;
67
+ id: string;
68
+ name: string;
69
+ } | null;
59
70
  planEndDate: null;
60
71
  planId: string;
61
72
  planStartDate: null;
@@ -84,6 +95,11 @@ export declare const WORKSPACE_ACTION_TYPES: {
84
95
  id: string;
85
96
  logo: string;
86
97
  name: string;
98
+ plan: {
99
+ currency?: string;
100
+ id: string;
101
+ name: string;
102
+ } | null;
87
103
  planEndDate: null;
88
104
  planId: string;
89
105
  planStartDate: null;
@@ -110,6 +126,11 @@ export declare const WORKSPACE_ACTION_TYPES: {
110
126
  id: string;
111
127
  logo: string;
112
128
  name: string;
129
+ plan: {
130
+ currency?: string;
131
+ id: string;
132
+ name: string;
133
+ } | null;
113
134
  planEndDate: null;
114
135
  planId: string;
115
136
  planStartDate: null;
@@ -136,6 +157,11 @@ export declare const WORKSPACE_ACTION_TYPES: {
136
157
  id: string;
137
158
  logo: string;
138
159
  name: string;
160
+ plan: {
161
+ currency?: string;
162
+ id: string;
163
+ name: string;
164
+ } | null;
139
165
  planEndDate: null;
140
166
  planId: string;
141
167
  planStartDate: null;
@@ -150,11 +176,13 @@ export declare const WORKSPACE_ACTION_TYPES: {
150
176
  errors: Record<string, string>;
151
177
  }>;
152
178
  type Plan = {
153
- name: string;
179
+ currency?: string;
154
180
  id: string;
181
+ name: string;
155
182
  };
156
- export declare const WorkspaceStateContextProvider: ({ children, }: {
183
+ export declare const WorkspaceStateContextProvider: ({ children, subdomain, }: {
157
184
  children: React.ReactNode;
185
+ subdomain?: string;
158
186
  }) => import("react").JSX.Element;
159
187
  export declare const useWorkspaceModule: () => {
160
188
  handleChange: (field: string, value: string | number | boolean | undefined | Record<string, string>) => void;
@@ -164,6 +192,7 @@ export declare const useWorkspaceModule: () => {
164
192
  handleView: () => void;
165
193
  plans: Plan[];
166
194
  updateLoading: boolean;
195
+ workspace: WorkspaceBE | null;
167
196
  handleSearch?: (query: string) => void;
168
197
  handlePageChange?: (page: number) => void;
169
198
  handlePageLimitChange?: (limit: number) => void;
@@ -181,6 +210,11 @@ export declare const useWorkspaceModule: () => {
181
210
  id: string;
182
211
  logo: string;
183
212
  name: string;
213
+ plan: {
214
+ currency?: string;
215
+ id: string;
216
+ name: string;
217
+ } | null;
184
218
  planEndDate: null;
185
219
  planId: string;
186
220
  planStartDate: null;
@@ -10,7 +10,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
10
10
  * Key responsibilities:
11
11
  * - Expose `useWorkspaceModule()` which UI components call for actions
12
12
  * - Keep module-specific `apiParams` and callbacks in one place
13
- * - Ensure cache invalidation and toast notifications on mutation
13
+ * - Keep the shared workspace cache in sync on mutation
14
14
  *
15
15
  * Exported utilities:
16
16
  * - `WorkspaceStateContextProvider` — provider component used by the page
@@ -26,10 +26,9 @@ import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-mod
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 { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
29
- import { WORKSPACE_API_ROUTES } from "../../constants";
30
- import { getCachedWorkspaceSync, invalidateWorkspaceCache } from "./cache";
29
+ import { AUTH_API_ROUTES, WORKSPACE_API_ROUTES } from "../../constants";
30
+ import { getCachedWorkspace, getCachedWorkspaceSync, setCachedWorkspace, } from "./cache";
31
31
  import { workspaceFormValidation } from "./validate";
32
- import { AUTH_API_ROUTES } from "../../constants";
33
32
  // ============================================================================
34
33
  // 1.1 DRAWER TYPES
35
34
  // ============================================================================
@@ -60,6 +59,7 @@ const workspaceConfig = {
60
59
  id: "",
61
60
  logo: "",
62
61
  name: "",
62
+ plan: null,
63
63
  planEndDate: null,
64
64
  planId: "",
65
65
  planStartDate: null,
@@ -81,23 +81,49 @@ const workspaceConfig = {
81
81
  // ============================================================================
82
82
  export const { actionTypes: WORKSPACE_ACTION_TYPES, config: workspaceModuleConfig, initialState: initialWorkspaceState, Provider: WorkspaceProvider, reducer: workspaceReducer, useContext: useWorkspaceContext, } = createGenericModule(workspaceConfig);
83
83
  const PlansContext = createContext([]);
84
- export const WorkspaceStateContextProvider = ({ children, }) => {
84
+ export const WorkspaceStateContextProvider = ({ children, subdomain, }) => {
85
85
  const [plans, setPlans] = useState([]);
86
+ // Render only once the cache holds the workspace for this tenant, otherwise
87
+ // the lazy `useState(() => getCachedWorkspaceSync())` below hydrates empty.
88
+ // Start `false` whenever a subdomain is given so SSR and hydration agree.
89
+ const [ready, setReady] = useState(() => !subdomain);
86
90
  const t = useTranslations("workspace");
91
+ useEffect(() => {
92
+ if (!subdomain)
93
+ return;
94
+ let isMounted = true;
95
+ (async () => {
96
+ const cached = getCachedWorkspaceSync();
97
+ if ((cached === null || cached === void 0 ? void 0 : cached.id) && cached.subdomain === subdomain) {
98
+ if (isMounted)
99
+ setReady(true);
100
+ return;
101
+ }
102
+ try {
103
+ await getCachedWorkspace(subdomain);
104
+ }
105
+ finally {
106
+ if (isMounted)
107
+ setReady(true);
108
+ }
109
+ })();
110
+ return () => {
111
+ isMounted = false;
112
+ };
113
+ }, [subdomain]);
87
114
  useEffect(() => {
88
115
  const fetchPlans = async () => {
89
116
  try {
90
- const response = await fetch(AUTH_API_ROUTES.PLAN, {
117
+ const response = await fetch(`${AUTH_API_ROUTES.PLAN}?currentPage=1&pageLimit=100`, {
91
118
  method: "GET",
92
119
  headers: {
93
120
  "Content-Type": "application/json",
94
- // "x-api-token": process.env.NEXT_PUBLIC_API_KEY!,
95
121
  },
96
122
  });
97
- if (response.ok) {
98
- const data = await response.json();
99
- setPlans((data.items || []));
100
- }
123
+ if (!response.ok)
124
+ throw new Error(`Plans fetch failed: ${response.status}`);
125
+ const data = await response.json();
126
+ setPlans((data.items || []));
101
127
  }
102
128
  catch (_a) {
103
129
  generateThemeToast({
@@ -108,6 +134,9 @@ export const WorkspaceStateContextProvider = ({ children, }) => {
108
134
  };
109
135
  fetchPlans();
110
136
  }, [t]);
137
+ if (!ready) {
138
+ return (_jsx("div", { className: "flex min-h-[50vh] items-center justify-center", children: _jsx("div", { className: "border-primary h-8 w-8 animate-spin rounded-full border-4 border-r-transparent" }) }));
139
+ }
111
140
  return (_jsx(PlansContext.Provider, { value: plans, children: _jsx(WorkspaceProvider, { children: children }) }));
112
141
  };
113
142
  // ============================================================================
@@ -207,11 +236,18 @@ export const useWorkspaceModule = () => {
207
236
  return;
208
237
  }
209
238
  if (data) {
239
+ const updated = data;
240
+ // Keep local state and the shared cache in sync. Invalidating without
241
+ // refetching used to leave every workspace-scoped module with no data.
242
+ dispatch({
243
+ type: WORKSPACE_ACTION_TYPES.SET_FORM_DATA,
244
+ payload: { form: updated },
245
+ });
246
+ setCachedWorkspace(updated);
210
247
  showToast(t("messagesWorkspaceUpdated"), TOAST_VARIANT.SUCCESS);
211
- invalidateWorkspaceCache();
212
248
  resetFormAndCloseDrawer();
213
249
  }
214
- }, [resetFormAndCloseDrawer, showToast, t]);
250
+ }, [dispatch, resetFormAndCloseDrawer, showToast, t]);
215
251
  const byIdCallback = useCallback(() => { }, []);
216
252
  const deleteCallback = useCallback(() => { }, []);
217
253
  // ============================================================================
@@ -224,7 +260,7 @@ export const useWorkspaceModule = () => {
224
260
  deleteParams: {},
225
261
  listCallback,
226
262
  listParams: {},
227
- listUrl: WORKSPACE_API_ROUTES.LIST,
263
+ listUrl: WORKSPACE_API_ROUTES.UNIT,
228
264
  searchQuery: "",
229
265
  unitByIdUrl: WORKSPACE_API_ROUTES.UNIT,
230
266
  unitUrl: WORKSPACE_API_ROUTES.UNIT,
@@ -242,7 +278,16 @@ export const useWorkspaceModule = () => {
242
278
  type: WORKSPACE_ACTION_TYPES.SET_INPUT_FIELD,
243
279
  payload: { key: field, value },
244
280
  });
245
- }, [dispatch]);
281
+ // A retried field must not keep blocking the Save button.
282
+ if (state.errors[field]) {
283
+ const nextErrors = Object.assign({}, state.errors);
284
+ delete nextErrors[field];
285
+ dispatch({
286
+ type: WORKSPACE_ACTION_TYPES.SET_ERRORS,
287
+ payload: { errors: nextErrors },
288
+ });
289
+ }
290
+ }, [dispatch, state.errors]);
246
291
  const handleCloseDrawer = useCallback(() => {
247
292
  resetFormAndCloseDrawer();
248
293
  }, [resetFormAndCloseDrawer]);
@@ -275,7 +320,6 @@ export const useWorkspaceModule = () => {
275
320
  });
276
321
  },
277
322
  errorCallback: (errors) => {
278
- console.log("Validation errors:", errors);
279
323
  dispatch({
280
324
  type: WORKSPACE_ACTION_TYPES.SET_ERRORS,
281
325
  payload: { errors },
@@ -308,5 +352,6 @@ export const useWorkspaceModule = () => {
308
352
  handleSubmit,
309
353
  handleView,
310
354
  plans,
311
- updateLoading });
355
+ updateLoading,
356
+ workspace });
312
357
  };
@@ -27,31 +27,34 @@ const SecretsEditor = ({ secrets, errors, onChange, secretPlaceholder, }) => {
27
27
  // WORKSPACE FORM
28
28
  // ============================================================================
29
29
  export const WorkspaceForm = () => {
30
- const { state, handleChange, plans } = useWorkspaceModule();
30
+ const { state, handleChange, plans, workspace } = useWorkspaceModule();
31
31
  const t = useTranslations("workspace");
32
32
  const secrets = state.secrets || {};
33
33
  const handleSecretChange = (key, value) => {
34
34
  handleChange("secrets", Object.assign(Object.assign({}, secrets), { [key]: value }));
35
35
  };
36
+ // Plans are priced per currency. Match the workspace's agreed currency and
37
+ // fall back to the full list when the workspace has none (new/unpriced).
38
+ const currency = state.agreedCurrency || (workspace === null || workspace === void 0 ? void 0 : workspace.agreedCurrency);
39
+ const currencyPlans = currency
40
+ ? plans.filter((plan) => !plan.currency || plan.currency === currency)
41
+ : plans;
42
+ const planOptions = (currencyPlans.length > 0 ? currencyPlans : plans).map((plan) => ({ value: plan.id, label: plan.name }));
36
43
  const { enhancedComboboxElement: planIdCombo } = useEnhancedCombobox({
37
44
  id: "planId",
38
45
  label: t("formPlanLabel"),
39
46
  value: state.planId || "",
40
- onValueChange: (value) => handleChange("planId", value),
41
- options: plans
42
- .filter(({ name }) => name.match("PKR"))
43
- .map((plan) => ({
44
- value: plan.id,
45
- label: plan.name,
46
- })),
47
+ onValueChange: (value) => handleChange("planId", value || ""),
48
+ options: planOptions,
47
49
  placeholder: t("formPlanPlaceholder"),
48
50
  info: t("formPlanInfo"),
49
51
  searchEndpoint: PLAN_API_ROUTES.UNIT,
52
+ searchParams: currency ? { currency } : undefined,
50
53
  formatSearchResult: (item) => {
51
- var _a, _b;
54
+ var _a, _b, _c, _d;
52
55
  return ({
53
- label: String((_a = item.label) !== null && _a !== void 0 ? _a : ""),
54
- value: String((_b = item.value) !== null && _b !== void 0 ? _b : ""),
56
+ label: String((_b = (_a = item.name) !== null && _a !== void 0 ? _a : item.label) !== null && _b !== void 0 ? _b : ""),
57
+ value: String((_d = (_c = item.id) !== null && _c !== void 0 ? _c : item.value) !== null && _d !== void 0 ? _d : ""),
55
58
  });
56
59
  },
57
60
  });
@@ -16,6 +16,11 @@ interface Props {
16
16
  tableDescription: string;
17
17
  tableTitle: string;
18
18
  userRole: USER_ROLE;
19
+ /**
20
+ * Tenant subdomain used to hydrate the workspace cache when it is empty.
21
+ * Optional so the module still renders in Storybook / standalone apps.
22
+ */
23
+ subdomain?: string;
19
24
  }
20
25
  export declare const WorkspacePage: FC<Props>;
21
26
  export {};
@@ -35,4 +35,4 @@ const WorkspaceInnerPage = ({ actionsButtonEdit, tableDescription, tableTitle, u
35
35
  // ============================================================================
36
36
  // ROOT PAGE COMPONENT (wraps with provider)
37
37
  // ============================================================================
38
- export const WorkspacePage = (props) => (_jsx(WorkspaceStateContextProvider, { children: _jsx(WorkspaceInnerPage, Object.assign({}, props)) }));
38
+ export const WorkspacePage = (props) => (_jsx(WorkspaceStateContextProvider, { subdomain: props.subdomain, children: _jsx(WorkspaceInnerPage, Object.assign({}, props)) }));
@@ -7,16 +7,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  */
8
8
  import { useWorkspaceModule } from "./context";
9
9
  import { useTranslations } from "next-intl";
10
- import Image from "next/image";
11
10
  import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@appcorp/shadcn/components/ui/card";
12
11
  import { Badge } from "@appcorp/shadcn/components/ui/badge";
13
12
  import { Separator } from "@appcorp/shadcn/components/ui/separator";
14
13
  import { Building2, Globe, FileText, Image as LucideImage, CheckCircle2, XCircle, Calendar, CreditCard, Users, BookOpen, GraduationCap, Home, HardDrive, Clock, Shield, } from "lucide-react";
15
14
  import { DATE_FORMATS, formatDate, } from "@react-pakistan/util-functions/general/format-date";
16
15
  import { formatValue } from "@react-pakistan/util-functions/general/format-value";
16
+ const APP_DOMAIN = process.env.NEXT_PUBLIC_APP_DOMAIN || "edupilotpro.com";
17
17
  export const WorkspaceView = () => {
18
- const { state } = useWorkspaceModule();
18
+ var _a, _b;
19
+ const { state, workspace } = useWorkspaceModule();
19
20
  const t = useTranslations("workspace");
21
+ const appDomain = APP_DOMAIN;
22
+ const planName = ((_a = state.plan) === null || _a === void 0 ? void 0 : _a.name) || ((_b = workspace === null || workspace === void 0 ? void 0 : workspace.plan) === null || _b === void 0 ? void 0 : _b.name) || formatValue(state.planId);
20
23
  const formatCurrency = (amount, currency) => {
21
24
  if (!amount)
22
25
  return "—";
@@ -40,17 +43,14 @@ export const WorkspaceView = () => {
40
43
  return `${mb} MB`;
41
44
  return `${(mb / 1024).toFixed(1)} GB`;
42
45
  };
43
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Building2, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionBasicInfo") })] }), _jsx(CardDescription, { children: t("viewBasicInfoDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-2 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1 md:col-span-2", 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("formNameLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.name) })] }), _jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Globe, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formSubdomainLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.subdomain) }), state.subdomain && (_jsxs("p", { className: "text-muted-foreground text-sm", children: [t("viewFieldUrlPrefix"), state.subdomain, ".edupilotpro.com"] }))] }), _jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formDescriptionLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.description) === "—"
46
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Building2, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionBasicInfo") })] }), _jsx(CardDescription, { children: t("viewBasicInfoDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-2 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1 md:col-span-2", 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("formNameLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.name) })] }), _jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Globe, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formSubdomainLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.subdomain) }), state.subdomain && (_jsxs("p", { className: "text-muted-foreground text-sm", children: [t("viewFieldUrlPrefix"), state.subdomain, ".", appDomain] }))] }), _jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formDescriptionLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.description) === "—"
44
47
  ? t("viewTextDescriptionNotProvided")
45
- : state.description })] }), state.logo && (_jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LucideImage, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formLogoLabel") })] }), _jsx("div", { className: "mt-2", children: _jsx(Image, { src: state.logo, alt: t("viewFieldLogoAlt"), width: 64, height: 64, className: "h-16 w-16 rounded border object-contain", onError: (e) => {
48
+ : state.description })] }), state.logo && (_jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LucideImage, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("formLogoLabel") })] }), _jsx("div", { className: "mt-2", children: _jsx("img", { src: state.logo, alt: t("viewFieldLogoAlt"), width: 64, height: 64, className: "h-16 w-16 rounded border object-contain", onError: (e) => {
46
49
  e.currentTarget.style.display = "none";
47
50
  } }) })] })), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStatus") }), _jsxs(Badge, { variant: state.enabled ? "default" : "destructive", className: "gap-1", children: [state.enabled ? (_jsx(CheckCircle2, { className: "h-3 w-3" })) : (_jsx(XCircle, { className: "h-3 w-3" })), state.enabled
48
51
  ? t("viewStatusEnabled")
49
- : t("viewStatusDisabled")] })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionPlanBilling") })] }), _jsx(CardDescription, { children: t("viewPlanBillingDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanIdLabel") })] }), _jsx("p", { className: "font-mono text-sm", children: formatValue(state.planId) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanStartDateLabel") })] }), _jsx("p", { className: "text-base", children: formatDate(state.planStartDate || "", DATE_FORMATS.LOCALE_DATE) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanEndDateLabel") })] }), _jsx("p", { className: "text-base", children: formatDate(state.planEndDate || "", DATE_FORMATS.LOCALE_DATE) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Clock, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldBillingIntervalLabel") })] }), _jsx("p", { className: "text-base", children: formatBillingInterval(state.billingInterval) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldAgreedPriceLabel") })] }), _jsx("p", { className: "text-base", children: formatCurrency(state.agreedPrice, state.agreedCurrency) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Globe, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCurrencyLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.agreedCurrency) })] })] }) })] }), _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("viewSectionUsageStats") })] }), _jsx(CardDescription, { children: t("viewUsageStatsDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-2 gap-6 md:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(GraduationCap, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStudentsLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.studentsCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldTeachersLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.teachersCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldClassesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.classesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCoursesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.coursesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Home, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldFamiliesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.familiesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(HardDrive, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStorageUsedLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: formatStorage(state.storageUsedMB) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionSecrets") })] }), _jsx(CardDescription, { children: t("viewSecretsDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsx("div", { className: "space-y-4", children: _jsxs("div", { children: [_jsxs("div", { className: "mb-2 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("viewFieldSecretsJsonLabel") })] }), state.secrets ? (_jsx("div", { className: "space-y-2", children: Object.keys(state.secrets)
50
- .length === 0 ? (_jsx("p", { className: "text-muted-foreground italic", children: t("viewFieldNoSecretsConfigured") })) : (Object.entries(state.secrets).map(([k, v]) => {
51
- var _a;
52
- return (_jsxs("div", { className: "bg-muted/10 border-border/60 flex items-center justify-between rounded-md border p-3", children: [_jsx("div", { className: "text-foreground text-sm font-medium", children: k }), _jsx("div", { className: "text-foreground/80 font-mono text-sm", children: typeof v === "string" && v.length > 0
53
- ? `${(_a = String(v)) === null || _a === void 0 ? void 0 : _a.slice(0, 3)}${"*".repeat(Math.max(0, String(v).length - 3))}`
54
- : "—" })] }, k));
55
- })) })) : (_jsx("p", { className: "text-muted-foreground italic", children: t("viewFieldNoSecretsConfigured") })), _jsx("p", { className: "text-muted-foreground mt-2 text-xs", children: t("viewFieldSecretsHelpText") })] }) }) })] })] }));
52
+ : t("viewStatusDisabled")] })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionPlanBilling") })] }), _jsx(CardDescription, { children: t("viewPlanBillingDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanIdLabel") })] }), _jsx("p", { className: "text-base", children: planName })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanStartDateLabel") })] }), _jsx("p", { className: "text-base", children: formatDate(state.planStartDate || "", DATE_FORMATS.LOCALE_DATE) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldPlanEndDateLabel") })] }), _jsx("p", { className: "text-base", children: formatDate(state.planEndDate || "", DATE_FORMATS.LOCALE_DATE) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Clock, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldBillingIntervalLabel") })] }), _jsx("p", { className: "text-base", children: formatBillingInterval(state.billingInterval) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldAgreedPriceLabel") })] }), _jsx("p", { className: "text-base", children: formatCurrency(state.agreedPrice, state.agreedCurrency) })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Globe, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCurrencyLabel") })] }), _jsx("p", { className: "text-base", children: formatValue(state.agreedCurrency) })] })] }) })] }), _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("viewSectionUsageStats") })] }), _jsx(CardDescription, { children: t("viewUsageStatsDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-2 gap-6 md:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(GraduationCap, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStudentsLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.studentsCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldTeachersLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.teachersCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldClassesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.classesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldCoursesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.coursesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Home, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldFamiliesLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: state.familiesCount })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(HardDrive, { className: "text-muted-foreground h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("viewFieldStorageUsedLabel") })] }), _jsx("p", { className: "text-primary text-2xl font-bold", children: formatStorage(state.storageUsedMB) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionSecrets") })] }), _jsx(CardDescription, { children: t("viewSecretsDescription") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsx("div", { className: "space-y-4", children: _jsxs("div", { children: [_jsxs("div", { className: "mb-2 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("viewFieldSecretsJsonLabel") })] }), state.secrets ? (_jsx("div", { className: "space-y-2", children: Object.keys(state.secrets)
53
+ .length === 0 ? (_jsx("p", { className: "text-muted-foreground italic", children: t("viewFieldNoSecretsConfigured") })) : (Object.entries(state.secrets).map(([k, v]) => (_jsxs("div", { className: "bg-muted/10 border-border/60 flex items-center justify-between rounded-md border p-3", children: [_jsx("div", { className: "text-foreground text-sm font-medium", children: k }), _jsx("div", { className: "text-foreground/80 text-sm", children: typeof v === "string" && v.length > 0
54
+ ? t("viewFieldSecretConfigured")
55
+ : "—" })] }, k)))) })) : (_jsx("p", { className: "text-muted-foreground italic", children: t("viewFieldNoSecretsConfigured") })), _jsx("p", { className: "text-muted-foreground mt-2 text-xs", children: t("viewFieldSecretsHelpText") })] }) }) })] })] }));
56
56
  };
@@ -17,8 +17,8 @@
17
17
  * - `WORKSPACE_USER_DRAWER` — drawer type constants
18
18
  * - `useWorkspaceUserModule()` — hook that returns handlers and state
19
19
  */
20
- import { RoleBE, WorkspaceBE, WorkspaceUserBE } from "../../type";
21
- import { type RowAction } from "@appcorp/shadcn/components/enhanced-table";
20
+ import { RoleBE, UserBE, WorkspaceBE, WorkspaceUserBE } from "../../type";
21
+ import { type RowAction, type TableRow } from "@appcorp/shadcn/components/enhanced-table";
22
22
  export declare const WORKSPACE_USER_DRAWER: {
23
23
  readonly FILTER_DRAWER: string;
24
24
  readonly FORM_DRAWER: string;
@@ -53,6 +53,7 @@ export declare const WORKSPACE_USER_ACTION_TYPES: {
53
53
  userId: string;
54
54
  workspaceId: string;
55
55
  role: RoleBE | undefined;
56
+ user: UserBE | undefined;
56
57
  workspace: WorkspaceBE | undefined;
57
58
  }>, initialWorkspaceUserState: {
58
59
  items: WorkspaceUserBE[];
@@ -70,6 +71,7 @@ export declare const WORKSPACE_USER_ACTION_TYPES: {
70
71
  userId: string;
71
72
  workspaceId: string;
72
73
  role: RoleBE | undefined;
74
+ user: UserBE | undefined;
73
75
  workspace: WorkspaceBE | undefined;
74
76
  }, WorkspaceUserProvider: import("react").FC<{
75
77
  children: React.ReactNode;
@@ -89,6 +91,7 @@ export declare const WORKSPACE_USER_ACTION_TYPES: {
89
91
  userId: string;
90
92
  workspaceId: string;
91
93
  role: RoleBE | undefined;
94
+ user: UserBE | undefined;
92
95
  workspace: WorkspaceBE | undefined;
93
96
  }, action: any) => {
94
97
  items: WorkspaceUserBE[];
@@ -106,6 +109,7 @@ export declare const WORKSPACE_USER_ACTION_TYPES: {
106
109
  userId: string;
107
110
  workspaceId: string;
108
111
  role: RoleBE | undefined;
112
+ user: UserBE | undefined;
109
113
  workspace: WorkspaceBE | undefined;
110
114
  }, useWorkspaceUserContext: () => import("@react-pakistan/util-functions/factory/generic-module-factory").GenericModuleContextWithHandlers<{
111
115
  items: WorkspaceUserBE[];
@@ -123,6 +127,7 @@ export declare const WORKSPACE_USER_ACTION_TYPES: {
123
127
  userId: string;
124
128
  workspaceId: string;
125
129
  role: RoleBE | undefined;
130
+ user: UserBE | undefined;
126
131
  workspace: WorkspaceBE | undefined;
127
132
  }>;
128
133
  export declare const useWorkspaceUserModule: () => {
@@ -133,15 +138,15 @@ export declare const useWorkspaceUserModule: () => {
133
138
  handleChange: (field: string, value: string | number | boolean | undefined | Record<string, string>) => void;
134
139
  handleCloseDrawer: () => void;
135
140
  handleCreate: () => void;
136
- handleDelete: (row?: WorkspaceUserBE) => void;
137
- handleEdit: () => void;
141
+ handleDelete: (row?: TableRow) => void;
142
+ handleEdit: (row?: TableRow) => void;
138
143
  handleFilters: () => void;
139
144
  handleMoreActions: () => void;
140
145
  handlePageChange: (page: number | unknown) => void;
141
146
  handlePageLimitChange: (limit: number) => void;
142
147
  handleSearch: (query: string) => void;
143
148
  handleSubmit: () => void;
144
- handleView: () => void;
149
+ handleView: (row?: TableRow) => void;
145
150
  headerActions: {
146
151
  enabled: boolean;
147
152
  handleOnClick: () => void;
@@ -169,6 +174,7 @@ export declare const useWorkspaceUserModule: () => {
169
174
  userId: string;
170
175
  workspaceId: string;
171
176
  role: RoleBE | undefined;
177
+ user: UserBE | undefined;
172
178
  workspace: WorkspaceBE | undefined;
173
179
  };
174
180
  dispatch: React.Dispatch<any>;