@alepha/ui 0.14.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/auth/Login-Cjxv3EDi.js.map +1 -1
- package/dist/auth/{Register-BKBIpHhW.js → Register-CGlbQ50l.js} +5 -2
- package/dist/auth/Register-CGlbQ50l.js.map +1 -0
- package/dist/auth/Register-CWdkXWkc.js +4 -0
- package/dist/auth/ResetPassword-DvqD_1SJ.js.map +1 -1
- package/dist/auth/index.d.ts +24 -20
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +6 -6
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +186 -186
- package/dist/core/index.d.ts.map +1 -1
- package/dist/demo/{DemoLogin--wE44i23.js → DemoLogin-DvltFTER.js} +2 -1
- package/dist/demo/DemoLogin-DvltFTER.js.map +1 -0
- package/dist/demo/{DemoRegister-BtrMksx6.js → DemoRegister-Vu6ZPWib.js} +6 -2
- package/dist/demo/DemoRegister-Vu6ZPWib.js.map +1 -0
- package/dist/demo/{DemoResetPassword-DVXiiiX7.js → DemoResetPassword-BFwmqwec.js} +2 -1
- package/dist/demo/DemoResetPassword-BFwmqwec.js.map +1 -0
- package/dist/demo/index.d.ts +12 -12
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +3 -3
- package/dist/json/index.d.ts +18 -19
- package/dist/json/index.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/auth/AuthRouter.ts +6 -6
- package/src/auth/components/Login.tsx +2 -2
- package/src/auth/components/Register.tsx +8 -3
- package/src/auth/components/ResetPassword.tsx +2 -2
- package/src/demo/components/auth/DemoLogin.tsx +4 -3
- package/src/demo/components/auth/DemoRegister.tsx +4 -3
- package/src/demo/components/auth/DemoResetPassword.tsx +3 -2
- package/dist/auth/Register-BKBIpHhW.js.map +0 -1
- package/dist/auth/Register-CtdvihIM.js +0 -4
- package/dist/demo/DemoLogin--wE44i23.js.map +0 -1
- package/dist/demo/DemoRegister-BtrMksx6.js.map +0 -1
- package/dist/demo/DemoResetPassword-DVXiiiX7.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/admin/AdminRouter.ts","../../src/admin/components/AdminLayout.tsx","../../src/admin/components/audits/AdminAudits.tsx","../../src/admin/components/files/AdminFiles.tsx","../../src/admin/components/jobs/AdminJobs.tsx","../../src/admin/components/notifications/AdminNotifications.tsx","../../src/admin/components/parameters/AdminParameters.tsx","../../src/admin/components/sessions/AdminSessions.tsx","../../src/admin/components/users/AdminUserAudits.tsx","../../src/admin/components/users/AdminUserCreate.tsx","../../src/admin/components/users/AdminUserDetails.tsx","../../src/admin/components/users/AdminUserLayout.tsx","../../src/admin/components/users/AdminUserSessions.tsx","../../src/admin/components/users/AdminUserSettings.tsx","../../src/admin/components/users/AdminUsers.tsx","../../src/admin/components/verifications/AdminVerifications.tsx","../../src/admin/MainRouter.ts","../../src/admin/index.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/admin/AdminRouter.ts","../../src/admin/components/AdminLayout.tsx","../../src/admin/components/audits/AdminAudits.tsx","../../src/admin/components/files/AdminFiles.tsx","../../src/admin/components/jobs/AdminJobs.tsx","../../src/admin/components/notifications/AdminNotifications.tsx","../../src/admin/components/parameters/AdminParameters.tsx","../../src/admin/components/sessions/AdminSessions.tsx","../../src/admin/components/users/AdminUserAudits.tsx","../../src/admin/components/users/AdminUserCreate.tsx","../../src/admin/components/users/AdminUserDetails.tsx","../../src/admin/components/users/AdminUserLayout.tsx","../../src/admin/components/users/AdminUserSessions.tsx","../../src/admin/components/users/AdminUserSettings.tsx","../../src/admin/components/users/AdminUsers.tsx","../../src/admin/components/verifications/AdminVerifications.tsx","../../src/admin/MainRouter.ts","../../src/admin/index.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Login-Cjxv3EDi.js","names":["IconGoogle","IconGithub"],"sources":["../../src/auth/components/Login.tsx"],"sourcesContent":["import { useAuth } from \"@alepha/react/auth\";\nimport { FormValidationError, useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, capitalize } from \"@alepha/ui\";\nimport { Card, Flex, Group, Image, Stack, Text, Title } from \"@mantine/core\";\nimport { IconLock, IconUser } from \"@tabler/icons-react\";\nimport { AlephaError, t } from \"alepha\";\nimport type { UserRealmConfig } from \"alepha/api/users\";\nimport { HttpError } from \"alepha/server\";\nimport { useMemo } from \"react\";\nimport type { AuthI18n } from \"../AuthI18n.ts\";\nimport type { AuthRouter } from \"../AuthRouter.ts\";\nimport IconGithub from \"./icons/IconGithub.tsx\";\nimport IconGoogle from \"./icons/IconGoogle.tsx\";\n\nexport interface LoginProps {\n realmConfig: UserRealmConfig;\n}\n\nconst Login = (props: LoginProps) => {\n const auth = useAuth();\n const router = useRouter<AuthRouter>();\n const { tr } = useI18n<AuthI18n, \"en\">();\n const redirect = router.query.r || \"/\";\n\n const credentialsProvider = props.realmConfig.authenticationMethods.find(\n (it) => it.type === \"CREDENTIALS\",\n );\n\n const settings = props.realmConfig.settings;\n\n // Determine what login methods are available\n const loginMethods = useMemo(() => {\n const methods = [];\n if (settings.usernameEnabled !== false) methods.push(\"username\");\n if (settings.emailEnabled !== false) methods.push(\"email\");\n if (settings.phoneEnabled === true) methods.push(\"phone\");\n return methods;\n }, [settings]);\n\n // Create identifier title based on enabled methods\n const identifierTitle = useMemo(() => {\n if (loginMethods.length === 0) return tr(\"loginUsername\");\n if (loginMethods.length === 1) {\n if (loginMethods[0] === \"username\") return tr(\"loginUsername\");\n if (loginMethods[0] === \"email\") return tr(\"loginEmail\");\n if (loginMethods[0] === \"phone\") return tr(\"loginPhone\");\n }\n const labels = loginMethods.map((m) => {\n if (m === \"username\") return tr(\"loginUsername\").toLowerCase();\n if (m === \"email\") return tr(\"loginEmail\").toLowerCase();\n if (m === \"phone\") return tr(\"loginPhone\").toLowerCase();\n return m;\n });\n return capitalize(\n `${labels.slice(0, -1).join(\", \")} or ${labels[labels.length - 1]}`,\n );\n }, [loginMethods, tr]);\n\n const form = useForm({\n schema: t.object({\n identifier: t.string({\n minLength: 1,\n }),\n password: t.string({\n minLength: settings.passwordPolicy?.minLength || 6,\n }),\n }),\n handler: async (data) => {\n if (!credentialsProvider) {\n throw new AlephaError(\"Credentials provider not configured\");\n }\n\n try {\n await auth.login(credentialsProvider.name, {\n username: data.identifier,\n password: data.password,\n realm: props.realmConfig.realmName,\n });\n await router.go(router.query.r || \"/\");\n } catch (error) {\n if (\n error instanceof HttpError &&\n error.error === \"InvalidCredentialsError\"\n ) {\n throw new FormValidationError({\n message: \"Invalid identifier or password\",\n path: \"/password\",\n });\n }\n throw error;\n }\n },\n });\n\n const getAutoCompleteType = () => {\n if (loginMethods.includes(\"email\")) {\n return \"email\";\n }\n if (loginMethods.includes(\"username\")) {\n return \"username\";\n }\n if (loginMethods.includes(\"phone\")) {\n return \"tel\";\n }\n return \"username\";\n };\n\n const externalLoginMethods = props.realmConfig.authenticationMethods.filter(\n (method) => method.type !== \"CREDENTIALS\",\n );\n\n const showOrDivider = credentialsProvider && externalLoginMethods.length > 0;\n\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n {/* Realm branding */}\n {(settings.logoUrl ||\n settings.displayName ||\n settings.description) && (\n <Stack gap={\"xs\"} align=\"center\" mb=\"xs\">\n {settings.logoUrl && (\n <Image\n src={settings.logoUrl}\n alt={settings.displayName || props.realmConfig.realmName}\n h={48}\n w=\"auto\"\n fit=\"contain\"\n />\n )}\n {settings.displayName && (\n <Title order={4} ta=\"center\">\n {settings.displayName}\n </Title>\n )}\n {settings.description && (\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {settings.description}\n </Text>\n )}\n </Stack>\n )}\n\n {/* Credentials login form */}\n {credentialsProvider && (\n <>\n <form {...form.props}>\n <Stack flex={1} gap={\"md\"}>\n <Control\n title={identifierTitle}\n input={form.input.identifier}\n icon={IconUser}\n text={{\n autoComplete: getAutoCompleteType(),\n }}\n />\n <Control\n title={tr(\"loginPassword\")}\n input={form.input.password}\n icon={IconLock}\n password={{\n autoComplete: \"current-password\",\n }}\n />\n <ActionButton variant={\"filled\"} form={form}>\n {tr(\"loginSignIn\")}\n </ActionButton>\n </Stack>\n </form>\n {settings.resetPasswordAllowed && (\n <Text size=\"sm\" ta=\"center\">\n <ActionButton\n href={router.path(\"resetPassword\", {\n query: { realm: props.realmConfig.realmName },\n })}\n anchorProps={{ inherit: true }}\n >\n {tr(\"loginForgotPassword\")}\n </ActionButton>\n </Text>\n )}\n </>\n )}\n\n {/* OR divider - only when both credentials AND external methods exist */}\n {showOrDivider && (\n <Group align=\"center\" justify=\"center\" gap={\"md\"}>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n <Text size=\"xs\" c={\"dimmed\"}>\n {tr(\"loginOr\")}\n </Text>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n </Group>\n )}\n\n {/* External login methods */}\n {externalLoginMethods.length > 0 && (\n <Stack gap={\"sm\"}>\n {externalLoginMethods.map((method) => (\n <ActionButton\n variant={\"default\"}\n key={method.type}\n leftSection={leftSection(method.name.toLowerCase())}\n onClick={() =>\n auth.login(method.name, {\n redirect,\n realm: props.realmConfig.realmName,\n })\n }\n >\n {tr(\"loginContinueWith\", {\n args: [capitalize(method.name)],\n })}\n </ActionButton>\n ))}\n </Stack>\n )}\n\n {/* Registration link */}\n {settings.registrationAllowed && (\n <Text size=\"sm\" ta=\"center\">\n {tr(\"loginNoAccount\")}{\" \"}\n <ActionButton\n href={router.path(\"register\", {\n query: { realm: props.realmConfig.realmName },\n })}\n anchorProps={{ inherit: true }}\n >\n {tr(\"loginSignUp\")}\n </ActionButton>\n </Text>\n )}\n </Stack>\n </Card>\n <ActionButton variant={\"subtle\"} href={\"/\"}>\n {tr(\"loginCancel\")}\n </ActionButton>\n </Stack>\n </Flex>\n );\n};\n\nexport default Login;\n\nconst leftSection = (name: string) => {\n if (name === \"google\") {\n return <IconGoogle />;\n }\n\n if (name === \"github\") {\n return <IconGithub />;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Login-Cjxv3EDi.js","names":["IconGoogle","IconGithub"],"sources":["../../src/auth/components/Login.tsx"],"sourcesContent":["import { useAuth } from \"@alepha/react/auth\";\nimport { FormValidationError, useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, capitalize } from \"@alepha/ui\";\nimport { Card, Flex, Group, Image, Stack, Text, Title } from \"@mantine/core\";\nimport { IconLock, IconUser } from \"@tabler/icons-react\";\nimport { AlephaError, t } from \"alepha\";\nimport type { RealmConfig } from \"alepha/api/users\";\nimport { HttpError } from \"alepha/server\";\nimport { useMemo } from \"react\";\nimport type { AuthI18n } from \"../AuthI18n.ts\";\nimport type { AuthRouter } from \"../AuthRouter.ts\";\nimport IconGithub from \"./icons/IconGithub.tsx\";\nimport IconGoogle from \"./icons/IconGoogle.tsx\";\n\nexport interface LoginProps {\n realmConfig: RealmConfig;\n}\n\nconst Login = (props: LoginProps) => {\n const auth = useAuth();\n const router = useRouter<AuthRouter>();\n const { tr } = useI18n<AuthI18n, \"en\">();\n const redirect = router.query.r || \"/\";\n\n const credentialsProvider = props.realmConfig.authenticationMethods.find(\n (it) => it.type === \"CREDENTIALS\",\n );\n\n const settings = props.realmConfig.settings;\n\n // Determine what login methods are available\n const loginMethods = useMemo(() => {\n const methods = [];\n if (settings.usernameEnabled !== false) methods.push(\"username\");\n if (settings.emailEnabled !== false) methods.push(\"email\");\n if (settings.phoneEnabled === true) methods.push(\"phone\");\n return methods;\n }, [settings]);\n\n // Create identifier title based on enabled methods\n const identifierTitle = useMemo(() => {\n if (loginMethods.length === 0) return tr(\"loginUsername\");\n if (loginMethods.length === 1) {\n if (loginMethods[0] === \"username\") return tr(\"loginUsername\");\n if (loginMethods[0] === \"email\") return tr(\"loginEmail\");\n if (loginMethods[0] === \"phone\") return tr(\"loginPhone\");\n }\n const labels = loginMethods.map((m) => {\n if (m === \"username\") return tr(\"loginUsername\").toLowerCase();\n if (m === \"email\") return tr(\"loginEmail\").toLowerCase();\n if (m === \"phone\") return tr(\"loginPhone\").toLowerCase();\n return m;\n });\n return capitalize(\n `${labels.slice(0, -1).join(\", \")} or ${labels[labels.length - 1]}`,\n );\n }, [loginMethods, tr]);\n\n const form = useForm({\n schema: t.object({\n identifier: t.string({\n minLength: 1,\n }),\n password: t.string({\n minLength: settings.passwordPolicy?.minLength || 6,\n }),\n }),\n handler: async (data) => {\n if (!credentialsProvider) {\n throw new AlephaError(\"Credentials provider not configured\");\n }\n\n try {\n await auth.login(credentialsProvider.name, {\n username: data.identifier,\n password: data.password,\n realm: props.realmConfig.realmName,\n });\n await router.go(router.query.r || \"/\");\n } catch (error) {\n if (\n error instanceof HttpError &&\n error.error === \"InvalidCredentialsError\"\n ) {\n throw new FormValidationError({\n message: \"Invalid identifier or password\",\n path: \"/password\",\n });\n }\n throw error;\n }\n },\n });\n\n const getAutoCompleteType = () => {\n if (loginMethods.includes(\"email\")) {\n return \"email\";\n }\n if (loginMethods.includes(\"username\")) {\n return \"username\";\n }\n if (loginMethods.includes(\"phone\")) {\n return \"tel\";\n }\n return \"username\";\n };\n\n const externalLoginMethods = props.realmConfig.authenticationMethods.filter(\n (method) => method.type !== \"CREDENTIALS\",\n );\n\n const showOrDivider = credentialsProvider && externalLoginMethods.length > 0;\n\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n {/* Realm branding */}\n {(settings.logoUrl ||\n settings.displayName ||\n settings.description) && (\n <Stack gap={\"xs\"} align=\"center\" mb=\"xs\">\n {settings.logoUrl && (\n <Image\n src={settings.logoUrl}\n alt={settings.displayName || props.realmConfig.realmName}\n h={48}\n w=\"auto\"\n fit=\"contain\"\n />\n )}\n {settings.displayName && (\n <Title order={4} ta=\"center\">\n {settings.displayName}\n </Title>\n )}\n {settings.description && (\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {settings.description}\n </Text>\n )}\n </Stack>\n )}\n\n {/* Credentials login form */}\n {credentialsProvider && (\n <>\n <form {...form.props}>\n <Stack flex={1} gap={\"md\"}>\n <Control\n title={identifierTitle}\n input={form.input.identifier}\n icon={IconUser}\n text={{\n autoComplete: getAutoCompleteType(),\n }}\n />\n <Control\n title={tr(\"loginPassword\")}\n input={form.input.password}\n icon={IconLock}\n password={{\n autoComplete: \"current-password\",\n }}\n />\n <ActionButton variant={\"filled\"} form={form}>\n {tr(\"loginSignIn\")}\n </ActionButton>\n </Stack>\n </form>\n {settings.resetPasswordAllowed && (\n <Text size=\"sm\" ta=\"center\">\n <ActionButton\n href={router.path(\"resetPassword\", {\n query: { realm: props.realmConfig.realmName },\n })}\n anchorProps={{ inherit: true }}\n >\n {tr(\"loginForgotPassword\")}\n </ActionButton>\n </Text>\n )}\n </>\n )}\n\n {/* OR divider - only when both credentials AND external methods exist */}\n {showOrDivider && (\n <Group align=\"center\" justify=\"center\" gap={\"md\"}>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n <Text size=\"xs\" c={\"dimmed\"}>\n {tr(\"loginOr\")}\n </Text>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n </Group>\n )}\n\n {/* External login methods */}\n {externalLoginMethods.length > 0 && (\n <Stack gap={\"sm\"}>\n {externalLoginMethods.map((method) => (\n <ActionButton\n variant={\"default\"}\n key={method.type}\n leftSection={leftSection(method.name.toLowerCase())}\n onClick={() =>\n auth.login(method.name, {\n redirect,\n realm: props.realmConfig.realmName,\n })\n }\n >\n {tr(\"loginContinueWith\", {\n args: [capitalize(method.name)],\n })}\n </ActionButton>\n ))}\n </Stack>\n )}\n\n {/* Registration link */}\n {settings.registrationAllowed && (\n <Text size=\"sm\" ta=\"center\">\n {tr(\"loginNoAccount\")}{\" \"}\n <ActionButton\n href={router.path(\"register\", {\n query: { realm: props.realmConfig.realmName },\n })}\n anchorProps={{ inherit: true }}\n >\n {tr(\"loginSignUp\")}\n </ActionButton>\n </Text>\n )}\n </Stack>\n </Card>\n <ActionButton variant={\"subtle\"} href={\"/\"}>\n {tr(\"loginCancel\")}\n </ActionButton>\n </Stack>\n </Flex>\n );\n};\n\nexport default Login;\n\nconst leftSection = (name: string) => {\n if (name === \"google\") {\n return <IconGoogle />;\n }\n\n if (name === \"github\") {\n return <IconGithub />;\n }\n};\n"],"mappings":"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"}
|
|
@@ -29,7 +29,10 @@ const Register = (props) => {
|
|
|
29
29
|
const form = useForm({
|
|
30
30
|
schema: useMemo(() => {
|
|
31
31
|
const registerSchema = t.object({
|
|
32
|
-
username: t.optional(t.text(
|
|
32
|
+
username: t.optional(t.text({
|
|
33
|
+
trim: true,
|
|
34
|
+
pattern: settings.usernameRegExp
|
|
35
|
+
})),
|
|
33
36
|
email: t.optional(t.email()),
|
|
34
37
|
phoneNumber: t.optional(t.e164()),
|
|
35
38
|
password: t.string({ minLength: 8 }),
|
|
@@ -359,4 +362,4 @@ const leftSection = (name) => {
|
|
|
359
362
|
|
|
360
363
|
//#endregion
|
|
361
364
|
export { Register_default as t };
|
|
362
|
-
//# sourceMappingURL=Register-
|
|
365
|
+
//# sourceMappingURL=Register-CGlbQ50l.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Register-CGlbQ50l.js","names":["IconGoogle","IconGithub"],"sources":["../../src/auth/components/Register.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useAuth } from \"@alepha/react/auth\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, capitalize } from \"@alepha/ui\";\nimport {\n Alert,\n Card,\n Flex,\n Group,\n Image,\n PinInput,\n Stack,\n Text,\n Title,\n} from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconLock,\n IconMail,\n IconPhone,\n IconUser,\n} from \"@tabler/icons-react\";\nimport { TypeBoxError, t } from \"alepha\";\nimport type {\n RealmConfig,\n RegistrationIntentResponse,\n UserController,\n} from \"alepha/api/users\";\nimport { useMemo, useState } from \"react\";\nimport type { AuthI18n } from \"../AuthI18n.ts\";\nimport type { AuthRouter } from \"../AuthRouter.ts\";\nimport IconGithub from \"./icons/IconGithub.tsx\";\nimport IconGoogle from \"./icons/IconGoogle.tsx\";\n\nexport interface RegisterProps {\n realmConfig: RealmConfig;\n}\n\ntype RegistrationPhase = \"form\" | \"verification\";\n\ninterface RegistrationState {\n phase: RegistrationPhase;\n intent?: RegistrationIntentResponse;\n credentials?: {\n identifier: string;\n password: string;\n };\n}\n\nconst Register = (props: RegisterProps) => {\n const auth = useAuth();\n const userCtrl = useClient<UserController>();\n const router = useRouter<AuthRouter>();\n const { tr } = useI18n<AuthI18n, \"en\">();\n const redirect = router.query.r || \"/\";\n\n const [registrationState, setRegistrationState] = useState<RegistrationState>(\n {\n phase: \"form\",\n },\n );\n const [emailCode, setEmailCode] = useState(\"\");\n const [phoneCode, setPhoneCode] = useState(\"\");\n const [verificationError, setVerificationError] = useState<string | null>(\n null,\n );\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const credentialsProvider = props.realmConfig.authenticationMethods.find(\n (it) => it.type === \"CREDENTIALS\",\n );\n\n const settings = props.realmConfig.settings || {};\n const isRegistrationAllowed = settings.registrationAllowed !== false;\n\n const registerSchema = useMemo(() => {\n const registerSchema = t.object({\n username: t.optional(\n t.text({\n trim: true,\n pattern: settings.usernameRegExp,\n }),\n ),\n email: t.optional(t.email()),\n phoneNumber: t.optional(t.e164()),\n password: t.string({ minLength: 8 }),\n confirmPassword: t.string({ minLength: 8 }),\n });\n\n const required = registerSchema.required as string[];\n\n if (settings.usernameRequired) required.push(\"username\");\n if (settings.emailRequired) required.push(\"email\");\n if (settings.phoneRequired) required.push(\"phoneNumber\");\n\n return registerSchema;\n }, []);\n\n const form = useForm({\n schema: registerSchema,\n handler: async (data) => {\n if (data.password !== data.confirmPassword) {\n throw new TypeBoxError({\n message: \"Passwords do not match\",\n instancePath: \"/confirmPassword\",\n keyword: \"not\",\n schemaPath: \"\",\n params: {},\n });\n }\n\n // Phase 1: Create registration intent\n const intent = await userCtrl.createRegistrationIntent({\n query: { userRealmName: props.realmConfig.realmName },\n body: {\n username: data.username,\n email: data.email,\n phoneNumber: data.phoneNumber,\n password: data.password,\n },\n });\n\n const identifier = data.username ?? data.email ?? data.phoneNumber;\n\n // Check if verification is needed\n if (\n intent.expectEmailVerification ||\n intent.expectPhoneVerification ||\n intent.expectCaptcha\n ) {\n // Move to verification phase\n setRegistrationState({\n phase: \"verification\",\n intent,\n credentials: identifier\n ? { identifier, password: data.password }\n : undefined,\n });\n return;\n }\n\n // No verification needed - complete registration immediately\n await userCtrl.createUserFromIntent({\n body: { intentId: intent.intentId },\n });\n\n // Auto-login after registration\n if (identifier && credentialsProvider) {\n await auth.login(credentialsProvider.name, {\n username: identifier,\n password: data.password,\n realm: props.realmConfig.realmName,\n });\n }\n\n await router.go(router.query.r || \"/\");\n },\n });\n\n const handleVerificationSubmit = async () => {\n if (!registrationState.intent) return;\n\n setIsSubmitting(true);\n setVerificationError(null);\n\n try {\n // Phase 2: Complete registration with verification codes\n await userCtrl.createUserFromIntent({\n body: {\n intentId: registrationState.intent.intentId,\n emailCode: registrationState.intent.expectEmailVerification\n ? emailCode\n : undefined,\n phoneCode: registrationState.intent.expectPhoneVerification\n ? phoneCode\n : undefined,\n },\n });\n\n // Auto-login after registration\n if (registrationState.credentials && credentialsProvider) {\n await auth.login(credentialsProvider.name, {\n username: registrationState.credentials.identifier,\n password: registrationState.credentials.password,\n realm: props.realmConfig.realmName,\n });\n }\n\n await router.go(router.query.r || \"/\");\n } catch (error) {\n setVerificationError(\n error instanceof Error ? error.message : \"Verification failed\",\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const canSubmitVerification = () => {\n if (!registrationState.intent) return false;\n\n if (\n registrationState.intent.expectEmailVerification &&\n emailCode.length !== 6\n ) {\n return false;\n }\n\n if (\n registrationState.intent.expectPhoneVerification &&\n phoneCode.length !== 6\n ) {\n return false;\n }\n\n return true;\n };\n\n // Verification phase UI\n if (registrationState.phase === \"verification\" && registrationState.intent) {\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"registerVerifyTitle\") ?? \"Verify your account\"}\n </Text>\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {tr(\"registerVerifyDescription\") ??\n \"Please enter the verification code(s) sent to you.\"}\n </Text>\n\n {verificationError && (\n <Alert variant=\"light\" color=\"red\" icon={<IconAlertCircle />}>\n <Text size=\"sm\">{verificationError}</Text>\n </Alert>\n )}\n\n {registrationState.intent.expectEmailVerification && (\n <Stack gap={\"xs\"}>\n <Text size=\"sm\" fw={500}>\n {tr(\"registerEmailCode\")}\n </Text>\n <Flex justify=\"center\">\n <PinInput\n length={6}\n value={emailCode}\n onChange={setEmailCode}\n type=\"number\"\n oneTimeCode\n aria-label=\"Email verification code\"\n />\n </Flex>\n </Stack>\n )}\n\n {registrationState.intent.expectPhoneVerification && (\n <Stack gap={\"xs\"}>\n <Text size=\"sm\" fw={500}>\n {tr(\"registerPhoneCode\")}\n </Text>\n <Flex justify=\"center\">\n <PinInput\n length={6}\n value={phoneCode}\n onChange={setPhoneCode}\n type=\"number\"\n oneTimeCode\n aria-label=\"Phone verification code\"\n />\n </Flex>\n </Stack>\n )}\n\n <ActionButton\n color={\"blue\"}\n onClick={handleVerificationSubmit}\n loading={isSubmitting}\n disabled={!canSubmitVerification()}\n >\n {tr(\"registerVerifySubmit\")}\n </ActionButton>\n\n <ActionButton\n variant=\"subtle\"\n onClick={() =>\n setRegistrationState({ phase: \"form\", intent: undefined })\n }\n >\n {tr(\"registerVerifyBack\") ?? \"Back to registration\"}\n </ActionButton>\n </Stack>\n </Card>\n </Stack>\n </Flex>\n );\n }\n\n // External login methods\n const externalMethods = props.realmConfig.authenticationMethods.filter(\n (method) => method.type !== \"CREDENTIALS\",\n );\n\n const showOrDivider = credentialsProvider && externalMethods.length > 0;\n\n // Registration form phase UI\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n {/* Realm branding */}\n {(settings.logoUrl ||\n settings.displayName ||\n settings.description) && (\n <Stack gap={\"xs\"} align=\"center\" mb=\"xs\">\n {settings.logoUrl && (\n <Image\n src={settings.logoUrl}\n alt={settings.displayName || props.realmConfig.realmName}\n h={48}\n w=\"auto\"\n fit=\"contain\"\n />\n )}\n {settings.displayName && (\n <Title order={4} ta=\"center\">\n {settings.displayName}\n </Title>\n )}\n {settings.description && (\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {settings.description}\n </Text>\n )}\n </Stack>\n )}\n\n {!isRegistrationAllowed ? (\n <>\n <Alert\n variant=\"light\"\n color=\"yellow\"\n icon={<IconAlertCircle />}\n >\n <Text size=\"sm\">{tr(\"registerDisabled\")}</Text>\n </Alert>\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n >\n {tr(\"registerBackToSignIn\")}\n </ActionButton>\n </>\n ) : (\n <>\n {/* Credentials registration form */}\n {credentialsProvider && (\n <form {...form.props}>\n <Stack flex={1} gap={\"md\"}>\n {settings.usernameEnabled !== false &&\n form.input.username && (\n <Control\n title={tr(\"registerUsername\")}\n input={form.input.username}\n icon={<IconUser />}\n text={{\n autoComplete: \"username\",\n }}\n />\n )}\n {settings.emailEnabled !== false && form.input.email && (\n <Control\n title={tr(\"registerEmail\")}\n input={form.input.email}\n icon={<IconMail />}\n text={{\n autoComplete: \"email\",\n }}\n />\n )}\n {settings.phoneEnabled === true &&\n form.input.phoneNumber && (\n <Control\n title={tr(\"registerPhone\")}\n input={form.input.phoneNumber}\n icon={<IconPhone />}\n text={{\n autoComplete: \"tel\",\n }}\n />\n )}\n <Control\n title={tr(\"registerPassword\")}\n input={form.input.password}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n }}\n />\n <Control\n title={tr(\"registerConfirmPassword\")}\n input={form.input.confirmPassword}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n }}\n />\n <ActionButton\n form={form}\n color={\"blue\"}\n variant={\"filled\"}\n >\n {tr(\"registerCreateAccount\")}\n </ActionButton>\n </Stack>\n </form>\n )}\n\n {/* OR divider - only when both credentials AND external methods exist */}\n {showOrDivider && (\n <Group align=\"center\" justify=\"center\" gap={\"md\"}>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n <Text size=\"xs\" c=\"dimmed\">\n {tr(\"registerOr\")}\n </Text>\n <Flex flex={1} h={\"1px\"} bg={\"var(--alepha-border)\"} />\n </Group>\n )}\n\n {/* External login methods */}\n {externalMethods.length > 0 && (\n <Stack gap={\"sm\"}>\n {externalMethods.map((method) => (\n <ActionButton\n variant={\"default\"}\n key={method.type}\n leftSection={leftSection(method.name.toLowerCase())}\n onClick={() =>\n auth.login(method.name, {\n redirect,\n realm: props.realmConfig.realmName,\n })\n }\n >\n {tr(\"registerContinueWith\", {\n args: [capitalize(method.name)],\n })}\n </ActionButton>\n ))}\n </Stack>\n )}\n\n {/* Sign in link */}\n <Text size=\"sm\" ta=\"center\">\n {tr(\"registerHaveAccount\")}{\" \"}\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n anchorProps={{ inherit: true }}\n >\n {tr(\"registerSignIn\")}\n </ActionButton>\n </Text>\n </>\n )}\n </Stack>\n </Card>\n <ActionButton variant={\"subtle\"} href={redirect}>\n {tr(\"registerCancel\")}\n </ActionButton>\n </Stack>\n </Flex>\n );\n};\n\nexport default Register;\n\nconst leftSection = (name: string) => {\n if (name === \"google\") {\n return <IconGoogle />;\n }\n\n if (name === \"github\") {\n return <IconGithub />;\n }\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResetPassword-DvqD_1SJ.js","names":[],"sources":["../../src/auth/components/ResetPassword.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control } from \"@alepha/ui\";\nimport {\n Alert,\n Card,\n Flex,\n Image,\n PinInput,\n Stack,\n Text,\n Title,\n} from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconInfoCircle,\n IconLock,\n IconMail,\n} from \"@tabler/icons-react\";\nimport { AlephaError, t } from \"alepha\";\nimport type {\n PasswordResetIntentResponse,\n UserController,\n UserRealmConfig,\n} from \"alepha/api/users\";\nimport { resetPasswordRequestSchema } from \"alepha/api/users\";\nimport { useState } from \"react\";\nimport type { AuthI18n } from \"../AuthI18n.ts\";\nimport type { AuthRouter } from \"../AuthRouter.ts\";\n\nexport interface ResetPasswordProps {\n realmConfig: UserRealmConfig;\n}\n\ntype Step = \"email\" | \"code\" | \"password\" | \"success\";\n\ninterface ResetState {\n step: Step;\n intent?: PasswordResetIntentResponse;\n email?: string;\n code?: string;\n}\n\nconst ResetPassword = (props: ResetPasswordProps) => {\n const router = useRouter<AuthRouter>();\n const userCtrl = useClient<UserController>();\n const { tr } = useI18n<AuthI18n, \"en\">();\n const [resetState, setResetState] = useState<ResetState>({ step: \"email\" });\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const redirect = router.query.r || \"/\";\n\n const settings = props.realmConfig.settings;\n const isResetPasswordAllowed = settings?.resetPasswordAllowed !== false;\n\n // Phase 1: Request password reset intent\n const emailForm = useForm({\n schema: resetPasswordRequestSchema,\n handler: async (data) => {\n setError(null);\n const intent = await userCtrl.createPasswordResetIntent({\n query: { userRealmName: props.realmConfig.realmName },\n body: { email: data.email },\n });\n\n setResetState({\n step: \"code\",\n intent,\n email: data.email,\n });\n },\n });\n\n // Phase 2: Complete password reset\n const passwordForm = useForm(\n {\n schema: t.object({\n password: t.string({ minLength: 8 }),\n confirmPassword: t.string({ minLength: 8 }),\n }),\n handler: async (data) => {\n if (data.password !== data.confirmPassword) {\n throw new AlephaError(\"Passwords do not match\");\n }\n\n if (!resetState.intent || !resetState.code) {\n throw new AlephaError(\"Invalid reset state\");\n }\n\n await userCtrl.completePasswordReset({\n body: {\n intentId: resetState.intent.intentId,\n code: resetState.code,\n newPassword: data.password,\n },\n });\n\n setResetState({ step: \"success\" });\n },\n },\n [resetState.intent, resetState.code],\n );\n\n const handleCodeComplete = (value: string) => {\n if (value.length === 6) {\n setResetState((prev) => ({\n ...prev,\n step: \"password\",\n code: value,\n }));\n }\n };\n\n const handleResendCode = async () => {\n if (!resetState.email) return;\n\n setIsSubmitting(true);\n setError(null);\n\n try {\n const intent = await userCtrl.createPasswordResetIntent({\n query: { userRealmName: props.realmConfig.realmName },\n body: { email: resetState.email },\n });\n\n setResetState((prev) => ({\n ...prev,\n intent,\n }));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to resend code\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n {/* Realm branding */}\n {(settings.logoUrl ||\n settings.displayName ||\n settings.description) && (\n <Stack gap={\"xs\"} align=\"center\" mb=\"xs\">\n {settings.logoUrl && (\n <Image\n src={settings.logoUrl}\n alt={settings.displayName || props.realmConfig.realmName}\n h={48}\n w=\"auto\"\n fit=\"contain\"\n />\n )}\n {settings.displayName && (\n <Title order={4} ta=\"center\">\n {settings.displayName}\n </Title>\n )}\n {settings.description && (\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {settings.description}\n </Text>\n )}\n </Stack>\n )}\n\n {error && (\n <Alert variant=\"light\" color=\"red\" icon={<IconAlertCircle />}>\n <Text size=\"sm\">{error}</Text>\n </Alert>\n )}\n\n {!isResetPasswordAllowed ? (\n <>\n <Alert\n variant=\"light\"\n color=\"yellow\"\n icon={<IconAlertCircle />}\n >\n <Text size=\"sm\">{tr(\"resetPasswordDisabled\")}</Text>\n </Alert>\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n >\n {tr(\"resetPasswordBackToSignIn\")}\n </ActionButton>\n </>\n ) : resetState.step === \"email\" ? (\n <form {...emailForm.props}>\n <Stack flex={1} gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Text size=\"sm\" c=\"dimmed\">\n {tr(\"resetPasswordEnterEmail\")}\n </Text>\n <Control\n title={tr(\"resetPasswordEmail\")}\n input={emailForm.input.email}\n icon={<IconMail />}\n text={{\n autoComplete: \"email\",\n autoFocus: true,\n disabled: !isResetPasswordAllowed,\n }}\n />\n <ActionButton\n form={emailForm}\n disabled={!isResetPasswordAllowed}\n >\n {tr(\"resetPasswordSendCode\")}\n </ActionButton>\n </Stack>\n </form>\n ) : resetState.step === \"code\" ? (\n <Stack gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Alert variant=\"light\" color=\"blue\" icon={<IconInfoCircle />}>\n <Text size=\"sm\">{tr(\"resetPasswordCodeSent\")}</Text>\n </Alert>\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {tr(\"resetPasswordEnterCode\")}\n </Text>\n <Flex justify=\"center\">\n <PinInput\n length={6}\n type=\"number\"\n autoFocus\n oneTimeCode\n onComplete={handleCodeComplete}\n aria-label=\"Password reset verification code\"\n />\n </Flex>\n <ActionButton\n variant=\"subtle\"\n onClick={handleResendCode}\n loading={isSubmitting}\n >\n {tr(\"resetPasswordResendCode\")}\n </ActionButton>\n </Stack>\n ) : resetState.step === \"password\" ? (\n <form {...passwordForm.props}>\n <Stack flex={1} gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Text size=\"sm\" c=\"dimmed\">\n {tr(\"resetPasswordEnterNewPassword\")}\n </Text>\n <Control\n title={tr(\"resetPasswordNewPassword\")}\n input={passwordForm.input.password}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n autoFocus: true,\n }}\n />\n <Control\n title={tr(\"resetPasswordConfirmPassword\")}\n input={passwordForm.input.confirmPassword}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n }}\n />\n <ActionButton form={passwordForm}>\n {tr(\"resetPasswordSetNewPassword\")}\n </ActionButton>\n </Stack>\n </form>\n ) : (\n <>\n <Alert variant=\"light\" color=\"green\" icon={<IconCheck />}>\n <Text size=\"sm\">{tr(\"resetPasswordSuccess\")}</Text>\n </Alert>\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n >\n {tr(\"resetPasswordBackToSignIn\")}\n </ActionButton>\n </>\n )}\n </Stack>\n </Card>\n <ActionButton variant={\"subtle\"} href={redirect}>\n {tr(\"resetPasswordCancel\")}\n </ActionButton>\n </Stack>\n </Flex>\n );\n};\n\nexport default ResetPassword;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResetPassword-DvqD_1SJ.js","names":[],"sources":["../../src/auth/components/ResetPassword.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control } from \"@alepha/ui\";\nimport {\n Alert,\n Card,\n Flex,\n Image,\n PinInput,\n Stack,\n Text,\n Title,\n} from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconInfoCircle,\n IconLock,\n IconMail,\n} from \"@tabler/icons-react\";\nimport { AlephaError, t } from \"alepha\";\nimport type {\n PasswordResetIntentResponse,\n RealmConfig,\n UserController,\n} from \"alepha/api/users\";\nimport { resetPasswordRequestSchema } from \"alepha/api/users\";\nimport { useState } from \"react\";\nimport type { AuthI18n } from \"../AuthI18n.ts\";\nimport type { AuthRouter } from \"../AuthRouter.ts\";\n\nexport interface ResetPasswordProps {\n realmConfig: RealmConfig;\n}\n\ntype Step = \"email\" | \"code\" | \"password\" | \"success\";\n\ninterface ResetState {\n step: Step;\n intent?: PasswordResetIntentResponse;\n email?: string;\n code?: string;\n}\n\nconst ResetPassword = (props: ResetPasswordProps) => {\n const router = useRouter<AuthRouter>();\n const userCtrl = useClient<UserController>();\n const { tr } = useI18n<AuthI18n, \"en\">();\n const [resetState, setResetState] = useState<ResetState>({ step: \"email\" });\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const redirect = router.query.r || \"/\";\n\n const settings = props.realmConfig.settings;\n const isResetPasswordAllowed = settings?.resetPasswordAllowed !== false;\n\n // Phase 1: Request password reset intent\n const emailForm = useForm({\n schema: resetPasswordRequestSchema,\n handler: async (data) => {\n setError(null);\n const intent = await userCtrl.createPasswordResetIntent({\n query: { userRealmName: props.realmConfig.realmName },\n body: { email: data.email },\n });\n\n setResetState({\n step: \"code\",\n intent,\n email: data.email,\n });\n },\n });\n\n // Phase 2: Complete password reset\n const passwordForm = useForm(\n {\n schema: t.object({\n password: t.string({ minLength: 8 }),\n confirmPassword: t.string({ minLength: 8 }),\n }),\n handler: async (data) => {\n if (data.password !== data.confirmPassword) {\n throw new AlephaError(\"Passwords do not match\");\n }\n\n if (!resetState.intent || !resetState.code) {\n throw new AlephaError(\"Invalid reset state\");\n }\n\n await userCtrl.completePasswordReset({\n body: {\n intentId: resetState.intent.intentId,\n code: resetState.code,\n newPassword: data.password,\n },\n });\n\n setResetState({ step: \"success\" });\n },\n },\n [resetState.intent, resetState.code],\n );\n\n const handleCodeComplete = (value: string) => {\n if (value.length === 6) {\n setResetState((prev) => ({\n ...prev,\n step: \"password\",\n code: value,\n }));\n }\n };\n\n const handleResendCode = async () => {\n if (!resetState.email) return;\n\n setIsSubmitting(true);\n setError(null);\n\n try {\n const intent = await userCtrl.createPasswordResetIntent({\n query: { userRealmName: props.realmConfig.realmName },\n body: { email: resetState.email },\n });\n\n setResetState((prev) => ({\n ...prev,\n intent,\n }));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to resend code\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <Flex flex={1} justify={\"center\"} align={\"center\"}>\n <Stack gap={\"sm\"} w={360}>\n <Card withBorder p={\"lg\"} bg={\"var(--alepha-elevated)\"}>\n <Stack gap={\"md\"}>\n {/* Realm branding */}\n {(settings.logoUrl ||\n settings.displayName ||\n settings.description) && (\n <Stack gap={\"xs\"} align=\"center\" mb=\"xs\">\n {settings.logoUrl && (\n <Image\n src={settings.logoUrl}\n alt={settings.displayName || props.realmConfig.realmName}\n h={48}\n w=\"auto\"\n fit=\"contain\"\n />\n )}\n {settings.displayName && (\n <Title order={4} ta=\"center\">\n {settings.displayName}\n </Title>\n )}\n {settings.description && (\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {settings.description}\n </Text>\n )}\n </Stack>\n )}\n\n {error && (\n <Alert variant=\"light\" color=\"red\" icon={<IconAlertCircle />}>\n <Text size=\"sm\">{error}</Text>\n </Alert>\n )}\n\n {!isResetPasswordAllowed ? (\n <>\n <Alert\n variant=\"light\"\n color=\"yellow\"\n icon={<IconAlertCircle />}\n >\n <Text size=\"sm\">{tr(\"resetPasswordDisabled\")}</Text>\n </Alert>\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n >\n {tr(\"resetPasswordBackToSignIn\")}\n </ActionButton>\n </>\n ) : resetState.step === \"email\" ? (\n <form {...emailForm.props}>\n <Stack flex={1} gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Text size=\"sm\" c=\"dimmed\">\n {tr(\"resetPasswordEnterEmail\")}\n </Text>\n <Control\n title={tr(\"resetPasswordEmail\")}\n input={emailForm.input.email}\n icon={<IconMail />}\n text={{\n autoComplete: \"email\",\n autoFocus: true,\n disabled: !isResetPasswordAllowed,\n }}\n />\n <ActionButton\n form={emailForm}\n disabled={!isResetPasswordAllowed}\n >\n {tr(\"resetPasswordSendCode\")}\n </ActionButton>\n </Stack>\n </form>\n ) : resetState.step === \"code\" ? (\n <Stack gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Alert variant=\"light\" color=\"blue\" icon={<IconInfoCircle />}>\n <Text size=\"sm\">{tr(\"resetPasswordCodeSent\")}</Text>\n </Alert>\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n {tr(\"resetPasswordEnterCode\")}\n </Text>\n <Flex justify=\"center\">\n <PinInput\n length={6}\n type=\"number\"\n autoFocus\n oneTimeCode\n onComplete={handleCodeComplete}\n aria-label=\"Password reset verification code\"\n />\n </Flex>\n <ActionButton\n variant=\"subtle\"\n onClick={handleResendCode}\n loading={isSubmitting}\n >\n {tr(\"resetPasswordResendCode\")}\n </ActionButton>\n </Stack>\n ) : resetState.step === \"password\" ? (\n <form {...passwordForm.props}>\n <Stack flex={1} gap={\"md\"}>\n <Text size=\"lg\" fw={500} ta=\"center\">\n {tr(\"resetPasswordTitle\")}\n </Text>\n <Text size=\"sm\" c=\"dimmed\">\n {tr(\"resetPasswordEnterNewPassword\")}\n </Text>\n <Control\n title={tr(\"resetPasswordNewPassword\")}\n input={passwordForm.input.password}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n autoFocus: true,\n }}\n />\n <Control\n title={tr(\"resetPasswordConfirmPassword\")}\n input={passwordForm.input.confirmPassword}\n icon={<IconLock />}\n password={{\n autoComplete: \"new-password\",\n }}\n />\n <ActionButton form={passwordForm}>\n {tr(\"resetPasswordSetNewPassword\")}\n </ActionButton>\n </Stack>\n </form>\n ) : (\n <>\n <Alert variant=\"light\" color=\"green\" icon={<IconCheck />}>\n <Text size=\"sm\">{tr(\"resetPasswordSuccess\")}</Text>\n </Alert>\n <ActionButton\n href={router.path(\"login\", {\n query: { realm: props.realmConfig.realmName },\n })}\n >\n {tr(\"resetPasswordBackToSignIn\")}\n </ActionButton>\n </>\n )}\n </Stack>\n </Card>\n <ActionButton variant={\"subtle\"} href={redirect}>\n {tr(\"resetPasswordCancel\")}\n </ActionButton>\n </Stack>\n </Flex>\n );\n};\n\nexport default ResetPassword;\n"],"mappings":";;;;;;;;;;;;;AA8CA,MAAM,iBAAiB,UAA8B;CACnD,MAAM,SAAS,WAAuB;CACtC,MAAM,WAAW,WAA2B;CAC5C,MAAM,EAAE,OAAO,SAAyB;CACxC,MAAM,CAAC,YAAY,iBAAiB,SAAqB,EAAE,MAAM,SAAS,CAAC;CAC3E,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,WAAW,OAAO,MAAM,KAAK;CAEnC,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,yBAAyB,UAAU,yBAAyB;CAGlE,MAAM,YAAY,QAAQ;EACxB,QAAQ;EACR,SAAS,OAAO,SAAS;AACvB,YAAS,KAAK;AAMd,iBAAc;IACZ,MAAM;IACN,QAPa,MAAM,SAAS,0BAA0B;KACtD,OAAO,EAAE,eAAe,MAAM,YAAY,WAAW;KACrD,MAAM,EAAE,OAAO,KAAK,OAAO;KAC5B,CAAC;IAKA,OAAO,KAAK;IACb,CAAC;;EAEL,CAAC;CAGF,MAAM,eAAe,QACnB;EACE,QAAQ,EAAE,OAAO;GACf,UAAU,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC;GACpC,iBAAiB,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC;GAC5C,CAAC;EACF,SAAS,OAAO,SAAS;AACvB,OAAI,KAAK,aAAa,KAAK,gBACzB,OAAM,IAAI,YAAY,yBAAyB;AAGjD,OAAI,CAAC,WAAW,UAAU,CAAC,WAAW,KACpC,OAAM,IAAI,YAAY,sBAAsB;AAG9C,SAAM,SAAS,sBAAsB,EACnC,MAAM;IACJ,UAAU,WAAW,OAAO;IAC5B,MAAM,WAAW;IACjB,aAAa,KAAK;IACnB,EACF,CAAC;AAEF,iBAAc,EAAE,MAAM,WAAW,CAAC;;EAErC,EACD,CAAC,WAAW,QAAQ,WAAW,KAAK,CACrC;CAED,MAAM,sBAAsB,UAAkB;AAC5C,MAAI,MAAM,WAAW,EACnB,gBAAe,UAAU;GACvB,GAAG;GACH,MAAM;GACN,MAAM;GACP,EAAE;;CAIP,MAAM,mBAAmB,YAAY;AACnC,MAAI,CAAC,WAAW,MAAO;AAEvB,kBAAgB,KAAK;AACrB,WAAS,KAAK;AAEd,MAAI;GACF,MAAM,SAAS,MAAM,SAAS,0BAA0B;IACtD,OAAO,EAAE,eAAe,MAAM,YAAY,WAAW;IACrD,MAAM,EAAE,OAAO,WAAW,OAAO;IAClC,CAAC;AAEF,kBAAe,UAAU;IACvB,GAAG;IACH;IACD,EAAE;WACI,KAAK;AACZ,YAAS,eAAe,QAAQ,IAAI,UAAU,wBAAwB;YAC9D;AACR,mBAAgB,MAAM;;;AAI1B,QACE,oBAAC;EAAK,MAAM;EAAG,SAAS;EAAU,OAAO;YACvC,qBAAC;GAAM,KAAK;GAAM,GAAG;cACnB,oBAAC;IAAK;IAAW,GAAG;IAAM,IAAI;cAC5B,qBAAC;KAAM,KAAK;;OAER,SAAS,WACT,SAAS,eACT,SAAS,gBACT,qBAAC;OAAM,KAAK;OAAM,OAAM;OAAS,IAAG;;QACjC,SAAS,WACR,oBAAC;SACC,KAAK,SAAS;SACd,KAAK,SAAS,eAAe,MAAM,YAAY;SAC/C,GAAG;SACH,GAAE;SACF,KAAI;UACJ;QAEH,SAAS,eACR,oBAAC;SAAM,OAAO;SAAG,IAAG;mBACjB,SAAS;UACJ;QAET,SAAS,eACR,oBAAC;SAAK,MAAK;SAAK,GAAE;SAAS,IAAG;mBAC3B,SAAS;UACL;;QAEH;MAGT,SACC,oBAAC;OAAM,SAAQ;OAAQ,OAAM;OAAM,MAAM,oBAAC,oBAAkB;iBAC1D,oBAAC;QAAK,MAAK;kBAAM;SAAa;QACxB;MAGT,CAAC,yBACA,4CACE,oBAAC;OACC,SAAQ;OACR,OAAM;OACN,MAAM,oBAAC,oBAAkB;iBAEzB,oBAAC;QAAK,MAAK;kBAAM,GAAG,wBAAwB;SAAQ;QAC9C,EACR,oBAAC;OACC,MAAM,OAAO,KAAK,SAAS,EACzB,OAAO,EAAE,OAAO,MAAM,YAAY,WAAW,EAC9C,CAAC;iBAED,GAAG,4BAA4B;QACnB,IACd,GACD,WAAW,SAAS,UACtB,oBAAC;OAAK,GAAI,UAAU;iBAClB,qBAAC;QAAM,MAAM;QAAG,KAAK;;SACnB,oBAAC;UAAK,MAAK;UAAK,IAAI;UAAK,IAAG;oBACzB,GAAG,qBAAqB;WACpB;SACP,oBAAC;UAAK,MAAK;UAAK,GAAE;oBACf,GAAG,0BAA0B;WACzB;SACP,oBAAC;UACC,OAAO,GAAG,qBAAqB;UAC/B,OAAO,UAAU,MAAM;UACvB,MAAM,oBAAC,aAAW;UAClB,MAAM;WACJ,cAAc;WACd,WAAW;WACX,UAAU,CAAC;WACZ;WACD;SACF,oBAAC;UACC,MAAM;UACN,UAAU,CAAC;oBAEV,GAAG,wBAAwB;WACf;;SACT;QACH,GACL,WAAW,SAAS,SACtB,qBAAC;OAAM,KAAK;;QACV,oBAAC;SAAK,MAAK;SAAK,IAAI;SAAK,IAAG;mBACzB,GAAG,qBAAqB;UACpB;QACP,oBAAC;SAAM,SAAQ;SAAQ,OAAM;SAAO,MAAM,oBAAC,mBAAiB;mBAC1D,oBAAC;UAAK,MAAK;oBAAM,GAAG,wBAAwB;WAAQ;UAC9C;QACR,oBAAC;SAAK,MAAK;SAAK,GAAE;SAAS,IAAG;mBAC3B,GAAG,yBAAyB;UACxB;QACP,oBAAC;SAAK,SAAQ;mBACZ,oBAAC;UACC,QAAQ;UACR,MAAK;UACL;UACA;UACA,YAAY;UACZ,cAAW;WACX;UACG;QACP,oBAAC;SACC,SAAQ;SACR,SAAS;SACT,SAAS;mBAER,GAAG,0BAA0B;UACjB;;QACT,GACN,WAAW,SAAS,aACtB,oBAAC;OAAK,GAAI,aAAa;iBACrB,qBAAC;QAAM,MAAM;QAAG,KAAK;;SACnB,oBAAC;UAAK,MAAK;UAAK,IAAI;UAAK,IAAG;oBACzB,GAAG,qBAAqB;WACpB;SACP,oBAAC;UAAK,MAAK;UAAK,GAAE;oBACf,GAAG,gCAAgC;WAC/B;SACP,oBAAC;UACC,OAAO,GAAG,2BAA2B;UACrC,OAAO,aAAa,MAAM;UAC1B,MAAM,oBAAC,aAAW;UAClB,UAAU;WACR,cAAc;WACd,WAAW;WACZ;WACD;SACF,oBAAC;UACC,OAAO,GAAG,+BAA+B;UACzC,OAAO,aAAa,MAAM;UAC1B,MAAM,oBAAC,aAAW;UAClB,UAAU,EACR,cAAc,gBACf;WACD;SACF,oBAAC;UAAa,MAAM;oBACjB,GAAG,8BAA8B;WACrB;;SACT;QACH,GAEP,4CACE,oBAAC;OAAM,SAAQ;OAAQ,OAAM;OAAQ,MAAM,oBAAC,cAAY;iBACtD,oBAAC;QAAK,MAAK;kBAAM,GAAG,uBAAuB;SAAQ;QAC7C,EACR,oBAAC;OACC,MAAM,OAAO,KAAK,SAAS,EACzB,OAAO,EAAE,OAAO,MAAM,YAAY,WAAW,EAC9C,CAAC;iBAED,GAAG,4BAA4B;QACnB,IACd;;MAEC;KACH,EACP,oBAAC;IAAa,SAAS;IAAU,MAAM;cACpC,GAAG,sBAAsB;KACb;IACT;GACH;;AAIX,4BAAe"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -5,11 +5,11 @@ import * as _alepha_react_router0 from "@alepha/react/router";
|
|
|
5
5
|
import * as alepha_server_links0 from "alepha/server/links";
|
|
6
6
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
7
7
|
import { ReactNode } from "react";
|
|
8
|
-
import {
|
|
8
|
+
import { RealmConfig, RealmController } from "alepha/api/users";
|
|
9
9
|
|
|
10
10
|
//#region ../../src/auth/AuthRouter.d.ts
|
|
11
11
|
declare class AuthRouter {
|
|
12
|
-
protected readonly
|
|
12
|
+
protected readonly realmClient: alepha_server_links0.HttpVirtualClient<RealmController>;
|
|
13
13
|
protected readonly auth: ReactAuth;
|
|
14
14
|
layout: _alepha_react_router0.PagePrimitive<_alepha_react_router0.PageConfigSchema, any, _alepha_react_router0.TPropsParentDefault>;
|
|
15
15
|
login: _alepha_react_router0.PagePrimitive<{
|
|
@@ -19,6 +19,7 @@ declare class AuthRouter {
|
|
|
19
19
|
}>;
|
|
20
20
|
}, {
|
|
21
21
|
realmConfig: {
|
|
22
|
+
realmName: string;
|
|
22
23
|
settings: {
|
|
23
24
|
description?: string | undefined;
|
|
24
25
|
displayName?: string | undefined;
|
|
@@ -28,6 +29,7 @@ declare class AuthRouter {
|
|
|
28
29
|
emailRequired: boolean;
|
|
29
30
|
usernameEnabled: boolean;
|
|
30
31
|
usernameRequired: boolean;
|
|
32
|
+
usernameRegExp: string;
|
|
31
33
|
phoneEnabled: boolean;
|
|
32
34
|
phoneRequired: boolean;
|
|
33
35
|
verifyEmailRequired: boolean;
|
|
@@ -43,7 +45,6 @@ declare class AuthRouter {
|
|
|
43
45
|
requireSpecialCharacters: boolean;
|
|
44
46
|
};
|
|
45
47
|
};
|
|
46
|
-
realmName: string;
|
|
47
48
|
authenticationMethods: {
|
|
48
49
|
type: "OAUTH2" | "OIDC" | "CREDENTIALS";
|
|
49
50
|
name: string;
|
|
@@ -57,6 +58,7 @@ declare class AuthRouter {
|
|
|
57
58
|
}>;
|
|
58
59
|
}, {
|
|
59
60
|
realmConfig: {
|
|
61
|
+
realmName: string;
|
|
60
62
|
settings: {
|
|
61
63
|
description?: string | undefined;
|
|
62
64
|
displayName?: string | undefined;
|
|
@@ -66,6 +68,7 @@ declare class AuthRouter {
|
|
|
66
68
|
emailRequired: boolean;
|
|
67
69
|
usernameEnabled: boolean;
|
|
68
70
|
usernameRequired: boolean;
|
|
71
|
+
usernameRegExp: string;
|
|
69
72
|
phoneEnabled: boolean;
|
|
70
73
|
phoneRequired: boolean;
|
|
71
74
|
verifyEmailRequired: boolean;
|
|
@@ -81,7 +84,6 @@ declare class AuthRouter {
|
|
|
81
84
|
requireSpecialCharacters: boolean;
|
|
82
85
|
};
|
|
83
86
|
};
|
|
84
|
-
realmName: string;
|
|
85
87
|
authenticationMethods: {
|
|
86
88
|
type: "OAUTH2" | "OIDC" | "CREDENTIALS";
|
|
87
89
|
name: string;
|
|
@@ -95,6 +97,7 @@ declare class AuthRouter {
|
|
|
95
97
|
}>;
|
|
96
98
|
}, {
|
|
97
99
|
realmConfig: {
|
|
100
|
+
realmName: string;
|
|
98
101
|
settings: {
|
|
99
102
|
description?: string | undefined;
|
|
100
103
|
displayName?: string | undefined;
|
|
@@ -104,6 +107,7 @@ declare class AuthRouter {
|
|
|
104
107
|
emailRequired: boolean;
|
|
105
108
|
usernameEnabled: boolean;
|
|
106
109
|
usernameRequired: boolean;
|
|
110
|
+
usernameRegExp: string;
|
|
107
111
|
phoneEnabled: boolean;
|
|
108
112
|
phoneRequired: boolean;
|
|
109
113
|
verifyEmailRequired: boolean;
|
|
@@ -119,7 +123,6 @@ declare class AuthRouter {
|
|
|
119
123
|
requireSpecialCharacters: boolean;
|
|
120
124
|
};
|
|
121
125
|
};
|
|
122
|
-
realmName: string;
|
|
123
126
|
authenticationMethods: {
|
|
124
127
|
type: "OAUTH2" | "OIDC" | "CREDENTIALS";
|
|
125
128
|
name: string;
|
|
@@ -133,7 +136,8 @@ declare class AuthRouter {
|
|
|
133
136
|
}>;
|
|
134
137
|
}, any, _alepha_react_router0.TPropsParentDefault>;
|
|
135
138
|
logout: _alepha_react_router0.PagePrimitive<_alepha_react_router0.PageConfigSchema, {}, _alepha_react_router0.TPropsParentDefault>;
|
|
136
|
-
protected loadRealmConfig(
|
|
139
|
+
protected loadRealmConfig(realmName?: string): Promise<{
|
|
140
|
+
realmName: string;
|
|
137
141
|
settings: {
|
|
138
142
|
description?: string | undefined;
|
|
139
143
|
displayName?: string | undefined;
|
|
@@ -143,6 +147,7 @@ declare class AuthRouter {
|
|
|
143
147
|
emailRequired: boolean;
|
|
144
148
|
usernameEnabled: boolean;
|
|
145
149
|
usernameRequired: boolean;
|
|
150
|
+
usernameRegExp: string;
|
|
146
151
|
phoneEnabled: boolean;
|
|
147
152
|
phoneRequired: boolean;
|
|
148
153
|
verifyEmailRequired: boolean;
|
|
@@ -158,7 +163,6 @@ declare class AuthRouter {
|
|
|
158
163
|
requireSpecialCharacters: boolean;
|
|
159
164
|
};
|
|
160
165
|
};
|
|
161
|
-
realmName: string;
|
|
162
166
|
authenticationMethods: {
|
|
163
167
|
type: "OAUTH2" | "OIDC" | "CREDENTIALS";
|
|
164
168
|
name: string;
|
|
@@ -169,43 +173,43 @@ declare class AuthRouter {
|
|
|
169
173
|
//#region ../../src/auth/components/buttons/UserButton.d.ts
|
|
170
174
|
interface UserButtonProps extends Omit<ActionProps, "menu" | "icon" | "onClick"> {
|
|
171
175
|
/**
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
* Additional menu items to display before the logout option
|
|
177
|
+
*/
|
|
174
178
|
menuItems?: ActionMenuItem[];
|
|
175
179
|
/**
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
* Custom logout label (default: "Sign out")
|
|
181
|
+
*/
|
|
178
182
|
logoutLabel?: string;
|
|
179
183
|
/**
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
* Menu configuration overrides
|
|
185
|
+
*/
|
|
182
186
|
menuConfig?: Partial<Omit<ActionMenuConfig, "items">>;
|
|
183
187
|
/**
|
|
184
|
-
|
|
185
|
-
|
|
188
|
+
* Whether to show a divider before logout (default: true when menuItems provided)
|
|
189
|
+
*/
|
|
186
190
|
showLogoutDivider?: boolean;
|
|
187
191
|
/**
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
* Custom icon to use instead of user avatar (default: IconUser)
|
|
193
|
+
*/
|
|
190
194
|
icon?: ReactNode;
|
|
191
195
|
}
|
|
192
196
|
declare const UserButton: (props: UserButtonProps) => react_jsx_runtime0.JSX.Element;
|
|
193
197
|
//#endregion
|
|
194
198
|
//#region ../../src/auth/components/Login.d.ts
|
|
195
199
|
interface LoginProps {
|
|
196
|
-
realmConfig:
|
|
200
|
+
realmConfig: RealmConfig;
|
|
197
201
|
}
|
|
198
202
|
declare const Login: (props: LoginProps) => react_jsx_runtime0.JSX.Element;
|
|
199
203
|
//#endregion
|
|
200
204
|
//#region ../../src/auth/components/Register.d.ts
|
|
201
205
|
interface RegisterProps {
|
|
202
|
-
realmConfig:
|
|
206
|
+
realmConfig: RealmConfig;
|
|
203
207
|
}
|
|
204
208
|
declare const Register: (props: RegisterProps) => react_jsx_runtime0.JSX.Element;
|
|
205
209
|
//#endregion
|
|
206
210
|
//#region ../../src/auth/components/ResetPassword.d.ts
|
|
207
211
|
interface ResetPasswordProps {
|
|
208
|
-
realmConfig:
|
|
212
|
+
realmConfig: RealmConfig;
|
|
209
213
|
}
|
|
210
214
|
declare const ResetPassword: (props: ResetPasswordProps) => react_jsx_runtime0.JSX.Element;
|
|
211
215
|
//#endregion
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/auth/AuthRouter.ts","../../src/auth/components/buttons/UserButton.tsx","../../src/auth/components/Login.tsx","../../src/auth/components/Register.tsx","../../src/auth/components/ResetPassword.tsx","../../src/auth/components/VerifyEmail.tsx","../../src/auth/index.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/auth/AuthRouter.ts","../../src/auth/components/buttons/UserButton.tsx","../../src/auth/components/Login.tsx","../../src/auth/components/Register.tsx","../../src/auth/components/ResetPassword.tsx","../../src/auth/components/VerifyEmail.tsx","../../src/auth/index.ts"],"mappings":";;;;;;;;;;cAuBa,UAAA;EAAA,mBAAA,WAAA,EACmB,oBAAA,CAAA,iBAAA,CAAA,eAAA;EAAA,mBAAA,IAAA,EACP,SAAA;EAAA,MAAA,EAEjB,qBAAA,CAAA,aAAA,CAFiB,qBAAA,CAEjB,gBAAA,OAAA,qBAAA,CAAA,mBAAA;EAAA,KAAA,wBAYD,aAAA;IAAA,KAAA;2BAZC,OAAA,CAAA,OAAA;MAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA6BE,aAAA;IAAA,KAAA;2BAjBH,OAAA,CAAA,OAAA;MAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uCAkCQ,aAAA;IAAA,KAAA;2BAjBL,OAAA,CAAA,OAAA;MAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCAkCG,aAAA;IAAA,KAAA;+BAjBE,OAAA,CAAA,OAAA;MAAA,KAAA;;;UA+BP,qBAAA,CAAA,aAAA,CAdK,qBAAA,CAcL,gBAAA,MAAA,qBAAA,CAAA,mBAAA;EAAA,UAAA,gBAAA,SAAA,YAa4C,OAAA;IAAA,SAAA;IAAA,QAAA;MAAA,WAAA;MAAA,WAAA;MAAA,OAAA;MAAA,mBAAA;MAAA,YAAA;MAAA,aAAA;MAAA,eAAA;MAAA,gBAAA;MAAA,cAAA;MAAA,YAAA;MAAA,aAAA;MAAA,mBAAA;MAAA,mBAAA;MAAA,wBAAA;MAAA,yBAAA;MAAA,oBAAA;MAAA,cAAA;QAAA,SAAA;QAAA,gBAAA;QAAA,gBAAA;QAAA,cAAA;QAAA,wBAAA;MAAA;IAAA;IAAA,qBAAA;MAAA,IAAA;MAAA,IAAA;IAAA;EAAA;AAAA;;;UCvGnC,eAAA,SACP,IAAA,CAAK,WAAA;EAAA;;;EAAA,SAAA,GAID,cAAA;EAAA;;;EAAA,WAAA;EAAA;;;EAAA,UAAA,GAUC,OAAA,CAAQ,IAAA,CAAK,gBAAA;EAAA;;;EAAA,iBAAA;EAAA;;;EAAA,IAAA,GAUnB,SAAA;AAAA;AAAA,cAGH,UAAA,GAAA,KAAA,EAAqB,eAAA,KAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UC1BzB,UAAA;EAAA,WAAA,EACF,WAAA;AAAA;AAAA,cAGT,KAAA,GAAA,KAAA,EAAgB,UAAA,KAAU,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UCgBf,aAAA;EAAA,WAAA,EACF,WAAA;AAAA;AAAA,cAcT,QAAA,GAAA,KAAA,EAAmB,aAAA,KAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UClBrB,kBAAA;EAAA,WAAA,EACF,WAAA;AAAA;AAAA,cAYT,aAAA,GAAA,KAAA,EAAwB,kBAAA,KAAkB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;KCnCpC,gBAAA;AAAA,cAIN,WAAA,GAAA,MAAA,EAAuB,gBAAA,KAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;ACS7C;;;;cAAa,YAAA,EAAY,OAAA,CAAA,OAAA,CAGvB,OAAA,CAHuB,MAAA"}
|
package/dist/auth/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./IconGoogle-Cm5d8J3f.js";
|
|
2
2
|
import { t as Login_default } from "./Login-Cjxv3EDi.js";
|
|
3
|
-
import { t as Register_default } from "./Register-
|
|
3
|
+
import { t as Register_default } from "./Register-CGlbQ50l.js";
|
|
4
4
|
import { t as ResetPassword_default } from "./ResetPassword-DvqD_1SJ.js";
|
|
5
5
|
import { t as VerifyEmail_default } from "./VerifyEmail-VaBruOnO.js";
|
|
6
6
|
import { AlephaReactAuth, ReactAuth, useAuth } from "@alepha/react/auth";
|
|
@@ -140,7 +140,7 @@ const realmQuerySchema = t.object({
|
|
|
140
140
|
realm: t.optional(t.string({ description: "User realm name for multi-tenant auth" }))
|
|
141
141
|
});
|
|
142
142
|
var AuthRouter = class {
|
|
143
|
-
|
|
143
|
+
realmClient = $client();
|
|
144
144
|
auth = $inject(ReactAuth);
|
|
145
145
|
layout = $page({
|
|
146
146
|
name: "AuthLayout",
|
|
@@ -172,7 +172,7 @@ var AuthRouter = class {
|
|
|
172
172
|
path: "/register",
|
|
173
173
|
schema: { query: realmQuerySchema },
|
|
174
174
|
can: () => !this.auth.user,
|
|
175
|
-
lazy: () => import("./Register-
|
|
175
|
+
lazy: () => import("./Register-CWdkXWkc.js"),
|
|
176
176
|
loader: async ({ query }) => {
|
|
177
177
|
return { realmConfig: await this.loadRealmConfig(query.realm) };
|
|
178
178
|
}
|
|
@@ -212,11 +212,11 @@ var AuthRouter = class {
|
|
|
212
212
|
return {};
|
|
213
213
|
}
|
|
214
214
|
});
|
|
215
|
-
async loadRealmConfig(
|
|
215
|
+
async loadRealmConfig(realmName) {
|
|
216
216
|
try {
|
|
217
|
-
return await this.
|
|
217
|
+
return await this.realmClient.getRealmConfig({ query: { realmName } });
|
|
218
218
|
} catch (e) {
|
|
219
|
-
if (e instanceof AlephaError) throw new AlephaError("Missing
|
|
219
|
+
if (e instanceof AlephaError) throw new AlephaError("Missing Realm Configuration - Did you forget to add '$realm()' to your application?", e);
|
|
220
220
|
throw e;
|
|
221
221
|
}
|
|
222
222
|
}
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/auth/AuthI18n.ts","../../src/auth/AuthRouter.ts","../../src/auth/components/buttons/UserButton.tsx","../../src/auth/index.ts"],"sourcesContent":["import { $dictionary } from \"@alepha/react/i18n\";\n\nexport class AuthI18n {\n en = $dictionary({\n name: \"alepha.ui.auth.en\",\n lazy: () => ({\n default: {\n loginSignIn: \"Sign in\",\n loginContinueWith: \"Continue with $1\",\n loginOr: \"OR\",\n loginCancel: \"Cancel\",\n loginForgotPassword: \"Forgot password?\",\n loginNoAccount: \"Don't have an account?\",\n loginSignUp: \"Sign up\",\n loginUsername: \"Username\",\n loginEmail: \"Email\",\n loginPhone: \"Phone number\",\n loginPassword: \"Password\",\n registerCreateAccount: \"Create account\",\n registerContinueWith: \"Continue with $1\",\n registerOr: \"OR\",\n registerCancel: \"Cancel\",\n registerHaveAccount: \"Already have an account?\",\n registerSignIn: \"Sign in\",\n registerUsername: \"Username\",\n registerEmail: \"Email\",\n registerPhone: \"Phone number\",\n registerPassword: \"Password\",\n registerConfirmPassword: \"Confirm password\",\n registerDisabled:\n \"Registration is not available. Please contact your administrator.\",\n registerBackToSignIn: \"Back to sign in\",\n registerVerifyTitle: \"Verify your account\",\n registerVerifyDescription:\n \"Please enter the verification code(s) sent to you.\",\n registerEmailCode: \"Email verification code\",\n registerPhoneCode: \"Phone verification code\",\n registerVerifySubmit: \"Complete Registration\",\n registerVerifyBack: \"Back to registration\",\n resetPasswordTitle: \"Reset password\",\n resetPasswordEmail: \"Email\",\n resetPasswordEnterEmail:\n \"Enter your email address to reset your password\",\n resetPasswordSendCode: \"Send verification code\",\n resetPasswordCodeSent: \"We've sent a verification code to your email.\",\n resetPasswordEnterCode: \"Enter the 6-digit code\",\n resetPasswordResendCode: \"Resend code\",\n resetPasswordEnterNewPassword: \"Create your new password\",\n resetPasswordNewPassword: \"New password\",\n resetPasswordConfirmPassword: \"Confirm password\",\n resetPasswordSetNewPassword: \"Set new password\",\n resetPasswordSuccess: \"Your password has been reset successfully.\",\n resetPasswordBackToSignIn: \"Back to sign in\",\n resetPasswordCancel: \"Cancel\",\n resetPasswordDisabled:\n \"Password reset is not available. Please contact your administrator.\",\n verifyEmailTitle: \"Email Verification\",\n verifyEmailVerifying: \"Verifying your email...\",\n verifyEmailPleaseWait:\n \"Please wait while we verify your email address.\",\n verifyEmailSuccess: \"Your email has been verified successfully.\",\n verifyEmailFailed:\n \"Failed to verify your email. The link may have expired or is invalid.\",\n verifyEmailMissingParams:\n \"Invalid verification link. Email and token are required.\",\n verifyEmailSignIn: \"Sign in to your account\",\n verifyEmailBackToSignIn: \"Back to sign in\",\n },\n }),\n });\n\n fr = $dictionary({\n lazy: () => ({\n default: {\n loginSignIn: \"Se connecter\",\n loginContinueWith: \"Continuer avec $1\",\n loginOr: \"OU\",\n loginCancel: \"Annuler\",\n loginForgotPassword: \"Mot de passe oublié ?\",\n loginNoAccount: \"Vous n'avez pas de compte ?\",\n loginSignUp: \"S'inscrire\",\n loginUsername: \"Nom d'utilisateur\",\n loginEmail: \"E-mail\",\n loginPhone: \"Numéro de téléphone\",\n loginPassword: \"Mot de passe\",\n registerCreateAccount: \"Créer un compte\",\n registerContinueWith: \"Continuer avec $1\",\n registerOr: \"OU\",\n registerCancel: \"Annuler\",\n registerHaveAccount: \"Vous avez déjà un compte ?\",\n registerSignIn: \"Se connecter\",\n registerUsername: \"Nom d'utilisateur\",\n registerEmail: \"E-mail\",\n registerPhone: \"Numéro de téléphone\",\n registerPassword: \"Mot de passe\",\n registerConfirmPassword: \"Confirmer le mot de passe\",\n registerDisabled:\n \"L'inscription n'est pas disponible. Veuillez contacter votre administrateur.\",\n registerBackToSignIn: \"Retour à la connexion\",\n registerVerifyTitle: \"Vérifiez votre compte\",\n registerVerifyDescription:\n \"Veuillez entrer le(s) code(s) de vérification qui vous ont été envoyés.\",\n registerEmailCode: \"Code de vérification par e-mail\",\n registerPhoneCode: \"Code de vérification par téléphone\",\n registerVerifySubmit: \"Terminer l'inscription\",\n registerVerifyBack: \"Retour à l'inscription\",\n resetPasswordTitle: \"Réinitialiser le mot de passe\",\n resetPasswordEmail: \"E-mail\",\n resetPasswordEnterEmail:\n \"Entrez votre adresse e-mail pour réinitialiser votre mot de passe\",\n resetPasswordSendCode: \"Envoyer le code de vérification\",\n resetPasswordCodeSent:\n \"Nous avons envoyé un code de vérification à votre e-mail.\",\n resetPasswordEnterCode: \"Entrez le code à 6 chiffres\",\n resetPasswordResendCode: \"Renvoyer le code\",\n resetPasswordEnterNewPassword: \"Créez votre nouveau mot de passe\",\n resetPasswordNewPassword: \"Nouveau mot de passe\",\n resetPasswordConfirmPassword: \"Confirmer le mot de passe\",\n resetPasswordSetNewPassword: \"Définir le nouveau mot de passe\",\n resetPasswordSuccess:\n \"Votre mot de passe a été réinitialisé avec succès.\",\n resetPasswordBackToSignIn: \"Retour à la connexion\",\n resetPasswordCancel: \"Annuler\",\n resetPasswordDisabled:\n \"La réinitialisation du mot de passe n'est pas disponible. Veuillez contacter votre administrateur.\",\n verifyEmailTitle: \"Vérification de l'e-mail\",\n verifyEmailVerifying: \"Vérification de votre e-mail...\",\n verifyEmailPleaseWait:\n \"Veuillez patienter pendant que nous vérifions votre adresse e-mail.\",\n verifyEmailSuccess: \"Votre e-mail a été vérifié avec succès.\",\n verifyEmailFailed:\n \"Échec de la vérification de votre e-mail. Le lien a peut-être expiré ou est invalide.\",\n verifyEmailMissingParams:\n \"Lien de vérification invalide. L'e-mail et le jeton sont requis.\",\n verifyEmailSignIn: \"Se connecter à votre compte\",\n verifyEmailBackToSignIn: \"Retour à la connexion\",\n },\n }),\n });\n}\n","import { ReactAuth } from \"@alepha/react/auth\";\nimport { $page } from \"@alepha/react/router\";\nimport {\n IconLogin2,\n IconLogout2,\n IconMailCheck,\n IconPasswordUser,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport { $inject, AlephaError, t } from \"alepha\";\nimport type { UserRealmController } from \"alepha/api/users\";\nimport { $client } from \"alepha/server/links\";\n\n/**\n * Schema for realm query parameter used across auth pages.\n */\nconst realmQuerySchema = t.object({\n r: t.optional(t.string({ description: \"Redirect URL after authentication\" })),\n realm: t.optional(\n t.string({ description: \"User realm name for multi-tenant auth\" }),\n ),\n});\n\nexport class AuthRouter {\n protected readonly userRealmClient = $client<UserRealmController>();\n protected readonly auth = $inject(ReactAuth);\n\n layout = $page({\n name: \"AuthLayout\",\n path: \"/auth\",\n lazy: () => import(\"./components/AuthLayout.tsx\"),\n children: () => [\n this.login,\n this.register,\n this.resetPassword,\n this.verifyEmail,\n ],\n });\n\n login = $page({\n icon: IconLogin2,\n label: \"Sign In\",\n description: \"Sign in to your account\",\n path: \"/login\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/Login.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n register = $page({\n icon: IconUserPlus,\n label: \"Register\",\n description: \"Create a new account\",\n path: \"/register\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/Register.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n resetPassword = $page({\n icon: IconPasswordUser,\n label: \"Reset Password\",\n description: \"Reset your account password\",\n path: \"/reset-password\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/ResetPassword.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n verifyEmail = $page({\n icon: IconMailCheck,\n label: \"Verify Email\",\n description: \"Verify your email address\",\n path: \"/verify-email\",\n schema: {\n query: t.object({\n email: t.optional(t.string()),\n token: t.optional(t.string()),\n }),\n },\n lazy: () => import(\"./components/VerifyEmail.tsx\"),\n });\n\n logout = $page({\n icon: IconLogout2,\n label: \"Sign Out\",\n description: \"Sign out of your account\",\n can: () => !!this.auth.user,\n path: \"/logout\",\n component: () => null,\n loader: () => {\n this.auth.logout();\n return {};\n },\n });\n\n protected async loadRealmConfig(userRealmName?: string) {\n try {\n return await this.userRealmClient.getRealmConfig({\n query: { userRealmName },\n });\n } catch (e) {\n if (e instanceof AlephaError) {\n throw new AlephaError(\n \"Missing User-Realm Configuration - Did you forget to add '$userRealm()' to your application?\",\n e,\n );\n }\n throw e;\n }\n }\n}\n","import { useAuth } from \"@alepha/react/auth\";\nimport { useRouter } from \"@alepha/react/router\";\nimport {\n ActionButton,\n type ActionMenuConfig,\n type ActionMenuItem,\n type ActionProps,\n ui,\n} from \"@alepha/ui\";\nimport { Avatar } from \"@mantine/core\";\nimport { IconLogin2, IconLogout, IconUser } from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\nimport type { AuthRouter } from \"../../AuthRouter.ts\";\n\nexport interface UserButtonProps\n extends Omit<ActionProps, \"menu\" | \"icon\" | \"onClick\"> {\n /**\n * Additional menu items to display before the logout option\n */\n menuItems?: ActionMenuItem[];\n\n /**\n * Custom logout label (default: \"Sign out\")\n */\n logoutLabel?: string;\n\n /**\n * Menu configuration overrides\n */\n menuConfig?: Partial<Omit<ActionMenuConfig, \"items\">>;\n\n /**\n * Whether to show a divider before logout (default: true when menuItems provided)\n */\n showLogoutDivider?: boolean;\n\n /**\n * Custom icon to use instead of user avatar (default: IconUser)\n */\n icon?: ReactNode;\n}\n\nconst UserButton = (props: UserButtonProps) => {\n const {\n menuItems = [],\n logoutLabel = \"Sign out\",\n menuConfig,\n showLogoutDivider = menuItems.length > 0,\n icon,\n children,\n ...buttonProps\n } = props;\n\n buttonProps.variant ??= \"subtle\";\n\n const auth = useAuth<{\n username?: string;\n email?: string;\n picture?: string;\n }>();\n\n const authRouter = useRouter<AuthRouter>();\n\n if (!auth.user) {\n return (\n <ActionButton\n {...buttonProps}\n icon={IconLogin2}\n href={authRouter.path(\"login\")}\n />\n );\n }\n\n const userLabel = auth.user.username || auth.user.email;\n\n const items: ActionMenuItem[] = [];\n\n // Add user info label if available\n if (auth.user.email && auth.user.username) {\n items.push({\n type: \"label\",\n label: auth.user.email,\n });\n }\n\n // Add custom menu items\n items.push(...menuItems);\n\n // Add divider before logout if needed\n if (showLogoutDivider && items.length > 0) {\n items.push({ type: \"divider\" });\n }\n\n // Add logout item\n items.push({\n label: logoutLabel,\n icon: <IconLogout size={ui.sizes.icon.md} />,\n color: \"red\",\n onClick: () => auth.logout(),\n });\n\n // Use leftSection for Avatar (JSX element), icon prop for component types\n const hasAvatar = !icon && auth.user.picture;\n\n return (\n <ActionButton\n {...buttonProps}\n icon={hasAvatar ? undefined : (icon ?? IconUser)}\n leftSection={\n hasAvatar ? (\n <Avatar\n src={`/api/files/${auth.user.picture}`}\n size={24}\n radius=\"xl\"\n />\n ) : undefined\n }\n menu={{\n position: \"bottom-end\",\n width: 200,\n ...menuConfig,\n items,\n }}\n >\n {children ?? userLabel}\n </ActionButton>\n );\n};\n\nexport default UserButton;\n","import { AlephaReactAuth } from \"@alepha/react/auth\";\nimport { AlephaReactI18n } from \"@alepha/react/i18n\";\nimport { AlephaUI } from \"@alepha/ui\";\nimport { $module } from \"alepha\";\nimport { AuthI18n } from \"./AuthI18n.ts\";\nimport { AuthRouter } from \"./AuthRouter.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./AuthRouter.ts\";\nexport type { UserButtonProps } from \"./components/buttons/UserButton.tsx\";\nexport { default as UserButton } from \"./components/buttons/UserButton.tsx\";\nexport { default as Login } from \"./components/Login.tsx\";\nexport { default as Register } from \"./components/Register.tsx\";\nexport { default as ResetPassword } from \"./components/ResetPassword.tsx\";\nexport { default as VerifyEmail } from \"./components/VerifyEmail.tsx\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Login UI Module\n *\n * @module alepha.ui.auth\n */\nexport const AlephaUIAuth = $module({\n name: \"alepha.ui.auth\",\n services: [AlephaUI, AlephaReactAuth, AlephaReactI18n, AuthRouter, AuthI18n],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/auth/AuthI18n.ts","../../src/auth/AuthRouter.ts","../../src/auth/components/buttons/UserButton.tsx","../../src/auth/index.ts"],"sourcesContent":["import { $dictionary } from \"@alepha/react/i18n\";\n\nexport class AuthI18n {\n en = $dictionary({\n name: \"alepha.ui.auth.en\",\n lazy: () => ({\n default: {\n loginSignIn: \"Sign in\",\n loginContinueWith: \"Continue with $1\",\n loginOr: \"OR\",\n loginCancel: \"Cancel\",\n loginForgotPassword: \"Forgot password?\",\n loginNoAccount: \"Don't have an account?\",\n loginSignUp: \"Sign up\",\n loginUsername: \"Username\",\n loginEmail: \"Email\",\n loginPhone: \"Phone number\",\n loginPassword: \"Password\",\n registerCreateAccount: \"Create account\",\n registerContinueWith: \"Continue with $1\",\n registerOr: \"OR\",\n registerCancel: \"Cancel\",\n registerHaveAccount: \"Already have an account?\",\n registerSignIn: \"Sign in\",\n registerUsername: \"Username\",\n registerEmail: \"Email\",\n registerPhone: \"Phone number\",\n registerPassword: \"Password\",\n registerConfirmPassword: \"Confirm password\",\n registerDisabled:\n \"Registration is not available. Please contact your administrator.\",\n registerBackToSignIn: \"Back to sign in\",\n registerVerifyTitle: \"Verify your account\",\n registerVerifyDescription:\n \"Please enter the verification code(s) sent to you.\",\n registerEmailCode: \"Email verification code\",\n registerPhoneCode: \"Phone verification code\",\n registerVerifySubmit: \"Complete Registration\",\n registerVerifyBack: \"Back to registration\",\n resetPasswordTitle: \"Reset password\",\n resetPasswordEmail: \"Email\",\n resetPasswordEnterEmail:\n \"Enter your email address to reset your password\",\n resetPasswordSendCode: \"Send verification code\",\n resetPasswordCodeSent: \"We've sent a verification code to your email.\",\n resetPasswordEnterCode: \"Enter the 6-digit code\",\n resetPasswordResendCode: \"Resend code\",\n resetPasswordEnterNewPassword: \"Create your new password\",\n resetPasswordNewPassword: \"New password\",\n resetPasswordConfirmPassword: \"Confirm password\",\n resetPasswordSetNewPassword: \"Set new password\",\n resetPasswordSuccess: \"Your password has been reset successfully.\",\n resetPasswordBackToSignIn: \"Back to sign in\",\n resetPasswordCancel: \"Cancel\",\n resetPasswordDisabled:\n \"Password reset is not available. Please contact your administrator.\",\n verifyEmailTitle: \"Email Verification\",\n verifyEmailVerifying: \"Verifying your email...\",\n verifyEmailPleaseWait:\n \"Please wait while we verify your email address.\",\n verifyEmailSuccess: \"Your email has been verified successfully.\",\n verifyEmailFailed:\n \"Failed to verify your email. The link may have expired or is invalid.\",\n verifyEmailMissingParams:\n \"Invalid verification link. Email and token are required.\",\n verifyEmailSignIn: \"Sign in to your account\",\n verifyEmailBackToSignIn: \"Back to sign in\",\n },\n }),\n });\n\n fr = $dictionary({\n lazy: () => ({\n default: {\n loginSignIn: \"Se connecter\",\n loginContinueWith: \"Continuer avec $1\",\n loginOr: \"OU\",\n loginCancel: \"Annuler\",\n loginForgotPassword: \"Mot de passe oublié ?\",\n loginNoAccount: \"Vous n'avez pas de compte ?\",\n loginSignUp: \"S'inscrire\",\n loginUsername: \"Nom d'utilisateur\",\n loginEmail: \"E-mail\",\n loginPhone: \"Numéro de téléphone\",\n loginPassword: \"Mot de passe\",\n registerCreateAccount: \"Créer un compte\",\n registerContinueWith: \"Continuer avec $1\",\n registerOr: \"OU\",\n registerCancel: \"Annuler\",\n registerHaveAccount: \"Vous avez déjà un compte ?\",\n registerSignIn: \"Se connecter\",\n registerUsername: \"Nom d'utilisateur\",\n registerEmail: \"E-mail\",\n registerPhone: \"Numéro de téléphone\",\n registerPassword: \"Mot de passe\",\n registerConfirmPassword: \"Confirmer le mot de passe\",\n registerDisabled:\n \"L'inscription n'est pas disponible. Veuillez contacter votre administrateur.\",\n registerBackToSignIn: \"Retour à la connexion\",\n registerVerifyTitle: \"Vérifiez votre compte\",\n registerVerifyDescription:\n \"Veuillez entrer le(s) code(s) de vérification qui vous ont été envoyés.\",\n registerEmailCode: \"Code de vérification par e-mail\",\n registerPhoneCode: \"Code de vérification par téléphone\",\n registerVerifySubmit: \"Terminer l'inscription\",\n registerVerifyBack: \"Retour à l'inscription\",\n resetPasswordTitle: \"Réinitialiser le mot de passe\",\n resetPasswordEmail: \"E-mail\",\n resetPasswordEnterEmail:\n \"Entrez votre adresse e-mail pour réinitialiser votre mot de passe\",\n resetPasswordSendCode: \"Envoyer le code de vérification\",\n resetPasswordCodeSent:\n \"Nous avons envoyé un code de vérification à votre e-mail.\",\n resetPasswordEnterCode: \"Entrez le code à 6 chiffres\",\n resetPasswordResendCode: \"Renvoyer le code\",\n resetPasswordEnterNewPassword: \"Créez votre nouveau mot de passe\",\n resetPasswordNewPassword: \"Nouveau mot de passe\",\n resetPasswordConfirmPassword: \"Confirmer le mot de passe\",\n resetPasswordSetNewPassword: \"Définir le nouveau mot de passe\",\n resetPasswordSuccess:\n \"Votre mot de passe a été réinitialisé avec succès.\",\n resetPasswordBackToSignIn: \"Retour à la connexion\",\n resetPasswordCancel: \"Annuler\",\n resetPasswordDisabled:\n \"La réinitialisation du mot de passe n'est pas disponible. Veuillez contacter votre administrateur.\",\n verifyEmailTitle: \"Vérification de l'e-mail\",\n verifyEmailVerifying: \"Vérification de votre e-mail...\",\n verifyEmailPleaseWait:\n \"Veuillez patienter pendant que nous vérifions votre adresse e-mail.\",\n verifyEmailSuccess: \"Votre e-mail a été vérifié avec succès.\",\n verifyEmailFailed:\n \"Échec de la vérification de votre e-mail. Le lien a peut-être expiré ou est invalide.\",\n verifyEmailMissingParams:\n \"Lien de vérification invalide. L'e-mail et le jeton sont requis.\",\n verifyEmailSignIn: \"Se connecter à votre compte\",\n verifyEmailBackToSignIn: \"Retour à la connexion\",\n },\n }),\n });\n}\n","import { ReactAuth } from \"@alepha/react/auth\";\nimport { $page } from \"@alepha/react/router\";\nimport {\n IconLogin2,\n IconLogout2,\n IconMailCheck,\n IconPasswordUser,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport { $inject, AlephaError, t } from \"alepha\";\nimport type { RealmController } from \"alepha/api/users\";\nimport { $client } from \"alepha/server/links\";\n\n/**\n * Schema for realm query parameter used across auth pages.\n */\nconst realmQuerySchema = t.object({\n r: t.optional(t.string({ description: \"Redirect URL after authentication\" })),\n realm: t.optional(\n t.string({ description: \"User realm name for multi-tenant auth\" }),\n ),\n});\n\nexport class AuthRouter {\n protected readonly realmClient = $client<RealmController>();\n protected readonly auth = $inject(ReactAuth);\n\n layout = $page({\n name: \"AuthLayout\",\n path: \"/auth\",\n lazy: () => import(\"./components/AuthLayout.tsx\"),\n children: () => [\n this.login,\n this.register,\n this.resetPassword,\n this.verifyEmail,\n ],\n });\n\n login = $page({\n icon: IconLogin2,\n label: \"Sign In\",\n description: \"Sign in to your account\",\n path: \"/login\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/Login.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n register = $page({\n icon: IconUserPlus,\n label: \"Register\",\n description: \"Create a new account\",\n path: \"/register\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/Register.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n resetPassword = $page({\n icon: IconPasswordUser,\n label: \"Reset Password\",\n description: \"Reset your account password\",\n path: \"/reset-password\",\n schema: {\n query: realmQuerySchema,\n },\n can: () => !this.auth.user,\n lazy: () => import(\"./components/ResetPassword.tsx\"),\n loader: async ({ query }) => {\n return {\n realmConfig: await this.loadRealmConfig(query.realm),\n };\n },\n });\n\n verifyEmail = $page({\n icon: IconMailCheck,\n label: \"Verify Email\",\n description: \"Verify your email address\",\n path: \"/verify-email\",\n schema: {\n query: t.object({\n email: t.optional(t.string()),\n token: t.optional(t.string()),\n }),\n },\n lazy: () => import(\"./components/VerifyEmail.tsx\"),\n });\n\n logout = $page({\n icon: IconLogout2,\n label: \"Sign Out\",\n description: \"Sign out of your account\",\n can: () => !!this.auth.user,\n path: \"/logout\",\n component: () => null,\n loader: () => {\n this.auth.logout();\n return {};\n },\n });\n\n protected async loadRealmConfig(realmName?: string) {\n try {\n return await this.realmClient.getRealmConfig({\n query: { realmName },\n });\n } catch (e) {\n if (e instanceof AlephaError) {\n throw new AlephaError(\n \"Missing Realm Configuration - Did you forget to add '$realm()' to your application?\",\n e,\n );\n }\n throw e;\n }\n }\n}\n","import { useAuth } from \"@alepha/react/auth\";\nimport { useRouter } from \"@alepha/react/router\";\nimport {\n ActionButton,\n type ActionMenuConfig,\n type ActionMenuItem,\n type ActionProps,\n ui,\n} from \"@alepha/ui\";\nimport { Avatar } from \"@mantine/core\";\nimport { IconLogin2, IconLogout, IconUser } from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\nimport type { AuthRouter } from \"../../AuthRouter.ts\";\n\nexport interface UserButtonProps\n extends Omit<ActionProps, \"menu\" | \"icon\" | \"onClick\"> {\n /**\n * Additional menu items to display before the logout option\n */\n menuItems?: ActionMenuItem[];\n\n /**\n * Custom logout label (default: \"Sign out\")\n */\n logoutLabel?: string;\n\n /**\n * Menu configuration overrides\n */\n menuConfig?: Partial<Omit<ActionMenuConfig, \"items\">>;\n\n /**\n * Whether to show a divider before logout (default: true when menuItems provided)\n */\n showLogoutDivider?: boolean;\n\n /**\n * Custom icon to use instead of user avatar (default: IconUser)\n */\n icon?: ReactNode;\n}\n\nconst UserButton = (props: UserButtonProps) => {\n const {\n menuItems = [],\n logoutLabel = \"Sign out\",\n menuConfig,\n showLogoutDivider = menuItems.length > 0,\n icon,\n children,\n ...buttonProps\n } = props;\n\n buttonProps.variant ??= \"subtle\";\n\n const auth = useAuth<{\n username?: string;\n email?: string;\n picture?: string;\n }>();\n\n const authRouter = useRouter<AuthRouter>();\n\n if (!auth.user) {\n return (\n <ActionButton\n {...buttonProps}\n icon={IconLogin2}\n href={authRouter.path(\"login\")}\n />\n );\n }\n\n const userLabel = auth.user.username || auth.user.email;\n\n const items: ActionMenuItem[] = [];\n\n // Add user info label if available\n if (auth.user.email && auth.user.username) {\n items.push({\n type: \"label\",\n label: auth.user.email,\n });\n }\n\n // Add custom menu items\n items.push(...menuItems);\n\n // Add divider before logout if needed\n if (showLogoutDivider && items.length > 0) {\n items.push({ type: \"divider\" });\n }\n\n // Add logout item\n items.push({\n label: logoutLabel,\n icon: <IconLogout size={ui.sizes.icon.md} />,\n color: \"red\",\n onClick: () => auth.logout(),\n });\n\n // Use leftSection for Avatar (JSX element), icon prop for component types\n const hasAvatar = !icon && auth.user.picture;\n\n return (\n <ActionButton\n {...buttonProps}\n icon={hasAvatar ? undefined : (icon ?? IconUser)}\n leftSection={\n hasAvatar ? (\n <Avatar\n src={`/api/files/${auth.user.picture}`}\n size={24}\n radius=\"xl\"\n />\n ) : undefined\n }\n menu={{\n position: \"bottom-end\",\n width: 200,\n ...menuConfig,\n items,\n }}\n >\n {children ?? userLabel}\n </ActionButton>\n );\n};\n\nexport default UserButton;\n","import { AlephaReactAuth } from \"@alepha/react/auth\";\nimport { AlephaReactI18n } from \"@alepha/react/i18n\";\nimport { AlephaUI } from \"@alepha/ui\";\nimport { $module } from \"alepha\";\nimport { AuthI18n } from \"./AuthI18n.ts\";\nimport { AuthRouter } from \"./AuthRouter.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./AuthRouter.ts\";\nexport type { UserButtonProps } from \"./components/buttons/UserButton.tsx\";\nexport { default as UserButton } from \"./components/buttons/UserButton.tsx\";\nexport { default as Login } from \"./components/Login.tsx\";\nexport { default as Register } from \"./components/Register.tsx\";\nexport { default as ResetPassword } from \"./components/ResetPassword.tsx\";\nexport { default as VerifyEmail } from \"./components/VerifyEmail.tsx\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Login UI Module\n *\n * @module alepha.ui.auth\n */\nexport const AlephaUIAuth = $module({\n name: \"alepha.ui.auth\",\n services: [AlephaUI, AlephaReactAuth, AlephaReactI18n, AuthRouter, AuthI18n],\n});\n"],"mappings":"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"}
|