@mesob/auth-react 0.5.11 → 0.6.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/{chunk-ORQZZUVL.js → chunk-2SYBZ6TR.js} +2 -4
- package/dist/chunk-2SYBZ6TR.js.map +1 -0
- package/dist/{chunk-LI7WPOVY.js → chunk-323NYGKW.js} +3 -6
- package/dist/chunk-323NYGKW.js.map +1 -0
- package/dist/{chunk-Y6KURGWG.js → chunk-34AJJ2CI.js} +2 -4
- package/dist/chunk-34AJJ2CI.js.map +1 -0
- package/dist/{chunk-WAMZL5CS.js → chunk-4O3LAHTY.js} +4 -7
- package/dist/chunk-4O3LAHTY.js.map +1 -0
- package/dist/{chunk-4YPLJ2P6.js → chunk-57G5Z44Y.js} +2 -6
- package/dist/chunk-57G5Z44Y.js.map +1 -0
- package/dist/{chunk-NJGVOQIU.js → chunk-5O6VWABF.js} +5 -8
- package/dist/chunk-5O6VWABF.js.map +1 -0
- package/dist/{chunk-UV7JR3YU.js → chunk-5PGLBU4A.js} +4 -6
- package/dist/chunk-5PGLBU4A.js.map +1 -0
- package/dist/{chunk-IUCTHMVY.js → chunk-5YAYZDKX.js} +2 -5
- package/dist/chunk-5YAYZDKX.js.map +1 -0
- package/dist/{chunk-M2K6O5CN.js → chunk-6BLYK6D6.js} +85 -29
- package/dist/chunk-6BLYK6D6.js.map +1 -0
- package/dist/{chunk-G2RWFKGF.js → chunk-6HVUVXQB.js} +2 -4
- package/dist/chunk-6HVUVXQB.js.map +1 -0
- package/dist/chunk-6YHUCPJ4.js +28 -0
- package/dist/chunk-6YHUCPJ4.js.map +1 -0
- package/dist/{chunk-B7BS57X7.js → chunk-7FSFDTQR.js} +10 -11
- package/dist/chunk-7FSFDTQR.js.map +1 -0
- package/dist/{chunk-22KICA5N.js → chunk-ABIQ2NM3.js} +3 -5
- package/dist/{chunk-22KICA5N.js.map → chunk-ABIQ2NM3.js.map} +1 -1
- package/dist/{chunk-TT3V6PC7.js → chunk-AHWUP6LB.js} +4 -6
- package/dist/chunk-AHWUP6LB.js.map +1 -0
- package/dist/{chunk-M677DPBR.js → chunk-AKJ3EHND.js} +2 -6
- package/dist/chunk-AKJ3EHND.js.map +1 -0
- package/dist/{chunk-J7NROVB4.js → chunk-CY3MODZU.js} +2 -2
- package/dist/{chunk-QNPK2H5A.js → chunk-F5SAYP67.js} +3 -5
- package/dist/{chunk-QNPK2H5A.js.map → chunk-F5SAYP67.js.map} +1 -1
- package/dist/{chunk-I46PN4JU.js → chunk-F6WCSBHX.js} +2 -5
- package/dist/chunk-F6WCSBHX.js.map +1 -0
- package/dist/{chunk-25DJGLNU.js → chunk-FWYDZVFS.js} +9 -10
- package/dist/chunk-FWYDZVFS.js.map +1 -0
- package/dist/{chunk-75K2SCNC.js → chunk-HXAIYFI4.js} +3 -6
- package/dist/chunk-HXAIYFI4.js.map +1 -0
- package/dist/{chunk-7QJBDRTL.js → chunk-IFXBZY6Q.js} +3 -6
- package/dist/chunk-IFXBZY6Q.js.map +1 -0
- package/dist/{chunk-UIXR5GF3.js → chunk-ITKSPHYO.js} +2 -2
- package/dist/{chunk-HOROLWBY.js → chunk-J4X3CA3V.js} +2 -4
- package/dist/chunk-J4X3CA3V.js.map +1 -0
- package/dist/{chunk-GP4XI5KB.js → chunk-L5UKZAA4.js} +3 -3
- package/dist/{chunk-VTANFZKG.js → chunk-LGAHTK5V.js} +5 -7
- package/dist/chunk-LGAHTK5V.js.map +1 -0
- package/dist/{chunk-EWXK56WQ.js → chunk-LNG736CV.js} +1 -1
- package/dist/{chunk-DQB4WY5T.js → chunk-LVA77T4L.js} +4 -6
- package/dist/chunk-LVA77T4L.js.map +1 -0
- package/dist/{chunk-WY2F7475.js → chunk-NNYLSQ2X.js} +9 -10
- package/dist/chunk-NNYLSQ2X.js.map +1 -0
- package/dist/{chunk-CBR5NTFM.js → chunk-OCFX4MBQ.js} +10 -11
- package/dist/chunk-OCFX4MBQ.js.map +1 -0
- package/dist/{chunk-XTLFZ77E.js → chunk-OMAJKLVW.js} +18 -20
- package/dist/chunk-OMAJKLVW.js.map +1 -0
- package/dist/{chunk-DRAUYDZ5.js → chunk-OUBYTCTD.js} +2 -4
- package/dist/chunk-OUBYTCTD.js.map +1 -0
- package/dist/{chunk-KL2XZKDU.js → chunk-Q5RYLX6Z.js} +2 -5
- package/dist/chunk-Q5RYLX6Z.js.map +1 -0
- package/dist/{chunk-OT2H5EHA.js → chunk-RGGHVAAK.js} +2 -5
- package/dist/chunk-RGGHVAAK.js.map +1 -0
- package/dist/{chunk-22WSB5V2.js → chunk-RI647FTV.js} +9 -10
- package/dist/chunk-RI647FTV.js.map +1 -0
- package/dist/{chunk-QRYUUXNJ.js → chunk-SCSRGIEL.js} +4 -7
- package/dist/chunk-SCSRGIEL.js.map +1 -0
- package/dist/chunk-SEBNQYIE.js +30 -0
- package/dist/chunk-SEBNQYIE.js.map +1 -0
- package/dist/{chunk-GWRMQSME.js → chunk-VQYNQ5X7.js} +7 -11
- package/dist/chunk-VQYNQ5X7.js.map +1 -0
- package/dist/{chunk-NPA7L57G.js → chunk-WG5H5PTL.js} +4 -4
- package/dist/chunk-WG5H5PTL.js.map +1 -0
- package/dist/{chunk-5FNUPWPO.js → chunk-WLKT5YFP.js} +12 -5
- package/dist/chunk-WLKT5YFP.js.map +1 -0
- package/dist/{chunk-2BF2JIDK.js → chunk-WM2ETQIY.js} +9 -10
- package/dist/chunk-WM2ETQIY.js.map +1 -0
- package/dist/{chunk-MPZAPUVR.js → chunk-WTWZOQTD.js} +4 -6
- package/dist/chunk-WTWZOQTD.js.map +1 -0
- package/dist/{chunk-VWGOCWRF.js → chunk-X6FJMSL3.js} +9 -10
- package/dist/chunk-X6FJMSL3.js.map +1 -0
- package/dist/{chunk-R7VVXH5U.js → chunk-YPFDH2E7.js} +3 -6
- package/dist/chunk-YPFDH2E7.js.map +1 -0
- package/dist/{chunk-LYCBL2W3.js → chunk-Z47HLNM4.js} +4 -7
- package/dist/chunk-Z47HLNM4.js.map +1 -0
- package/dist/{chunk-ZIUAYN37.js → chunk-ZHRM4QOO.js} +2 -2
- package/dist/components/auth/countdown.js +3 -3
- package/dist/components/auth/forgot-password.js +4 -3
- package/dist/components/auth/reset-password-form.js +4 -3
- package/dist/components/auth/set-password.js +4 -3
- package/dist/components/auth/sign-in.js +4 -3
- package/dist/components/auth/sign-up.js +4 -3
- package/dist/components/auth/verification-form.js +4 -4
- package/dist/components/auth/verify-email.js +6 -5
- package/dist/components/auth/verify-phone.js +6 -5
- package/dist/components/authorization/deny.js +1 -2
- package/dist/components/authorization/grant.js +1 -2
- package/dist/components/iam/domains-page.js +7 -6
- package/dist/components/iam/iam-guard.js +2 -3
- package/dist/components/iam/permission-selector.js +1 -2
- package/dist/components/iam/permissions-page.js +3 -4
- package/dist/components/iam/permissions.js +1 -2
- package/dist/components/iam/role-detail-layout.js +2 -2
- package/dist/components/iam/role-detail-page.js +3 -4
- package/dist/components/iam/role-permissions-page.js +4 -5
- package/dist/components/iam/roles-page.js +7 -6
- package/dist/components/iam/roles.js +1 -2
- package/dist/components/iam/sessions-page.js +7 -6
- package/dist/components/iam/sessions.js +1 -2
- package/dist/components/iam/tenants-page.js +8 -7
- package/dist/components/iam/tenants.js +1 -2
- package/dist/components/iam/users-page.js +8 -7
- package/dist/components/iam/users.js +1 -2
- package/dist/components/profile/account.js +1 -2
- package/dist/components/profile/change-email-form.js +8 -8
- package/dist/components/profile/change-password-form.js +1 -2
- package/dist/components/profile/change-phone-form.js +8 -8
- package/dist/components/profile/otp-verification-modal.js +5 -5
- package/dist/components/profile/profile-layout.js +4 -3
- package/dist/components/profile/request-change-email-form.js +1 -2
- package/dist/components/profile/request-change-phone-form.js +1 -2
- package/dist/components/profile/security.js +13 -13
- package/dist/components/profile/verify-change-email-form.js +6 -6
- package/dist/components/profile/verify-change-phone-form.js +6 -6
- package/dist/index.js +66 -61
- package/dist/index.js.map +1 -1
- package/dist/pages/auth/layout.js +11 -9
- package/dist/pages/auth/layout.js.map +1 -1
- package/dist/pages/auth/route.d.ts +4 -0
- package/dist/pages/auth/route.js +44 -0
- package/dist/pages/auth/route.js.map +1 -0
- package/dist/pages/iam/shared/navigation.d.ts +1 -1
- package/dist/pages/iam/tenants/tenant-selector.d.ts +1 -0
- package/dist/pages/iam/tenants/tenant-selector.js +5 -5
- package/dist/pages/iam/tenants/tenants-data.d.ts +1 -0
- package/dist/pages/iam/tenants/tenants-data.js +1 -1
- package/dist/pages/iam/users/user-selector.d.ts +1 -0
- package/dist/pages/iam/users/user-selector.js +5 -5
- package/dist/pages/iam/users/users-data.d.ts +1 -0
- package/dist/pages/profile/account.js +1 -3
- package/dist/pages/profile/account.js.map +1 -1
- package/dist/pages/profile/security.js +1 -3
- package/dist/pages/profile/security.js.map +1 -1
- package/dist/providers/nuqs-adapter.d.ts +6 -0
- package/dist/utils/navigation.d.ts +5 -0
- package/package.json +43 -8
- package/dist/chunk-22WSB5V2.js.map +0 -1
- package/dist/chunk-25DJGLNU.js.map +0 -1
- package/dist/chunk-2BF2JIDK.js.map +0 -1
- package/dist/chunk-4YPLJ2P6.js.map +0 -1
- package/dist/chunk-5FNUPWPO.js.map +0 -1
- package/dist/chunk-75K2SCNC.js.map +0 -1
- package/dist/chunk-7QJBDRTL.js.map +0 -1
- package/dist/chunk-B7BS57X7.js.map +0 -1
- package/dist/chunk-CBR5NTFM.js.map +0 -1
- package/dist/chunk-DQB4WY5T.js.map +0 -1
- package/dist/chunk-DRAUYDZ5.js.map +0 -1
- package/dist/chunk-G2RWFKGF.js.map +0 -1
- package/dist/chunk-GWRMQSME.js.map +0 -1
- package/dist/chunk-HOROLWBY.js.map +0 -1
- package/dist/chunk-I46PN4JU.js.map +0 -1
- package/dist/chunk-IUCTHMVY.js.map +0 -1
- package/dist/chunk-KL2XZKDU.js.map +0 -1
- package/dist/chunk-LI7WPOVY.js.map +0 -1
- package/dist/chunk-LYCBL2W3.js.map +0 -1
- package/dist/chunk-M2K6O5CN.js.map +0 -1
- package/dist/chunk-M677DPBR.js.map +0 -1
- package/dist/chunk-MPZAPUVR.js.map +0 -1
- package/dist/chunk-NJGVOQIU.js.map +0 -1
- package/dist/chunk-NPA7L57G.js.map +0 -1
- package/dist/chunk-ORQZZUVL.js.map +0 -1
- package/dist/chunk-OT2H5EHA.js.map +0 -1
- package/dist/chunk-QRYUUXNJ.js.map +0 -1
- package/dist/chunk-R7VVXH5U.js.map +0 -1
- package/dist/chunk-SLIIENXJ.js +0 -34
- package/dist/chunk-SLIIENXJ.js.map +0 -1
- package/dist/chunk-TT3V6PC7.js.map +0 -1
- package/dist/chunk-UV7JR3YU.js.map +0 -1
- package/dist/chunk-VTANFZKG.js.map +0 -1
- package/dist/chunk-VWGOCWRF.js.map +0 -1
- package/dist/chunk-WAMZL5CS.js.map +0 -1
- package/dist/chunk-WY2F7475.js.map +0 -1
- package/dist/chunk-XTLFZ77E.js.map +0 -1
- package/dist/chunk-Y6KURGWG.js.map +0 -1
- /package/dist/{chunk-J7NROVB4.js.map → chunk-CY3MODZU.js.map} +0 -0
- /package/dist/{chunk-UIXR5GF3.js.map → chunk-ITKSPHYO.js.map} +0 -0
- /package/dist/{chunk-GP4XI5KB.js.map → chunk-L5UKZAA4.js.map} +0 -0
- /package/dist/{chunk-EWXK56WQ.js.map → chunk-LNG736CV.js.map} +0 -0
- /package/dist/{chunk-ZIUAYN37.js.map → chunk-ZHRM4QOO.js.map} +0 -0
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNavigate,
|
|
3
|
+
isExternalHref
|
|
4
|
+
} from "./chunk-6YHUCPJ4.js";
|
|
5
|
+
import {
|
|
6
|
+
createTranslator
|
|
7
|
+
} from "./chunk-SEBNQYIE.js";
|
|
8
|
+
|
|
1
9
|
// src/utils/cookie.ts
|
|
2
10
|
var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
3
11
|
var getSessionCookieName = (config) => {
|
|
@@ -16,32 +24,6 @@ import createFetchClient from "openapi-fetch";
|
|
|
16
24
|
import createClient from "openapi-react-query";
|
|
17
25
|
import { createContext, useContext, useMemo, useState } from "react";
|
|
18
26
|
|
|
19
|
-
// src/lib/translations.ts
|
|
20
|
-
function createTranslator(messages, namespace) {
|
|
21
|
-
return (key, params) => {
|
|
22
|
-
const fullKey = namespace ? `${namespace}.${key}` : key;
|
|
23
|
-
const keys = fullKey.split(".");
|
|
24
|
-
let value = messages;
|
|
25
|
-
for (const k of keys) {
|
|
26
|
-
if (value && typeof value === "object" && value !== null) {
|
|
27
|
-
value = value[k];
|
|
28
|
-
} else {
|
|
29
|
-
return fullKey;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (typeof value !== "string") {
|
|
33
|
-
return fullKey;
|
|
34
|
-
}
|
|
35
|
-
if (params) {
|
|
36
|
-
return value.replace(
|
|
37
|
-
/\{(\w+)\}/g,
|
|
38
|
-
(_, param) => String(params[param] ?? `{${param}}`)
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return value;
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
27
|
// src/types.ts
|
|
46
28
|
var defaultAuthClientConfig = {
|
|
47
29
|
features: {
|
|
@@ -281,14 +263,88 @@ function AuthStateProvider({
|
|
|
281
263
|
) }) });
|
|
282
264
|
}
|
|
283
265
|
|
|
266
|
+
// src/pages/iam/shared/navigation.tsx
|
|
267
|
+
import { useMesob } from "@mesob/ui/providers";
|
|
268
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
269
|
+
function AppLink({
|
|
270
|
+
href,
|
|
271
|
+
children,
|
|
272
|
+
onClick: userOnClick,
|
|
273
|
+
...props
|
|
274
|
+
}) {
|
|
275
|
+
const { config } = useConfig();
|
|
276
|
+
const mesob = useMesob();
|
|
277
|
+
const AuthLink = config.navigation?.linkComponent;
|
|
278
|
+
const Link = mesob?.navigation?.Link;
|
|
279
|
+
const locale = mesob?.locale;
|
|
280
|
+
const navigate = createNavigate({
|
|
281
|
+
onNavigate: config.navigation?.onNavigate,
|
|
282
|
+
mesobNavigate: mesob?.navigate
|
|
283
|
+
});
|
|
284
|
+
const handleClick = (event) => {
|
|
285
|
+
userOnClick?.(event);
|
|
286
|
+
if (event.defaultPrevented) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (isExternalHref(href)) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (props.target && props.target !== "_self") {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
event.preventDefault();
|
|
299
|
+
navigate(href);
|
|
300
|
+
};
|
|
301
|
+
if (AuthLink) {
|
|
302
|
+
return /* @__PURE__ */ jsx2(
|
|
303
|
+
AuthLink,
|
|
304
|
+
{
|
|
305
|
+
href,
|
|
306
|
+
onClick: handleClick,
|
|
307
|
+
...locale ? { locale } : {},
|
|
308
|
+
...props,
|
|
309
|
+
children
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (Link) {
|
|
314
|
+
return /* @__PURE__ */ jsx2(
|
|
315
|
+
Link,
|
|
316
|
+
{
|
|
317
|
+
href,
|
|
318
|
+
onClick: handleClick,
|
|
319
|
+
...locale ? { locale } : {},
|
|
320
|
+
...props,
|
|
321
|
+
children
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
return /* @__PURE__ */ jsx2("a", { href, onClick: handleClick, ...props, children });
|
|
326
|
+
}
|
|
327
|
+
function useNavigate() {
|
|
328
|
+
const { config } = useConfig();
|
|
329
|
+
const mesob = useMesob();
|
|
330
|
+
const navigate = createNavigate({
|
|
331
|
+
onNavigate: config.navigation?.onNavigate,
|
|
332
|
+
mesobNavigate: mesob?.navigate
|
|
333
|
+
});
|
|
334
|
+
return (href) => {
|
|
335
|
+
navigate(href);
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
284
339
|
export {
|
|
285
|
-
createTranslator,
|
|
286
340
|
getSessionCookieName,
|
|
287
341
|
hasAuthCookie,
|
|
288
342
|
useSession,
|
|
289
343
|
useApi,
|
|
290
344
|
useConfig,
|
|
291
345
|
useHasAuthCookie,
|
|
292
|
-
MesobAuthProvider
|
|
346
|
+
MesobAuthProvider,
|
|
347
|
+
AppLink,
|
|
348
|
+
useNavigate
|
|
293
349
|
};
|
|
294
|
-
//# sourceMappingURL=chunk-
|
|
350
|
+
//# sourceMappingURL=chunk-6BLYK6D6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/cookie.ts","../src/provider.tsx","../src/types.ts","../src/utils/custom-fetch.ts","../src/pages/iam/shared/navigation.tsx"],"sourcesContent":["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.prefix?.trim() || config.cookiePrefix?.trim() || '';\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 { 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 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 await signOutMutation.mutateAsync({});\n clearAuth();\n\n const redirectUrl = config.navigation?.defaultRedirectUrl || '/';\n\n if (config.navigation?.onNavigate) {\n config.navigation.onNavigate(redirectUrl);\n } else if (typeof window !== 'undefined') {\n window.location.href = redirectUrl;\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 { PermissionTree } from '@mesob/common';\n\nexport type UIConfig = {\n logo: React.ReactNode;\n name: React.ReactNode;\n logoImage?: string;\n};\n\nexport type FeaturesConfig = {\n enableSignup?: boolean;\n enablePasswordReset?: boolean;\n enableEmailSignup?: boolean;\n enablePhoneSignup?: boolean;\n enableEmailSignIn?: boolean;\n enablePhoneSignIn?: boolean;\n allowedSignupEmailDomains?: string[];\n enableSocialSignup?: boolean;\n socialProviders?: string[];\n};\n\nexport type TenantConfig = {\n enabled: boolean;\n tenantId: string;\n};\n\nexport type NavigationConfig = {\n locale?: string;\n defaultRedirectUrl?: string;\n onNavigate?: (path: string) => void;\n linkComponent?: React.ComponentType<\n React.ComponentProps<'a'> & { href: string }\n >;\n links?: {\n signIn?: string;\n signUp?: string;\n forgotPassword?: string;\n setPassword?: string;\n };\n};\n\nexport type AuthClientConfig = {\n baseURL: string;\n ui: UIConfig;\n features?: FeaturesConfig;\n tenant?: TenantConfig;\n permissions?: PermissionTree;\n navigation?: NavigationConfig;\n messages?: Record<string, unknown>;\n /** @deprecated use `prefix` */\n cookiePrefix?: string;\n /** Must match API `prefix` (session cookie + KV namespace). */\n prefix?: string;\n phoneRegex?: RegExp | string;\n /** Default userType filter for users list (e.g. 'employee'). Omit or 'all' to show all. */\n defaultUserType?: string;\n};\n\ntype DefaultAuthClientConfig = {\n readonly features: {\n readonly enableSignup: true;\n readonly enablePasswordReset: true;\n readonly enableEmailSignup: true;\n readonly enablePhoneSignup: true;\n readonly enableEmailSignIn: true;\n readonly enablePhoneSignIn: true;\n readonly allowedSignupEmailDomains: [];\n readonly enableSocialSignup: false;\n readonly socialProviders: [];\n };\n readonly navigation: {\n readonly locale: 'en';\n };\n readonly prefix: 'msb';\n readonly phoneRegex: RegExp;\n};\n\nexport const defaultAuthClientConfig: DefaultAuthClientConfig = {\n features: {\n enableSignup: true,\n enablePasswordReset: true,\n enableEmailSignup: true,\n enablePhoneSignup: true,\n enableEmailSignIn: true,\n enablePhoneSignIn: true,\n allowedSignupEmailDomains: [],\n enableSocialSignup: false,\n socialProviders: [],\n },\n navigation: {\n locale: 'en',\n },\n prefix: 'msb',\n phoneRegex: /^\\+251[79]\\d{8}$/,\n} as const;\n\nexport type User = {\n id: string;\n tenantId: string;\n fullName: string;\n email: string | null;\n phone: string | null;\n handle?: string;\n image: string | null;\n emailVerified: boolean;\n phoneVerified: boolean;\n lastSignInAt?: string | null;\n /** Role IDs */\n roles?: string[] | null;\n roleCodes?: string[] | null;\n permissions?: string[] | null;\n};\n\nexport type Session = {\n id?: string;\n expiresAt: string;\n createdAt?: string;\n userAgent?: string | null;\n ip?: string | null;\n};\n\nexport type AuthResponse = {\n user: User;\n session: Session;\n sessionToken?: string;\n sessionExpiresAt?: string;\n};\n\nexport type AuthErrorCode =\n | 'USER_NOT_FOUND'\n | 'INVALID_PASSWORD'\n | 'USER_EXISTS'\n | 'VERIFICATION_EXPIRED'\n | 'VERIFICATION_MISMATCH'\n | 'VERIFICATION_NOT_FOUND'\n | 'TOO_MANY_ATTEMPTS'\n | 'REQUIRES_VERIFICATION'\n | 'UNAUTHORIZED'\n | 'ACCESS_DENIED'\n | 'HAS_NO_PASSWORD'\n | 'PASSWORD_ALREADY_SET'\n | 'RATE_LIMITED';\n\nexport type AuthError = {\n message: string;\n code?: AuthErrorCode;\n status?: number;\n details?: Record<string, unknown>;\n};\n","import type { AuthClientConfig } from '../types';\n\nexport const createCustomFetch = (_config: AuthClientConfig) => {\n return (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (input instanceof Request) {\n return fetch(input, { ...init, credentials: 'include' });\n }\n return fetch(input, { ...init, credentials: 'include' });\n };\n};\n","'use client';\n\nimport { useMesob } from '@mesob/ui/providers';\nimport type { ReactNode } from 'react';\nimport { useConfig } from '../../../provider';\nimport { createNavigate, isExternalHref } from '../../../utils/navigation';\n\ntype AppLinkProps = React.ComponentProps<'a'> & {\n href: string;\n children: ReactNode;\n};\n\nexport function AppLink({\n href,\n children,\n onClick: userOnClick,\n ...props\n}: AppLinkProps) {\n const { config } = useConfig();\n const mesob = useMesob();\n const AuthLink = config.navigation?.linkComponent;\n const Link = mesob?.navigation?.Link;\n const locale = mesob?.locale;\n const navigate = createNavigate({\n onNavigate: config.navigation?.onNavigate,\n mesobNavigate: mesob?.navigate,\n });\n\n const handleClick: React.MouseEventHandler<HTMLAnchorElement> = (event) => {\n userOnClick?.(event);\n if (event.defaultPrevented) {\n return;\n }\n if (isExternalHref(href)) {\n return;\n }\n if (\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return;\n }\n if (props.target && props.target !== '_self') {\n return;\n }\n event.preventDefault();\n navigate(href);\n };\n\n if (AuthLink) {\n return (\n <AuthLink\n href={href}\n onClick={handleClick}\n {...(locale ? ({ locale } as object) : {})}\n {...props}\n >\n {children}\n </AuthLink>\n );\n }\n\n if (Link) {\n return (\n <Link\n href={href}\n onClick={handleClick}\n {...(locale ? ({ locale } as object) : {})}\n {...props}\n >\n {children}\n </Link>\n );\n }\n\n return (\n <a href={href} onClick={handleClick} {...props}>\n {children}\n </a>\n );\n}\n\nexport function useNavigate() {\n const { config } = useConfig();\n const mesob = useMesob();\n const navigate = createNavigate({\n onNavigate: config.navigation?.onNavigate,\n mesobNavigate: mesob?.navigate,\n });\n\n return (href: string) => {\n navigate(href);\n };\n}\n"],"mappings":"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","names":["error","jsx"]}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChangeProfile
|
|
3
3
|
} from "./chunk-C5ZW7FD2.js";
|
|
4
|
-
import {
|
|
5
|
-
useSession
|
|
6
|
-
} from "./chunk-M2K6O5CN.js";
|
|
7
4
|
|
|
8
5
|
// src/components/profile/account.tsx
|
|
6
|
+
import { useSession } from "@mesob/auth-react";
|
|
9
7
|
import { Badge, Card, CardContent, Separator } from "@mesob/ui/components";
|
|
10
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
9
|
function Account() {
|
|
@@ -86,4 +84,4 @@ function Account() {
|
|
|
86
84
|
export {
|
|
87
85
|
Account
|
|
88
86
|
};
|
|
89
|
-
//# sourceMappingURL=chunk-
|
|
87
|
+
//# sourceMappingURL=chunk-6HVUVXQB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/profile/account.tsx"],"sourcesContent":["'use client';\n\nimport { useSession } from '@mesob/auth-react';\nimport { Badge, Card, CardContent, Separator } from '@mesob/ui/components';\nimport { ChangeProfile } from './change-profile';\n\nexport function Account() {\n const { user, isAuthenticated } = useSession();\n\n if (!(isAuthenticated && user)) {\n return (\n <div className=\"rounded-[1.75rem] border border-dashed border-border bg-muted/30 p-8 text-sm text-muted-foreground\">\n Sign in required.\n </div>\n );\n }\n\n const contactItems = [\n {\n label: 'Email',\n value: user.email ?? 'No email address',\n status: user.emailVerified ? 'Verified' : 'Needs verification',\n },\n {\n label: 'Phone',\n value: user.phone ?? 'No phone number',\n status: user.phoneVerified ? 'Verified' : 'Needs verification',\n },\n ];\n\n return (\n <div className=\"mx-auto flex w-full max-w-6xl flex-col gap-6\">\n <div className=\"relative overflow-hidden rounded-[2rem] border border-border/60 bg-gradient-to-br from-background via-background to-primary/5 p-6 shadow-sm\">\n <div className=\"absolute inset-y-0 right-0 w-1/3 bg-[radial-gradient(circle_at_top,_hsl(var(--primary)/0.16),_transparent_60%)]\" />\n <div className=\"relative flex flex-col gap-5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\">Profile console</Badge>\n <Badge variant=\"secondary\">Live session data</Badge>\n </div>\n <div className=\"max-w-2xl space-y-2\">\n <h2 className=\"text-2xl font-semibold tracking-tight\">\n Account details, contact state, and verification status.\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n Keep the identity surface minimal here. Contact updates and\n sensitive changes stay in security.\n </p>\n </div>\n <ChangeProfile user={user} />\n </div>\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-[1.5fr_0.9fr]\">\n <Card className=\"rounded-[1.75rem] border-border/60 shadow-sm\">\n <CardContent className=\"space-y-5 p-6\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium text-muted-foreground\">\n Contact channels\n </div>\n <div className=\"text-lg font-semibold\">Primary profile data</div>\n </div>\n <Separator />\n <div className=\"space-y-4\">\n {contactItems.map((item) => (\n <div\n key={item.label}\n className=\"flex flex-col gap-3 rounded-2xl border border-border/60 bg-muted/20 p-4 md:flex-row md:items-center md:justify-between\"\n >\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium\">{item.label}</div>\n <div className=\"text-sm text-muted-foreground\">\n {item.value}\n </div>\n </div>\n <Badge variant=\"outline\">{item.status}</Badge>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n\n <Card className=\"rounded-[1.75rem] border-border/60 shadow-sm\">\n <CardContent className=\"space-y-5 p-6\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium text-muted-foreground\">\n Snapshot\n </div>\n <div className=\"text-lg font-semibold\">\n Session-facing identity\n </div>\n </div>\n <Separator />\n <div className=\"grid gap-3\">\n <div className=\"rounded-2xl bg-primary/[0.06] p-4\">\n <div className=\"text-xs uppercase tracking-[0.18em] text-muted-foreground\">\n Full name\n </div>\n <div className=\"mt-2 text-base font-semibold\">\n {user.fullName}\n </div>\n </div>\n <div className=\"rounded-2xl bg-muted/25 p-4\">\n <div className=\"text-xs uppercase tracking-[0.18em] text-muted-foreground\">\n Last sign in\n </div>\n <div className=\"mt-2 text-base font-semibold\">\n {user.lastSignInAt\n ? new Date(user.lastSignInAt).toLocaleString()\n : 'No activity recorded'}\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,OAAO,MAAM,aAAa,iBAAiB;AAQ9C,cAwBI,YAxBJ;AALC,SAAS,UAAU;AACxB,QAAM,EAAE,MAAM,gBAAgB,IAAI,WAAW;AAE7C,MAAI,EAAE,mBAAmB,OAAO;AAC9B,WACE,oBAAC,SAAI,WAAU,sGAAqG,+BAEpH;AAAA,EAEJ;AAEA,QAAM,eAAe;AAAA,IACnB;AAAA,MACE,OAAO;AAAA,MACP,OAAO,KAAK,SAAS;AAAA,MACrB,QAAQ,KAAK,gBAAgB,aAAa;AAAA,IAC5C;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,OAAO,KAAK,SAAS;AAAA,MACrB,QAAQ,KAAK,gBAAgB,aAAa;AAAA,IAC5C;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,gDACb;AAAA,yBAAC,SAAI,WAAU,+IACb;AAAA,0BAAC,SAAI,WAAU,mHAAkH;AAAA,MACjI,qBAAC,SAAI,WAAU,gCACb;AAAA,6BAAC,SAAI,WAAU,qCACb;AAAA,8BAAC,SAAM,SAAQ,WAAU,6BAAe;AAAA,UACxC,oBAAC,SAAM,SAAQ,aAAY,+BAAiB;AAAA,WAC9C;AAAA,QACA,qBAAC,SAAI,WAAU,uBACb;AAAA,8BAAC,QAAG,WAAU,yCAAwC,sEAEtD;AAAA,UACA,oBAAC,OAAE,WAAU,iCAAgC,6GAG7C;AAAA,WACF;AAAA,QACA,oBAAC,iBAAc,MAAY;AAAA,SAC7B;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,yCACb;AAAA,0BAAC,QAAK,WAAU,gDACd,+BAAC,eAAY,WAAU,iBACrB;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAI,WAAU,6CAA4C,8BAE3D;AAAA,UACA,oBAAC,SAAI,WAAU,yBAAwB,kCAAoB;AAAA,WAC7D;AAAA,QACA,oBAAC,aAAU;AAAA,QACX,oBAAC,SAAI,WAAU,aACZ,uBAAa,IAAI,CAAC,SACjB;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YAEV;AAAA,mCAAC,SAAI,WAAU,aACb;AAAA,oCAAC,SAAI,WAAU,uBAAuB,eAAK,OAAM;AAAA,gBACjD,oBAAC,SAAI,WAAU,iCACZ,eAAK,OACR;AAAA,iBACF;AAAA,cACA,oBAAC,SAAM,SAAQ,WAAW,eAAK,QAAO;AAAA;AAAA;AAAA,UATjC,KAAK;AAAA,QAUZ,CACD,GACH;AAAA,SACF,GACF;AAAA,MAEA,oBAAC,QAAK,WAAU,gDACd,+BAAC,eAAY,WAAU,iBACrB;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAI,WAAU,6CAA4C,sBAE3D;AAAA,UACA,oBAAC,SAAI,WAAU,yBAAwB,qCAEvC;AAAA,WACF;AAAA,QACA,oBAAC,aAAU;AAAA,QACX,qBAAC,SAAI,WAAU,cACb;AAAA,+BAAC,SAAI,WAAU,qCACb;AAAA,gCAAC,SAAI,WAAU,6DAA4D,uBAE3E;AAAA,YACA,oBAAC,SAAI,WAAU,gCACZ,eAAK,UACR;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,+BACb;AAAA,gCAAC,SAAI,WAAU,6DAA4D,0BAE3E;AAAA,YACA,oBAAC,SAAI,WAAU,gCACZ,eAAK,eACF,IAAI,KAAK,KAAK,YAAY,EAAE,eAAe,IAC3C,wBACN;AAAA,aACF;AAAA,WACF;AAAA,SACF,GACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/utils/navigation.ts
|
|
2
|
+
function createNavigate({
|
|
3
|
+
onNavigate,
|
|
4
|
+
mesobNavigate
|
|
5
|
+
}) {
|
|
6
|
+
return (path) => {
|
|
7
|
+
if (onNavigate) {
|
|
8
|
+
onNavigate(path);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (mesobNavigate) {
|
|
12
|
+
mesobNavigate(path);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (typeof window !== "undefined") {
|
|
16
|
+
window.location.href = path;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function isExternalHref(href) {
|
|
21
|
+
return /^([a-zA-Z][a-zA-Z\d+\-.]*:)?\/\//.test(href) || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
createNavigate,
|
|
26
|
+
isExternalHref
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-6YHUCPJ4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/navigation.ts"],"sourcesContent":["export function createNavigate({\n onNavigate,\n mesobNavigate,\n}: {\n onNavigate?: (path: string) => void;\n mesobNavigate?: (path: string) => void;\n}): (path: string) => void {\n return (path: string) => {\n if (onNavigate) {\n onNavigate(path);\n return;\n }\n if (mesobNavigate) {\n mesobNavigate(path);\n return;\n }\n if (typeof window !== 'undefined') {\n window.location.href = path;\n }\n };\n}\n\nexport function isExternalHref(href: string): boolean {\n return (\n /^([a-zA-Z][a-zA-Z\\d+\\-.]*:)?\\/\\//.test(href) ||\n href.startsWith('mailto:') ||\n href.startsWith('tel:') ||\n href.startsWith('#')\n );\n}\n"],"mappings":";AAAO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AACF,GAG2B;AACzB,SAAO,CAAC,SAAiB;AACvB,QAAI,YAAY;AACd,iBAAW,IAAI;AACf;AAAA,IACF;AACA,QAAI,eAAe;AACjB,oBAAc,IAAI;AAClB;AAAA,IACF;AACA,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,EACF;AACF;AAEO,SAAS,eAAe,MAAuB;AACpD,SACE,mCAAmC,KAAK,IAAI,KAC5C,KAAK,WAAW,SAAS,KACzB,KAAK,WAAW,MAAM,KACtB,KAAK,WAAW,GAAG;AAEvB;","names":[]}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VerificationForm
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-L5UKZAA4.js";
|
|
4
4
|
import {
|
|
5
5
|
handleError
|
|
6
6
|
} from "./chunk-W7UHDTBI.js";
|
|
7
|
+
import {
|
|
8
|
+
createNavigate
|
|
9
|
+
} from "./chunk-6YHUCPJ4.js";
|
|
7
10
|
import {
|
|
8
11
|
AuthLayout
|
|
9
12
|
} from "./chunk-DPH2PHK3.js";
|
|
10
13
|
import {
|
|
11
14
|
useTranslator
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
useApi,
|
|
15
|
-
useConfig
|
|
16
|
-
} from "./chunk-M2K6O5CN.js";
|
|
15
|
+
} from "./chunk-WG5H5PTL.js";
|
|
17
16
|
|
|
18
17
|
// src/components/auth/verify-phone.tsx
|
|
18
|
+
import { useApi, useConfig } from "@mesob/auth-react";
|
|
19
19
|
import { Alert, AlertDescription, AlertTitle } from "@mesob/ui/components";
|
|
20
20
|
import { useMesob } from "@mesob/ui/providers";
|
|
21
21
|
import { IconAlertCircle } from "@tabler/icons-react";
|
|
@@ -46,10 +46,9 @@ var VerifyPhone = ({
|
|
|
46
46
|
"/phone/verification/request"
|
|
47
47
|
);
|
|
48
48
|
const signInLink = config.navigation?.links?.signIn || "/auth/sign-in";
|
|
49
|
-
const onNavigate =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
49
|
+
const onNavigate = createNavigate({
|
|
50
|
+
onNavigate: config.navigation?.onNavigate,
|
|
51
|
+
mesobNavigate: mesob?.navigate
|
|
53
52
|
});
|
|
54
53
|
const logoImage = config.ui.logoImage;
|
|
55
54
|
const defaultRedirect = redirectUrl || config.navigation?.defaultRedirectUrl || "/";
|
|
@@ -191,4 +190,4 @@ var VerifyPhone = ({
|
|
|
191
190
|
export {
|
|
192
191
|
VerifyPhone
|
|
193
192
|
};
|
|
194
|
-
//# sourceMappingURL=chunk-
|
|
193
|
+
//# sourceMappingURL=chunk-7FSFDTQR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/auth/verify-phone.tsx"],"sourcesContent":["'use client';\n\nimport { useApi, useConfig } from '@mesob/auth-react';\nimport { Alert, AlertDescription, AlertTitle } from '@mesob/ui/components';\nimport { useMesob } from '@mesob/ui/providers';\nimport { IconAlertCircle } from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { toast } from 'sonner';\nimport { useTranslator } from '../../hooks/use-translator';\nimport type { AuthResponse } from '../../types';\nimport type { AuthErrorContent } from '../../utils/handle-error';\nimport { handleError } from '../../utils/handle-error';\nimport { createNavigate } from '../../utils/navigation';\nimport { AuthLayout } from './auth-layout';\nimport { VerificationForm } from './verification-form';\n\ntype VerifyPhoneProps = {\n verificationId: string;\n context: 'sign-in' | 'sign-up';\n phone?: string;\n redirectUrl?: string;\n};\n\nexport const VerifyPhone = ({\n verificationId,\n context,\n phone = '',\n redirectUrl,\n}: VerifyPhoneProps) => {\n const { hooks, refresh, setAuth } = useApi();\n const { config } = useConfig();\n const mesob = useMesob();\n const Link = mesob?.navigation?.Link;\n const t = useTranslator('Auth.verification');\n const common = useTranslator('Common');\n const footer = useTranslator('Auth.forgotPassword.footer');\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthErrorContent | null>(null);\n\n const verifyPhoneMutation = hooks.useMutation(\n 'post',\n '/phone/verification/confirm',\n );\n const requestPhoneMutation = hooks.useMutation(\n 'post',\n '/phone/verification/request',\n );\n\n const signInLink = config.navigation?.links?.signIn || '/auth/sign-in';\n const onNavigate = createNavigate({\n onNavigate: config.navigation?.onNavigate,\n mesobNavigate: mesob?.navigate,\n });\n const logoImage = config.ui.logoImage;\n const defaultRedirect =\n redirectUrl || config.navigation?.defaultRedirectUrl || '/';\n\n useEffect(() => {\n if (error) {\n toast.error(error.title || 'Error', {\n description: error.description,\n });\n }\n }, [error]);\n\n const handleSubmit = async (values: { code: string }) => {\n if (!verificationId) {\n setError({\n title: t('errors.fallback'),\n description: t('errors.fallback'),\n });\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const res = await verifyPhoneMutation.mutateAsync({\n body: {\n verificationId,\n code: values.code,\n context,\n },\n });\n if (res && 'user' in res && 'session' in res && res.session) {\n setAuth(res as AuthResponse);\n onNavigate(defaultRedirect);\n return;\n }\n await refresh();\n onNavigate(defaultRedirect);\n } catch (err) {\n handleError(err, setError, t);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleResend = async () => {\n setError(null);\n try {\n const targetPhone = context === 'sign-up' ? phone : null;\n if (!targetPhone) {\n setError({\n title: t('errors.fallback'),\n description: t('phone.missingPhone'),\n });\n return;\n }\n const res = await requestPhoneMutation.mutateAsync({\n body: {\n phone: targetPhone,\n context,\n },\n });\n if (res && 'verificationId' in res && res.verificationId) {\n onNavigate(\n `/auth/verify-phone?context=${context}&verificationId=${res.verificationId}&phone=${targetPhone}`,\n );\n return;\n }\n if (res && 'message' in res && typeof res.message === 'string') {\n toast.info(t('errors.resendAmbiguousTitle'), {\n description: t('errors.resendAmbiguousDescription'),\n });\n return;\n }\n setError({\n title: t('errors.fallback'),\n description: t('phone.resendFailed'),\n });\n } catch (err) {\n handleError(err, setError, t);\n }\n };\n\n if (!verificationId) {\n return (\n <AuthLayout\n title={common('invalidLinkTitle')}\n description={common('invalidLinkDescription')}\n footer={\n <Link href={signInLink} className=\"text-primary hover:underline\">\n {footer('backToSignIn')}\n </Link>\n }\n >\n <div />\n </AuthLayout>\n );\n }\n\n let errorContent: AuthErrorContent | null = null;\n if (error) {\n if (typeof error === 'string') {\n errorContent = { title: 'Error', description: error };\n } else {\n errorContent = error;\n }\n }\n\n return (\n <AuthLayout\n title={config.ui.name}\n description={t('phone.description', {\n target: phone || t('phone.missingPhone'),\n })}\n logoImage={logoImage}\n footer={\n Link ? (\n <Link href={signInLink} className=\"text-primary hover:underline\">\n {footer('backToSignIn')}\n </Link>\n ) : (\n <a\n href={signInLink}\n onClick={(e) => {\n e.preventDefault();\n onNavigate(signInLink);\n }}\n className=\"text-primary hover:underline\"\n >\n {footer('backToSignIn')}\n </a>\n )\n }\n >\n <VerificationForm\n verificationId={verificationId}\n onSubmit={handleSubmit}\n onResend={handleResend}\n isLoading={\n isLoading ||\n verifyPhoneMutation.isPending ||\n requestPhoneMutation.isPending\n }\n error={error}\n />\n {errorContent && (\n <Alert variant=\"destructive\" className=\"mt-4\">\n <IconAlertCircle className=\"h-4 w-4\" />\n <AlertTitle>{errorContent.title}</AlertTitle>\n <AlertDescription>{errorContent.description}</AlertDescription>\n </Alert>\n )}\n </AuthLayout>\n );\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppLink
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
useSession
|
|
6
|
-
} from "./chunk-M2K6O5CN.js";
|
|
3
|
+
} from "./chunk-6BLYK6D6.js";
|
|
7
4
|
|
|
8
5
|
// src/pages/profile/_components/profile-sidebar.tsx
|
|
6
|
+
import { useSession } from "@mesob/auth-react";
|
|
9
7
|
import { Avatar, AvatarFallback, AvatarImage } from "@mesob/ui/components";
|
|
10
8
|
import { cn } from "@mesob/ui/lib/utils";
|
|
11
9
|
import { IconLock, IconUser } from "@tabler/icons-react";
|
|
@@ -92,4 +90,4 @@ export {
|
|
|
92
90
|
ProfileSidebar,
|
|
93
91
|
ProfileLayout
|
|
94
92
|
};
|
|
95
|
-
//# sourceMappingURL=chunk-
|
|
93
|
+
//# sourceMappingURL=chunk-ABIQ2NM3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pages/profile/_components/profile-sidebar.tsx","../src/components/profile/profile-layout.tsx"],"sourcesContent":["'use client';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@mesob/ui/components';\nimport { cn } from '@mesob/ui/lib/utils';\nimport { IconLock, IconUser } from '@tabler/icons-react';\nimport { useMemo } from 'react';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/pages/profile/_components/profile-sidebar.tsx","../src/components/profile/profile-layout.tsx"],"sourcesContent":["'use client';\n\nimport { useSession } from '@mesob/auth-react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@mesob/ui/components';\nimport { cn } from '@mesob/ui/lib/utils';\nimport { IconLock, IconUser } from '@tabler/icons-react';\nimport { useMemo } from 'react';\nimport { AppLink } from '../../iam/shared/navigation';\n\nconst profileNavItems = [\n {\n title: 'Account',\n href: '/profile/account',\n icon: IconUser,\n caption: 'Identity and contact',\n },\n {\n title: 'Security',\n href: '/profile/security',\n icon: IconLock,\n caption: 'Password and recovery',\n },\n];\n\nexport function ProfileSidebar() {\n const { user } = useSession();\n const pathname = useMemo(() => {\n if (typeof window === 'undefined') {\n return '';\n }\n return window.location.pathname;\n }, []);\n\n const initials =\n user?.fullName\n ?.split(' ')\n .map((part) => part[0])\n .join('')\n .toUpperCase()\n .slice(0, 2) || 'U';\n\n return (\n <aside className=\"w-full border-b border-border/60 bg-gradient-to-b from-muted/30 via-background to-background lg:w-80 lg:border-b-0 lg:border-r\">\n <div className=\"flex h-full flex-col gap-6 p-4 lg:p-5\">\n <div className=\"rounded-[1.75rem] border border-border/60 bg-background/85 p-5 shadow-sm backdrop-blur\">\n <div className=\"flex items-center gap-4\">\n <Avatar className=\"h-12 w-12 ring-4 ring-primary/10\">\n <AvatarImage src={user?.image || ''} alt={user?.fullName || ''} />\n <AvatarFallback>{initials}</AvatarFallback>\n </Avatar>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-muted-foreground\">\n Profile workspace\n </div>\n <div className=\"truncate text-base font-semibold\">\n {user?.fullName ?? 'Account'}\n </div>\n </div>\n </div>\n </div>\n\n <nav className=\"grid gap-2\">\n {profileNavItems.map((item) => {\n const Icon = item.icon;\n const isActive = pathname === item.href;\n\n return (\n <AppLink\n key={item.href}\n href={item.href}\n className={cn(\n 'group rounded-[1.5rem] border px-4 py-4 transition-all',\n isActive\n ? 'border-primary/30 bg-primary/[0.08] shadow-sm'\n : 'border-border/60 bg-background/70 hover:border-primary/20 hover:bg-muted/30',\n )}\n >\n <div className=\"flex items-start gap-3\">\n <div\n className={cn(\n 'rounded-2xl p-2 transition-colors',\n isActive\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-foreground',\n )}\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"font-medium\">{item.title}</div>\n <div className=\"text-sm text-muted-foreground\">\n {item.caption}\n </div>\n </div>\n </div>\n </AppLink>\n );\n })}\n </nav>\n </div>\n </aside>\n );\n}\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { ProfileSidebar } from '../../pages/profile/_components/profile-sidebar';\n\nexport function ProfileLayout({ children }: { children: ReactNode }) {\n return (\n <div className=\"flex flex-1 flex-col overflow-hidden lg:flex-row\">\n <ProfileSidebar />\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden bg-[linear-gradient(180deg,hsl(var(--muted)/0.18),transparent_18rem)]\">\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,UAAU;AACnB,SAAS,UAAU,gBAAgB;AACnC,SAAS,eAAe;AAwCZ,SACE,KADF;AArCZ,IAAM,kBAAkB;AAAA,EACtB;AAAA,IACE,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AACF;AAEO,SAAS,iBAAiB;AAC/B,QAAM,EAAE,KAAK,IAAI,WAAW;AAC5B,QAAM,WAAW,QAAQ,MAAM;AAC7B,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO;AAAA,IACT;AACA,WAAO,OAAO,SAAS;AAAA,EACzB,GAAG,CAAC,CAAC;AAEL,QAAM,WACJ,MAAM,UACF,MAAM,GAAG,EACV,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,EACrB,KAAK,EAAE,EACP,YAAY,EACZ,MAAM,GAAG,CAAC,KAAK;AAEpB,SACE,oBAAC,WAAM,WAAU,kIACf,+BAAC,SAAI,WAAU,yCACb;AAAA,wBAAC,SAAI,WAAU,0FACb,+BAAC,SAAI,WAAU,2BACb;AAAA,2BAAC,UAAO,WAAU,oCAChB;AAAA,4BAAC,eAAY,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,YAAY,IAAI;AAAA,QAChE,oBAAC,kBAAgB,oBAAS;AAAA,SAC5B;AAAA,MACA,qBAAC,SAAI,WAAU,WACb;AAAA,4BAAC,SAAI,WAAU,sDAAqD,+BAEpE;AAAA,QACA,oBAAC,SAAI,WAAU,oCACZ,gBAAM,YAAY,WACrB;AAAA,SACF;AAAA,OACF,GACF;AAAA,IAEA,oBAAC,SAAI,WAAU,cACZ,0BAAgB,IAAI,CAAC,SAAS;AAC7B,YAAM,OAAO,KAAK;AAClB,YAAM,WAAW,aAAa,KAAK;AAEnC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,YACT;AAAA,YACA,WACI,kDACA;AAAA,UACN;AAAA,UAEA,+BAAC,SAAI,WAAU,0BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,WACI,uCACA;AAAA,gBACN;AAAA,gBAEA,8BAAC,QAAK,WAAU,UAAS;AAAA;AAAA,YAC3B;AAAA,YACA,qBAAC,SAAI,WAAU,WACb;AAAA,kCAAC,SAAI,WAAU,eAAe,eAAK,OAAM;AAAA,cACzC,oBAAC,SAAI,WAAU,iCACZ,eAAK,SACR;AAAA,eACF;AAAA,aACF;AAAA;AAAA,QA1BK,KAAK;AAAA,MA2BZ;AAAA,IAEJ,CAAC,GACH;AAAA,KACF,GACF;AAEJ;;;AC/FI,SACE,OAAAA,MADF,QAAAC,aAAA;AAFG,SAAS,cAAc,EAAE,SAAS,GAA4B;AACnE,SACE,gBAAAA,MAAC,SAAI,WAAU,oDACb;AAAA,oBAAAD,KAAC,kBAAe;AAAA,IAChB,gBAAAA,KAAC,SAAI,WAAU,sHACZ,UACH;AAAA,KACF;AAEJ;","names":["jsx","jsxs"]}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PermissionSelector
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F6WCSBHX.js";
|
|
4
4
|
import {
|
|
5
5
|
IAMGuard
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
useApi
|
|
9
|
-
} from "./chunk-M2K6O5CN.js";
|
|
6
|
+
} from "./chunk-ZHRM4QOO.js";
|
|
10
7
|
|
|
11
8
|
// src/components/iam/role-permissions-page.tsx
|
|
9
|
+
import { useApi } from "@mesob/auth-react";
|
|
12
10
|
import {
|
|
13
11
|
Badge,
|
|
14
12
|
Button,
|
|
@@ -289,4 +287,4 @@ function RolePermissionsPageContent({ roleId }) {
|
|
|
289
287
|
export {
|
|
290
288
|
RolePermissionsPage
|
|
291
289
|
};
|
|
292
|
-
//# sourceMappingURL=chunk-
|
|
290
|
+
//# sourceMappingURL=chunk-AHWUP6LB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/iam/role-permissions-page.tsx"],"sourcesContent":["'use client';\n\nimport { useApi } from '@mesob/auth-react';\nimport {\n Badge,\n Button,\n DataTablePagination,\n DeleteConfirmButton,\n DisplayTable,\n EntityEmptyState,\n EntityFilter,\n EntityHeader,\n EntityLoadingState,\n EntitySearch,\n EntitySort,\n EntityViewToggle,\n PageBody,\n Tbody,\n Td,\n Th,\n Thead,\n Tr,\n useEntityPagination,\n useEntityParams,\n} from '@mesob/ui/components';\nimport { IconKey, IconPlus, IconTrash } from '@tabler/icons-react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport type { ReactNode } from 'react';\nimport { useMemo } from 'react';\nimport { toast } from 'sonner';\nimport type { paths } from '../../data/openapi';\nimport { IAMGuard } from './iam-guard';\nimport { PermissionSelector } from './permission-selector';\n\ntype Permission = {\n id: string;\n description?: unknown;\n activity: string;\n application: string;\n feature: string;\n};\n\ntype RolePermissionsPageProps = {\n roleId: string;\n};\n\nconst TABLE_COLUMN_COUNT = 4;\n\nexport function RolePermissionsPage({ roleId }: RolePermissionsPageProps) {\n return (\n <IAMGuard>\n <RolePermissionsPageContent roleId={roleId} />\n </IAMGuard>\n );\n}\n\nfunction RolePermissionsPageContent({ roleId }: RolePermissionsPageProps) {\n const { hooks } = useApi();\n const qc = useQueryClient();\n const { queryConfig, params, setParams } = useEntityParams({\n searchKey: 'search',\n defaultSort: 'application',\n defaultOrder: 'asc',\n });\n const permissionsQuery = useMemo(\n () =>\n ({\n params: {\n path: { id: roleId },\n query: queryConfig.params.query,\n },\n }) as {\n params: {\n path: { id: string };\n query: NonNullable<\n paths['/roles/{id}/permissions']['get']['parameters']['query']\n >;\n };\n },\n [queryConfig, roleId],\n );\n\n const { data, isPending, isFetching } = hooks.useQuery(\n 'get',\n '/roles/{id}/permissions',\n permissionsQuery,\n { enabled: !!roleId },\n );\n const assignPermissions = hooks.useMutation(\n 'post',\n '/roles/{id}/permissions',\n {\n onSuccess: (result: { created?: number } | undefined) => {\n qc.invalidateQueries({ queryKey: ['get', '/roles'] });\n qc.invalidateQueries({ queryKey: ['get', '/roles/{id}'] });\n qc.invalidateQueries({ queryKey: ['get', '/roles/{id}/permissions'] });\n toast.success(\n result?.created\n ? `${result.created} permission(s) added`\n : 'No changes',\n );\n },\n onError: () => {\n toast.error('Failed to assign permissions');\n },\n },\n );\n const revokePermission = hooks.useMutation(\n 'delete',\n '/roles/{id}/permissions/{permissionId}',\n {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/roles'] });\n qc.invalidateQueries({ queryKey: ['get', '/roles/{id}'] });\n qc.invalidateQueries({\n queryKey: ['get', '/roles/{id}/permissions'],\n });\n toast.success('Permission removed');\n },\n onError: () => {\n toast.error('Failed to remove permission');\n },\n },\n );\n\n const permissions = (data?.permissions ?? []) as Permission[];\n const { total, pageCount } = useEntityPagination({\n items: permissions,\n total: data?.total,\n pageSize: params.pageSize,\n });\n const isLoading = isPending || isFetching;\n const currentView = (params.view || 'table') as 'table' | 'card';\n\n if (!roleId) {\n return null;\n }\n\n let content: ReactNode;\n if (isLoading) {\n content = (\n <EntityLoadingState\n view={currentView}\n rowCount={params.pageSize}\n columnCount={TABLE_COLUMN_COUNT}\n cardCount={params.pageSize}\n />\n );\n } else if (total === 0) {\n content = (\n <EntityEmptyState\n icon={IconKey}\n entityName=\"permission\"\n title=\"No permissions assigned\"\n description=\"Assign permissions from the selector to grant access to this role.\"\n />\n );\n } else if (currentView === 'table') {\n content = (\n <div className=\"space-y-4\">\n <DisplayTable withTableBorder>\n <Thead>\n <Tr>\n <Th>Permission</Th>\n <Th>Application</Th>\n <Th>Feature</Th>\n <Th className=\"w-[60px]\" />\n </Tr>\n </Thead>\n <Tbody>\n {permissions.map((permission) => (\n <Tr key={permission.id}>\n <Td>\n <div className=\"space-y-1\">\n <p className=\"font-medium\">{permission.activity}</p>\n <p className=\"font-mono text-xs text-muted-foreground\">\n {permission.id}\n </p>\n </div>\n </Td>\n <Td>\n <Badge variant=\"secondary\">{permission.application}</Badge>\n </Td>\n <Td>\n <Badge variant=\"outline\">{permission.feature}</Badge>\n </Td>\n <Td>\n <DeleteConfirmButton\n entityName=\"permission\"\n onConfirm={() =>\n revokePermission.mutate({\n params: {\n path: { id: roleId, permissionId: permission.id },\n },\n })\n }\n triggerClassName=\"size-8 text-destructive hover:text-destructive\"\n />\n </Td>\n </Tr>\n ))}\n </Tbody>\n </DisplayTable>\n <DataTablePagination\n pageIndex={params.page - 1}\n pageSize={params.pageSize}\n pageCount={pageCount}\n totalRows={total}\n onPageChange={(page) => setParams({ page: page + 1 })}\n onPageSizeChange={(pageSize) => setParams({ pageSize, page: 1 })}\n />\n </div>\n );\n } else {\n content = (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {permissions.map((permission) => (\n <div\n key={permission.id}\n className=\"rounded-xl border border-border/60 bg-card p-4 shadow-sm\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-2\">\n <p className=\"font-semibold\">{permission.activity}</p>\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"secondary\">{permission.application}</Badge>\n <Badge variant=\"outline\">{permission.feature}</Badge>\n </div>\n <p className=\"font-mono text-xs text-muted-foreground\">\n {permission.id}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() =>\n revokePermission.mutate({\n params: {\n path: { id: roleId, permissionId: permission.id },\n },\n })\n }\n disabled={revokePermission.isPending}\n >\n <IconTrash className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n ))}\n </div>\n <DataTablePagination\n pageIndex={params.page - 1}\n pageSize={params.pageSize}\n pageCount={pageCount}\n totalRows={total}\n onPageChange={(page) => setParams({ page: page + 1 })}\n onPageSizeChange={(pageSize) => setParams({ pageSize, page: 1 })}\n />\n </div>\n );\n }\n\n return (\n <PageBody className=\"px-0 pb-6\">\n <EntityHeader\n icon={<IconKey className=\"h-5 w-5\" />}\n title=\"Role permissions\"\n actions={\n <PermissionSelector\n trigger={\n <Button size=\"sm\" loading={assignPermissions.isPending}>\n <IconPlus className=\"h-4 w-4\" />\n Add permissions\n </Button>\n }\n onSelect={(selectedPermissions) => {\n if (!selectedPermissions.length) {\n return;\n }\n\n assignPermissions.mutate({\n params: { path: { id: roleId } },\n body: {\n permissionIds: selectedPermissions.map(\n (permission) => permission.id,\n ),\n },\n });\n }}\n excludeIds={permissions.map((permission) => permission.id)}\n />\n }\n search={\n <EntitySearch\n paramKey=\"search\"\n placeholder=\"Search assigned 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=\"application\"\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 {content}\n </PageBody>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useApi,
|
|
3
|
-
useSession
|
|
4
|
-
} from "./chunk-M2K6O5CN.js";
|
|
5
|
-
|
|
6
1
|
// src/components/profile/request-change-email-form.tsx
|
|
7
2
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
3
|
+
import { useApi, useSession } from "@mesob/auth-react";
|
|
8
4
|
import { Button, Input, Label, Spinner } from "@mesob/ui/components";
|
|
9
5
|
import { IconEye, IconEyeOff } from "@tabler/icons-react";
|
|
10
6
|
import { useEffect, useState } from "react";
|
|
@@ -252,4 +248,4 @@ function RequestChangeEmailForm({
|
|
|
252
248
|
export {
|
|
253
249
|
RequestChangeEmailForm
|
|
254
250
|
};
|
|
255
|
-
//# sourceMappingURL=chunk-
|
|
251
|
+
//# sourceMappingURL=chunk-AKJ3EHND.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/profile/request-change-email-form.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { useApi, useSession } from '@mesob/auth-react';\nimport { Button, Input, Label, Spinner } from '@mesob/ui/components';\nimport { IconEye, IconEyeOff } from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { toast } from 'sonner';\nimport { z } from 'zod';\n\nconst emailPasswordSchema = z.object({\n email: z.string().email('Invalid email address'),\n password: z\n .string()\n .min(8, 'Password must be at least 8 characters')\n .max(128, 'Password too long'),\n});\n\ntype EmailPasswordFormData = z.infer<typeof emailPasswordSchema>;\n\ntype AuthErrorLike = {\n code?: string;\n message?: string;\n name?: string;\n};\n\nfunction isAuthError(error: unknown): error is AuthErrorLike {\n return (\n typeof error === 'object' &&\n error !== null &&\n ('code' in error || 'message' in error || 'name' in error)\n );\n}\n\nfunction getErrorCode(error: AuthErrorLike): string | undefined {\n if (error.code) {\n return error.code;\n }\n if (error.message) {\n const upperMessage = error.message.toUpperCase().trim();\n const validCodes = [\n 'USER_NOT_FOUND',\n 'USER_EXISTS',\n 'INVALID_PASSWORD',\n 'VERIFICATION_EXPIRED',\n 'VERIFICATION_MISMATCH',\n 'VERIFICATION_NOT_FOUND',\n 'TOO_MANY_ATTEMPTS',\n 'UNAUTHORIZED',\n ];\n if (validCodes.includes(upperMessage)) {\n return upperMessage;\n }\n }\n return undefined;\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (isAuthError(error)) {\n const errorCode = getErrorCode(error);\n switch (errorCode) {\n case 'USER_EXISTS':\n return 'This email is already taken. Please use a different email.';\n case 'VERIFICATION_EXPIRED':\n return 'Verification code has expired. Please request a new one.';\n case 'VERIFICATION_MISMATCH':\n return 'Invalid verification code. Please try again.';\n case 'VERIFICATION_NOT_FOUND':\n return 'Verification not found. Please request a new code.';\n default:\n return error.message || 'An error occurred. Please try again.';\n }\n }\n if (error instanceof Error) {\n return error.message;\n }\n return 'An error occurred. Please try again.';\n}\n\ninterface RequestChangeEmailFormProps {\n onSuccess: (verificationId: string, email: string) => void;\n onCancel: () => void;\n buttonText: string;\n}\n\nexport function RequestChangeEmailForm({\n onSuccess,\n onCancel,\n buttonText,\n}: RequestChangeEmailFormProps) {\n const { user } = useSession();\n const { hooks } = useApi();\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isChecking, setIsChecking] = useState(true);\n const [showPassword, setShowPassword] = useState(false);\n\n const getPendingAccountChangeQuery = hooks.useQuery(\n 'get',\n '/account-change/pending',\n {},\n { enabled: false },\n );\n const verifyPasswordMutation = hooks.useMutation('post', '/password/verify');\n const checkUserMutation = hooks.useMutation('post', '/check-account');\n const requestEmailVerificationMutation = hooks.useMutation(\n 'post',\n '/email/verification/request',\n );\n\n const emailPasswordForm = useForm<EmailPasswordFormData>({\n resolver: zodResolver(emailPasswordSchema),\n defaultValues: {\n email: '',\n password: '',\n },\n });\n\n const {\n register,\n handleSubmit,\n getValues,\n setValue,\n formState: { errors },\n } = emailPasswordForm;\n\n useEffect(() => {\n let active = true;\n const run = async () => {\n try {\n const data = await getPendingAccountChangeQuery.refetch();\n if (!active) {\n return;\n }\n const accountChange = data.data?.accountChange;\n const verificationId = data.data?.verificationId;\n if (accountChange?.changeType !== 'email') {\n setIsChecking(false);\n return;\n }\n if (!accountChange.newEmail) {\n setIsChecking(false);\n return;\n }\n if (getValues('email')) {\n setIsChecking(false);\n return;\n }\n setValue('email', accountChange.newEmail, { shouldValidate: true });\n\n if (verificationId) {\n toast.message('Resuming verification…');\n onSuccess(verificationId, accountChange.newEmail);\n return;\n }\n\n setIsChecking(false);\n } catch {\n setIsChecking(false);\n }\n };\n run().catch(() => undefined);\n return () => {\n active = false;\n };\n }, [getPendingAccountChangeQuery.refetch, getValues, onSuccess, setValue]);\n\n const onEmailPasswordSubmit = async (data: EmailPasswordFormData) => {\n if (!user) {\n toast.error('User not found');\n return;\n }\n\n try {\n setIsSubmitting(true);\n\n // Verify password first\n await verifyPasswordMutation.mutateAsync({\n body: { password: data.password },\n });\n\n // Check if email exists\n const checkResult = await checkUserMutation.mutateAsync({\n body: { identifier: data.email },\n });\n if (checkResult.data?.exists) {\n // Check if it belongs to current user\n if (user?.email?.toLowerCase() === data.email.toLowerCase()) {\n toast.error('This is already your current email address.');\n return;\n }\n toast.error(\n 'This email is already taken. Please use a different email.',\n );\n return;\n }\n\n // Request verification\n const verification = await requestEmailVerificationMutation.mutateAsync({\n body: { email: data.email },\n });\n\n toast.success('Verification code sent to your email');\n onSuccess(verification.data?.verificationId ?? '', data.email);\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n if (isAuthError(error)) {\n const errorCode = getErrorCode(error);\n if (\n errorCode === 'INVALID_PASSWORD' ||\n errorCode === 'USER_NOT_FOUND'\n ) {\n toast.error('Incorrect password. Please try again.');\n return;\n }\n }\n toast.error(errorMessage);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const isLoading = isSubmitting || isChecking;\n\n return (\n <form\n onSubmit={handleSubmit(onEmailPasswordSubmit)}\n className=\"p-4 space-y-4 border-t\"\n >\n <div className=\"space-y-4 w-full md:w-1/2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"email\">New Email Address</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"Enter your new email\"\n {...register('email')}\n disabled={isLoading}\n />\n {errors.email && (\n <p className=\"text-sm text-destructive\">{errors.email.message}</p>\n )}\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"password\">Password</Label>\n <div className=\"relative\">\n <Input\n id=\"password\"\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"current-password\"\n placeholder=\"Enter your password\"\n {...register('password')}\n disabled={isLoading}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n disabled={isLoading}\n >\n {showPassword ? (\n <IconEyeOff className=\"h-4 w-4\" />\n ) : (\n <IconEye className=\"h-4 w-4\" />\n )}\n </button>\n </div>\n {errors.password && (\n <p className=\"text-sm text-destructive\">\n {errors.password.message}\n </p>\n )}\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={isLoading}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading && <Spinner className=\"mr-2 h-4 w-4\" />}\n {isChecking ? 'Checking…' : buttonText}\n </Button>\n </div>\n </form>\n );\n}\n"],"mappings":";AAEA,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,kBAAkB;AACnC,SAAS,QAAQ,OAAO,OAAO,eAAe;AAC9C,SAAS,SAAS,kBAAkB;AACpC,SAAS,WAAW,gBAAgB;AACpC,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,SAAS;AA6NV,SACE,KADF;AA3NR,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB;AAAA,EAC/C,UAAU,EACP,OAAO,EACP,IAAI,GAAG,wCAAwC,EAC/C,IAAI,KAAK,mBAAmB;AACjC,CAAC;AAUD,SAAS,YAAY,OAAwC;AAC3D,SACE,OAAO,UAAU,YACjB,UAAU,SACT,UAAU,SAAS,aAAa,SAAS,UAAU;AAExD;AAEA,SAAS,aAAa,OAA0C;AAC9D,MAAI,MAAM,MAAM;AACd,WAAO,MAAM;AAAA,EACf;AACA,MAAI,MAAM,SAAS;AACjB,UAAM,eAAe,MAAM,QAAQ,YAAY,EAAE,KAAK;AACtD,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,WAAW,SAAS,YAAY,GAAG;AACrC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,YAAY,KAAK,GAAG;AACtB,UAAM,YAAY,aAAa,KAAK;AACpC,YAAQ,WAAW;AAAA,MACjB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,MAAM,WAAW;AAAA,IAC5B;AAAA,EACF;AACA,MAAI,iBAAiB,OAAO;AAC1B,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAQO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,QAAM,EAAE,KAAK,IAAI,WAAW;AAC5B,QAAM,EAAE,MAAM,IAAI,OAAO;AACzB,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AAEtD,QAAM,+BAA+B,MAAM;AAAA,IACzC;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,EAAE,SAAS,MAAM;AAAA,EACnB;AACA,QAAM,yBAAyB,MAAM,YAAY,QAAQ,kBAAkB;AAC3E,QAAM,oBAAoB,MAAM,YAAY,QAAQ,gBAAgB;AACpE,QAAM,mCAAmC,MAAM;AAAA,IAC7C;AAAA,IACA;AAAA,EACF;AAEA,QAAM,oBAAoB,QAA+B;AAAA,IACvD,UAAU,YAAY,mBAAmB;AAAA,IACzC,eAAe;AAAA,MACb,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,EAAE,OAAO;AAAA,EACtB,IAAI;AAEJ,YAAU,MAAM;AACd,QAAI,SAAS;AACb,UAAM,MAAM,YAAY;AACtB,UAAI;AACF,cAAM,OAAO,MAAM,6BAA6B,QAAQ;AACxD,YAAI,CAAC,QAAQ;AACX;AAAA,QACF;AACA,cAAM,gBAAgB,KAAK,MAAM;AACjC,cAAM,iBAAiB,KAAK,MAAM;AAClC,YAAI,eAAe,eAAe,SAAS;AACzC,wBAAc,KAAK;AACnB;AAAA,QACF;AACA,YAAI,CAAC,cAAc,UAAU;AAC3B,wBAAc,KAAK;AACnB;AAAA,QACF;AACA,YAAI,UAAU,OAAO,GAAG;AACtB,wBAAc,KAAK;AACnB;AAAA,QACF;AACA,iBAAS,SAAS,cAAc,UAAU,EAAE,gBAAgB,KAAK,CAAC;AAElE,YAAI,gBAAgB;AAClB,gBAAM,QAAQ,6BAAwB;AACtC,oBAAU,gBAAgB,cAAc,QAAQ;AAChD;AAAA,QACF;AAEA,sBAAc,KAAK;AAAA,MACrB,QAAQ;AACN,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF;AACA,QAAI,EAAE,MAAM,MAAM,MAAS;AAC3B,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,6BAA6B,SAAS,WAAW,WAAW,QAAQ,CAAC;AAEzE,QAAM,wBAAwB,OAAO,SAAgC;AACnE,QAAI,CAAC,MAAM;AACT,YAAM,MAAM,gBAAgB;AAC5B;AAAA,IACF;AAEA,QAAI;AACF,sBAAgB,IAAI;AAGpB,YAAM,uBAAuB,YAAY;AAAA,QACvC,MAAM,EAAE,UAAU,KAAK,SAAS;AAAA,MAClC,CAAC;AAGD,YAAM,cAAc,MAAM,kBAAkB,YAAY;AAAA,QACtD,MAAM,EAAE,YAAY,KAAK,MAAM;AAAA,MACjC,CAAC;AACD,UAAI,YAAY,MAAM,QAAQ;AAE5B,YAAI,MAAM,OAAO,YAAY,MAAM,KAAK,MAAM,YAAY,GAAG;AAC3D,gBAAM,MAAM,6CAA6C;AACzD;AAAA,QACF;AACA,cAAM;AAAA,UACJ;AAAA,QACF;AACA;AAAA,MACF;AAGA,YAAM,eAAe,MAAM,iCAAiC,YAAY;AAAA,QACtE,MAAM,EAAE,OAAO,KAAK,MAAM;AAAA,MAC5B,CAAC;AAED,YAAM,QAAQ,sCAAsC;AACpD,gBAAU,aAAa,MAAM,kBAAkB,IAAI,KAAK,KAAK;AAAA,IAC/D,SAAS,OAAO;AACd,YAAM,eAAe,gBAAgB,KAAK;AAC1C,UAAI,YAAY,KAAK,GAAG;AACtB,cAAM,YAAY,aAAa,KAAK;AACpC,YACE,cAAc,sBACd,cAAc,kBACd;AACA,gBAAM,MAAM,uCAAuC;AACnD;AAAA,QACF;AAAA,MACF;AACA,YAAM,MAAM,YAAY;AAAA,IAC1B,UAAE;AACA,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB;AAElC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,aAAa,qBAAqB;AAAA,MAC5C,WAAU;AAAA,MAEV;AAAA,6BAAC,SAAI,WAAU,6BACb;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAM,SAAQ,SAAQ,+BAAiB;AAAA,YACxC;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,aAAY;AAAA,gBACX,GAAG,SAAS,OAAO;AAAA,gBACpB,UAAU;AAAA;AAAA,YACZ;AAAA,YACC,OAAO,SACN,oBAAC,OAAE,WAAU,4BAA4B,iBAAO,MAAM,SAAQ;AAAA,aAElE;AAAA,UACA,qBAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAM,SAAQ,YAAW,sBAAQ;AAAA,YAClC,qBAAC,SAAI,WAAU,YACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,IAAG;AAAA,kBACH,MAAM,eAAe,SAAS;AAAA,kBAC9B,cAAa;AAAA,kBACb,aAAY;AAAA,kBACX,GAAG,SAAS,UAAU;AAAA,kBACvB,UAAU;AAAA;AAAA,cACZ;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,MAAM,gBAAgB,CAAC,YAAY;AAAA,kBAC5C,WAAU;AAAA,kBACV,UAAU;AAAA,kBAET,yBACC,oBAAC,cAAW,WAAU,WAAU,IAEhC,oBAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,cAEjC;AAAA,eACF;AAAA,YACC,OAAO,YACN,oBAAC,OAAE,WAAU,4BACV,iBAAO,SAAS,SACnB;AAAA,aAEJ;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS;AAAA,cACT,UAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UACA,qBAAC,UAAO,MAAK,UAAS,UAAU,WAC7B;AAAA,yBAAa,oBAAC,WAAQ,WAAU,gBAAe;AAAA,YAC/C,aAAa,mBAAc;AAAA,aAC9B;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useTranslator
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WG5H5PTL.js";
|
|
4
4
|
|
|
5
5
|
// src/components/auth/countdown.tsx
|
|
6
6
|
import { Spinner } from "@mesob/ui/components";
|
|
@@ -61,4 +61,4 @@ var Countdown = ({
|
|
|
61
61
|
export {
|
|
62
62
|
Countdown
|
|
63
63
|
};
|
|
64
|
-
//# sourceMappingURL=chunk-
|
|
64
|
+
//# sourceMappingURL=chunk-CY3MODZU.js.map
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppLink,
|
|
3
3
|
useNavigate
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
useApi
|
|
7
|
-
} from "./chunk-M2K6O5CN.js";
|
|
4
|
+
} from "./chunk-6BLYK6D6.js";
|
|
8
5
|
|
|
9
6
|
// src/pages/iam/shared/page-helpers.tsx
|
|
7
|
+
import { useApi } from "@mesob/auth-react";
|
|
10
8
|
import { jsx } from "react/jsx-runtime";
|
|
11
9
|
var authApi$ = {
|
|
12
10
|
useQuery(...args) {
|
|
@@ -33,4 +31,4 @@ export {
|
|
|
33
31
|
Link,
|
|
34
32
|
useRouter
|
|
35
33
|
};
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
34
|
+
//# sourceMappingURL=chunk-F5SAYP67.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pages/iam/shared/page-helpers.tsx"],"sourcesContent":["'use client';\n\nimport
|
|
1
|
+
{"version":3,"sources":["../src/pages/iam/shared/page-helpers.tsx"],"sourcesContent":["'use client';\n\nimport { useApi } from '@mesob/auth-react';\nimport type { ComponentProps } from 'react';\nimport { AppLink, useNavigate } from './navigation';\n\nexport { defaultEntityQueryOptions } from '../../../lib/query-options';\n\ntype HookArgs = [string, string, ...unknown[]];\n\nexport const authApi$ = {\n useQuery(...args: HookArgs) {\n const { hooks } = useApi();\n\n return hooks.useQuery(...args);\n },\n useMutation(...args: HookArgs) {\n const { hooks } = useApi();\n\n return hooks.useMutation(...args);\n },\n};\n\nexport function Link(props: ComponentProps<typeof AppLink>) {\n return <AppLink {...props} />;\n}\n\nexport function useRouter() {\n const navigate = useNavigate();\n\n return {\n push: navigate,\n };\n}\n"],"mappings":";;;;;;AAEA,SAAS,cAAc;AAsBd;AAdF,IAAM,WAAW;AAAA,EACtB,YAAY,MAAgB;AAC1B,UAAM,EAAE,MAAM,IAAI,OAAO;AAEzB,WAAO,MAAM,SAAS,GAAG,IAAI;AAAA,EAC/B;AAAA,EACA,eAAe,MAAgB;AAC7B,UAAM,EAAE,MAAM,IAAI,OAAO;AAEzB,WAAO,MAAM,YAAY,GAAG,IAAI;AAAA,EAClC;AACF;AAEO,SAAS,KAAK,OAAuC;AAC1D,SAAO,oBAAC,WAAS,GAAG,OAAO;AAC7B;AAEO,SAAS,YAAY;AAC1B,QAAM,WAAW,YAAY;AAE7B,SAAO;AAAA,IACL,MAAM;AAAA,EACR;AACF;","names":[]}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useApi
|
|
3
|
-
} from "./chunk-M2K6O5CN.js";
|
|
4
|
-
|
|
5
1
|
// src/components/iam/permission-selector.tsx
|
|
2
|
+
import { useApi } from "@mesob/auth-react";
|
|
6
3
|
import {
|
|
7
4
|
Badge,
|
|
8
5
|
EntitySelector,
|
|
@@ -90,4 +87,4 @@ function PermissionSelector({
|
|
|
90
87
|
export {
|
|
91
88
|
PermissionSelector
|
|
92
89
|
};
|
|
93
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-F6WCSBHX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/iam/permission-selector.tsx"],"sourcesContent":["'use client';\n\nimport { useApi } from '@mesob/auth-react';\nimport {\n Badge,\n EntitySelector,\n type EntitySelectorColumn,\n type EntitySelectorConfig,\n useEntitySectionState,\n} from '@mesob/ui/components';\nimport { IconKey } from '@tabler/icons-react';\nimport type { ReactNode } from 'react';\nimport type { paths } from '../../data/openapi';\n\ntype Permission = {\n id: string;\n description?: unknown;\n activity: string;\n application: string;\n feature: string;\n};\n\nconst permissionColumns: EntitySelectorColumn<Permission>[] = [\n {\n key: 'permission',\n header: 'Permission',\n cell: (permission) => (\n <div className=\"space-y-1\">\n <p className=\"font-medium\">{permission.activity}</p>\n <p className=\"font-mono text-xs text-muted-foreground\">\n {permission.id}\n </p>\n </div>\n ),\n },\n {\n key: 'scope',\n header: 'Scope',\n cell: (permission) => (\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"secondary\">{permission.application}</Badge>\n <Badge variant=\"outline\">{permission.feature}</Badge>\n </div>\n ),\n },\n];\n\ntype PermissionSelectorProps = {\n trigger: ReactNode;\n multiple?: boolean;\n onSelect: (permissions: Permission[]) => void;\n excludeIds?: string[];\n};\n\nexport function PermissionSelector({\n trigger,\n multiple = true,\n onSelect,\n excludeIds = [],\n}: PermissionSelectorProps) {\n const { hooks } = useApi();\n const state = useEntitySectionState({\n defaultSort: 'id',\n defaultOrder: 'asc',\n defaultPageSize: 10,\n searchParamName: 'search',\n });\n const permissionsQuery = state.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 );\n\n const items = ((data?.permissions ?? []) as Permission[]).filter(\n (permission) => !excludeIds.includes(permission.id),\n );\n\n const config: EntitySelectorConfig<Permission> = {\n title: 'Select permission(s)',\n multiple,\n entityName: 'permission',\n entityIcon: IconKey,\n columns: permissionColumns,\n getItemLabel: (permission) => permission.id,\n searchPlaceholder: 'Search permissions...',\n filterOptions: [\n { label: 'All', value: '' },\n { label: 'Application', value: 'application' },\n { label: 'Feature', value: 'feature' },\n { label: 'Activity', value: 'activity' },\n ],\n sortOptions: [\n { label: 'ID', value: 'id' },\n { label: 'Application', value: 'application' },\n { label: 'Feature', value: 'feature' },\n { label: 'Activity', value: 'activity' },\n ],\n showViewToggle: false,\n wrapHeaderInCard: false,\n };\n\n return (\n <EntitySelector<Permission>\n trigger={trigger}\n config={config}\n onSelect={onSelect}\n items={items}\n total={items.length}\n isLoading={isPending || isFetching}\n state={state}\n />\n );\n}\n"],"mappings":";AAEA,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,OACK;AACP,SAAS,eAAe;AAiBlB,SACE,KADF;AALN,IAAM,oBAAwD;AAAA,EAC5D;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,CAAC,eACL,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,OAAE,WAAU,eAAe,qBAAW,UAAS;AAAA,MAChD,oBAAC,OAAE,WAAU,2CACV,qBAAW,IACd;AAAA,OACF;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,CAAC,eACL,qBAAC,SAAI,WAAU,wBACb;AAAA,0BAAC,SAAM,SAAQ,aAAa,qBAAW,aAAY;AAAA,MACnD,oBAAC,SAAM,SAAQ,WAAW,qBAAW,SAAQ;AAAA,OAC/C;AAAA,EAEJ;AACF;AASO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,aAAa,CAAC;AAChB,GAA4B;AAC1B,QAAM,EAAE,MAAM,IAAI,OAAO;AACzB,QAAM,QAAQ,sBAAsB;AAAA,IAClC,aAAa;AAAA,IACb,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,EACnB,CAAC;AACD,QAAM,mBAAmB,MAAM;AAM/B,QAAM,EAAE,MAAM,WAAW,WAAW,IAAI,MAAM;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,SAAU,MAAM,eAAe,CAAC,GAAoB;AAAA,IACxD,CAAC,eAAe,CAAC,WAAW,SAAS,WAAW,EAAE;AAAA,EACpD;AAEA,QAAM,SAA2C;AAAA,IAC/C,OAAO;AAAA,IACP;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc,CAAC,eAAe,WAAW;AAAA,IACzC,mBAAmB;AAAA,IACnB,eAAe;AAAA,MACb,EAAE,OAAO,OAAO,OAAO,GAAG;AAAA,MAC1B,EAAE,OAAO,eAAe,OAAO,cAAc;AAAA,MAC7C,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,MACrC,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,IACzC;AAAA,IACA,aAAa;AAAA,MACX,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,MAC3B,EAAE,OAAO,eAAe,OAAO,cAAc;AAAA,MAC7C,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,MACrC,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,IACzC;AAAA,IACA,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,MAAM;AAAA,MACb,WAAW,aAAa;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;","names":[]}
|