@mesob/auth-react 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/components/auth/auth-card.js +1 -1
  2. package/dist/components/auth/auth-card.js.map +1 -1
  3. package/dist/components/auth/{auth-page-layout.d.ts → auth-layout.d.ts} +3 -3
  4. package/dist/components/auth/{auth-page-layout.js → auth-layout.js} +4 -4
  5. package/dist/components/auth/auth-layout.js.map +1 -0
  6. package/dist/components/auth/countdown.js +15 -6
  7. package/dist/components/auth/countdown.js.map +1 -1
  8. package/dist/components/auth/forgot-password.d.ts +1 -9
  9. package/dist/components/auth/forgot-password.js +267 -43
  10. package/dist/components/auth/forgot-password.js.map +1 -1
  11. package/dist/components/auth/reset-password-form.d.ts +2 -10
  12. package/dist/components/auth/reset-password-form.js +366 -118
  13. package/dist/components/auth/reset-password-form.js.map +1 -1
  14. package/dist/components/auth/sign-in.d.ts +2 -10
  15. package/dist/components/auth/sign-in.js +358 -130
  16. package/dist/components/auth/sign-in.js.map +1 -1
  17. package/dist/components/auth/sign-up.d.ts +2 -10
  18. package/dist/components/auth/sign-up.js +379 -131
  19. package/dist/components/auth/sign-up.js.map +1 -1
  20. package/dist/components/auth/verification-form.d.ts +2 -16
  21. package/dist/components/auth/verification-form.js +15 -6
  22. package/dist/components/auth/verification-form.js.map +1 -1
  23. package/dist/components/auth/verify-email.d.ts +10 -0
  24. package/dist/components/auth/{pages/verify-email-page.js → verify-email.js} +54 -34
  25. package/dist/components/auth/verify-email.js.map +1 -0
  26. package/dist/components/auth/verify-phone.d.ts +11 -0
  27. package/dist/components/auth/{pages/verify-phone-page.js → verify-phone.js} +53 -46
  28. package/dist/components/auth/verify-phone.js.map +1 -0
  29. package/dist/components/iam/permissions.d.ts +5 -0
  30. package/dist/components/iam/{permissions/permissions-page.js → permissions.js} +29 -13
  31. package/dist/components/iam/permissions.js.map +1 -0
  32. package/dist/components/iam/roles.d.ts +5 -0
  33. package/dist/components/iam/{roles/roles-page.js → roles.js} +29 -13
  34. package/dist/components/iam/roles.js.map +1 -0
  35. package/dist/components/iam/sessions.d.ts +5 -0
  36. package/dist/components/iam/{sessions/sessions-page.js → sessions.js} +13 -11
  37. package/dist/components/iam/sessions.js.map +1 -0
  38. package/dist/components/iam/tenants.d.ts +5 -0
  39. package/dist/components/iam/{tenants/tenants-page.js → tenants.js} +13 -11
  40. package/dist/components/iam/tenants.js.map +1 -0
  41. package/dist/components/iam/users.d.ts +5 -0
  42. package/dist/components/iam/{users/users-page.js → users.js} +13 -11
  43. package/dist/components/iam/users.js.map +1 -0
  44. package/dist/components/profile/account.d.ts +5 -0
  45. package/dist/components/profile/account.js +66 -0
  46. package/dist/components/profile/account.js.map +1 -0
  47. package/dist/components/profile/change-email-form.d.ts +5 -0
  48. package/dist/components/profile/change-email-form.js +721 -0
  49. package/dist/components/profile/change-email-form.js.map +1 -0
  50. package/dist/components/profile/change-password-form.d.ts +5 -0
  51. package/dist/components/profile/change-password-form.js +256 -0
  52. package/dist/components/profile/change-password-form.js.map +1 -0
  53. package/dist/components/profile/change-phone-form.d.ts +5 -0
  54. package/dist/components/profile/change-phone-form.js +758 -0
  55. package/dist/components/profile/change-phone-form.js.map +1 -0
  56. package/dist/components/profile/change-profile.d.ts +9 -0
  57. package/dist/components/profile/change-profile.js +37 -0
  58. package/dist/components/profile/change-profile.js.map +1 -0
  59. package/dist/components/profile/otp-verification-modal.d.ts +15 -0
  60. package/dist/components/profile/otp-verification-modal.js +261 -0
  61. package/dist/components/profile/otp-verification-modal.js.map +1 -0
  62. package/dist/components/profile/request-change-email-form.d.ts +10 -0
  63. package/dist/components/profile/request-change-email-form.js +293 -0
  64. package/dist/components/profile/request-change-email-form.js.map +1 -0
  65. package/dist/components/profile/request-change-phone-form.d.ts +10 -0
  66. package/dist/components/profile/request-change-phone-form.js +331 -0
  67. package/dist/components/profile/request-change-phone-form.js.map +1 -0
  68. package/dist/components/profile/security.d.ts +5 -0
  69. package/dist/components/profile/security.js +1439 -0
  70. package/dist/components/profile/security.js.map +1 -0
  71. package/dist/components/profile/verify-change-email-form.d.ts +11 -0
  72. package/dist/components/profile/verify-change-email-form.js +402 -0
  73. package/dist/components/profile/verify-change-email-form.js.map +1 -0
  74. package/dist/components/profile/verify-change-phone-form.d.ts +11 -0
  75. package/dist/components/profile/verify-change-phone-form.js +406 -0
  76. package/dist/components/profile/verify-change-phone-form.js.map +1 -0
  77. package/dist/components/shared/{data-table/data-table.js → data-table.js} +2 -2
  78. package/dist/components/shared/data-table.js.map +1 -0
  79. package/dist/index.d.ts +42 -93
  80. package/dist/index.js +2298 -1300
  81. package/dist/index.js.map +1 -1
  82. package/dist/types-D3s9oE-5.d.ts +75 -0
  83. package/dist/verification-form-ipSRTtQB.d.ts +22 -0
  84. package/package.json +3 -2
  85. package/dist/components/auth/auth-page-layout.js.map +0 -1
  86. package/dist/components/auth/pages/forgot-password-page.d.ts +0 -6
  87. package/dist/components/auth/pages/forgot-password-page.js +0 -362
  88. package/dist/components/auth/pages/forgot-password-page.js.map +0 -1
  89. package/dist/components/auth/pages/reset-password-page.d.ts +0 -9
  90. package/dist/components/auth/pages/reset-password-page.js +0 -514
  91. package/dist/components/auth/pages/reset-password-page.js.map +0 -1
  92. package/dist/components/auth/pages/sign-in-page.d.ts +0 -8
  93. package/dist/components/auth/pages/sign-in-page.js +0 -565
  94. package/dist/components/auth/pages/sign-in-page.js.map +0 -1
  95. package/dist/components/auth/pages/sign-up-page.d.ts +0 -9
  96. package/dist/components/auth/pages/sign-up-page.js +0 -518
  97. package/dist/components/auth/pages/sign-up-page.js.map +0 -1
  98. package/dist/components/auth/pages/verify-email-page.d.ts +0 -10
  99. package/dist/components/auth/pages/verify-email-page.js.map +0 -1
  100. package/dist/components/auth/pages/verify-phone-page.d.ts +0 -11
  101. package/dist/components/auth/pages/verify-phone-page.js.map +0 -1
  102. package/dist/components/iam/permissions/permissions-page.d.ts +0 -5
  103. package/dist/components/iam/permissions/permissions-page.js.map +0 -1
  104. package/dist/components/iam/roles/roles-page.d.ts +0 -5
  105. package/dist/components/iam/roles/roles-page.js.map +0 -1
  106. package/dist/components/iam/sessions/sessions-page.d.ts +0 -5
  107. package/dist/components/iam/sessions/sessions-page.js.map +0 -1
  108. package/dist/components/iam/tenants/tenants-page.d.ts +0 -5
  109. package/dist/components/iam/tenants/tenants-page.js.map +0 -1
  110. package/dist/components/iam/users/users-page.d.ts +0 -5
  111. package/dist/components/iam/users/users-page.js.map +0 -1
  112. package/dist/components/profile/profile-page.d.ts +0 -8
  113. package/dist/components/profile/profile-page.js +0 -163
  114. package/dist/components/profile/profile-page.js.map +0 -1
  115. package/dist/components/shared/data-table/data-table.js.map +0 -1
  116. package/dist/handle-error-BqDMxnQZ.d.ts +0 -8
  117. /package/dist/components/shared/{data-table/data-table.d.ts → data-table.d.ts} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/auth/pages/verify-email-page.tsx","../../../../src/lib/translations.ts","../../../../src/provider.tsx","../../../../src/constants/auth.error.codes.ts","../../../../src/utils/handle-error.ts","../../../../src/components/auth/auth-page-layout.tsx","../../../../src/components/auth/verification-form.tsx","../../../../src/hooks/use-translator.ts","../../../../src/components/auth/countdown.tsx"],"sourcesContent":["'use client';\n\nimport {\n Alert,\n AlertDescription,\n AlertTitle,\n} from '@mesob/ui/components/alert';\nimport { toast } from '@mesob/ui/components/sonner';\nimport { IconAlertCircle } from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { createTranslator } from '../../../lib/translations';\nimport { useApi, useConfig } from '../../../provider';\nimport type { AuthErrorContent } from '../../../utils/handle-error';\nimport { handleError } from '../../../utils/handle-error';\n\nimport { AuthPageLayout } from '../auth-page-layout';\nimport { VerificationForm } from '../verification-form';\n\ntype VerifyEmailPageProps = {\n verificationId: string;\n email: string;\n redirectUrl?: string; // Can override defaultRedirectUrl from config\n};\n\nexport const VerifyEmailPage = ({\n verificationId,\n email,\n redirectUrl,\n}: VerifyEmailPageProps) => {\n const { hooks, setAuth } = useApi();\n const { config } = useConfig();\n const t = createTranslator(config.messages || {}, 'Auth.verification');\n const common = createTranslator(config.messages || {}, 'Common');\n const footer = createTranslator(\n config.messages || {},\n 'Auth.forgotPassword.footer',\n );\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthErrorContent | null>(null);\n\n const verifyEmailMutation = hooks.useMutation(\n 'post',\n '/email/verification/confirm',\n );\n const requestEmailMutation = hooks.useMutation(\n 'post',\n '/email/verification/request',\n );\n\n const signInLink = config.navigation?.links?.signIn || '/auth/sign-in';\n const Link = config.navigation?.linkComponent;\n const onNavigate =\n config.navigation?.onNavigate ||\n ((path: string) => {\n if (typeof window !== 'undefined') {\n window.location.href = path;\n }\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.missingVerificationId'),\n });\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const res = await verifyEmailMutation.mutateAsync({\n body: {\n verificationId,\n code: values.code,\n },\n });\n if ('user' in res && 'session' in res) {\n setAuth(res);\n }\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 res = await requestEmailMutation.mutateAsync({\n body: {\n email,\n },\n });\n if (res.verificationId) {\n onNavigate(\n `/auth/verify-email?verificationId=${res.verificationId}&email=${encodeURIComponent(email)}`,\n );\n } else {\n setError({\n title: t('errors.fallback'),\n description: t('errors.resendFailed'),\n });\n }\n } catch (err) {\n handleError(err, setError, t);\n }\n };\n\n if (!verificationId) {\n return (\n <AuthPageLayout\n title={common('invalidLinkTitle')}\n description={common('invalidLinkDescription')}\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 <div />\n </AuthPageLayout>\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 <AuthPageLayout\n title={t('email.title')}\n description={t('email.description')}\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 verifyEmailMutation.isPending ||\n requestEmailMutation.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 </AuthPageLayout>\n );\n};\n","type Messages = Record<string, unknown>;\n\nexport function createTranslator(messages: Messages, namespace?: string) {\n return (key: string, params?: Record<string, string | number>): string => {\n const fullKey = namespace ? `${namespace}.${key}` : key;\n const keys = fullKey.split('.');\n\n let value: unknown = messages;\n for (const k of keys) {\n if (value && typeof value === 'object' && value !== null) {\n value = (value as Record<string, unknown>)[k];\n } else {\n return fullKey;\n }\n }\n\n if (typeof value !== 'string') {\n return fullKey;\n }\n\n // Simple parameter replacement\n if (params) {\n return value.replace(/\\{(\\w+)\\}/g, (_, param) =>\n String(params[param] ?? `{${param}}`),\n );\n }\n\n return value;\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","export const AUTH_ERROR_MAPPING: Record<\n string,\n { title: string; description: string }\n> = {\n USER_NOT_FOUND: {\n title: 'Account Not Found',\n description:\n 'We could not find an account with that identifier. Please check your spelling or sign up.',\n },\n INVALID_PASSWORD: {\n title: 'Invalid Password',\n description: 'The password you entered is incorrect. Please try again.',\n },\n USER_EXISTS: {\n title: 'Account Already Exists',\n description:\n 'An account with this identifier already exists. Please sign in instead.',\n },\n VERIFICATION_EXPIRED: {\n title: 'Verification Expired',\n description:\n 'The verification code or link has expired. Please request a new one.',\n },\n VERIFICATION_MISMATCH: {\n title: 'Invalid Code',\n description:\n 'The verification code you entered is invalid. Please double-check and try again.',\n },\n VERIFICATION_NOT_FOUND: {\n title: 'Verification Not Found',\n description:\n 'We could not find a pending verification request. Please restart the process.',\n },\n TOO_MANY_ATTEMPTS: {\n title: 'Too Many Attempts',\n description:\n 'You have made too many requests recently. Please wait a moment before trying again.',\n },\n REQUIRES_VERIFICATION: {\n title: 'Verification Required',\n description:\n 'You need to verify your account before you can continue. Please check your email or phone.',\n },\n UNAUTHORIZED: {\n title: 'Unauthorized',\n description:\n 'You are not authorized to perform this action. Please sign in again.',\n },\n ACCESS_DENIED: {\n title: 'Access Denied',\n description:\n 'You do not have permission to access this resource. Please contact support if you believe this is an error.',\n },\n HAS_NO_PASSWORD: {\n title: 'No Password Set',\n description:\n 'Your account does not have a password set (e.g. social login). Please sign in with your provider or reset your password.',\n },\n};\n\nexport const validCodes = Object.keys(AUTH_ERROR_MAPPING);\n","import { AUTH_ERROR_MAPPING, validCodes } from '../constants/auth.error.codes';\nimport type { AuthError } from '../types';\n\nexport type AuthErrorContent = {\n title: string;\n description: string;\n};\n\n// Translator function type\ntype TranslatorFunction = (\n key: string,\n params?: Record<string, string | number>,\n) => string;\n\n// Type guard to check if error is an AuthError\nfunction isAuthError(err: unknown): err is AuthError {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'message' in err &&\n typeof (err as { message: unknown }).message === 'string'\n );\n}\n\nfunction extractErrorCode(err: AuthError): string {\n if (err.code && validCodes.includes(err.code)) {\n return err.code;\n }\n if (err.message) {\n const messageUpper = err.message.toUpperCase().trim();\n if (validCodes.includes(messageUpper)) {\n return messageUpper;\n }\n }\n return '';\n}\n\nfunction handleAuthError(\n err: AuthError,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const errorCode = extractErrorCode(err);\n\n if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {\n const mapping = AUTH_ERROR_MAPPING[errorCode];\n setError({\n title: mapping.title,\n description: mapping.description,\n });\n return;\n }\n\n setError({\n title: t('errors.fallback'),\n description: err.message || t('errors.fallback'),\n });\n}\n\nfunction handleGenericError(\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const message = err instanceof Error ? err.message : t('errors.fallback');\n setError({\n title: 'Error',\n description: message,\n });\n}\n\nexport const handleError = (\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) => {\n if (isAuthError(err)) {\n handleAuthError(err, setError, t);\n } else {\n handleGenericError(err, setError, t);\n }\n};\n","'use client';\n\nimport type { ReactNode } from 'react';\n\ntype AuthPageLayoutProps = {\n title: string;\n description?: string;\n children: ReactNode;\n footer?: ReactNode;\n logoImage?: string;\n};\n\nexport const AuthPageLayout = ({\n title,\n description,\n children,\n footer,\n logoImage,\n}: AuthPageLayoutProps) => {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex size-8 mb-6 w-full items-center justify-center rounded-md\">\n {/** biome-ignore lint/performance/noImgElement: logo image */}\n <img src={logoImage || ''} alt=\"Mesob\" width={42} height={42} />\n </div>\n <div className=\"text-center\">\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n {description && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n\n {children}\n\n <div className=\"mt-2 w-full\">\n {footer && (\n <div className=\"w-full text-center text-sm text-muted-foreground\">\n {footer}\n </div>\n )}\n </div>\n </div>\n );\n};\n","'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { Button } from '@mesob/ui/components/button';\nimport { Field, FieldError, FieldGroup } from '@mesob/ui/components/field';\nimport {\n InputOTP,\n InputOTPGroup,\n InputOTPSlot,\n} from '@mesob/ui/components/input-otp';\nimport { Spinner } from '@mesob/ui/components/spinner';\nimport { Controller, useForm } from 'react-hook-form';\nimport { z } from 'zod';\nimport { useTranslator } from '../../hooks/use-translator';\nimport type { AuthErrorContent } from '../../utils/handle-error';\nimport { Countdown } from './countdown';\n\ntype VerificationFormValues = {\n code: string;\n};\n\ntype VerificationFormProps = {\n verificationId: string;\n onSubmit: (values: VerificationFormValues) => Promise<void> | void;\n onResend: () => Promise<void> | void;\n isLoading?: boolean;\n error?: AuthErrorContent | string | null;\n};\n\nconst verificationSchema = (t: (key: string) => string) =>\n z.object({\n code: z.string().length(6, t('form.codeLength')),\n });\n\nexport const VerificationForm = ({\n onSubmit,\n onResend,\n isLoading = false,\n}: VerificationFormProps) => {\n const t = useTranslator('Auth.verification');\n const form = useForm<VerificationFormValues>({\n resolver: zodResolver(verificationSchema(t)),\n defaultValues: {\n code: '',\n },\n });\n\n const handleSubmit = form.handleSubmit(async (values) => {\n await onSubmit(values);\n });\n\n return (\n <form id=\"verification-form\" onSubmit={handleSubmit}>\n <FieldGroup>\n <Controller\n name=\"code\"\n control={form.control}\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid}>\n <div className=\"flex justify-center\">\n <InputOTP\n maxLength={6}\n id=\"otp\"\n required\n value={field.value ?? ''}\n onChange={field.onChange}\n onBlur={field.onBlur}\n containerClassName=\"gap-4 justify-center mb-2 flex items-center\"\n aria-invalid={fieldState.invalid}\n >\n <InputOTPGroup className=\"gap-3 *:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:text-xl\">\n <InputOTPSlot className=\"h-12\" index={0} />\n <InputOTPSlot className=\"h-12\" index={1} />\n <InputOTPSlot className=\"h-12\" index={2} />\n <InputOTPSlot className=\"h-12\" index={3} />\n <InputOTPSlot className=\"h-12\" index={4} />\n <InputOTPSlot className=\"h-12\" index={5} />\n </InputOTPGroup>\n </InputOTP>\n </div>\n {fieldState.invalid && <FieldError errors={[fieldState.error]} />}\n </Field>\n )}\n />\n </FieldGroup>\n <div className=\"flex justify-between items-center mt-4\">\n <Countdown onResend={onResend} resending={isLoading} />\n <Button\n type=\"submit\"\n form=\"verification-form\"\n disabled={isLoading || form.watch('code').length !== 6}\n >\n {isLoading && <Spinner />}\n {t('form.confirm')}\n </Button>\n </div>\n </form>\n );\n};\n","import { createTranslator } from '../lib/translations';\nimport { useConfig } from '../provider';\n\nexport function useTranslator(namespace?: string) {\n const { config } = useConfig();\n return createTranslator(config.messages || {}, namespace);\n}\n","'use client';\n\nimport { Button } from '@mesob/ui/components/button';\nimport { Spinner } from '@mesob/ui/components/spinner';\nimport { useEffect, useState } from 'react';\nimport { useTranslator } from '../../hooks/use-translator';\n\ntype CountdownProps = {\n initialSeconds?: number;\n onResend: () => Promise<void> | void;\n resending?: boolean;\n};\n\nexport const Countdown = ({\n initialSeconds = 60,\n onResend,\n resending = false,\n}: CountdownProps) => {\n const t = useTranslator('Common');\n const [seconds, setSeconds] = useState(initialSeconds);\n const [isResending, setIsResending] = useState(false);\n\n useEffect(() => {\n if (seconds <= 0) {\n return;\n }\n\n const timer = setInterval(() => {\n setSeconds((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [seconds]);\n\n const handleResend = async () => {\n setIsResending(true);\n try {\n await onResend();\n setSeconds(initialSeconds);\n } catch (_error) {\n // Error handling is done by parent\n } finally {\n setIsResending(false);\n }\n };\n\n if (seconds > 0) {\n return (\n <Button variant=\"ghost\" disabled>\n {t('resendIn', { seconds })}\n </Button>\n );\n }\n\n return (\n <Button\n variant=\"ghost\"\n onClick={handleResend}\n disabled={isResending || resending}\n >\n {isResending || (resending && <Spinner />)}\n {t('resend')}\n </Button>\n );\n};\n"],"mappings":"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","names":["useEffect","useState","jsx","Button","Spinner","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs","Button","Spinner","jsx","jsxs","useState","useEffect"]}
@@ -1,11 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- type VerifyPhonePageProps = {
4
- verificationId: string;
5
- context: 'sign-in' | 'sign-up';
6
- phone?: string;
7
- redirectUrl?: string;
8
- };
9
- declare const VerifyPhonePage: ({ verificationId, context, phone, redirectUrl, }: VerifyPhonePageProps) => react_jsx_runtime.JSX.Element;
10
-
11
- export { VerifyPhonePage };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/auth/pages/verify-phone-page.tsx","../../../../src/lib/translations.ts","../../../../src/provider.tsx","../../../../src/constants/auth.error.codes.ts","../../../../src/utils/handle-error.ts","../../../../src/components/auth/auth-page-layout.tsx","../../../../src/components/auth/verification-form.tsx","../../../../src/hooks/use-translator.ts","../../../../src/components/auth/countdown.tsx"],"sourcesContent":["'use client';\n\nimport {\n Alert,\n AlertDescription,\n AlertTitle,\n} from '@mesob/ui/components/alert';\nimport { toast } from '@mesob/ui/components/sonner';\nimport { IconAlertCircle } from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { createTranslator } from '../../../lib/translations';\nimport { useApi, useConfig } from '../../../provider';\nimport type { AuthResponse } from '../../../types';\nimport type { AuthErrorContent } from '../../../utils/handle-error';\nimport { handleError } from '../../../utils/handle-error';\n\nimport { AuthPageLayout } from '../auth-page-layout';\nimport { VerificationForm } from '../verification-form';\n\ntype VerifyPhonePageProps = {\n verificationId: string;\n context: 'sign-in' | 'sign-up';\n phone?: string;\n redirectUrl?: string; // Can override defaultRedirectUrl from config\n};\n\nexport const VerifyPhonePage = ({\n verificationId,\n context,\n phone = '',\n redirectUrl,\n}: VerifyPhonePageProps) => {\n const { hooks, refresh, setAuth } = useApi();\n const { config } = useConfig();\n const t = createTranslator(config.messages || {}, 'Auth.verification');\n const common = createTranslator(config.messages || {}, 'Common');\n const footer = createTranslator(\n config.messages || {},\n 'Auth.forgotPassword.footer',\n );\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 Link = config.navigation?.linkComponent;\n const onNavigate =\n config.navigation?.onNavigate ||\n ((path: string) => {\n if (typeof window !== 'undefined') {\n window.location.href = path;\n }\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 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 <AuthPageLayout\n title={common('invalidLinkTitle')}\n description={common('invalidLinkDescription')}\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 <div />\n </AuthPageLayout>\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 <AuthPageLayout\n title={t('phone.title')}\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 </AuthPageLayout>\n );\n};\n","type Messages = Record<string, unknown>;\n\nexport function createTranslator(messages: Messages, namespace?: string) {\n return (key: string, params?: Record<string, string | number>): string => {\n const fullKey = namespace ? `${namespace}.${key}` : key;\n const keys = fullKey.split('.');\n\n let value: unknown = messages;\n for (const k of keys) {\n if (value && typeof value === 'object' && value !== null) {\n value = (value as Record<string, unknown>)[k];\n } else {\n return fullKey;\n }\n }\n\n if (typeof value !== 'string') {\n return fullKey;\n }\n\n // Simple parameter replacement\n if (params) {\n return value.replace(/\\{(\\w+)\\}/g, (_, param) =>\n String(params[param] ?? `{${param}}`),\n );\n }\n\n return value;\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","export const AUTH_ERROR_MAPPING: Record<\n string,\n { title: string; description: string }\n> = {\n USER_NOT_FOUND: {\n title: 'Account Not Found',\n description:\n 'We could not find an account with that identifier. Please check your spelling or sign up.',\n },\n INVALID_PASSWORD: {\n title: 'Invalid Password',\n description: 'The password you entered is incorrect. Please try again.',\n },\n USER_EXISTS: {\n title: 'Account Already Exists',\n description:\n 'An account with this identifier already exists. Please sign in instead.',\n },\n VERIFICATION_EXPIRED: {\n title: 'Verification Expired',\n description:\n 'The verification code or link has expired. Please request a new one.',\n },\n VERIFICATION_MISMATCH: {\n title: 'Invalid Code',\n description:\n 'The verification code you entered is invalid. Please double-check and try again.',\n },\n VERIFICATION_NOT_FOUND: {\n title: 'Verification Not Found',\n description:\n 'We could not find a pending verification request. Please restart the process.',\n },\n TOO_MANY_ATTEMPTS: {\n title: 'Too Many Attempts',\n description:\n 'You have made too many requests recently. Please wait a moment before trying again.',\n },\n REQUIRES_VERIFICATION: {\n title: 'Verification Required',\n description:\n 'You need to verify your account before you can continue. Please check your email or phone.',\n },\n UNAUTHORIZED: {\n title: 'Unauthorized',\n description:\n 'You are not authorized to perform this action. Please sign in again.',\n },\n ACCESS_DENIED: {\n title: 'Access Denied',\n description:\n 'You do not have permission to access this resource. Please contact support if you believe this is an error.',\n },\n HAS_NO_PASSWORD: {\n title: 'No Password Set',\n description:\n 'Your account does not have a password set (e.g. social login). Please sign in with your provider or reset your password.',\n },\n};\n\nexport const validCodes = Object.keys(AUTH_ERROR_MAPPING);\n","import { AUTH_ERROR_MAPPING, validCodes } from '../constants/auth.error.codes';\nimport type { AuthError } from '../types';\n\nexport type AuthErrorContent = {\n title: string;\n description: string;\n};\n\n// Translator function type\ntype TranslatorFunction = (\n key: string,\n params?: Record<string, string | number>,\n) => string;\n\n// Type guard to check if error is an AuthError\nfunction isAuthError(err: unknown): err is AuthError {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'message' in err &&\n typeof (err as { message: unknown }).message === 'string'\n );\n}\n\nfunction extractErrorCode(err: AuthError): string {\n if (err.code && validCodes.includes(err.code)) {\n return err.code;\n }\n if (err.message) {\n const messageUpper = err.message.toUpperCase().trim();\n if (validCodes.includes(messageUpper)) {\n return messageUpper;\n }\n }\n return '';\n}\n\nfunction handleAuthError(\n err: AuthError,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const errorCode = extractErrorCode(err);\n\n if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {\n const mapping = AUTH_ERROR_MAPPING[errorCode];\n setError({\n title: mapping.title,\n description: mapping.description,\n });\n return;\n }\n\n setError({\n title: t('errors.fallback'),\n description: err.message || t('errors.fallback'),\n });\n}\n\nfunction handleGenericError(\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const message = err instanceof Error ? err.message : t('errors.fallback');\n setError({\n title: 'Error',\n description: message,\n });\n}\n\nexport const handleError = (\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) => {\n if (isAuthError(err)) {\n handleAuthError(err, setError, t);\n } else {\n handleGenericError(err, setError, t);\n }\n};\n","'use client';\n\nimport type { ReactNode } from 'react';\n\ntype AuthPageLayoutProps = {\n title: string;\n description?: string;\n children: ReactNode;\n footer?: ReactNode;\n logoImage?: string;\n};\n\nexport const AuthPageLayout = ({\n title,\n description,\n children,\n footer,\n logoImage,\n}: AuthPageLayoutProps) => {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex size-8 mb-6 w-full items-center justify-center rounded-md\">\n {/** biome-ignore lint/performance/noImgElement: logo image */}\n <img src={logoImage || ''} alt=\"Mesob\" width={42} height={42} />\n </div>\n <div className=\"text-center\">\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n {description && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n\n {children}\n\n <div className=\"mt-2 w-full\">\n {footer && (\n <div className=\"w-full text-center text-sm text-muted-foreground\">\n {footer}\n </div>\n )}\n </div>\n </div>\n );\n};\n","'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { Button } from '@mesob/ui/components/button';\nimport { Field, FieldError, FieldGroup } from '@mesob/ui/components/field';\nimport {\n InputOTP,\n InputOTPGroup,\n InputOTPSlot,\n} from '@mesob/ui/components/input-otp';\nimport { Spinner } from '@mesob/ui/components/spinner';\nimport { Controller, useForm } from 'react-hook-form';\nimport { z } from 'zod';\nimport { useTranslator } from '../../hooks/use-translator';\nimport type { AuthErrorContent } from '../../utils/handle-error';\nimport { Countdown } from './countdown';\n\ntype VerificationFormValues = {\n code: string;\n};\n\ntype VerificationFormProps = {\n verificationId: string;\n onSubmit: (values: VerificationFormValues) => Promise<void> | void;\n onResend: () => Promise<void> | void;\n isLoading?: boolean;\n error?: AuthErrorContent | string | null;\n};\n\nconst verificationSchema = (t: (key: string) => string) =>\n z.object({\n code: z.string().length(6, t('form.codeLength')),\n });\n\nexport const VerificationForm = ({\n onSubmit,\n onResend,\n isLoading = false,\n}: VerificationFormProps) => {\n const t = useTranslator('Auth.verification');\n const form = useForm<VerificationFormValues>({\n resolver: zodResolver(verificationSchema(t)),\n defaultValues: {\n code: '',\n },\n });\n\n const handleSubmit = form.handleSubmit(async (values) => {\n await onSubmit(values);\n });\n\n return (\n <form id=\"verification-form\" onSubmit={handleSubmit}>\n <FieldGroup>\n <Controller\n name=\"code\"\n control={form.control}\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid}>\n <div className=\"flex justify-center\">\n <InputOTP\n maxLength={6}\n id=\"otp\"\n required\n value={field.value ?? ''}\n onChange={field.onChange}\n onBlur={field.onBlur}\n containerClassName=\"gap-4 justify-center mb-2 flex items-center\"\n aria-invalid={fieldState.invalid}\n >\n <InputOTPGroup className=\"gap-3 *:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:text-xl\">\n <InputOTPSlot className=\"h-12\" index={0} />\n <InputOTPSlot className=\"h-12\" index={1} />\n <InputOTPSlot className=\"h-12\" index={2} />\n <InputOTPSlot className=\"h-12\" index={3} />\n <InputOTPSlot className=\"h-12\" index={4} />\n <InputOTPSlot className=\"h-12\" index={5} />\n </InputOTPGroup>\n </InputOTP>\n </div>\n {fieldState.invalid && <FieldError errors={[fieldState.error]} />}\n </Field>\n )}\n />\n </FieldGroup>\n <div className=\"flex justify-between items-center mt-4\">\n <Countdown onResend={onResend} resending={isLoading} />\n <Button\n type=\"submit\"\n form=\"verification-form\"\n disabled={isLoading || form.watch('code').length !== 6}\n >\n {isLoading && <Spinner />}\n {t('form.confirm')}\n </Button>\n </div>\n </form>\n );\n};\n","import { createTranslator } from '../lib/translations';\nimport { useConfig } from '../provider';\n\nexport function useTranslator(namespace?: string) {\n const { config } = useConfig();\n return createTranslator(config.messages || {}, namespace);\n}\n","'use client';\n\nimport { Button } from '@mesob/ui/components/button';\nimport { Spinner } from '@mesob/ui/components/spinner';\nimport { useEffect, useState } from 'react';\nimport { useTranslator } from '../../hooks/use-translator';\n\ntype CountdownProps = {\n initialSeconds?: number;\n onResend: () => Promise<void> | void;\n resending?: boolean;\n};\n\nexport const Countdown = ({\n initialSeconds = 60,\n onResend,\n resending = false,\n}: CountdownProps) => {\n const t = useTranslator('Common');\n const [seconds, setSeconds] = useState(initialSeconds);\n const [isResending, setIsResending] = useState(false);\n\n useEffect(() => {\n if (seconds <= 0) {\n return;\n }\n\n const timer = setInterval(() => {\n setSeconds((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [seconds]);\n\n const handleResend = async () => {\n setIsResending(true);\n try {\n await onResend();\n setSeconds(initialSeconds);\n } catch (_error) {\n // Error handling is done by parent\n } finally {\n setIsResending(false);\n }\n };\n\n if (seconds > 0) {\n return (\n <Button variant=\"ghost\" disabled>\n {t('resendIn', { seconds })}\n </Button>\n );\n }\n\n return (\n <Button\n variant=\"ghost\"\n onClick={handleResend}\n disabled={isResending || resending}\n >\n {isResending || (resending && <Spinner />)}\n {t('resend')}\n </Button>\n );\n};\n"],"mappings":"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","names":["useEffect","useState","jsx","Button","Spinner","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs","Button","Spinner","jsx","jsxs","useState","useEffect"]}
@@ -1,5 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- declare function PermissionsPage(): react_jsx_runtime.JSX.Element;
4
-
5
- export { PermissionsPage };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/iam/permissions/permissions-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// Permission type from OpenAPI schema\ntype Permission = {\n id: string;\n code: string;\n name: string;\n description: string;\n resource: string;\n action: string;\n createdAt: string;\n};\n\nexport function PermissionsPage() {\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', '/permissions', {\n params: {\n query: {\n page: String(page),\n limit: String(limit),\n },\n },\n });\n\n const columns: DataTableColumn<Permission>[] = [\n {\n key: 'name',\n header: 'Permission',\n cell: (permission) => (\n <div>\n <p className=\"font-medium\">{permission.name}</p>\n <Badge variant=\"outline\" className=\"mt-1 font-mono text-xs\">\n {permission.code}\n </Badge>\n </div>\n ),\n },\n {\n key: 'resource',\n header: 'Resource',\n cell: (permission) => (\n <Badge variant=\"secondary\">{permission.resource}</Badge>\n ),\n },\n {\n key: 'action',\n header: 'Action',\n cell: (permission) => (\n <Badge variant=\"outline\">{permission.action}</Badge>\n ),\n },\n {\n key: 'description',\n header: 'Description',\n cell: (permission) => (\n <p className=\"text-sm text-muted-foreground max-w-xs truncate\">\n {permission.description}\n </p>\n ),\n },\n {\n key: 'actions',\n header: 'Actions',\n cell: (_permission) => (\n <Button variant=\"outline\" size=\"sm\">\n Edit\n </Button>\n ),\n },\n ];\n\n if (error) {\n return (\n <div className=\"p-6 text-center\">\n <p className=\"text-destructive\">Error loading permissions</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\">Permissions</h1>\n <p className=\"text-muted-foreground\">Manage system permissions</p>\n </div>\n <Button>Create Permission</Button>\n </div>\n\n <DataTable\n data={(data as { permissions: Permission[] })?.permissions || []}\n columns={columns}\n isLoading={isLoading}\n emptyMessage=\"No permissions found\"\n />\n\n {data &&\n 'permissions' in data &&\n data.permissions &&\n (data.permissions as Permission[]).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"]}
@@ -1,5 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- declare function RolesPage(): react_jsx_runtime.JSX.Element;
4
-
5
- export { RolesPage };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/iam/roles/roles-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// Role type from OpenAPI schema\ntype Role = {\n id: string;\n code: string;\n name: string;\n description: string;\n createdAt: string;\n};\n\nexport function RolesPage() {\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', '/roles', {\n params: {\n query: {\n page: String(page),\n limit: String(limit),\n },\n },\n });\n\n const columns: DataTableColumn<Role>[] = [\n {\n key: 'name',\n header: 'Role',\n cell: (role) => (\n <div>\n <p className=\"font-medium\">{role.name}</p>\n <Badge variant=\"outline\" className=\"mt-1\">\n {role.code}\n </Badge>\n </div>\n ),\n },\n {\n key: 'description',\n header: 'Description',\n cell: (role) => (\n <p className=\"text-sm text-muted-foreground\">{role.description}</p>\n ),\n },\n {\n key: 'createdAt',\n header: 'Created',\n cell: (role) => (\n <p className=\"text-sm\">\n {new Date(role.createdAt).toLocaleDateString()}\n </p>\n ),\n },\n {\n key: 'actions',\n header: 'Actions',\n cell: (_role) => (\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n Permissions\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 roles</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\">Roles</h1>\n <p className=\"text-muted-foreground\">Manage user roles</p>\n </div>\n <Button>Create Role</Button>\n </div>\n\n <DataTable\n data={(data as { roles: Role[] })?.roles || []}\n columns={columns}\n isLoading={isLoading}\n emptyMessage=\"No roles found\"\n />\n\n {data &&\n 'roles' in data &&\n data.roles &&\n (data.roles as Role[]).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"]}
@@ -1,5 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- declare function SessionsPage(): react_jsx_runtime.JSX.Element;
4
-
5
- export { SessionsPage };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/iam/sessions/sessions-page.tsx","../../../../src/provider.tsx","../../../../src/components/shared/data-table/data-table.tsx","../../../../src/components/skeletons/table-skeleton.tsx"],"sourcesContent":["'use client';\n\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// Session type from OpenAPI schema\ntype Session = {\n id: string;\n userId: string;\n expiresAt: string;\n createdAt: string;\n userAgent: string | null;\n ip: string | null;\n};\n\nexport function SessionsPage() {\n const { hooks } = useApi();\n const [selectedSessionId] = useState<string | null>(null);\n\n // Fetch sessions\n const { data, isLoading, error, refetch } = hooks.useQuery(\n 'get',\n '/sessions',\n );\n\n // Delete session mutation\n const deleteMutation = hooks.useMutation('delete', '/sessions/{id}', {\n onSuccess: () => {\n // Refetch sessions after deletion\n refetch();\n },\n });\n\n const handleDeleteSession = async (sessionId: string) => {\n if (confirm('Are you sure you want to revoke this session?')) {\n try {\n await deleteMutation.mutateAsync({\n params: {\n path: { id: sessionId },\n },\n });\n } catch (_err) {\n // Error handled by mutation error state\n }\n }\n };\n\n const columns: DataTableColumn<Session>[] = [\n {\n key: 'createdAt',\n header: 'Created',\n cell: (session) => (\n <div>\n <p className=\"text-sm font-medium\">\n {new Date(session.createdAt).toLocaleDateString()}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {new Date(session.createdAt).toLocaleTimeString()}\n </p>\n </div>\n ),\n },\n {\n key: 'expiresAt',\n header: 'Expires',\n cell: (session) => (\n <p className=\"text-sm\">\n {new Date(session.expiresAt).toLocaleDateString()}\n </p>\n ),\n },\n {\n key: 'userAgent',\n header: 'Device',\n cell: (session) => (\n <p className=\"text-sm truncate max-w-xs\">\n {session.userAgent || 'Unknown'}\n </p>\n ),\n },\n {\n key: 'ip',\n header: 'IP Address',\n cell: (session) => (\n <p className=\"text-sm font-mono\">{session.ip || 'Unknown'}</p>\n ),\n },\n {\n key: 'actions',\n header: 'Actions',\n cell: (session) => (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => handleDeleteSession(session.id)}\n disabled={\n deleteMutation.isPending && selectedSessionId === session.id\n }\n >\n {deleteMutation.isPending && selectedSessionId === session.id\n ? 'Revoking...'\n : 'Revoke'}\n </Button>\n ),\n },\n ];\n\n if (error) {\n return (\n <div className=\"p-6 text-center\">\n <p className=\"text-destructive\">Error loading sessions</p>\n </div>\n );\n }\n\n return (\n <div className=\"w-full p-6 space-y-4\">\n <div>\n <h1 className=\"text-3xl font-bold\">Sessions</h1>\n <p className=\"text-muted-foreground\">View and manage active sessions</p>\n </div>\n\n <DataTable\n data={(data as { sessions: Session[] })?.sessions || []}\n columns={columns}\n isLoading={isLoading}\n emptyMessage=\"No active sessions\"\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"]}
@@ -1,5 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- declare function TenantsPage(): react_jsx_runtime.JSX.Element;
4
-
5
- export { TenantsPage };
@@ -1 +0,0 @@
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":"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","names":["useState","jsx","_","jsx","jsxs","jsx","jsxs","useState"]}
@@ -1,5 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- declare function UsersPage(): react_jsx_runtime.JSX.Element;
4
-
5
- export { UsersPage };
@@ -1 +0,0 @@
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"]}
@@ -1,8 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
-
3
- type ProfilePageProps = {
4
- onEditClick?: () => void;
5
- };
6
- declare function ProfilePage({ onEditClick }: ProfilePageProps): react_jsx_runtime.JSX.Element;
7
-
8
- export { ProfilePage };