@mesob/auth-react 0.0.8 → 0.1.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.
- package/dist/components/auth/auth-page-layout.d.ts +1 -3
- package/dist/components/auth/auth-page-layout.js +1 -17
- package/dist/components/auth/auth-page-layout.js.map +1 -1
- package/dist/components/auth/countdown.js +70 -9
- package/dist/components/auth/countdown.js.map +1 -1
- package/dist/components/auth/forgot-password.js +101 -35
- package/dist/components/auth/forgot-password.js.map +1 -1
- package/dist/components/auth/pages/forgot-password-page.d.ts +2 -13
- package/dist/components/auth/pages/forgot-password-page.js +198 -126
- package/dist/components/auth/pages/forgot-password-page.js.map +1 -1
- package/dist/components/auth/pages/reset-password-page.d.ts +1 -12
- package/dist/components/auth/pages/reset-password-page.js +288 -200
- package/dist/components/auth/pages/reset-password-page.js.map +1 -1
- package/dist/components/auth/pages/sign-in-page.d.ts +1 -12
- package/dist/components/auth/pages/sign-in-page.js +352 -230
- package/dist/components/auth/pages/sign-in-page.js.map +1 -1
- package/dist/components/auth/pages/sign-up-page.d.ts +1 -11
- package/dist/components/auth/pages/sign-up-page.js +310 -216
- package/dist/components/auth/pages/sign-up-page.js.map +1 -1
- package/dist/components/auth/pages/verify-email-page.d.ts +2 -12
- package/dist/components/auth/pages/verify-email-page.js +203 -135
- package/dist/components/auth/pages/verify-email-page.js.map +1 -1
- package/dist/components/auth/pages/verify-phone-page.d.ts +1 -11
- package/dist/components/auth/pages/verify-phone-page.js +206 -137
- package/dist/components/auth/pages/verify-phone-page.js.map +1 -1
- package/dist/components/auth/reset-password-form.d.ts +1 -1
- package/dist/components/auth/reset-password-form.js +188 -106
- package/dist/components/auth/reset-password-form.js.map +1 -1
- package/dist/components/auth/sign-in.d.ts +3 -3
- package/dist/components/auth/sign-in.js +228 -109
- package/dist/components/auth/sign-in.js.map +1 -1
- package/dist/components/auth/sign-up.js +210 -122
- package/dist/components/auth/sign-up.js.map +1 -1
- package/dist/components/auth/verification-form.d.ts +1 -1
- package/dist/components/auth/verification-form.js +101 -53
- package/dist/components/auth/verification-form.js.map +1 -1
- package/dist/components/error-boundary.d.ts +27 -0
- package/dist/components/error-boundary.js +49 -0
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/components/iam/permissions/permissions-page.d.ts +5 -0
- package/dist/components/iam/permissions/permissions-page.js +201 -0
- package/dist/components/iam/permissions/permissions-page.js.map +1 -0
- package/dist/components/iam/roles/roles-page.d.ts +5 -0
- package/dist/components/iam/roles/roles-page.js +199 -0
- package/dist/components/iam/roles/roles-page.js.map +1 -0
- package/dist/components/iam/sessions/sessions-page.d.ts +5 -0
- package/dist/components/iam/sessions/sessions-page.js +202 -0
- package/dist/components/iam/sessions/sessions-page.js.map +1 -0
- package/dist/components/iam/tenants/tenants-page.d.ts +5 -0
- package/dist/components/iam/tenants/tenants-page.js +202 -0
- package/dist/components/iam/tenants/tenants-page.js.map +1 -0
- package/dist/components/iam/users/users-page.d.ts +5 -0
- package/dist/components/iam/users/users-page.js +211 -0
- package/dist/components/iam/users/users-page.js.map +1 -0
- package/dist/components/profile/profile-page.d.ts +8 -0
- package/dist/components/profile/profile-page.js +163 -0
- package/dist/components/profile/profile-page.js.map +1 -0
- package/dist/components/shared/data-table/data-table.d.ts +22 -0
- package/dist/components/shared/data-table/data-table.js +85 -0
- package/dist/components/shared/data-table/data-table.js.map +1 -0
- package/dist/components/skeletons/auth-form-skeleton.d.ts +5 -0
- package/dist/components/skeletons/auth-form-skeleton.js +32 -0
- package/dist/components/skeletons/auth-form-skeleton.js.map +1 -0
- package/dist/components/skeletons/profile-skeleton.d.ts +5 -0
- package/dist/components/skeletons/profile-skeleton.js +33 -0
- package/dist/components/skeletons/profile-skeleton.js.map +1 -0
- package/dist/components/skeletons/table-skeleton.d.ts +9 -0
- package/dist/components/skeletons/table-skeleton.js +39 -0
- package/dist/components/skeletons/table-skeleton.js.map +1 -0
- package/dist/handle-error-BqDMxnQZ.d.ts +8 -0
- package/dist/index.d.ts +75 -208
- package/dist/index.js +2091 -1057
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
- package/dist/handle-error-H0iqQxJ5.d.ts +0 -6
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/iam/tenants/tenants-page.tsx
|
|
4
|
+
import { Badge } from "@mesob/ui/components/badge";
|
|
5
|
+
import { Button } from "@mesob/ui/components/button";
|
|
6
|
+
import { useState as useState2 } from "react";
|
|
7
|
+
|
|
8
|
+
// src/provider.tsx
|
|
9
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
10
|
+
import { deepmerge } from "deepmerge-ts";
|
|
11
|
+
import createFetchClient from "openapi-fetch";
|
|
12
|
+
import createClient from "openapi-react-query";
|
|
13
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
|
+
var SessionContext = createContext(null);
|
|
16
|
+
var ApiContext = createContext(null);
|
|
17
|
+
var ConfigContext = createContext(null);
|
|
18
|
+
var queryClient = new QueryClient({
|
|
19
|
+
defaultOptions: {
|
|
20
|
+
queries: {
|
|
21
|
+
staleTime: 1e3 * 60 * 5,
|
|
22
|
+
gcTime: 1e3 * 60 * 10,
|
|
23
|
+
retry: 1,
|
|
24
|
+
refetchOnWindowFocus: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
function useApi() {
|
|
29
|
+
const context = useContext(ApiContext);
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("useApi must be used within MesobAuthProvider");
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/components/shared/data-table/data-table.tsx
|
|
37
|
+
import {
|
|
38
|
+
Table,
|
|
39
|
+
TableBody,
|
|
40
|
+
TableCell,
|
|
41
|
+
TableHead,
|
|
42
|
+
TableHeader,
|
|
43
|
+
TableRow
|
|
44
|
+
} from "@mesob/ui/components/table";
|
|
45
|
+
|
|
46
|
+
// src/components/skeletons/table-skeleton.tsx
|
|
47
|
+
import { Skeleton } from "@mesob/ui/components/skeleton";
|
|
48
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
49
|
+
function TableSkeleton({ columns = 5, rows = 10 }) {
|
|
50
|
+
const headerKeys = Array.from({ length: columns }, (_, i) => `header-${i}`);
|
|
51
|
+
const rowKeys = Array.from({ length: rows }, (_, i) => `row-${i}`);
|
|
52
|
+
const cellKeys = Array.from(
|
|
53
|
+
{ length: rows },
|
|
54
|
+
(_, rowIdx) => Array.from({ length: columns }, (_2, colIdx) => `cell-${rowIdx}-${colIdx}`)
|
|
55
|
+
);
|
|
56
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-4", children: [
|
|
57
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
58
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-8 w-48" }),
|
|
59
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-32" })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
62
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 flex-1 max-w-sm" }),
|
|
63
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" }),
|
|
64
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" })
|
|
65
|
+
] }),
|
|
66
|
+
/* @__PURE__ */ jsxs("div", { className: "border rounded-lg overflow-hidden", children: [
|
|
67
|
+
/* @__PURE__ */ jsx2("div", { className: "flex gap-4 p-4 bg-muted", children: headerKeys.map((key) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 flex-1" }, key)) }),
|
|
68
|
+
rowKeys.map((rowKey, rowIdx) => /* @__PURE__ */ jsx2("div", { className: "flex gap-4 p-4 border-t", children: cellKeys[rowIdx]?.map((cellKey) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 flex-1" }, cellKey)) }, rowKey))
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
71
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-32" }),
|
|
72
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
73
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-20" }),
|
|
74
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-20" })
|
|
75
|
+
] })
|
|
76
|
+
] })
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/components/shared/data-table/data-table.tsx
|
|
81
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
82
|
+
function DataTable({
|
|
83
|
+
data,
|
|
84
|
+
columns,
|
|
85
|
+
isLoading,
|
|
86
|
+
onRowClick,
|
|
87
|
+
emptyMessage = "No data available",
|
|
88
|
+
actions
|
|
89
|
+
}) {
|
|
90
|
+
if (isLoading) {
|
|
91
|
+
return /* @__PURE__ */ jsx3(TableSkeleton, { columns: columns.length, rows: 5 });
|
|
92
|
+
}
|
|
93
|
+
if (data.length === 0) {
|
|
94
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center min-h-[400px] p-6", children: [
|
|
95
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground", children: emptyMessage }),
|
|
96
|
+
actions && /* @__PURE__ */ jsx3("div", { className: "mt-4", children: actions })
|
|
97
|
+
] });
|
|
98
|
+
}
|
|
99
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full space-y-4", children: [
|
|
100
|
+
actions && /* @__PURE__ */ jsx3("div", { className: "flex justify-end", children: actions }),
|
|
101
|
+
/* @__PURE__ */ jsx3("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs2(Table, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx3(TableHeader, { children: /* @__PURE__ */ jsx3(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx3(TableHead, { children: column.header }, column.key)) }) }),
|
|
103
|
+
/* @__PURE__ */ jsx3(TableBody, { children: data.map((row) => /* @__PURE__ */ jsx3(
|
|
104
|
+
TableRow,
|
|
105
|
+
{
|
|
106
|
+
onClick: () => onRowClick?.(row),
|
|
107
|
+
className: onRowClick ? "cursor-pointer hover:bg-muted/50" : "",
|
|
108
|
+
children: columns.map((column) => /* @__PURE__ */ jsx3(TableCell, { children: column.cell(row) }, `${row.id}-${column.key}`))
|
|
109
|
+
},
|
|
110
|
+
row.id
|
|
111
|
+
)) })
|
|
112
|
+
] }) })
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/components/iam/tenants/tenants-page.tsx
|
|
117
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
118
|
+
function TenantsPage() {
|
|
119
|
+
const { hooks } = useApi();
|
|
120
|
+
const [page, setPage] = useState2(1);
|
|
121
|
+
const limit = 20;
|
|
122
|
+
const { data, isLoading, error } = hooks.useQuery("get", "/tenants", {
|
|
123
|
+
params: {
|
|
124
|
+
query: {
|
|
125
|
+
page: String(page),
|
|
126
|
+
limit: String(limit)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const columns = [
|
|
131
|
+
{
|
|
132
|
+
key: "name",
|
|
133
|
+
header: "Tenant",
|
|
134
|
+
cell: (tenant) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
135
|
+
/* @__PURE__ */ jsx4("p", { className: "font-medium", children: tenant.name }),
|
|
136
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-sm text-muted-foreground", children: [
|
|
137
|
+
"/",
|
|
138
|
+
tenant.slug
|
|
139
|
+
] })
|
|
140
|
+
] })
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
key: "status",
|
|
144
|
+
header: "Status",
|
|
145
|
+
cell: (tenant) => /* @__PURE__ */ jsx4(Badge, { variant: tenant.status === "active" ? "default" : "secondary", children: tenant.status })
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
key: "createdAt",
|
|
149
|
+
header: "Created",
|
|
150
|
+
cell: (tenant) => /* @__PURE__ */ jsx4("p", { className: "text-sm", children: new Date(tenant.createdAt).toLocaleDateString() })
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
key: "actions",
|
|
154
|
+
header: "Actions",
|
|
155
|
+
cell: (_tenant) => /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
156
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", size: "sm", children: "Domains" }),
|
|
157
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", size: "sm", children: "Edit" })
|
|
158
|
+
] })
|
|
159
|
+
}
|
|
160
|
+
];
|
|
161
|
+
if (error) {
|
|
162
|
+
return /* @__PURE__ */ jsx4("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsx4("p", { className: "text-destructive", children: "Error loading tenants" }) });
|
|
163
|
+
}
|
|
164
|
+
return /* @__PURE__ */ jsxs3("div", { className: "w-full p-6 space-y-4", children: [
|
|
165
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center", children: [
|
|
166
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
167
|
+
/* @__PURE__ */ jsx4("h1", { className: "text-3xl font-bold", children: "Tenants" }),
|
|
168
|
+
/* @__PURE__ */ jsx4("p", { className: "text-muted-foreground", children: "Manage tenant organizations" })
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsx4(Button, { children: "Create Tenant" })
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsx4(
|
|
173
|
+
DataTable,
|
|
174
|
+
{
|
|
175
|
+
data: data?.tenants || [],
|
|
176
|
+
columns,
|
|
177
|
+
isLoading,
|
|
178
|
+
emptyMessage: "No tenants found"
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
data && "tenants" in data && data.tenants && data.tenants.length >= limit && /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center", children: [
|
|
182
|
+
/* @__PURE__ */ jsx4(
|
|
183
|
+
Button,
|
|
184
|
+
{
|
|
185
|
+
variant: "outline",
|
|
186
|
+
disabled: page === 1,
|
|
187
|
+
onClick: () => setPage(page - 1),
|
|
188
|
+
children: "Previous"
|
|
189
|
+
}
|
|
190
|
+
),
|
|
191
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-sm text-muted-foreground", children: [
|
|
192
|
+
"Page ",
|
|
193
|
+
page
|
|
194
|
+
] }),
|
|
195
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", onClick: () => setPage(page + 1), children: "Next" })
|
|
196
|
+
] })
|
|
197
|
+
] });
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
TenantsPage
|
|
201
|
+
};
|
|
202
|
+
//# sourceMappingURL=tenants-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/iam/tenants/tenants-page.tsx","../../../../src/provider.tsx","../../../../src/components/shared/data-table/data-table.tsx","../../../../src/components/skeletons/table-skeleton.tsx"],"sourcesContent":["'use client';\n\nimport { Badge } from '@mesob/ui/components/badge';\nimport { Button } from '@mesob/ui/components/button';\nimport { useState } from 'react';\nimport { useApi } from '../../../provider';\nimport {\n DataTable,\n type DataTableColumn,\n} from '../../shared/data-table/data-table';\n\n// Tenant type from OpenAPI schema\ntype Tenant = {\n id: string;\n name: string;\n slug: string;\n status: string;\n createdAt: string;\n};\n\nexport function TenantsPage() {\n const { hooks } = useApi();\n const [page, setPage] = useState(1);\n const limit = 20;\n\n // Use openapi-react-query hooks\n const { data, isLoading, error } = hooks.useQuery('get', '/tenants', {\n params: {\n query: {\n page: String(page),\n limit: String(limit),\n },\n },\n });\n\n const columns: DataTableColumn<Tenant>[] = [\n {\n key: 'name',\n header: 'Tenant',\n cell: (tenant) => (\n <div>\n <p className=\"font-medium\">{tenant.name}</p>\n <p className=\"text-sm text-muted-foreground\">/{tenant.slug}</p>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n cell: (tenant) => (\n <Badge variant={tenant.status === 'active' ? 'default' : 'secondary'}>\n {tenant.status}\n </Badge>\n ),\n },\n {\n key: 'createdAt',\n header: 'Created',\n cell: (tenant) => (\n <p className=\"text-sm\">\n {new Date(tenant.createdAt).toLocaleDateString()}\n </p>\n ),\n },\n {\n key: 'actions',\n header: 'Actions',\n cell: (_tenant) => (\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n Domains\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Edit\n </Button>\n </div>\n ),\n },\n ];\n\n if (error) {\n return (\n <div className=\"p-6 text-center\">\n <p className=\"text-destructive\">Error loading tenants</p>\n </div>\n );\n }\n\n return (\n <div className=\"w-full p-6 space-y-4\">\n <div className=\"flex justify-between items-center\">\n <div>\n <h1 className=\"text-3xl font-bold\">Tenants</h1>\n <p className=\"text-muted-foreground\">Manage tenant organizations</p>\n </div>\n <Button>Create Tenant</Button>\n </div>\n\n <DataTable\n data={(data as { tenants: Tenant[] })?.tenants || []}\n columns={columns}\n isLoading={isLoading}\n emptyMessage=\"No tenants found\"\n />\n\n {data &&\n 'tenants' in data &&\n data.tenants &&\n (data.tenants as Tenant[]).length >= limit && (\n <div className=\"flex justify-between items-center\">\n <Button\n variant=\"outline\"\n disabled={page === 1}\n onClick={() => setPage(page - 1)}\n >\n Previous\n </Button>\n <span className=\"text-sm text-muted-foreground\">Page {page}</span>\n <Button variant=\"outline\" onClick={() => setPage(page + 1)}>\n Next\n </Button>\n </div>\n )}\n </div>\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, useEffect, 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 { createCustomFetch } from './utils/custom-fetch';\n\ntype OpenApiHooks = any;\n\ntype SessionState = {\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n isAuthenticated: boolean;\n error: Error | null;\n};\n\ntype SessionContextValue = SessionState & {\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 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 staleTime: 1000 * 60 * 5,\n gcTime: 1000 * 60 * 10,\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n});\n\nexport function useSession() {\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() {\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() {\n const context = useContext(ConfigContext);\n if (!context) {\n throw new Error('useConfig must be used within MesobAuthProvider');\n }\n return context;\n}\n\ntype MesobAuthProviderProps = {\n config: AuthClientConfig;\n children: ReactNode;\n};\n\nexport function MesobAuthProvider({\n config,\n children,\n}: MesobAuthProviderProps) {\n const mergedConfig = deepmerge(\n { ...defaultAuthClientConfig } as Partial<AuthClientConfig>,\n config,\n ) as AuthClientConfig;\n\n const api = createFetchClient<paths>({\n baseUrl: mergedConfig.baseURL,\n fetch: createCustomFetch(mergedConfig),\n });\n\n const hooks = createClient(api);\n\n return (\n <QueryClientProvider client={queryClient}>\n <AuthStateProvider config={mergedConfig} hooks={hooks}>\n {children}\n </AuthStateProvider>\n </QueryClientProvider>\n );\n}\n\ntype AuthStateProviderProps = {\n config: AuthClientConfig;\n hooks: OpenApiHooks;\n children: ReactNode;\n};\n\nfunction AuthStateProvider({\n config,\n hooks,\n children,\n}: AuthStateProviderProps) {\n const [authState, setAuthState] = useState<{\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n error: Error | null;\n }>({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n\n const {\n data: sessionData,\n isLoading: sessionLoading,\n error: sessionError,\n refetch,\n } = hooks.useQuery(\n 'get',\n '/session',\n {},\n {\n enabled: true,\n refetchOnMount: true,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n );\n\n useEffect(() => {\n if (sessionLoading) {\n setAuthState((prev) => ({ ...prev, isLoading: true }));\n return;\n }\n\n if (sessionError) {\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: sessionError as Error,\n });\n return;\n }\n\n if (sessionData) {\n setAuthState({\n user: sessionData.user,\n session: sessionData.session,\n isLoading: false,\n error: null,\n });\n return;\n }\n\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n }, [sessionData, sessionLoading, sessionError]);\n\n const refresh = async () => {\n await refetch();\n };\n\n const setAuth = (auth: AuthResponse) => {\n setAuthState({\n user: auth.user,\n session: auth.session,\n isLoading: false,\n error: null,\n });\n };\n\n const clearAuth = () => {\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n };\n\n const signOutMutation = hooks.useMutation('post', '/sign-out');\n\n const signOut = async () => {\n try {\n await signOutMutation.mutateAsync({});\n } finally {\n clearAuth();\n }\n };\n\n const t = createTranslator(config.messages || {});\n\n return (\n <ConfigContext.Provider value={{ config, t }}>\n <ApiContext.Provider value={{ hooks, setAuth, clearAuth, refresh }}>\n <SessionContext.Provider\n value={{\n user: authState.user,\n session: authState.session,\n isLoading: authState.isLoading,\n isAuthenticated: !!authState.user && !!authState.session,\n error: authState.error,\n refresh,\n signOut,\n }}\n >\n {children}\n </SessionContext.Provider>\n </ApiContext.Provider>\n </ConfigContext.Provider>\n );\n}\n","'use client';\n\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@mesob/ui/components/table';\nimport type { ReactNode } from 'react';\nimport { TableSkeleton } from '../../skeletons/table-skeleton';\n\nexport type DataTableColumn<T> = {\n key: string;\n header: string;\n cell: (row: T) => ReactNode;\n sortable?: boolean;\n};\n\ntype DataTableProps<T> = {\n data: T[];\n columns: DataTableColumn<T>[];\n isLoading?: boolean;\n onRowClick?: (row: T) => void;\n emptyMessage?: string;\n actions?: ReactNode;\n};\n\nexport function DataTable<T extends { id: string }>({\n data,\n columns,\n isLoading,\n onRowClick,\n emptyMessage = 'No data available',\n actions,\n}: DataTableProps<T>) {\n if (isLoading) {\n return <TableSkeleton columns={columns.length} rows={5} />;\n }\n\n if (data.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] p-6\">\n <p className=\"text-muted-foreground\">{emptyMessage}</p>\n {actions && <div className=\"mt-4\">{actions}</div>}\n </div>\n );\n }\n\n return (\n <div className=\"w-full space-y-4\">\n {actions && <div className=\"flex justify-end\">{actions}</div>}\n\n <div className=\"border rounded-lg overflow-hidden\">\n <Table>\n <TableHeader>\n <TableRow>\n {columns.map((column) => (\n <TableHead key={column.key}>{column.header}</TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {data.map((row) => (\n <TableRow\n key={row.id}\n onClick={() => onRowClick?.(row)}\n className={onRowClick ? 'cursor-pointer hover:bg-muted/50' : ''}\n >\n {columns.map((column) => (\n <TableCell key={`${row.id}-${column.key}`}>\n {column.cell(row)}\n </TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </div>\n );\n}\n","'use client';\n\nimport { Skeleton } from '@mesob/ui/components/skeleton';\n\ntype TableSkeletonProps = {\n columns?: number;\n rows?: number;\n};\n\nexport function TableSkeleton({ columns = 5, rows = 10 }: TableSkeletonProps) {\n const headerKeys = Array.from({ length: columns }, (_, i) => `header-${i}`);\n const rowKeys = Array.from({ length: rows }, (_, i) => `row-${i}`);\n const cellKeys = Array.from({ length: rows }, (_, rowIdx) =>\n Array.from({ length: columns }, (_, colIdx) => `cell-${rowIdx}-${colIdx}`),\n );\n\n return (\n <div className=\"w-full space-y-4\">\n {/* Header */}\n <div className=\"flex justify-between items-center\">\n <Skeleton className=\"h-8 w-48\" />\n <Skeleton className=\"h-10 w-32\" />\n </div>\n\n {/* Search/filters */}\n <div className=\"flex gap-4\">\n <Skeleton className=\"h-10 flex-1 max-w-sm\" />\n <Skeleton className=\"h-10 w-24\" />\n <Skeleton className=\"h-10 w-24\" />\n </div>\n\n {/* Table */}\n <div className=\"border rounded-lg overflow-hidden\">\n {/* Table header */}\n <div className=\"flex gap-4 p-4 bg-muted\">\n {headerKeys.map((key) => (\n <Skeleton key={key} className=\"h-4 flex-1\" />\n ))}\n </div>\n\n {/* Table rows */}\n {rowKeys.map((rowKey, rowIdx) => (\n <div key={rowKey} className=\"flex gap-4 p-4 border-t\">\n {cellKeys[rowIdx]?.map((cellKey) => (\n <Skeleton key={cellKey} className=\"h-4 flex-1\" />\n ))}\n </div>\n ))}\n </div>\n\n {/* Pagination */}\n <div className=\"flex justify-between items-center\">\n <Skeleton className=\"h-4 w-32\" />\n <div className=\"flex gap-2\">\n <Skeleton className=\"h-10 w-20\" />\n <Skeleton className=\"h-10 w-20\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,YAAAA,iBAAgB;;;ACFzB,SAAS,aAAa,2BAA2B;AACjD,SAAS,iBAAiB;AAC1B,OAAO,uBAAuB;AAC9B,OAAO,kBAAkB;AAEzB,SAAS,eAAe,YAAY,WAAW,gBAAgB;AAqGzD;AA9DN,IAAM,iBAAiB,cAA0C,IAAI;AACrE,IAAM,aAAa,cAAsC,IAAI;AAC7D,IAAM,gBAAgB,cAAyC,IAAI;AAEnE,IAAM,cAAc,IAAI,YAAY;AAAA,EAClC,gBAAgB;AAAA,IACd,SAAS;AAAA,MACP,WAAW,MAAO,KAAK;AAAA,MACvB,QAAQ,MAAO,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,sBAAsB;AAAA,IACxB;AAAA,EACF;AACF,CAAC;AAUM,SAAS,SAAS;AACvB,QAAM,UAAU,WAAW,UAAU;AACrC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO;AACT;;;ACzEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACPP,SAAS,gBAAgB;AAiBnB,SACE,OAAAC,MADF;AAVC,SAAS,cAAc,EAAE,UAAU,GAAG,OAAO,GAAG,GAAuB;AAC5E,QAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE;AAC1E,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE;AACjE,QAAM,WAAW,MAAM;AAAA,IAAK,EAAE,QAAQ,KAAK;AAAA,IAAG,CAAC,GAAG,WAChD,MAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,CAACC,IAAG,WAAW,QAAQ,MAAM,IAAI,MAAM,EAAE;AAAA,EAC3E;AAEA,SACE,qBAAC,SAAI,WAAU,oBAEb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,sBAAAD,KAAC,YAAS,WAAU,YAAW;AAAA,MAC/B,gBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,OAClC;AAAA,IAGA,qBAAC,SAAI,WAAU,cACb;AAAA,sBAAAA,KAAC,YAAS,WAAU,wBAAuB;AAAA,MAC3C,gBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,MAChC,gBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,OAClC;AAAA,IAGA,qBAAC,SAAI,WAAU,qCAEb;AAAA,sBAAAA,KAAC,SAAI,WAAU,2BACZ,qBAAW,IAAI,CAAC,QACf,gBAAAA,KAAC,YAAmB,WAAU,gBAAf,GAA4B,CAC5C,GACH;AAAA,MAGC,QAAQ,IAAI,CAAC,QAAQ,WACpB,gBAAAA,KAAC,SAAiB,WAAU,2BACzB,mBAAS,MAAM,GAAG,IAAI,CAAC,YACtB,gBAAAA,KAAC,YAAuB,WAAU,gBAAnB,OAAgC,CAChD,KAHO,MAIV,CACD;AAAA,OACH;AAAA,IAGA,qBAAC,SAAI,WAAU,qCACb;AAAA,sBAAAA,KAAC,YAAS,WAAU,YAAW;AAAA,MAC/B,qBAAC,SAAI,WAAU,cACb;AAAA,wBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,QAChC,gBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,SAClC;AAAA,OACF;AAAA,KACF;AAEJ;;;ADtBW,gBAAAE,MAKL,QAAAC,aALK;AATJ,SAAS,UAAoC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AACF,GAAsB;AACpB,MAAI,WAAW;AACb,WAAO,gBAAAD,KAAC,iBAAc,SAAS,QAAQ,QAAQ,MAAM,GAAG;AAAA,EAC1D;AAEA,MAAI,KAAK,WAAW,GAAG;AACrB,WACE,gBAAAC,MAAC,SAAI,WAAU,+DACb;AAAA,sBAAAD,KAAC,OAAE,WAAU,yBAAyB,wBAAa;AAAA,MAClD,WAAW,gBAAAA,KAAC,SAAI,WAAU,QAAQ,mBAAQ;AAAA,OAC7C;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,WAAU,oBACZ;AAAA,eAAW,gBAAAD,KAAC,SAAI,WAAU,oBAAoB,mBAAQ;AAAA,IAEvD,gBAAAA,KAAC,SAAI,WAAU,qCACb,0BAAAC,MAAC,SACC;AAAA,sBAAAD,KAAC,eACC,0BAAAA,KAAC,YACE,kBAAQ,IAAI,CAAC,WACZ,gBAAAA,KAAC,aAA4B,iBAAO,UAApB,OAAO,GAAoB,CAC5C,GACH,GACF;AAAA,MACA,gBAAAA,KAAC,aACE,eAAK,IAAI,CAAC,QACT,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,aAAa,GAAG;AAAA,UAC/B,WAAW,aAAa,qCAAqC;AAAA,UAE5D,kBAAQ,IAAI,CAAC,WACZ,gBAAAA,KAAC,aACE,iBAAO,KAAK,GAAG,KADF,GAAG,IAAI,EAAE,IAAI,OAAO,GAAG,EAEvC,CACD;AAAA;AAAA,QARI,IAAI;AAAA,MASX,CACD,GACH;AAAA,OACF,GACF;AAAA,KACF;AAEJ;;;AFzCU,gBAAAE,MACA,QAAAC,aADA;AArBH,SAAS,cAAc;AAC5B,QAAM,EAAE,MAAM,IAAI,OAAO;AACzB,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,CAAC;AAClC,QAAM,QAAQ;AAGd,QAAM,EAAE,MAAM,WAAW,MAAM,IAAI,MAAM,SAAS,OAAO,YAAY;AAAA,IACnE,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM,OAAO,IAAI;AAAA,QACjB,OAAO,OAAO,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,UAAqC;AAAA,IACzC;AAAA,MACE,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,MAAM,CAAC,WACL,gBAAAD,MAAC,SACC;AAAA,wBAAAD,KAAC,OAAE,WAAU,eAAe,iBAAO,MAAK;AAAA,QACxC,gBAAAC,MAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,UAAE,OAAO;AAAA,WAAK;AAAA,SAC7D;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,MAAM,CAAC,WACL,gBAAAD,KAAC,SAAM,SAAS,OAAO,WAAW,WAAW,YAAY,aACtD,iBAAO,QACV;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,MAAM,CAAC,WACL,gBAAAA,KAAC,OAAE,WAAU,WACV,cAAI,KAAK,OAAO,SAAS,EAAE,mBAAmB,GACjD;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,MAAM,CAAC,YACL,gBAAAC,MAAC,SAAI,WAAU,cACb;AAAA,wBAAAD,KAAC,UAAO,SAAQ,WAAU,MAAK,MAAK,qBAEpC;AAAA,QACA,gBAAAA,KAAC,UAAO,SAAQ,WAAU,MAAK,MAAK,kBAEpC;AAAA,SACF;AAAA,IAEJ;AAAA,EACF;AAEA,MAAI,OAAO;AACT,WACE,gBAAAA,KAAC,SAAI,WAAU,mBACb,0BAAAA,KAAC,OAAE,WAAU,oBAAmB,mCAAqB,GACvD;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,WAAU,wBACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,qCACb;AAAA,sBAAAA,MAAC,SACC;AAAA,wBAAAD,KAAC,QAAG,WAAU,sBAAqB,qBAAO;AAAA,QAC1C,gBAAAA,KAAC,OAAE,WAAU,yBAAwB,yCAA2B;AAAA,SAClE;AAAA,MACA,gBAAAA,KAAC,UAAO,2BAAa;AAAA,OACvB;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAO,MAAgC,WAAW,CAAC;AAAA,QACnD;AAAA,QACA;AAAA,QACA,cAAa;AAAA;AAAA,IACf;AAAA,IAEC,QACC,aAAa,QACb,KAAK,WACJ,KAAK,QAAqB,UAAU,SACnC,gBAAAC,MAAC,SAAI,WAAU,qCACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,UAAU,SAAS;AAAA,UACnB,SAAS,MAAM,QAAQ,OAAO,CAAC;AAAA,UAChC;AAAA;AAAA,MAED;AAAA,MACA,gBAAAC,MAAC,UAAK,WAAU,iCAAgC;AAAA;AAAA,QAAM;AAAA,SAAK;AAAA,MAC3D,gBAAAD,KAAC,UAAO,SAAQ,WAAU,SAAS,MAAM,QAAQ,OAAO,CAAC,GAAG,kBAE5D;AAAA,OACF;AAAA,KAEN;AAEJ;","names":["useState","jsx","_","jsx","jsxs","jsx","jsxs","useState"]}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/iam/users/users-page.tsx
|
|
4
|
+
import { Badge } from "@mesob/ui/components/badge";
|
|
5
|
+
import { Button } from "@mesob/ui/components/button";
|
|
6
|
+
import { useState as useState2 } from "react";
|
|
7
|
+
|
|
8
|
+
// src/provider.tsx
|
|
9
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
10
|
+
import { deepmerge } from "deepmerge-ts";
|
|
11
|
+
import createFetchClient from "openapi-fetch";
|
|
12
|
+
import createClient from "openapi-react-query";
|
|
13
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
|
+
var SessionContext = createContext(null);
|
|
16
|
+
var ApiContext = createContext(null);
|
|
17
|
+
var ConfigContext = createContext(null);
|
|
18
|
+
var queryClient = new QueryClient({
|
|
19
|
+
defaultOptions: {
|
|
20
|
+
queries: {
|
|
21
|
+
staleTime: 1e3 * 60 * 5,
|
|
22
|
+
gcTime: 1e3 * 60 * 10,
|
|
23
|
+
retry: 1,
|
|
24
|
+
refetchOnWindowFocus: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
function useApi() {
|
|
29
|
+
const context = useContext(ApiContext);
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("useApi must be used within MesobAuthProvider");
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/components/shared/data-table/data-table.tsx
|
|
37
|
+
import {
|
|
38
|
+
Table,
|
|
39
|
+
TableBody,
|
|
40
|
+
TableCell,
|
|
41
|
+
TableHead,
|
|
42
|
+
TableHeader,
|
|
43
|
+
TableRow
|
|
44
|
+
} from "@mesob/ui/components/table";
|
|
45
|
+
|
|
46
|
+
// src/components/skeletons/table-skeleton.tsx
|
|
47
|
+
import { Skeleton } from "@mesob/ui/components/skeleton";
|
|
48
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
49
|
+
function TableSkeleton({ columns = 5, rows = 10 }) {
|
|
50
|
+
const headerKeys = Array.from({ length: columns }, (_, i) => `header-${i}`);
|
|
51
|
+
const rowKeys = Array.from({ length: rows }, (_, i) => `row-${i}`);
|
|
52
|
+
const cellKeys = Array.from(
|
|
53
|
+
{ length: rows },
|
|
54
|
+
(_, rowIdx) => Array.from({ length: columns }, (_2, colIdx) => `cell-${rowIdx}-${colIdx}`)
|
|
55
|
+
);
|
|
56
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-4", children: [
|
|
57
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
58
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-8 w-48" }),
|
|
59
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-32" })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
62
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 flex-1 max-w-sm" }),
|
|
63
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" }),
|
|
64
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" })
|
|
65
|
+
] }),
|
|
66
|
+
/* @__PURE__ */ jsxs("div", { className: "border rounded-lg overflow-hidden", children: [
|
|
67
|
+
/* @__PURE__ */ jsx2("div", { className: "flex gap-4 p-4 bg-muted", children: headerKeys.map((key) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 flex-1" }, key)) }),
|
|
68
|
+
rowKeys.map((rowKey, rowIdx) => /* @__PURE__ */ jsx2("div", { className: "flex gap-4 p-4 border-t", children: cellKeys[rowIdx]?.map((cellKey) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 flex-1" }, cellKey)) }, rowKey))
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
71
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-32" }),
|
|
72
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
73
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-20" }),
|
|
74
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-20" })
|
|
75
|
+
] })
|
|
76
|
+
] })
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/components/shared/data-table/data-table.tsx
|
|
81
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
82
|
+
function DataTable({
|
|
83
|
+
data,
|
|
84
|
+
columns,
|
|
85
|
+
isLoading,
|
|
86
|
+
onRowClick,
|
|
87
|
+
emptyMessage = "No data available",
|
|
88
|
+
actions
|
|
89
|
+
}) {
|
|
90
|
+
if (isLoading) {
|
|
91
|
+
return /* @__PURE__ */ jsx3(TableSkeleton, { columns: columns.length, rows: 5 });
|
|
92
|
+
}
|
|
93
|
+
if (data.length === 0) {
|
|
94
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center min-h-[400px] p-6", children: [
|
|
95
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground", children: emptyMessage }),
|
|
96
|
+
actions && /* @__PURE__ */ jsx3("div", { className: "mt-4", children: actions })
|
|
97
|
+
] });
|
|
98
|
+
}
|
|
99
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full space-y-4", children: [
|
|
100
|
+
actions && /* @__PURE__ */ jsx3("div", { className: "flex justify-end", children: actions }),
|
|
101
|
+
/* @__PURE__ */ jsx3("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs2(Table, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx3(TableHeader, { children: /* @__PURE__ */ jsx3(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx3(TableHead, { children: column.header }, column.key)) }) }),
|
|
103
|
+
/* @__PURE__ */ jsx3(TableBody, { children: data.map((row) => /* @__PURE__ */ jsx3(
|
|
104
|
+
TableRow,
|
|
105
|
+
{
|
|
106
|
+
onClick: () => onRowClick?.(row),
|
|
107
|
+
className: onRowClick ? "cursor-pointer hover:bg-muted/50" : "",
|
|
108
|
+
children: columns.map((column) => /* @__PURE__ */ jsx3(TableCell, { children: column.cell(row) }, `${row.id}-${column.key}`))
|
|
109
|
+
},
|
|
110
|
+
row.id
|
|
111
|
+
)) })
|
|
112
|
+
] }) })
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/components/iam/users/users-page.tsx
|
|
117
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
118
|
+
function UsersPage() {
|
|
119
|
+
const { hooks } = useApi();
|
|
120
|
+
const [page, setPage] = useState2(1);
|
|
121
|
+
const limit = 20;
|
|
122
|
+
const { data, isLoading, error } = hooks.useQuery("get", "/users", {
|
|
123
|
+
params: {
|
|
124
|
+
query: {
|
|
125
|
+
page: String(page),
|
|
126
|
+
limit: String(limit)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const columns = [
|
|
131
|
+
{
|
|
132
|
+
key: "fullName",
|
|
133
|
+
header: "Name",
|
|
134
|
+
cell: (user) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
135
|
+
/* @__PURE__ */ jsx4("p", { className: "font-medium", children: user.fullName }),
|
|
136
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-sm text-muted-foreground", children: [
|
|
137
|
+
"@",
|
|
138
|
+
user.handle
|
|
139
|
+
] })
|
|
140
|
+
] })
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
key: "contact",
|
|
144
|
+
header: "Contact",
|
|
145
|
+
cell: (user) => /* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
|
|
146
|
+
user.email && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
147
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm", children: user.email }),
|
|
148
|
+
user.emailVerified && /* @__PURE__ */ jsx4(Badge, { variant: "outline", className: "text-xs", children: "Verified" })
|
|
149
|
+
] }),
|
|
150
|
+
user.phone && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
151
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm", children: user.phone }),
|
|
152
|
+
user.phoneVerified && /* @__PURE__ */ jsx4(Badge, { variant: "outline", className: "text-xs", children: "Verified" })
|
|
153
|
+
] })
|
|
154
|
+
] })
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
key: "lastSignIn",
|
|
158
|
+
header: "Last Sign In",
|
|
159
|
+
cell: (user) => /* @__PURE__ */ jsx4("p", { className: "text-sm", children: user.lastSignInAt ? new Date(user.lastSignInAt).toLocaleDateString() : "Never" })
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
key: "actions",
|
|
163
|
+
header: "Actions",
|
|
164
|
+
cell: (_user) => /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
165
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", size: "sm", children: "View" }),
|
|
166
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", size: "sm", children: "Edit" })
|
|
167
|
+
] })
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
if (error) {
|
|
171
|
+
return /* @__PURE__ */ jsx4("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsx4("p", { className: "text-destructive", children: "Error loading users" }) });
|
|
172
|
+
}
|
|
173
|
+
return /* @__PURE__ */ jsxs3("div", { className: "w-full p-6 space-y-4", children: [
|
|
174
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center", children: [
|
|
175
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
176
|
+
/* @__PURE__ */ jsx4("h1", { className: "text-3xl font-bold", children: "Users" }),
|
|
177
|
+
/* @__PURE__ */ jsx4("p", { className: "text-muted-foreground", children: "Manage user accounts" })
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsx4(Button, { children: "Create User" })
|
|
180
|
+
] }),
|
|
181
|
+
/* @__PURE__ */ jsx4(
|
|
182
|
+
DataTable,
|
|
183
|
+
{
|
|
184
|
+
data: data?.users || [],
|
|
185
|
+
columns,
|
|
186
|
+
isLoading,
|
|
187
|
+
emptyMessage: "No users found"
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
data && "users" in data && data.users && data.users.length >= limit && /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center", children: [
|
|
191
|
+
/* @__PURE__ */ jsx4(
|
|
192
|
+
Button,
|
|
193
|
+
{
|
|
194
|
+
variant: "outline",
|
|
195
|
+
disabled: page === 1,
|
|
196
|
+
onClick: () => setPage(page - 1),
|
|
197
|
+
children: "Previous"
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-sm text-muted-foreground", children: [
|
|
201
|
+
"Page ",
|
|
202
|
+
page
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx4(Button, { variant: "outline", onClick: () => setPage(page + 1), children: "Next" })
|
|
205
|
+
] })
|
|
206
|
+
] });
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
UsersPage
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=users-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/iam/users/users-page.tsx","../../../../src/provider.tsx","../../../../src/components/shared/data-table/data-table.tsx","../../../../src/components/skeletons/table-skeleton.tsx"],"sourcesContent":["'use client';\n\nimport { Badge } from '@mesob/ui/components/badge';\nimport { Button } from '@mesob/ui/components/button';\nimport { useState } from 'react';\nimport { useApi } from '../../../provider';\nimport {\n DataTable,\n type DataTableColumn,\n} from '../../shared/data-table/data-table';\n\n// User type from OpenAPI schema\ntype User = {\n id: string;\n fullName: string;\n email: string | null;\n phone: string | null;\n handle: string;\n emailVerified: boolean;\n phoneVerified: boolean;\n lastSignInAt: string | null;\n};\n\nexport function UsersPage() {\n const { hooks } = useApi();\n const [page, setPage] = useState(1);\n const limit = 20;\n\n // Use openapi-react-query hooks\n const { data, isLoading, error } = hooks.useQuery('get', '/users', {\n params: {\n query: {\n page: String(page),\n limit: String(limit),\n },\n },\n });\n\n const columns: DataTableColumn<User>[] = [\n {\n key: 'fullName',\n header: 'Name',\n cell: (user) => (\n <div>\n <p className=\"font-medium\">{user.fullName}</p>\n <p className=\"text-sm text-muted-foreground\">@{user.handle}</p>\n </div>\n ),\n },\n {\n key: 'contact',\n header: 'Contact',\n cell: (user) => (\n <div className=\"space-y-1\">\n {user.email && (\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm\">{user.email}</p>\n {user.emailVerified && (\n <Badge variant=\"outline\" className=\"text-xs\">\n Verified\n </Badge>\n )}\n </div>\n )}\n {user.phone && (\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm\">{user.phone}</p>\n {user.phoneVerified && (\n <Badge variant=\"outline\" className=\"text-xs\">\n Verified\n </Badge>\n )}\n </div>\n )}\n </div>\n ),\n },\n {\n key: 'lastSignIn',\n header: 'Last Sign In',\n cell: (user) => (\n <p className=\"text-sm\">\n {user.lastSignInAt\n ? new Date(user.lastSignInAt).toLocaleDateString()\n : 'Never'}\n </p>\n ),\n },\n {\n key: 'actions',\n header: 'Actions',\n cell: (_user) => (\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n View\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Edit\n </Button>\n </div>\n ),\n },\n ];\n\n if (error) {\n return (\n <div className=\"p-6 text-center\">\n <p className=\"text-destructive\">Error loading users</p>\n </div>\n );\n }\n\n return (\n <div className=\"w-full p-6 space-y-4\">\n <div className=\"flex justify-between items-center\">\n <div>\n <h1 className=\"text-3xl font-bold\">Users</h1>\n <p className=\"text-muted-foreground\">Manage user accounts</p>\n </div>\n <Button>Create User</Button>\n </div>\n\n <DataTable\n data={(data as { users: User[] })?.users || []}\n columns={columns}\n isLoading={isLoading}\n emptyMessage=\"No users found\"\n />\n\n {data &&\n 'users' in data &&\n data.users &&\n (data.users as User[]).length >= limit && (\n <div className=\"flex justify-between items-center\">\n <Button\n variant=\"outline\"\n disabled={page === 1}\n onClick={() => setPage(page - 1)}\n >\n Previous\n </Button>\n <span className=\"text-sm text-muted-foreground\">Page {page}</span>\n <Button variant=\"outline\" onClick={() => setPage(page + 1)}>\n Next\n </Button>\n </div>\n )}\n </div>\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, useEffect, 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 { createCustomFetch } from './utils/custom-fetch';\n\ntype OpenApiHooks = any;\n\ntype SessionState = {\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n isAuthenticated: boolean;\n error: Error | null;\n};\n\ntype SessionContextValue = SessionState & {\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 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 staleTime: 1000 * 60 * 5,\n gcTime: 1000 * 60 * 10,\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n});\n\nexport function useSession() {\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() {\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() {\n const context = useContext(ConfigContext);\n if (!context) {\n throw new Error('useConfig must be used within MesobAuthProvider');\n }\n return context;\n}\n\ntype MesobAuthProviderProps = {\n config: AuthClientConfig;\n children: ReactNode;\n};\n\nexport function MesobAuthProvider({\n config,\n children,\n}: MesobAuthProviderProps) {\n const mergedConfig = deepmerge(\n { ...defaultAuthClientConfig } as Partial<AuthClientConfig>,\n config,\n ) as AuthClientConfig;\n\n const api = createFetchClient<paths>({\n baseUrl: mergedConfig.baseURL,\n fetch: createCustomFetch(mergedConfig),\n });\n\n const hooks = createClient(api);\n\n return (\n <QueryClientProvider client={queryClient}>\n <AuthStateProvider config={mergedConfig} hooks={hooks}>\n {children}\n </AuthStateProvider>\n </QueryClientProvider>\n );\n}\n\ntype AuthStateProviderProps = {\n config: AuthClientConfig;\n hooks: OpenApiHooks;\n children: ReactNode;\n};\n\nfunction AuthStateProvider({\n config,\n hooks,\n children,\n}: AuthStateProviderProps) {\n const [authState, setAuthState] = useState<{\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n error: Error | null;\n }>({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n\n const {\n data: sessionData,\n isLoading: sessionLoading,\n error: sessionError,\n refetch,\n } = hooks.useQuery(\n 'get',\n '/session',\n {},\n {\n enabled: true,\n refetchOnMount: true,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n );\n\n useEffect(() => {\n if (sessionLoading) {\n setAuthState((prev) => ({ ...prev, isLoading: true }));\n return;\n }\n\n if (sessionError) {\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: sessionError as Error,\n });\n return;\n }\n\n if (sessionData) {\n setAuthState({\n user: sessionData.user,\n session: sessionData.session,\n isLoading: false,\n error: null,\n });\n return;\n }\n\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n }, [sessionData, sessionLoading, sessionError]);\n\n const refresh = async () => {\n await refetch();\n };\n\n const setAuth = (auth: AuthResponse) => {\n setAuthState({\n user: auth.user,\n session: auth.session,\n isLoading: false,\n error: null,\n });\n };\n\n const clearAuth = () => {\n setAuthState({\n user: null,\n session: null,\n isLoading: false,\n error: null,\n });\n };\n\n const signOutMutation = hooks.useMutation('post', '/sign-out');\n\n const signOut = async () => {\n try {\n await signOutMutation.mutateAsync({});\n } finally {\n clearAuth();\n }\n };\n\n const t = createTranslator(config.messages || {});\n\n return (\n <ConfigContext.Provider value={{ config, t }}>\n <ApiContext.Provider value={{ hooks, setAuth, clearAuth, refresh }}>\n <SessionContext.Provider\n value={{\n user: authState.user,\n session: authState.session,\n isLoading: authState.isLoading,\n isAuthenticated: !!authState.user && !!authState.session,\n error: authState.error,\n refresh,\n signOut,\n }}\n >\n {children}\n </SessionContext.Provider>\n </ApiContext.Provider>\n </ConfigContext.Provider>\n );\n}\n","'use client';\n\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@mesob/ui/components/table';\nimport type { ReactNode } from 'react';\nimport { TableSkeleton } from '../../skeletons/table-skeleton';\n\nexport type DataTableColumn<T> = {\n key: string;\n header: string;\n cell: (row: T) => ReactNode;\n sortable?: boolean;\n};\n\ntype DataTableProps<T> = {\n data: T[];\n columns: DataTableColumn<T>[];\n isLoading?: boolean;\n onRowClick?: (row: T) => void;\n emptyMessage?: string;\n actions?: ReactNode;\n};\n\nexport function DataTable<T extends { id: string }>({\n data,\n columns,\n isLoading,\n onRowClick,\n emptyMessage = 'No data available',\n actions,\n}: DataTableProps<T>) {\n if (isLoading) {\n return <TableSkeleton columns={columns.length} rows={5} />;\n }\n\n if (data.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] p-6\">\n <p className=\"text-muted-foreground\">{emptyMessage}</p>\n {actions && <div className=\"mt-4\">{actions}</div>}\n </div>\n );\n }\n\n return (\n <div className=\"w-full space-y-4\">\n {actions && <div className=\"flex justify-end\">{actions}</div>}\n\n <div className=\"border rounded-lg overflow-hidden\">\n <Table>\n <TableHeader>\n <TableRow>\n {columns.map((column) => (\n <TableHead key={column.key}>{column.header}</TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {data.map((row) => (\n <TableRow\n key={row.id}\n onClick={() => onRowClick?.(row)}\n className={onRowClick ? 'cursor-pointer hover:bg-muted/50' : ''}\n >\n {columns.map((column) => (\n <TableCell key={`${row.id}-${column.key}`}>\n {column.cell(row)}\n </TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </div>\n );\n}\n","'use client';\n\nimport { Skeleton } from '@mesob/ui/components/skeleton';\n\ntype TableSkeletonProps = {\n columns?: number;\n rows?: number;\n};\n\nexport function TableSkeleton({ columns = 5, rows = 10 }: TableSkeletonProps) {\n const headerKeys = Array.from({ length: columns }, (_, i) => `header-${i}`);\n const rowKeys = Array.from({ length: rows }, (_, i) => `row-${i}`);\n const cellKeys = Array.from({ length: rows }, (_, rowIdx) =>\n Array.from({ length: columns }, (_, colIdx) => `cell-${rowIdx}-${colIdx}`),\n );\n\n return (\n <div className=\"w-full space-y-4\">\n {/* Header */}\n <div className=\"flex justify-between items-center\">\n <Skeleton className=\"h-8 w-48\" />\n <Skeleton className=\"h-10 w-32\" />\n </div>\n\n {/* Search/filters */}\n <div className=\"flex gap-4\">\n <Skeleton className=\"h-10 flex-1 max-w-sm\" />\n <Skeleton className=\"h-10 w-24\" />\n <Skeleton className=\"h-10 w-24\" />\n </div>\n\n {/* Table */}\n <div className=\"border rounded-lg overflow-hidden\">\n {/* Table header */}\n <div className=\"flex gap-4 p-4 bg-muted\">\n {headerKeys.map((key) => (\n <Skeleton key={key} className=\"h-4 flex-1\" />\n ))}\n </div>\n\n {/* Table rows */}\n {rowKeys.map((rowKey, rowIdx) => (\n <div key={rowKey} className=\"flex gap-4 p-4 border-t\">\n {cellKeys[rowIdx]?.map((cellKey) => (\n <Skeleton key={cellKey} className=\"h-4 flex-1\" />\n ))}\n </div>\n ))}\n </div>\n\n {/* Pagination */}\n <div className=\"flex justify-between items-center\">\n <Skeleton className=\"h-4 w-32\" />\n <div className=\"flex gap-2\">\n <Skeleton className=\"h-10 w-20\" />\n <Skeleton className=\"h-10 w-20\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useState","jsx","_","jsx","jsxs","jsx","jsxs","useState"]}
|