@mesob/auth-react 0.3.5 → 0.4.0

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 (124) hide show
  1. package/dist/components/auth/forgot-password.js +5 -1
  2. package/dist/components/auth/forgot-password.js.map +1 -1
  3. package/dist/components/auth/reset-password-form.js +5 -1
  4. package/dist/components/auth/reset-password-form.js.map +1 -1
  5. package/dist/components/auth/set-password.d.ts +9 -0
  6. package/dist/components/auth/set-password.js +527 -0
  7. package/dist/components/auth/set-password.js.map +1 -0
  8. package/dist/components/auth/sign-in.js +22 -1
  9. package/dist/components/auth/sign-in.js.map +1 -1
  10. package/dist/components/auth/sign-up.js +7 -5
  11. package/dist/components/auth/sign-up.js.map +1 -1
  12. package/dist/components/auth/verify-email.js +5 -1
  13. package/dist/components/auth/verify-email.js.map +1 -1
  14. package/dist/components/auth/verify-phone.js +5 -1
  15. package/dist/components/auth/verify-phone.js.map +1 -1
  16. package/dist/components/authorization/deny.d.ts +11 -0
  17. package/dist/components/authorization/deny.js +52 -0
  18. package/dist/components/authorization/deny.js.map +1 -0
  19. package/dist/components/authorization/grant.d.ts +12 -0
  20. package/dist/components/authorization/grant.js +57 -0
  21. package/dist/components/authorization/grant.js.map +1 -0
  22. package/dist/components/error-boundary.d.ts +2 -2
  23. package/dist/components/iam/permission-selector.d.ts +19 -0
  24. package/dist/components/iam/permission-selector.js +122 -0
  25. package/dist/components/iam/permission-selector.js.map +1 -0
  26. package/dist/components/iam/permissions.js +12 -31
  27. package/dist/components/iam/permissions.js.map +1 -1
  28. package/dist/components/iam/role-detail-layout.d.ts +11 -0
  29. package/dist/components/iam/role-detail-layout.js +137 -0
  30. package/dist/components/iam/role-detail-layout.js.map +1 -0
  31. package/dist/components/iam/role-detail-page.d.ts +9 -0
  32. package/dist/components/iam/role-detail-page.js +229 -0
  33. package/dist/components/iam/role-detail-page.js.map +1 -0
  34. package/dist/components/iam/role-permissions-page.d.ts +8 -0
  35. package/dist/components/iam/role-permissions-page.js +397 -0
  36. package/dist/components/iam/role-permissions-page.js.map +1 -0
  37. package/dist/components/iam/roles.js +11 -8
  38. package/dist/components/iam/roles.js.map +1 -1
  39. package/dist/components/iam/users.js +1 -7
  40. package/dist/components/iam/users.js.map +1 -1
  41. package/dist/components/profile/account.js +110 -19
  42. package/dist/components/profile/account.js.map +1 -1
  43. package/dist/components/profile/change-profile.d.ts +2 -1
  44. package/dist/components/profile/change-profile.js +16 -8
  45. package/dist/components/profile/change-profile.js.map +1 -1
  46. package/dist/components/profile/security.js +51 -17
  47. package/dist/components/profile/security.js.map +1 -1
  48. package/dist/index.d.ts +9 -1
  49. package/dist/index.js +1813 -725
  50. package/dist/index.js.map +1 -1
  51. package/dist/pages/auth/forgot-password.d.ts +7 -0
  52. package/dist/pages/auth/forgot-password.js +784 -0
  53. package/dist/pages/auth/forgot-password.js.map +1 -0
  54. package/dist/pages/auth/layout.d.ts +8 -0
  55. package/dist/pages/auth/layout.js +562 -0
  56. package/dist/pages/auth/layout.js.map +1 -0
  57. package/dist/pages/auth/reset-password.d.ts +10 -0
  58. package/dist/pages/auth/reset-password.js +913 -0
  59. package/dist/pages/auth/reset-password.js.map +1 -0
  60. package/dist/pages/auth/set-password.d.ts +10 -0
  61. package/dist/pages/auth/set-password.js +946 -0
  62. package/dist/pages/auth/set-password.js.map +1 -0
  63. package/dist/pages/auth/sign-in.d.ts +10 -0
  64. package/dist/pages/auth/sign-in.js +984 -0
  65. package/dist/pages/auth/sign-in.js.map +1 -0
  66. package/dist/pages/auth/sign-up.d.ts +10 -0
  67. package/dist/pages/auth/sign-up.js +940 -0
  68. package/dist/pages/auth/sign-up.js.map +1 -0
  69. package/dist/pages/auth/verify-email.d.ts +10 -0
  70. package/dist/pages/auth/verify-email.js +950 -0
  71. package/dist/pages/auth/verify-email.js.map +1 -0
  72. package/dist/pages/auth/verify-phone.d.ts +10 -0
  73. package/dist/pages/auth/verify-phone.js +964 -0
  74. package/dist/pages/auth/verify-phone.js.map +1 -0
  75. package/dist/pages/iam/permissions.d.ts +5 -0
  76. package/dist/pages/iam/permissions.js +308 -0
  77. package/dist/pages/iam/permissions.js.map +1 -0
  78. package/dist/pages/iam/role-detail-layout.d.ts +12 -0
  79. package/dist/pages/iam/role-detail-layout.js +145 -0
  80. package/dist/pages/iam/role-detail-layout.js.map +1 -0
  81. package/dist/pages/iam/role-detail.d.ts +12 -0
  82. package/dist/pages/iam/role-detail.js +241 -0
  83. package/dist/pages/iam/role-detail.js.map +1 -0
  84. package/dist/pages/iam/role-permissions.d.ts +12 -0
  85. package/dist/pages/iam/role-permissions.js +409 -0
  86. package/dist/pages/iam/role-permissions.js.map +1 -0
  87. package/dist/pages/iam/role-users.d.ts +12 -0
  88. package/dist/pages/iam/role-users.js +825 -0
  89. package/dist/pages/iam/role-users.js.map +1 -0
  90. package/dist/pages/iam/roles.d.ts +5 -0
  91. package/dist/pages/iam/roles.js +684 -0
  92. package/dist/pages/iam/roles.js.map +1 -0
  93. package/dist/pages/iam/sessions.d.ts +5 -0
  94. package/dist/pages/iam/sessions.js +315 -0
  95. package/dist/pages/iam/sessions.js.map +1 -0
  96. package/dist/pages/iam/tenant-detail.d.ts +10 -0
  97. package/dist/pages/iam/tenant-detail.js +186 -0
  98. package/dist/pages/iam/tenant-detail.js.map +1 -0
  99. package/dist/pages/iam/tenants.d.ts +5 -0
  100. package/dist/pages/iam/tenants.js +610 -0
  101. package/dist/pages/iam/tenants.js.map +1 -0
  102. package/dist/pages/iam/user-activity.d.ts +10 -0
  103. package/dist/pages/iam/user-activity.js +850 -0
  104. package/dist/pages/iam/user-activity.js.map +1 -0
  105. package/dist/pages/iam/user-detail-layout.d.ts +12 -0
  106. package/dist/pages/iam/user-detail-layout.js +106 -0
  107. package/dist/pages/iam/user-detail-layout.js.map +1 -0
  108. package/dist/pages/iam/user-detail.d.ts +10 -0
  109. package/dist/pages/iam/user-detail.js +102 -0
  110. package/dist/pages/iam/user-detail.js.map +1 -0
  111. package/dist/pages/iam/users.d.ts +5 -0
  112. package/dist/pages/iam/users.js +1275 -0
  113. package/dist/pages/iam/users.js.map +1 -0
  114. package/dist/pages/profile/account.d.ts +5 -0
  115. package/dist/pages/profile/account.js +182 -0
  116. package/dist/pages/profile/account.js.map +1 -0
  117. package/dist/pages/profile/layout.d.ts +8 -0
  118. package/dist/pages/profile/layout.js +133 -0
  119. package/dist/pages/profile/layout.js.map +1 -0
  120. package/dist/pages/profile/security.d.ts +5 -0
  121. package/dist/pages/profile/security.js +1539 -0
  122. package/dist/pages/profile/security.js.map +1 -0
  123. package/dist/{types-vcfvnAzQ.d.ts → types-g9QcNRxT.d.ts} +13 -7
  124. package/package.json +102 -3
@@ -0,0 +1,308 @@
1
+ "use client";
2
+
3
+ // src/pages/iam/permissions.tsx
4
+ import {
5
+ Button,
6
+ EntityFilter,
7
+ EntityHeader,
8
+ EntitySearch,
9
+ EntitySort,
10
+ EntityViewToggle,
11
+ PageBody,
12
+ PageContainer,
13
+ useBreadcrumbs,
14
+ useEntityPagination,
15
+ useEntityParams
16
+ } from "@mesob/ui/components";
17
+ import { IconKey as IconKey2 } from "@tabler/icons-react";
18
+ import { useQueryClient } from "@tanstack/react-query";
19
+ import { toast } from "sonner";
20
+
21
+ // src/lib/query-options.ts
22
+ import { keepPreviousData } from "@tanstack/react-query";
23
+ var defaultEntityQueryOptions = {
24
+ refetchOnMount: false,
25
+ refetchOnWindowFocus: false,
26
+ refetchOnReconnect: false,
27
+ staleTime: 60 * 1e3,
28
+ gcTime: 5 * 60 * 1e3,
29
+ placeholderData: keepPreviousData,
30
+ retry: 1
31
+ };
32
+
33
+ // src/provider.tsx
34
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
35
+ import { deepmerge } from "deepmerge-ts";
36
+ import createFetchClient from "openapi-fetch";
37
+ import createClient from "openapi-react-query";
38
+ import { createContext, useContext, useMemo, useState } from "react";
39
+
40
+ // src/utils/cookie.ts
41
+ var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
42
+
43
+ // src/provider.tsx
44
+ import { jsx } from "react/jsx-runtime";
45
+ var SessionContext = createContext(null);
46
+ var ApiContext = createContext(null);
47
+ var ConfigContext = createContext(null);
48
+ var queryClient = new QueryClient({
49
+ defaultOptions: {
50
+ queries: {
51
+ refetchOnWindowFocus: false
52
+ }
53
+ }
54
+ });
55
+ function useApi() {
56
+ const context = useContext(ApiContext);
57
+ if (!context) {
58
+ throw new Error("useApi must be used within MesobAuthProvider");
59
+ }
60
+ return context;
61
+ }
62
+
63
+ // src/pages/iam/permissions/_components/permissions-list.tsx
64
+ import {
65
+ Badge as Badge2,
66
+ DataTablePagination,
67
+ DisplayTable,
68
+ EntityEmptyState,
69
+ EntityLoadingState,
70
+ Tbody,
71
+ Td,
72
+ Th,
73
+ Thead,
74
+ Tr
75
+ } from "@mesob/ui/components";
76
+ import { IconKey } from "@tabler/icons-react";
77
+
78
+ // src/pages/iam/permissions/_components/permission-card.tsx
79
+ import { Badge, Card, CardContent, CardHeader } from "@mesob/ui/components";
80
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
81
+ function descStr(d) {
82
+ if (d == null) {
83
+ return "";
84
+ }
85
+ if (typeof d === "string") {
86
+ return d;
87
+ }
88
+ if (typeof d === "object" && d !== null && "en" in d) {
89
+ return String(d.en ?? "");
90
+ }
91
+ return String(d);
92
+ }
93
+ function PermissionCard({ permission }) {
94
+ return /* @__PURE__ */ jsxs(Card, { className: "hover:shadow-md transition-shadow", children: [
95
+ /* @__PURE__ */ jsxs(CardHeader, { className: "pb-2", children: [
96
+ /* @__PURE__ */ jsx2("p", { className: "font-semibold", children: permission.activity }),
97
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
98
+ /* @__PURE__ */ jsx2(Badge, { variant: "secondary", children: permission.application }),
99
+ /* @__PURE__ */ jsx2(Badge, { variant: "outline", children: permission.feature })
100
+ ] })
101
+ ] }),
102
+ /* @__PURE__ */ jsx2(CardContent, { children: descStr(permission.description) && /* @__PURE__ */ jsx2("p", { className: "text-sm text-muted-foreground line-clamp-2", children: descStr(permission.description) }) })
103
+ ] });
104
+ }
105
+
106
+ // src/pages/iam/permissions/_components/permissions-list.tsx
107
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
108
+ var TABLE_COLUMN_COUNT = 4;
109
+ function descStr2(d) {
110
+ if (d == null) {
111
+ return "";
112
+ }
113
+ if (typeof d === "string") {
114
+ return d;
115
+ }
116
+ if (typeof d === "object" && d !== null && "en" in d) {
117
+ return String(d.en ?? "");
118
+ }
119
+ return String(d);
120
+ }
121
+ function PermissionsList({
122
+ data,
123
+ isLoading,
124
+ view,
125
+ pageIndex,
126
+ pageSize,
127
+ pageCount,
128
+ totalRows,
129
+ onPageChange,
130
+ onPageSizeChange
131
+ }) {
132
+ if (isLoading) {
133
+ return /* @__PURE__ */ jsx3(
134
+ EntityLoadingState,
135
+ {
136
+ view,
137
+ rowCount: pageSize,
138
+ columnCount: TABLE_COLUMN_COUNT,
139
+ cardCount: pageSize
140
+ }
141
+ );
142
+ }
143
+ if (totalRows === 0) {
144
+ return /* @__PURE__ */ jsx3(
145
+ EntityEmptyState,
146
+ {
147
+ icon: IconKey,
148
+ entityName: "permission",
149
+ title: "No permissions",
150
+ description: "Permissions are managed by the system."
151
+ }
152
+ );
153
+ }
154
+ if (view === "table") {
155
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
156
+ /* @__PURE__ */ jsxs2(DisplayTable, { withTableBorder: true, children: [
157
+ /* @__PURE__ */ jsx3(Thead, { children: /* @__PURE__ */ jsxs2(Tr, { children: [
158
+ /* @__PURE__ */ jsx3(Th, { children: "Application" }),
159
+ /* @__PURE__ */ jsx3(Th, { children: "Feature" }),
160
+ /* @__PURE__ */ jsx3(Th, { children: "Activity" }),
161
+ /* @__PURE__ */ jsx3(Th, { children: "Description" })
162
+ ] }) }),
163
+ /* @__PURE__ */ jsx3(Tbody, { children: data.map((p) => /* @__PURE__ */ jsxs2(Tr, { children: [
164
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Badge2, { variant: "secondary", children: p.application }) }),
165
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Badge2, { variant: "outline", children: p.feature }) }),
166
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3("span", { className: "font-medium", children: p.activity }) }),
167
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground line-clamp-1 max-w-[200px]", children: descStr2(p.description) || "\u2014" }) })
168
+ ] }, p.id)) })
169
+ ] }),
170
+ /* @__PURE__ */ jsx3(
171
+ DataTablePagination,
172
+ {
173
+ pageIndex,
174
+ pageSize,
175
+ pageCount,
176
+ totalRows,
177
+ onPageChange,
178
+ onPageSizeChange
179
+ }
180
+ )
181
+ ] });
182
+ }
183
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
184
+ /* @__PURE__ */ jsx3("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: data.map((p) => /* @__PURE__ */ jsx3(PermissionCard, { permission: p }, p.id)) }),
185
+ /* @__PURE__ */ jsx3(
186
+ DataTablePagination,
187
+ {
188
+ pageIndex,
189
+ pageSize,
190
+ pageCount,
191
+ totalRows,
192
+ onPageChange,
193
+ onPageSizeChange
194
+ }
195
+ )
196
+ ] });
197
+ }
198
+
199
+ // src/pages/iam/permissions.tsx
200
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
201
+ function PermissionsPage() {
202
+ const { hooks } = useApi();
203
+ const qc = useQueryClient();
204
+ useBreadcrumbs({
205
+ items: [
206
+ { label: "Home", href: "/dashboard" },
207
+ { label: "IAM", href: "/iam/permissions" },
208
+ { label: "Permissions" }
209
+ ]
210
+ });
211
+ const { queryConfig, params, setParams } = useEntityParams({
212
+ searchKey: "search",
213
+ defaultSort: "id",
214
+ defaultOrder: "asc"
215
+ });
216
+ const permissionsQuery = queryConfig;
217
+ const { data, isPending, isFetching } = hooks.useQuery(
218
+ "get",
219
+ "/permissions",
220
+ permissionsQuery,
221
+ defaultEntityQueryOptions
222
+ );
223
+ const seedPermissions = hooks.useMutation("post", "/permissions/seed", {
224
+ onSuccess: () => {
225
+ qc.invalidateQueries({ queryKey: ["get", "/permissions"] });
226
+ toast.success("Permissions seeded");
227
+ },
228
+ onError: () => {
229
+ toast.error("Failed to seed permissions");
230
+ }
231
+ });
232
+ const isLoading = isPending || isFetching;
233
+ const permissions = data?.permissions ?? [];
234
+ const { total, pageCount } = useEntityPagination({
235
+ items: permissions,
236
+ total: data?.total,
237
+ pageSize: params.pageSize
238
+ });
239
+ return /* @__PURE__ */ jsx4(PageContainer, { className: "flex flex-1 flex-col gap-4 p-4 pt-0", children: /* @__PURE__ */ jsxs3(PageBody, { className: "px-0", children: [
240
+ /* @__PURE__ */ jsx4(
241
+ EntityHeader,
242
+ {
243
+ icon: /* @__PURE__ */ jsx4(IconKey2, { className: "h-5 w-5" }),
244
+ title: "Permissions",
245
+ actions: /* @__PURE__ */ jsx4(
246
+ Button,
247
+ {
248
+ variant: "outline",
249
+ onClick: () => seedPermissions.mutate({}),
250
+ loading: seedPermissions.isPending,
251
+ children: "Seed permissions"
252
+ }
253
+ ),
254
+ search: /* @__PURE__ */ jsx4(
255
+ EntitySearch,
256
+ {
257
+ paramKey: "search",
258
+ placeholder: "Search permissions..."
259
+ }
260
+ ),
261
+ filter: /* @__PURE__ */ jsx4(
262
+ EntityFilter,
263
+ {
264
+ options: [
265
+ { label: "All", value: "" },
266
+ { label: "Application", value: "application" },
267
+ { label: "Feature", value: "feature" },
268
+ { label: "Activity", value: "activity" }
269
+ ],
270
+ placeholder: "Search field"
271
+ }
272
+ ),
273
+ sort: /* @__PURE__ */ jsx4(
274
+ EntitySort,
275
+ {
276
+ defaultSort: "id",
277
+ defaultOrder: "asc",
278
+ options: [
279
+ { label: "ID", value: "id" },
280
+ { label: "Application", value: "application" },
281
+ { label: "Feature", value: "feature" },
282
+ { label: "Activity", value: "activity" }
283
+ ]
284
+ }
285
+ ),
286
+ view: /* @__PURE__ */ jsx4(EntityViewToggle, { views: ["table", "card"] })
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsx4(
290
+ PermissionsList,
291
+ {
292
+ data: permissions,
293
+ isLoading,
294
+ view: params.view || "table",
295
+ pageIndex: params.page - 1,
296
+ pageSize: params.pageSize,
297
+ pageCount,
298
+ totalRows: total,
299
+ onPageChange: (p) => setParams({ page: p + 1 }),
300
+ onPageSizeChange: (size) => setParams({ pageSize: size, page: 1 })
301
+ }
302
+ )
303
+ ] }) });
304
+ }
305
+ export {
306
+ PermissionsPage as default
307
+ };
308
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/pages/iam/permissions.tsx","../../../src/lib/query-options.ts","../../../src/provider.tsx","../../../src/utils/cookie.ts","../../../src/pages/iam/permissions/_components/permissions-list.tsx","../../../src/pages/iam/permissions/_components/permission-card.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n EntityFilter,\n EntityHeader,\n EntitySearch,\n EntitySort,\n EntityViewToggle,\n PageBody,\n PageContainer,\n useBreadcrumbs,\n useEntityPagination,\n useEntityParams,\n} from '@mesob/ui/components';\nimport { IconKey } from '@tabler/icons-react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { toast } from 'sonner';\nimport type { paths } from '../../data/openapi';\nimport { defaultEntityQueryOptions } from '../../lib/query-options';\nimport { useApi } from '../../provider';\nimport { PermissionsList } from './permissions/_components/permissions-list';\n\nexport default function PermissionsPage() {\n const { hooks } = useApi();\n const qc = useQueryClient();\n useBreadcrumbs({\n items: [\n { label: 'Home', href: '/dashboard' },\n { label: 'IAM', href: '/iam/permissions' },\n { label: 'Permissions' },\n ],\n });\n const { queryConfig, params, setParams } = useEntityParams({\n searchKey: 'search',\n defaultSort: 'id',\n defaultOrder: 'asc',\n });\n const permissionsQuery = queryConfig as {\n params: {\n query: NonNullable<paths['/permissions']['get']['parameters']['query']>;\n };\n };\n\n const { data, isPending, isFetching } = hooks.useQuery(\n 'get',\n '/permissions',\n permissionsQuery,\n defaultEntityQueryOptions,\n );\n\n const seedPermissions = hooks.useMutation('post', '/permissions/seed', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/permissions'] });\n toast.success('Permissions seeded');\n },\n onError: () => {\n toast.error('Failed to seed permissions');\n },\n });\n\n const isLoading = isPending || isFetching;\n const permissions = data?.permissions ?? [];\n const { total, pageCount } = useEntityPagination({\n items: permissions,\n total: data?.total,\n pageSize: params.pageSize,\n });\n\n return (\n <PageContainer className=\"flex flex-1 flex-col gap-4 p-4 pt-0\">\n <PageBody className=\"px-0\">\n <EntityHeader\n icon={<IconKey className=\"h-5 w-5\" />}\n title=\"Permissions\"\n actions={\n <Button\n variant=\"outline\"\n onClick={() => seedPermissions.mutate({})}\n loading={seedPermissions.isPending}\n >\n Seed permissions\n </Button>\n }\n search={\n <EntitySearch\n paramKey=\"search\"\n placeholder=\"Search permissions...\"\n />\n }\n filter={\n <EntityFilter\n options={[\n { label: 'All', value: '' },\n { label: 'Application', value: 'application' },\n { label: 'Feature', value: 'feature' },\n { label: 'Activity', value: 'activity' },\n ]}\n placeholder=\"Search field\"\n />\n }\n sort={\n <EntitySort\n defaultSort=\"id\"\n defaultOrder=\"asc\"\n options={[\n { label: 'ID', value: 'id' },\n { label: 'Application', value: 'application' },\n { label: 'Feature', value: 'feature' },\n { label: 'Activity', value: 'activity' },\n ]}\n />\n }\n view={<EntityViewToggle views={['table', 'card']} />}\n />\n <PermissionsList\n data={permissions}\n isLoading={isLoading}\n view={(params.view || 'table') as 'table' | 'card'}\n pageIndex={params.page - 1}\n pageSize={params.pageSize}\n pageCount={pageCount}\n totalRows={total}\n onPageChange={(p) => setParams({ page: p + 1 })}\n onPageSizeChange={(size) => setParams({ pageSize: size, page: 1 })}\n />\n </PageBody>\n </PageContainer>\n );\n}\n","import { keepPreviousData } from '@tanstack/react-query';\n\nexport const defaultEntityQueryOptions = {\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n staleTime: 60 * 1000,\n gcTime: 5 * 60 * 1000,\n placeholderData: keepPreviousData,\n retry: 1,\n};\n","'use client';\n\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { deepmerge } from 'deepmerge-ts';\nimport createFetchClient from 'openapi-fetch';\nimport createClient from 'openapi-react-query';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useMemo, useState } from 'react';\nimport type { paths } from './data/openapi';\nimport { createTranslator } from './lib/translations';\nimport {\n type AuthClientConfig,\n type AuthResponse,\n defaultAuthClientConfig,\n type Session,\n type User,\n} from './types';\nimport { getSessionCookieName } from './utils/cookie';\nimport { createCustomFetch } from './utils/custom-fetch';\n\n// biome-ignore lint/suspicious/noExplicitAny: OpenAPI hooks type\ntype OpenApiHooks = any;\n\n// --- Utility: Check if running on server ---\nfunction isServer(): boolean {\n return typeof document === 'undefined';\n}\n\n/**\n * @deprecated Cookie is httpOnly and cannot be read client-side.\n * Use `useSession().isAuthenticated` instead.\n * This function always returns false on client.\n */\nexport function hasAuthCookie(_cookieName: string): boolean {\n // Cookie is httpOnly, can't check client-side\n // Always return false - use useSession() for auth status\n return false;\n}\n\n// --- Types ---\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\ntype AuthState = {\n user: User | null;\n session: Session | null;\n status: AuthStatus;\n error: Error | null;\n};\n\ntype SessionContextValue = AuthState & {\n isLoading: boolean;\n isAuthenticated: boolean;\n refresh: () => Promise<void>;\n signOut: () => Promise<void>;\n};\n\ntype ApiContextValue = {\n hooks: OpenApiHooks;\n setAuth: (auth: AuthResponse) => void;\n clearAuth: () => void;\n refresh: () => Promise<void>;\n};\n\ntype ConfigContextValue = {\n config: AuthClientConfig;\n cookieName: string;\n t: (key: string, params?: Record<string, string | number>) => string;\n};\n\nconst SessionContext = createContext<SessionContextValue | null>(null);\nconst ApiContext = createContext<ApiContextValue | null>(null);\nconst ConfigContext = createContext<ConfigContextValue | null>(null);\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n },\n },\n});\n\n// --- Hooks ---\n\n/**\n * Get session state including user, session, and auth status.\n * - `status`: 'loading' | 'authenticated' | 'unauthenticated'\n * - `isLoading`: true while fetching session\n * - `isAuthenticated`: true if user and session exist\n */\nexport function useSession(): SessionContextValue {\n const context = useContext(SessionContext);\n if (!context) {\n throw new Error('useSession must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useApi(): ApiContextValue {\n const context = useContext(ApiContext);\n if (!context) {\n throw new Error('useApi must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useConfig(): ConfigContextValue {\n const context = useContext(ConfigContext);\n if (!context) {\n throw new Error('useConfig must be used within MesobAuthProvider');\n }\n return context;\n}\n\n/**\n * @deprecated Cookie is httpOnly, can't be checked client-side.\n * Use `useSession().isAuthenticated` instead.\n */\nexport function useHasAuthCookie(): boolean {\n const { status } = useSession();\n return status === 'authenticated' || status === 'loading';\n}\n\n// --- Provider ---\n\ntype MesobAuthProviderProps = {\n config: AuthClientConfig;\n children: ReactNode;\n};\n\nexport function MesobAuthProvider({\n config,\n children,\n}: MesobAuthProviderProps) {\n const mergedConfig = useMemo(\n () =>\n deepmerge(\n { ...defaultAuthClientConfig } as Partial<AuthClientConfig>,\n config,\n ) as AuthClientConfig,\n [config],\n );\n\n const api = useMemo(\n () =>\n createFetchClient<paths>({\n baseUrl: mergedConfig.baseURL,\n fetch: createCustomFetch(mergedConfig),\n }),\n [mergedConfig],\n );\n\n const hooks = useMemo(() => createClient(api), [api]);\n const cookieName = useMemo(\n () => getSessionCookieName(mergedConfig),\n [mergedConfig],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <AuthStateProvider\n config={mergedConfig}\n hooks={hooks}\n cookieName={cookieName}\n >\n {children}\n </AuthStateProvider>\n </QueryClientProvider>\n );\n}\n\ntype AuthStateProviderProps = {\n config: AuthClientConfig;\n hooks: OpenApiHooks;\n cookieName: string;\n children: ReactNode;\n};\n\nfunction AuthStateProvider({\n config,\n hooks,\n cookieName,\n children,\n}: AuthStateProviderProps) {\n // Manual override for sign-out / sign-in\n const [override, setOverride] = useState<AuthState | null>(null);\n\n // Always fetch session - cookie is httpOnly, can't check client-side\n // Server will read the cookie and return user/session if valid\n const {\n data: sessionData,\n isLoading,\n isFetched,\n error: sessionError,\n refetch,\n } = hooks.useQuery(\n 'get',\n '/session',\n {},\n {\n enabled: !(override || isServer()),\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n gcTime: 0,\n staleTime: 0,\n },\n );\n\n // Derive state directly - no useEffect\n const user = override?.user ?? sessionData?.user ?? null;\n const session = override?.session ?? sessionData?.session ?? null;\n const error = override?.error ?? (sessionError as Error | null);\n\n // Check error status code\n const errorStatus = (() => {\n if (!sessionError) {\n return null;\n }\n const err = sessionError as { status?: number };\n return err.status ?? null;\n })();\n\n // Check if error is a network/connection error\n const isNetworkError = (() => {\n if (!sessionError) {\n return false;\n }\n const error = sessionError as Error & { cause?: unknown; data?: unknown };\n const errorMessage =\n error.message || String(error) || JSON.stringify(error);\n // Network errors: TypeError, DOMException, or fetch failures\n if (\n error instanceof TypeError ||\n error instanceof DOMException ||\n error.name === 'TypeError' ||\n errorMessage.includes('Failed to fetch') ||\n errorMessage.includes('ERR_CONNECTION_REFUSED') ||\n errorMessage.includes('NetworkError') ||\n errorMessage.includes('Network request failed') ||\n errorMessage.includes('fetch failed')\n ) {\n return true;\n }\n // Check error cause\n if (error.cause) {\n const causeStr = String(error.cause);\n if (\n causeStr.includes('Failed to fetch') ||\n causeStr.includes('ERR_CONNECTION_REFUSED') ||\n causeStr.includes('NetworkError')\n ) {\n return true;\n }\n }\n return false;\n })();\n\n // Compute status\n // biome-ignore lint: Status determination requires multiple checks\n const status: AuthStatus = (() => {\n if (override) {\n return override.status;\n }\n if (isServer()) {\n return 'loading';\n }\n if (user && session) {\n return 'authenticated';\n }\n // Check for network errors or auth errors first - allow auth page to show\n if (isNetworkError || errorStatus === 401) {\n return 'unauthenticated';\n }\n // If we have an error but it's not a network error, still check loading state\n if (sessionError && !isNetworkError && errorStatus !== 401) {\n if (errorStatus && errorStatus >= 500) {\n return 'authenticated';\n }\n // Other errors mean unauthenticated\n if (isFetched) {\n return 'unauthenticated';\n }\n }\n if (isLoading || !isFetched) {\n return 'loading';\n }\n if (isFetched && !user && !session) {\n return 'unauthenticated';\n }\n return 'unauthenticated';\n })();\n\n const signOutMutation = hooks.useMutation('post', '/sign-out');\n const t = createTranslator(config.messages || {});\n\n const setAuth = (auth: AuthResponse) => {\n setOverride({\n user: auth.user,\n session: auth.session,\n status: 'authenticated',\n error: null,\n });\n };\n\n const clearAuth = () => {\n setOverride({\n user: null,\n session: null,\n status: 'unauthenticated',\n error: null,\n });\n };\n\n const refresh = async () => {\n setOverride(null);\n await refetch();\n };\n\n const signOut = async () => {\n try {\n await signOutMutation.mutateAsync({});\n } finally {\n clearAuth();\n }\n };\n\n return (\n <ConfigContext.Provider value={{ config, cookieName, t }}>\n <ApiContext.Provider value={{ hooks, setAuth, clearAuth, refresh }}>\n <SessionContext.Provider\n value={{\n user,\n session,\n status,\n error,\n isLoading: status === 'loading',\n isAuthenticated: status === 'authenticated',\n refresh,\n signOut,\n }}\n >\n {children}\n </SessionContext.Provider>\n </ApiContext.Provider>\n </ConfigContext.Provider>\n );\n}\n","import type { AuthClientConfig } from '../types';\n\nconst isProduction =\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production';\n\nexport const getSessionCookieName = (config: AuthClientConfig): string => {\n const prefix = config.cookiePrefix || '';\n const baseName = 'session_token';\n if (prefix) {\n return `${prefix}_${baseName}`;\n }\n return isProduction ? '__Host-session_token' : baseName;\n};\n","'use client';\n\nimport {\n Badge,\n DataTablePagination,\n DisplayTable,\n EntityEmptyState,\n EntityLoadingState,\n Tbody,\n Td,\n Th,\n Thead,\n Tr,\n} from '@mesob/ui/components';\nimport { IconKey } from '@tabler/icons-react';\nimport { PermissionCard } from './permission-card';\nimport type { Permission } from './permissions-data';\n\nconst TABLE_COLUMN_COUNT = 4;\n\nfunction descStr(d: unknown): string {\n if (d == null) {\n return '';\n }\n if (typeof d === 'string') {\n return d;\n }\n if (typeof d === 'object' && d !== null && 'en' in d) {\n return String((d as { en?: string }).en ?? '');\n }\n return String(d);\n}\n\ntype PermissionsListProps = {\n data: Permission[];\n isLoading?: boolean;\n view: 'table' | 'card';\n pageIndex: number;\n pageSize: number;\n pageCount: number;\n totalRows: number;\n onPageChange: (page: number) => void;\n onPageSizeChange: (size: number) => void;\n};\n\nexport function PermissionsList({\n data,\n isLoading,\n view,\n pageIndex,\n pageSize,\n pageCount,\n totalRows,\n onPageChange,\n onPageSizeChange,\n}: PermissionsListProps) {\n if (isLoading) {\n return (\n <EntityLoadingState\n view={view}\n rowCount={pageSize}\n columnCount={TABLE_COLUMN_COUNT}\n cardCount={pageSize}\n />\n );\n }\n if (totalRows === 0) {\n return (\n <EntityEmptyState\n icon={IconKey}\n entityName=\"permission\"\n title=\"No permissions\"\n description=\"Permissions are managed by the system.\"\n />\n );\n }\n if (view === 'table') {\n return (\n <div className=\"space-y-4\">\n <DisplayTable withTableBorder>\n <Thead>\n <Tr>\n <Th>Application</Th>\n <Th>Feature</Th>\n <Th>Activity</Th>\n <Th>Description</Th>\n </Tr>\n </Thead>\n <Tbody>\n {data.map((p) => (\n <Tr key={p.id}>\n <Td>\n <Badge variant=\"secondary\">{p.application}</Badge>\n </Td>\n <Td>\n <Badge variant=\"outline\">{p.feature}</Badge>\n </Td>\n <Td>\n <span className=\"font-medium\">{p.activity}</span>\n </Td>\n <Td>\n <span className=\"text-muted-foreground line-clamp-1 max-w-[200px]\">\n {descStr(p.description) || '—'}\n </span>\n </Td>\n </Tr>\n ))}\n </Tbody>\n </DisplayTable>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n }\n return (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {data.map((p) => (\n <PermissionCard key={p.id} permission={p} />\n ))}\n </div>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n}\n","'use client';\n\nimport { Badge, Card, CardContent, CardHeader } from '@mesob/ui/components';\nimport type { Permission } from './permissions-data';\n\ntype PermissionCardProps = { permission: Permission };\n\nfunction descStr(d: unknown): string {\n if (d == null) {\n return '';\n }\n if (typeof d === 'string') {\n return d;\n }\n if (typeof d === 'object' && d !== null && 'en' in d) {\n return String((d as { en?: string }).en ?? '');\n }\n return String(d);\n}\n\nexport function PermissionCard({ permission }: PermissionCardProps) {\n return (\n <Card className=\"hover:shadow-md transition-shadow\">\n <CardHeader className=\"pb-2\">\n <p className=\"font-semibold\">{permission.activity}</p>\n <div className=\"flex flex-wrap gap-1\">\n <Badge variant=\"secondary\">{permission.application}</Badge>\n <Badge variant=\"outline\">{permission.feature}</Badge>\n </div>\n </CardHeader>\n <CardContent>\n {descStr(permission.description) && (\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {descStr(permission.description)}\n </p>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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","names":["IconKey","Badge","jsx","jsx","jsxs","descStr","Badge","jsx","jsxs","IconKey"]}
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type RoleDetailLayoutPageProps = {
5
+ children: ReactNode;
6
+ params: Promise<{
7
+ id: string;
8
+ }>;
9
+ };
10
+ declare function RoleDetailLayoutPage({ children, params, }: RoleDetailLayoutPageProps): Promise<react_jsx_runtime.JSX.Element>;
11
+
12
+ export { RoleDetailLayoutPage as default };
@@ -0,0 +1,145 @@
1
+ // src/components/iam/role-detail-layout.tsx
2
+ import {
3
+ EntityDetailHeader,
4
+ EntityEmptyState,
5
+ PageContainer,
6
+ useBreadcrumbs
7
+ } from "@mesob/ui/components";
8
+ import { IconShield } from "@tabler/icons-react";
9
+ import { useMemo as useMemo2 } from "react";
10
+
11
+ // src/provider.tsx
12
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
13
+ import { deepmerge } from "deepmerge-ts";
14
+ import createFetchClient from "openapi-fetch";
15
+ import createClient from "openapi-react-query";
16
+ import { createContext, useContext, useMemo, useState } from "react";
17
+
18
+ // src/utils/cookie.ts
19
+ var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
20
+
21
+ // src/provider.tsx
22
+ import { jsx } from "react/jsx-runtime";
23
+ var SessionContext = createContext(null);
24
+ var ApiContext = createContext(null);
25
+ var ConfigContext = createContext(null);
26
+ var queryClient = new QueryClient({
27
+ defaultOptions: {
28
+ queries: {
29
+ refetchOnWindowFocus: false
30
+ }
31
+ }
32
+ });
33
+ function useApi() {
34
+ const context = useContext(ApiContext);
35
+ if (!context) {
36
+ throw new Error("useApi must be used within MesobAuthProvider");
37
+ }
38
+ return context;
39
+ }
40
+ function useConfig() {
41
+ const context = useContext(ConfigContext);
42
+ if (!context) {
43
+ throw new Error("useConfig must be used within MesobAuthProvider");
44
+ }
45
+ return context;
46
+ }
47
+
48
+ // src/components/iam/role-detail-layout.tsx
49
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
50
+ function str(value) {
51
+ if (value == null) {
52
+ return "";
53
+ }
54
+ if (typeof value === "string") {
55
+ return value;
56
+ }
57
+ if (typeof value === "object" && value !== null && "en" in value) {
58
+ const localized = value;
59
+ return localized.en ?? localized.am ?? "";
60
+ }
61
+ return String(value);
62
+ }
63
+ function RoleDetailLayout({
64
+ roleId,
65
+ basePath = "/iam/roles",
66
+ children
67
+ }) {
68
+ const { hooks } = useApi();
69
+ const { config } = useConfig();
70
+ const { data, isLoading, isFetching, isError } = hooks.useQuery(
71
+ "get",
72
+ "/roles/{id}",
73
+ { params: { path: { id: roleId } } },
74
+ { enabled: !!roleId }
75
+ );
76
+ const role = data?.role;
77
+ const title = role ? str(role.name) || role.code : roleId ?? "Role";
78
+ useBreadcrumbs({
79
+ items: [
80
+ { label: "Home", href: "/dashboard" },
81
+ { label: "IAM", href: "/iam" },
82
+ { label: "Roles", href: basePath },
83
+ { label: title }
84
+ ]
85
+ });
86
+ const tabs = useMemo2(
87
+ () => [
88
+ { value: "detail", name: "Detail", href: `${basePath}/${roleId}` },
89
+ {
90
+ value: "permissions",
91
+ name: "Permissions",
92
+ href: `${basePath}/${roleId}/permissions`
93
+ },
94
+ {
95
+ value: "users",
96
+ name: "Users",
97
+ href: `${basePath}/${roleId}/users`
98
+ }
99
+ ],
100
+ [basePath, roleId]
101
+ );
102
+ if (!roleId) {
103
+ return null;
104
+ }
105
+ const loading = isLoading || isFetching;
106
+ if (!(loading || role)) {
107
+ return /* @__PURE__ */ jsx2(PageContainer, { className: "flex flex-1 flex-col gap-4 p-4 pt-0", children: /* @__PURE__ */ jsx2(
108
+ EntityEmptyState,
109
+ {
110
+ icon: IconShield,
111
+ entityName: "role",
112
+ title: isError ? "Role unavailable" : "Role not found",
113
+ description: isError ? "Failed to load this role." : "This role no longer exists.",
114
+ actionLabel: "Back to roles",
115
+ onAction: () => config.navigation?.onNavigate?.(basePath) ?? window.history.back()
116
+ }
117
+ ) });
118
+ }
119
+ return /* @__PURE__ */ jsxs(PageContainer, { className: "flex flex-1 flex-col gap-4 p-4 pt-0", children: [
120
+ /* @__PURE__ */ jsx2(
121
+ EntityDetailHeader,
122
+ {
123
+ title,
124
+ icon: /* @__PURE__ */ jsx2(IconShield, { className: "h-5 w-5" }),
125
+ loading,
126
+ tabs
127
+ }
128
+ ),
129
+ children
130
+ ] });
131
+ }
132
+
133
+ // src/pages/iam/role-detail-layout.tsx
134
+ import { jsx as jsx3 } from "react/jsx-runtime";
135
+ async function RoleDetailLayoutPage({
136
+ children,
137
+ params
138
+ }) {
139
+ const { id } = await params;
140
+ return /* @__PURE__ */ jsx3(RoleDetailLayout, { roleId: id, basePath: "/iam/roles", children });
141
+ }
142
+ export {
143
+ RoleDetailLayoutPage as default
144
+ };
145
+ //# sourceMappingURL=role-detail-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/iam/role-detail-layout.tsx","../../../src/provider.tsx","../../../src/utils/cookie.ts","../../../src/pages/iam/role-detail-layout.tsx"],"sourcesContent":["'use client';\n\nimport {\n EntityDetailHeader,\n EntityEmptyState,\n PageContainer,\n type TabItem,\n useBreadcrumbs,\n} from '@mesob/ui/components';\nimport { IconShield } from '@tabler/icons-react';\nimport type { ReactNode } from 'react';\nimport { useMemo } from 'react';\nimport { useApi, useConfig } from '../../provider';\n\nfunction str(value: unknown): string {\n if (value == null) {\n return '';\n }\n if (typeof value === 'string') {\n return value;\n }\n if (typeof value === 'object' && value !== null && 'en' in value) {\n const localized = value as { en?: string; am?: string };\n return localized.en ?? localized.am ?? '';\n }\n return String(value);\n}\n\ntype RoleDetailLayoutProps = {\n roleId: string;\n basePath?: string;\n children: ReactNode;\n};\n\nexport function RoleDetailLayout({\n roleId,\n basePath = '/iam/roles',\n children,\n}: RoleDetailLayoutProps) {\n const { hooks } = useApi();\n const { config } = useConfig();\n const { data, isLoading, isFetching, isError } = hooks.useQuery(\n 'get',\n '/roles/{id}',\n { params: { path: { id: roleId } } },\n { enabled: !!roleId },\n );\n\n const role = data?.role;\n const title = role ? str(role.name) || role.code : (roleId ?? 'Role');\n\n useBreadcrumbs({\n items: [\n { label: 'Home', href: '/dashboard' },\n { label: 'IAM', href: '/iam' },\n { label: 'Roles', href: basePath },\n { label: title },\n ],\n });\n\n const tabs: TabItem[] = useMemo(\n () => [\n { value: 'detail', name: 'Detail', href: `${basePath}/${roleId}` },\n {\n value: 'permissions',\n name: 'Permissions',\n href: `${basePath}/${roleId}/permissions`,\n },\n {\n value: 'users',\n name: 'Users',\n href: `${basePath}/${roleId}/users`,\n },\n ],\n [basePath, roleId],\n );\n\n if (!roleId) {\n return null;\n }\n\n const loading = isLoading || isFetching;\n if (!(loading || role)) {\n return (\n <PageContainer className=\"flex flex-1 flex-col gap-4 p-4 pt-0\">\n <EntityEmptyState\n icon={IconShield}\n entityName=\"role\"\n title={isError ? 'Role unavailable' : 'Role not found'}\n description={\n isError\n ? 'Failed to load this role.'\n : 'This role no longer exists.'\n }\n actionLabel=\"Back to roles\"\n onAction={() =>\n config.navigation?.onNavigate?.(basePath) ?? window.history.back()\n }\n />\n </PageContainer>\n );\n }\n\n return (\n <PageContainer className=\"flex flex-1 flex-col gap-4 p-4 pt-0\">\n <EntityDetailHeader\n title={title}\n icon={<IconShield className=\"h-5 w-5\" />}\n loading={loading}\n tabs={tabs}\n />\n {children}\n </PageContainer>\n );\n}\n","'use client';\n\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { deepmerge } from 'deepmerge-ts';\nimport createFetchClient from 'openapi-fetch';\nimport createClient from 'openapi-react-query';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useMemo, useState } from 'react';\nimport type { paths } from './data/openapi';\nimport { createTranslator } from './lib/translations';\nimport {\n type AuthClientConfig,\n type AuthResponse,\n defaultAuthClientConfig,\n type Session,\n type User,\n} from './types';\nimport { getSessionCookieName } from './utils/cookie';\nimport { createCustomFetch } from './utils/custom-fetch';\n\n// biome-ignore lint/suspicious/noExplicitAny: OpenAPI hooks type\ntype OpenApiHooks = any;\n\n// --- Utility: Check if running on server ---\nfunction isServer(): boolean {\n return typeof document === 'undefined';\n}\n\n/**\n * @deprecated Cookie is httpOnly and cannot be read client-side.\n * Use `useSession().isAuthenticated` instead.\n * This function always returns false on client.\n */\nexport function hasAuthCookie(_cookieName: string): boolean {\n // Cookie is httpOnly, can't check client-side\n // Always return false - use useSession() for auth status\n return false;\n}\n\n// --- Types ---\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\ntype AuthState = {\n user: User | null;\n session: Session | null;\n status: AuthStatus;\n error: Error | null;\n};\n\ntype SessionContextValue = AuthState & {\n isLoading: boolean;\n isAuthenticated: boolean;\n refresh: () => Promise<void>;\n signOut: () => Promise<void>;\n};\n\ntype ApiContextValue = {\n hooks: OpenApiHooks;\n setAuth: (auth: AuthResponse) => void;\n clearAuth: () => void;\n refresh: () => Promise<void>;\n};\n\ntype ConfigContextValue = {\n config: AuthClientConfig;\n cookieName: string;\n t: (key: string, params?: Record<string, string | number>) => string;\n};\n\nconst SessionContext = createContext<SessionContextValue | null>(null);\nconst ApiContext = createContext<ApiContextValue | null>(null);\nconst ConfigContext = createContext<ConfigContextValue | null>(null);\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n },\n },\n});\n\n// --- Hooks ---\n\n/**\n * Get session state including user, session, and auth status.\n * - `status`: 'loading' | 'authenticated' | 'unauthenticated'\n * - `isLoading`: true while fetching session\n * - `isAuthenticated`: true if user and session exist\n */\nexport function useSession(): SessionContextValue {\n const context = useContext(SessionContext);\n if (!context) {\n throw new Error('useSession must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useApi(): ApiContextValue {\n const context = useContext(ApiContext);\n if (!context) {\n throw new Error('useApi must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useConfig(): ConfigContextValue {\n const context = useContext(ConfigContext);\n if (!context) {\n throw new Error('useConfig must be used within MesobAuthProvider');\n }\n return context;\n}\n\n/**\n * @deprecated Cookie is httpOnly, can't be checked client-side.\n * Use `useSession().isAuthenticated` instead.\n */\nexport function useHasAuthCookie(): boolean {\n const { status } = useSession();\n return status === 'authenticated' || status === 'loading';\n}\n\n// --- Provider ---\n\ntype MesobAuthProviderProps = {\n config: AuthClientConfig;\n children: ReactNode;\n};\n\nexport function MesobAuthProvider({\n config,\n children,\n}: MesobAuthProviderProps) {\n const mergedConfig = useMemo(\n () =>\n deepmerge(\n { ...defaultAuthClientConfig } as Partial<AuthClientConfig>,\n config,\n ) as AuthClientConfig,\n [config],\n );\n\n const api = useMemo(\n () =>\n createFetchClient<paths>({\n baseUrl: mergedConfig.baseURL,\n fetch: createCustomFetch(mergedConfig),\n }),\n [mergedConfig],\n );\n\n const hooks = useMemo(() => createClient(api), [api]);\n const cookieName = useMemo(\n () => getSessionCookieName(mergedConfig),\n [mergedConfig],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <AuthStateProvider\n config={mergedConfig}\n hooks={hooks}\n cookieName={cookieName}\n >\n {children}\n </AuthStateProvider>\n </QueryClientProvider>\n );\n}\n\ntype AuthStateProviderProps = {\n config: AuthClientConfig;\n hooks: OpenApiHooks;\n cookieName: string;\n children: ReactNode;\n};\n\nfunction AuthStateProvider({\n config,\n hooks,\n cookieName,\n children,\n}: AuthStateProviderProps) {\n // Manual override for sign-out / sign-in\n const [override, setOverride] = useState<AuthState | null>(null);\n\n // Always fetch session - cookie is httpOnly, can't check client-side\n // Server will read the cookie and return user/session if valid\n const {\n data: sessionData,\n isLoading,\n isFetched,\n error: sessionError,\n refetch,\n } = hooks.useQuery(\n 'get',\n '/session',\n {},\n {\n enabled: !(override || isServer()),\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n gcTime: 0,\n staleTime: 0,\n },\n );\n\n // Derive state directly - no useEffect\n const user = override?.user ?? sessionData?.user ?? null;\n const session = override?.session ?? sessionData?.session ?? null;\n const error = override?.error ?? (sessionError as Error | null);\n\n // Check error status code\n const errorStatus = (() => {\n if (!sessionError) {\n return null;\n }\n const err = sessionError as { status?: number };\n return err.status ?? null;\n })();\n\n // Check if error is a network/connection error\n const isNetworkError = (() => {\n if (!sessionError) {\n return false;\n }\n const error = sessionError as Error & { cause?: unknown; data?: unknown };\n const errorMessage =\n error.message || String(error) || JSON.stringify(error);\n // Network errors: TypeError, DOMException, or fetch failures\n if (\n error instanceof TypeError ||\n error instanceof DOMException ||\n error.name === 'TypeError' ||\n errorMessage.includes('Failed to fetch') ||\n errorMessage.includes('ERR_CONNECTION_REFUSED') ||\n errorMessage.includes('NetworkError') ||\n errorMessage.includes('Network request failed') ||\n errorMessage.includes('fetch failed')\n ) {\n return true;\n }\n // Check error cause\n if (error.cause) {\n const causeStr = String(error.cause);\n if (\n causeStr.includes('Failed to fetch') ||\n causeStr.includes('ERR_CONNECTION_REFUSED') ||\n causeStr.includes('NetworkError')\n ) {\n return true;\n }\n }\n return false;\n })();\n\n // Compute status\n // biome-ignore lint: Status determination requires multiple checks\n const status: AuthStatus = (() => {\n if (override) {\n return override.status;\n }\n if (isServer()) {\n return 'loading';\n }\n if (user && session) {\n return 'authenticated';\n }\n // Check for network errors or auth errors first - allow auth page to show\n if (isNetworkError || errorStatus === 401) {\n return 'unauthenticated';\n }\n // If we have an error but it's not a network error, still check loading state\n if (sessionError && !isNetworkError && errorStatus !== 401) {\n if (errorStatus && errorStatus >= 500) {\n return 'authenticated';\n }\n // Other errors mean unauthenticated\n if (isFetched) {\n return 'unauthenticated';\n }\n }\n if (isLoading || !isFetched) {\n return 'loading';\n }\n if (isFetched && !user && !session) {\n return 'unauthenticated';\n }\n return 'unauthenticated';\n })();\n\n const signOutMutation = hooks.useMutation('post', '/sign-out');\n const t = createTranslator(config.messages || {});\n\n const setAuth = (auth: AuthResponse) => {\n setOverride({\n user: auth.user,\n session: auth.session,\n status: 'authenticated',\n error: null,\n });\n };\n\n const clearAuth = () => {\n setOverride({\n user: null,\n session: null,\n status: 'unauthenticated',\n error: null,\n });\n };\n\n const refresh = async () => {\n setOverride(null);\n await refetch();\n };\n\n const signOut = async () => {\n try {\n await signOutMutation.mutateAsync({});\n } finally {\n clearAuth();\n }\n };\n\n return (\n <ConfigContext.Provider value={{ config, cookieName, t }}>\n <ApiContext.Provider value={{ hooks, setAuth, clearAuth, refresh }}>\n <SessionContext.Provider\n value={{\n user,\n session,\n status,\n error,\n isLoading: status === 'loading',\n isAuthenticated: status === 'authenticated',\n refresh,\n signOut,\n }}\n >\n {children}\n </SessionContext.Provider>\n </ApiContext.Provider>\n </ConfigContext.Provider>\n );\n}\n","import type { AuthClientConfig } from '../types';\n\nconst isProduction =\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production';\n\nexport const getSessionCookieName = (config: AuthClientConfig): string => {\n const prefix = config.cookiePrefix || '';\n const baseName = 'session_token';\n if (prefix) {\n return `${prefix}_${baseName}`;\n }\n return isProduction ? '__Host-session_token' : baseName;\n};\n","import type { ReactNode } from 'react';\nimport { RoleDetailLayout } from '../../components/iam/role-detail-layout';\n\ntype RoleDetailLayoutPageProps = {\n children: ReactNode;\n params: Promise<{ id: string }>;\n};\n\nexport default async function RoleDetailLayoutPage({\n children,\n params,\n}: RoleDetailLayoutPageProps) {\n const { id } = await params;\n\n return (\n <RoleDetailLayout roleId={id} basePath=\"/iam/roles\">\n {children}\n </RoleDetailLayout>\n );\n}\n"],"mappings":"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","names":["useMemo","jsx","useMemo","jsx"]}
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Metadata } from 'next';
3
+
4
+ type RoleDetailPageProps = {
5
+ params: Promise<{
6
+ id: string;
7
+ }>;
8
+ };
9
+ declare const metadata: Metadata;
10
+ declare function RoleDetailRoutePage({ params, }: RoleDetailPageProps): Promise<react_jsx_runtime.JSX.Element>;
11
+
12
+ export { RoleDetailRoutePage as default, metadata };