@open-mercato/onboarding 0.6.6-develop.6184.1.b7e55f8d61 → 0.6.6-develop.6198.1.0822f97cc6
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.
|
@@ -16,6 +16,7 @@ import { Input } from "@open-mercato/ui/primitives/input";
|
|
|
16
16
|
import { EmailInput } from "@open-mercato/ui/primitives/email-input";
|
|
17
17
|
import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
|
|
18
18
|
import { Label } from "@open-mercato/ui/primitives/label";
|
|
19
|
+
import { Alert, AlertDescription } from "@open-mercato/ui/primitives/alert";
|
|
19
20
|
function OnboardingPageClient({ onboardingEnabled }) {
|
|
20
21
|
const t = useT();
|
|
21
22
|
const translate = (key, fallback, params) => translateWithFallback(t, key, fallback, params);
|
|
@@ -183,11 +184,11 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
183
184
|
/* @__PURE__ */ jsx(CardDescription, { children: translate("onboarding.subtitle", "Tell us a bit about you and we will set everything up.") })
|
|
184
185
|
] }) }),
|
|
185
186
|
/* @__PURE__ */ jsxs(CardContent, { className: onboardingDisabled ? "pb-10 opacity-45" : "pb-10", children: [
|
|
186
|
-
state === "success" && emailSubmitted && /* @__PURE__ */ jsxs("div", { className: "mb-6 rounded-md border border-
|
|
187
|
+
state === "success" && emailSubmitted && /* @__PURE__ */ jsxs("div", { className: "mb-6 rounded-md border border-status-success-border bg-status-success-bg px-4 py-3 text-sm text-status-success-text", role: "status", "aria-live": "polite", children: [
|
|
187
188
|
/* @__PURE__ */ jsx("strong", { className: "block text-sm font-medium", children: translate("onboarding.form.successTitle", "Check your inbox") }),
|
|
188
189
|
/* @__PURE__ */ jsx("p", { children: translate("onboarding.form.successBody", "We sent a verification link to {email}. Confirm it within 24 hours to activate your workspace.", { email: emailSubmitted }) })
|
|
189
190
|
] }),
|
|
190
|
-
state !== "success" && (globalError || verifyStatusError) && /* @__PURE__ */ jsx(
|
|
191
|
+
state !== "success" && (globalError || verifyStatusError) && /* @__PURE__ */ jsx(Alert, { status: "error", style: "lighter", showIcon: false, className: "mb-4 text-sm", "aria-live": "assertive", children: /* @__PURE__ */ jsx(AlertDescription, { children: globalError || verifyStatusError }) }),
|
|
191
192
|
/* @__PURE__ */ jsxs("form", { className: "grid gap-4", onSubmit, noValidate: true, children: [
|
|
192
193
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
193
194
|
/* @__PURE__ */ jsx(Label, { htmlFor: "email", children: translate("onboarding.form.email", "Work email") }),
|
|
@@ -199,11 +200,10 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
199
200
|
required: true,
|
|
200
201
|
disabled,
|
|
201
202
|
"aria-invalid": Boolean(fieldErrors.email),
|
|
202
|
-
"aria-describedby": fieldErrors.email ? "email-error" : void 0
|
|
203
|
-
className: fieldErrors.email ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
203
|
+
"aria-describedby": fieldErrors.email ? "email-error" : void 0
|
|
204
204
|
}
|
|
205
205
|
),
|
|
206
|
-
fieldErrors.email && /* @__PURE__ */ jsx("p", { id: "email-error", className: "text-xs text-
|
|
206
|
+
fieldErrors.email && /* @__PURE__ */ jsx("p", { id: "email-error", className: "text-xs text-status-error-text", children: fieldErrors.email })
|
|
207
207
|
] }),
|
|
208
208
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1 sm:grid-cols-2 sm:gap-4", children: [
|
|
209
209
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
@@ -218,11 +218,10 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
218
218
|
disabled,
|
|
219
219
|
autoComplete: "given-name",
|
|
220
220
|
"aria-invalid": Boolean(fieldErrors.firstName),
|
|
221
|
-
"aria-describedby": fieldErrors.firstName ? "firstName-error" : void 0
|
|
222
|
-
className: fieldErrors.firstName ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
221
|
+
"aria-describedby": fieldErrors.firstName ? "firstName-error" : void 0
|
|
223
222
|
}
|
|
224
223
|
),
|
|
225
|
-
fieldErrors.firstName && /* @__PURE__ */ jsx("p", { id: "firstName-error", className: "text-xs text-
|
|
224
|
+
fieldErrors.firstName && /* @__PURE__ */ jsx("p", { id: "firstName-error", className: "text-xs text-status-error-text", children: fieldErrors.firstName })
|
|
226
225
|
] }),
|
|
227
226
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
228
227
|
/* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: translate("onboarding.form.lastName", "Last name") }),
|
|
@@ -236,11 +235,10 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
236
235
|
disabled,
|
|
237
236
|
autoComplete: "family-name",
|
|
238
237
|
"aria-invalid": Boolean(fieldErrors.lastName),
|
|
239
|
-
"aria-describedby": fieldErrors.lastName ? "lastName-error" : void 0
|
|
240
|
-
className: fieldErrors.lastName ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
238
|
+
"aria-describedby": fieldErrors.lastName ? "lastName-error" : void 0
|
|
241
239
|
}
|
|
242
240
|
),
|
|
243
|
-
fieldErrors.lastName && /* @__PURE__ */ jsx("p", { id: "lastName-error", className: "text-xs text-
|
|
241
|
+
fieldErrors.lastName && /* @__PURE__ */ jsx("p", { id: "lastName-error", className: "text-xs text-status-error-text", children: fieldErrors.lastName })
|
|
244
242
|
] })
|
|
245
243
|
] }),
|
|
246
244
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
@@ -255,11 +253,10 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
255
253
|
disabled,
|
|
256
254
|
autoComplete: "organization",
|
|
257
255
|
"aria-invalid": Boolean(fieldErrors.organizationName),
|
|
258
|
-
"aria-describedby": fieldErrors.organizationName ? "organizationName-error" : void 0
|
|
259
|
-
className: fieldErrors.organizationName ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
256
|
+
"aria-describedby": fieldErrors.organizationName ? "organizationName-error" : void 0
|
|
260
257
|
}
|
|
261
258
|
),
|
|
262
|
-
fieldErrors.organizationName && /* @__PURE__ */ jsx("p", { id: "organizationName-error", className: "text-xs text-
|
|
259
|
+
fieldErrors.organizationName && /* @__PURE__ */ jsx("p", { id: "organizationName-error", className: "text-xs text-status-error-text", children: fieldErrors.organizationName })
|
|
263
260
|
] }),
|
|
264
261
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
265
262
|
/* @__PURE__ */ jsx(Label, { htmlFor: "password", children: translate("onboarding.form.password", "Password") }),
|
|
@@ -273,12 +270,11 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
273
270
|
autoComplete: "new-password",
|
|
274
271
|
minLength: passwordPolicy.minLength,
|
|
275
272
|
"aria-invalid": Boolean(fieldErrors.password),
|
|
276
|
-
"aria-describedby": fieldErrors.password ? "password-error" : void 0
|
|
277
|
-
className: fieldErrors.password ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
273
|
+
"aria-describedby": fieldErrors.password ? "password-error" : void 0
|
|
278
274
|
}
|
|
279
275
|
),
|
|
280
276
|
passwordDescription ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: passwordDescription }) : null,
|
|
281
|
-
fieldErrors.password && /* @__PURE__ */ jsx("p", { id: "password-error", className: "text-xs text-
|
|
277
|
+
fieldErrors.password && /* @__PURE__ */ jsx("p", { id: "password-error", className: "text-xs text-status-error-text", children: fieldErrors.password })
|
|
282
278
|
] }),
|
|
283
279
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
284
280
|
/* @__PURE__ */ jsx(Label, { htmlFor: "confirmPassword", children: translate("onboarding.form.confirmPassword", "Confirm password") }),
|
|
@@ -291,11 +287,10 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
291
287
|
disabled,
|
|
292
288
|
autoComplete: "new-password",
|
|
293
289
|
"aria-invalid": Boolean(fieldErrors.confirmPassword),
|
|
294
|
-
"aria-describedby": fieldErrors.confirmPassword ? "confirmPassword-error" : void 0
|
|
295
|
-
className: fieldErrors.confirmPassword ? "border-red-500 aria-invalid:ring-destructive" : void 0
|
|
290
|
+
"aria-describedby": fieldErrors.confirmPassword ? "confirmPassword-error" : void 0
|
|
296
291
|
}
|
|
297
292
|
),
|
|
298
|
-
fieldErrors.confirmPassword && /* @__PURE__ */ jsx("p", { id: "confirmPassword-error", className: "text-xs text-
|
|
293
|
+
fieldErrors.confirmPassword && /* @__PURE__ */ jsx("p", { id: "confirmPassword-error", className: "text-xs text-status-error-text", children: fieldErrors.confirmPassword })
|
|
299
294
|
] }),
|
|
300
295
|
/* @__PURE__ */ jsxs("label", { className: "flex items-start gap-3 text-sm text-muted-foreground", children: [
|
|
301
296
|
/* @__PURE__ */ jsx(
|
|
@@ -322,7 +317,7 @@ function OnboardingPageClient({ onboardingEnabled }) {
|
|
|
322
317
|
/* @__PURE__ */ jsx("a", { className: "underline hover:text-foreground", href: "/terms", target: "_blank", rel: "noreferrer", children: translate("onboarding.form.termsLink", "Terms of Service") }),
|
|
323
318
|
translate("onboarding.form.termsAnd", " and "),
|
|
324
319
|
/* @__PURE__ */ jsx("a", { className: "underline hover:text-foreground", href: "/privacy", target: "_blank", rel: "noreferrer", children: translate("onboarding.form.privacyLink", "Privacy Policy") }),
|
|
325
|
-
fieldErrors.termsAccepted && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-
|
|
320
|
+
fieldErrors.termsAccepted && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-status-error-text", children: fieldErrors.termsAccepted })
|
|
326
321
|
] })
|
|
327
322
|
] }),
|
|
328
323
|
/* @__PURE__ */ jsxs("label", { className: "flex items-start gap-3 text-sm text-muted-foreground", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/onboarding/frontend/onboarding/OnboardingPageClient.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { useState } from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { onboardingStartSchema } from '@open-mercato/onboarding/modules/onboarding/data/validators'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Label } from '@open-mercato/ui/primitives/label'\n\ntype SubmissionState = 'idle' | 'loading' | 'success'\ntype FieldErrors = Partial<Record<\n 'email' | 'firstName' | 'lastName' | 'organizationName' | 'password' | 'confirmPassword' | 'termsAccepted' | 'marketingConsent',\n string\n>>\n\ntype Props = {\n onboardingEnabled: boolean\n}\n\nexport default function OnboardingPageClient({ onboardingEnabled }: Props) {\n const t = useT()\n const translate = (key: string, fallback: string, params?: Record<string, string | number>) =>\n translateWithFallback(t, key, fallback, params)\n const locale = useLocale()\n const searchParams = useSearchParams()\n const [state, setState] = useState<SubmissionState>('idle')\n const [globalError, setGlobalError] = useState<string | null>(null)\n const [fieldErrors, setFieldErrors] = useState<FieldErrors>({})\n const [termsAccepted, setTermsAccepted] = useState(false)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailSubmitted, setEmailSubmitted] = useState<string | null>(null)\n const passwordPolicy = getPasswordPolicy()\n const passwordRequirements = formatPasswordRequirements(passwordPolicy, translate, 'onboarding.password.requirements')\n const passwordDescription = passwordRequirements\n ? translate(\n 'onboarding.password.requirements.help',\n 'Password requirements: {requirements}',\n { requirements: passwordRequirements },\n )\n : ''\n\n async function onSubmit(event: React.FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (!onboardingEnabled) return\n\n setState('loading')\n setGlobalError(null)\n setFieldErrors({})\n\n const form = new FormData(event.currentTarget)\n const payload = {\n email: String(form.get('email') ?? '').trim(),\n firstName: String(form.get('firstName') ?? '').trim(),\n lastName: String(form.get('lastName') ?? '').trim(),\n organizationName: String(form.get('organizationName') ?? '').trim(),\n password: String(form.get('password') ?? ''),\n confirmPassword: String(form.get('confirmPassword') ?? ''),\n termsAccepted,\n marketingConsent,\n locale,\n }\n\n const parsed = onboardingStartSchema.safeParse(payload)\n if (!parsed.success) {\n const issueMap: FieldErrors = {}\n parsed.error.issues.forEach((issue) => {\n const path = issue.path[0]\n if (!path) return\n switch (path) {\n case 'email':\n issueMap.email = translate('onboarding.errors.emailInvalid', 'Enter a valid work email.')\n break\n case 'firstName':\n issueMap.firstName = translate('onboarding.errors.firstNameRequired', 'First name is required.')\n break\n case 'lastName':\n issueMap.lastName = translate('onboarding.errors.lastNameRequired', 'Last name is required.')\n break\n case 'organizationName':\n issueMap.organizationName = translate('onboarding.errors.organizationNameRequired', 'Organization name is required.')\n break\n case 'password':\n issueMap.password = translate(\n 'onboarding.errors.passwordRequired',\n 'Password must meet the requirements: {requirements}.',\n { requirements: passwordRequirements },\n )\n break\n case 'confirmPassword':\n issueMap.confirmPassword = translate('onboarding.errors.passwordMismatch', 'Passwords must match.')\n break\n case 'termsAccepted':\n issueMap.termsAccepted = translate('onboarding.form.termsRequired', 'Please accept the terms to continue.')\n break\n default:\n break\n }\n })\n if (!issueMap.termsAccepted && !termsAccepted) {\n issueMap.termsAccepted = translate('onboarding.form.termsRequired', 'Please accept the terms to continue.')\n }\n setFieldErrors(issueMap)\n setState('idle')\n return\n }\n\n try {\n const call = await apiCall<{ ok?: boolean; error?: string; email?: string; fieldErrors?: Record<string, string> }>(\n '/api/onboarding/onboarding',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...parsed.data, termsAccepted: true, marketingConsent }),\n },\n )\n const data = call.result ?? {}\n if (!call.ok || data.ok === false) {\n if (data.fieldErrors && typeof data.fieldErrors === 'object') {\n const mapped: FieldErrors = {}\n for (const key of Object.keys(data.fieldErrors)) {\n const value = data.fieldErrors[key]\n if (typeof value === 'string' && value.trim()) {\n mapped[key as keyof FieldErrors] = value\n }\n }\n setFieldErrors(mapped)\n }\n const message = typeof data.error === 'string' && data.error.trim()\n ? data.error\n : translate('onboarding.form.genericError', 'Something went wrong. Please try again.')\n setGlobalError(message)\n setState('idle')\n return\n }\n setEmailSubmitted(data.email ?? parsed.data.email)\n setState('success')\n } catch (err) {\n const message = err instanceof Error ? err.message : ''\n setGlobalError(message || translate('onboarding.form.genericError', 'Something went wrong. Please try again.'))\n setState('idle')\n }\n }\n\n const onboardingDisabled = !onboardingEnabled\n const submitting = state === 'loading'\n const disabled = onboardingDisabled || submitting || state === 'success'\n const verifyStatusMessages: Record<string, string> = {\n redirect_misconfigured: translate(\n 'onboarding.verifyStatus.redirectMisconfigured',\n 'The verification link cannot redirect safely because APP_URL does not match the URL that handled the request. Check APP_URL and APP_ALLOWED_ORIGINS, then open the verification link again.',\n ),\n origin_not_allowed: translate(\n 'onboarding.verifyStatus.originNotAllowed',\n 'The verification link was opened from an origin that is not allowed. Check APP_URL and APP_ALLOWED_ORIGINS, then open the verification link again.',\n ),\n url_not_configured: translate(\n 'onboarding.verifyStatus.urlNotConfigured',\n 'Onboarding verification is not configured. APP_URL must be set before verification links can be used.',\n ),\n already_exists: translate(\n 'onboarding.verifyStatus.alreadyExists',\n 'We already have an account with this email. Try signing in or resetting your password.',\n ),\n invalid: translate(\n 'onboarding.verifyStatus.invalid',\n 'The verification link is invalid or expired. Submit the onboarding form again to receive a new link.',\n ),\n error: translate(\n 'onboarding.verifyStatus.error',\n 'We could not complete verification. Please try again or contact support.',\n ),\n }\n const verifyStatus = searchParams.get('status') ?? ''\n const verifyStatusError = verifyStatusMessages[verifyStatus] ?? null\n\n return (\n <div className=\"relative flex min-h-svh items-center justify-center bg-muted/50 px-4 pb-24\">\n <Card className=\"relative w-full max-w-lg overflow-hidden shadow-lg\">\n {onboardingDisabled ? (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-background/80 p-6 backdrop-blur-[2px]\">\n <div\n className=\"max-w-sm rounded-xl border border-border/70 bg-background/95 px-5 py-4 text-center shadow-sm\"\n role=\"alert\"\n aria-live=\"polite\"\n >\n <p className=\"text-base font-semibold text-foreground\">\n {translate('onboarding.disabled.title', 'Self-service onboarding is currently unavailable')}\n </p>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {translate(\n 'onboarding.disabled.description',\n 'Workspace creation is currently handled manually. Contact the Open Mercato team or your administrator to request access before continuing.',\n )}\n </p>\n <div className=\"mt-4 flex justify-center\">\n <Button asChild variant=\"outline\">\n <Link href=\"/login\">\n {translate('onboarding.disabled.cta', 'Go to login')}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n ) : null}\n <CardHeader className=\"flex flex-col gap-4 p-10 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <Image alt=\"Open Mercato\" src=\"/open-mercato.svg\" width={120} height={120} priority />\n <CardTitle className=\"text-2xl font-semibold\">\n {translate('onboarding.title', 'Create your Open Mercato workspace')}\n </CardTitle>\n <CardDescription>\n {translate('onboarding.subtitle', 'Tell us a bit about you and we will set everything up.')}\n </CardDescription>\n </div>\n </CardHeader>\n <CardContent className={onboardingDisabled ? 'pb-10 opacity-45' : 'pb-10'}>\n {state === 'success' && emailSubmitted && (\n <div className=\"mb-6 rounded-md border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900\" role=\"status\" aria-live=\"polite\">\n <strong className=\"block text-sm font-medium\">\n {translate('onboarding.form.successTitle', 'Check your inbox')}\n </strong>\n <p>\n {translate('onboarding.form.successBody', 'We sent a verification link to {email}. Confirm it within 24 hours to activate your workspace.', { email: emailSubmitted })}\n </p>\n </div>\n )}\n {state !== 'success' && (globalError || verifyStatusError) && (\n <div className=\"mb-4 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\" role=\"alert\" aria-live=\"assertive\">\n {globalError || verifyStatusError}\n </div>\n )}\n <form className=\"grid gap-4\" onSubmit={onSubmit} noValidate>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"email\">{translate('onboarding.form.email', 'Work email')}</Label>\n <EmailInput\n id=\"email\"\n name=\"email\"\n required\n disabled={disabled}\n aria-invalid={Boolean(fieldErrors.email)}\n aria-describedby={fieldErrors.email ? 'email-error' : undefined}\n className={fieldErrors.email ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {fieldErrors.email && (\n <p id=\"email-error\" className=\"text-xs text-red-600\">{fieldErrors.email}</p>\n )}\n </div>\n <div className=\"grid gap-1 sm:grid-cols-2 sm:gap-4\">\n <div className=\"grid gap-1\">\n <Label htmlFor=\"firstName\">{translate('onboarding.form.firstName', 'First name')}</Label>\n <Input\n id=\"firstName\"\n name=\"firstName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"given-name\"\n aria-invalid={Boolean(fieldErrors.firstName)}\n aria-describedby={fieldErrors.firstName ? 'firstName-error' : undefined}\n className={fieldErrors.firstName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {fieldErrors.firstName && (\n <p id=\"firstName-error\" className=\"text-xs text-red-600\">{fieldErrors.firstName}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"lastName\">{translate('onboarding.form.lastName', 'Last name')}</Label>\n <Input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"family-name\"\n aria-invalid={Boolean(fieldErrors.lastName)}\n aria-describedby={fieldErrors.lastName ? 'lastName-error' : undefined}\n className={fieldErrors.lastName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {fieldErrors.lastName && (\n <p id=\"lastName-error\" className=\"text-xs text-red-600\">{fieldErrors.lastName}</p>\n )}\n </div>\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"organizationName\">{translate('onboarding.form.organizationName', 'Organization name')}</Label>\n <Input\n id=\"organizationName\"\n name=\"organizationName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"organization\"\n aria-invalid={Boolean(fieldErrors.organizationName)}\n aria-describedby={fieldErrors.organizationName ? 'organizationName-error' : undefined}\n className={fieldErrors.organizationName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {fieldErrors.organizationName && (\n <p id=\"organizationName-error\" className=\"text-xs text-red-600\">{fieldErrors.organizationName}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"password\">{translate('onboarding.form.password', 'Password')}</Label>\n <PasswordInput\n id=\"password\"\n name=\"password\"\n required\n disabled={disabled}\n autoComplete=\"new-password\"\n minLength={passwordPolicy.minLength}\n aria-invalid={Boolean(fieldErrors.password)}\n aria-describedby={fieldErrors.password ? 'password-error' : undefined}\n className={fieldErrors.password ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {passwordDescription ? (\n <p className=\"text-xs text-muted-foreground\">{passwordDescription}</p>\n ) : null}\n {fieldErrors.password && (\n <p id=\"password-error\" className=\"text-xs text-red-600\">{fieldErrors.password}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"confirmPassword\">{translate('onboarding.form.confirmPassword', 'Confirm password')}</Label>\n <PasswordInput\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n required\n disabled={disabled}\n autoComplete=\"new-password\"\n aria-invalid={Boolean(fieldErrors.confirmPassword)}\n aria-describedby={fieldErrors.confirmPassword ? 'confirmPassword-error' : undefined}\n className={fieldErrors.confirmPassword ? 'border-red-500 aria-invalid:ring-destructive' : undefined}\n />\n {fieldErrors.confirmPassword && (\n <p id=\"confirmPassword-error\" className=\"text-xs text-red-600\">{fieldErrors.confirmPassword}</p>\n )}\n </div>\n <label className=\"flex items-start gap-3 text-sm text-muted-foreground\">\n <Checkbox\n id=\"terms\"\n checked={termsAccepted}\n disabled={disabled}\n onCheckedChange={(value: boolean | 'indeterminate') => {\n setTermsAccepted(value === true)\n if (value === true) {\n setFieldErrors((prev) => {\n const next = { ...prev }\n delete next.termsAccepted\n return next\n })\n }\n }}\n aria-invalid={Boolean(fieldErrors.termsAccepted)}\n />\n <span>\n {translate('onboarding.form.termsLabel', 'I have read and accept the ')}\n <a className=\"underline hover:text-foreground\" href=\"/terms\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.termsLink', 'Terms of Service')}\n </a>\n {translate('onboarding.form.termsAnd', ' and ')}\n <a className=\"underline hover:text-foreground\" href=\"/privacy\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.privacyLink', 'Privacy Policy')}\n </a>\n {fieldErrors.termsAccepted && (\n <span className=\"mt-1 block text-xs text-red-600\">{fieldErrors.termsAccepted}</span>\n )}\n </span>\n </label>\n <label className=\"flex items-start gap-3 text-sm text-muted-foreground\">\n <Checkbox\n id=\"marketingConsent\"\n checked={marketingConsent}\n disabled={disabled}\n onCheckedChange={(value: boolean | 'indeterminate') => {\n setMarketingConsent(value === true)\n }}\n />\n <span>\n {translate(\n 'onboarding.form.marketingLabel',\n \"I consent to receiving direct marketing from CT Tornado by email at the address I provide. I'm aware that I can withdraw my consent at any time (e.g., via the unsubscribe link), and that CT Tornado may also process my email address under its legitimate interests to manage its mailing list, keep proof of my consent, and record opt-outs. See our {termsLink} and {privacyLink}.\",\n )\n .split(/{termsLink}|{privacyLink}/)\n .map((part, index, parts) => (\n <span key={index}>\n {part}\n {index < parts.length - 1 && (\n index === 0 ? (\n <a className=\"underline hover:text-foreground\" href=\"/terms\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.termsLink', 'Terms of Service')}\n </a>\n ) : (\n <a className=\"underline hover:text-foreground\" href=\"/privacy\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.privacyLink', 'Privacy Policy')}\n </a>\n )\n )}\n </span>\n ))}\n </span>\n </label>\n <Button\n type=\"submit\"\n disabled={disabled}\n className=\"mt-2 h-11 bg-foreground text-background transition hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {submitting\n ? translate('onboarding.form.loading', 'Sending...')\n : translate('onboarding.form.submit', 'Send verification email')}\n </Button>\n </form>\n </CardContent>\n </Card>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { useState } from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { onboardingStartSchema } from '@open-mercato/onboarding/modules/onboarding/data/validators'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\n\ntype SubmissionState = 'idle' | 'loading' | 'success'\ntype FieldErrors = Partial<Record<\n 'email' | 'firstName' | 'lastName' | 'organizationName' | 'password' | 'confirmPassword' | 'termsAccepted' | 'marketingConsent',\n string\n>>\n\ntype Props = {\n onboardingEnabled: boolean\n}\n\nexport default function OnboardingPageClient({ onboardingEnabled }: Props) {\n const t = useT()\n const translate = (key: string, fallback: string, params?: Record<string, string | number>) =>\n translateWithFallback(t, key, fallback, params)\n const locale = useLocale()\n const searchParams = useSearchParams()\n const [state, setState] = useState<SubmissionState>('idle')\n const [globalError, setGlobalError] = useState<string | null>(null)\n const [fieldErrors, setFieldErrors] = useState<FieldErrors>({})\n const [termsAccepted, setTermsAccepted] = useState(false)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailSubmitted, setEmailSubmitted] = useState<string | null>(null)\n const passwordPolicy = getPasswordPolicy()\n const passwordRequirements = formatPasswordRequirements(passwordPolicy, translate, 'onboarding.password.requirements')\n const passwordDescription = passwordRequirements\n ? translate(\n 'onboarding.password.requirements.help',\n 'Password requirements: {requirements}',\n { requirements: passwordRequirements },\n )\n : ''\n\n async function onSubmit(event: React.FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (!onboardingEnabled) return\n\n setState('loading')\n setGlobalError(null)\n setFieldErrors({})\n\n const form = new FormData(event.currentTarget)\n const payload = {\n email: String(form.get('email') ?? '').trim(),\n firstName: String(form.get('firstName') ?? '').trim(),\n lastName: String(form.get('lastName') ?? '').trim(),\n organizationName: String(form.get('organizationName') ?? '').trim(),\n password: String(form.get('password') ?? ''),\n confirmPassword: String(form.get('confirmPassword') ?? ''),\n termsAccepted,\n marketingConsent,\n locale,\n }\n\n const parsed = onboardingStartSchema.safeParse(payload)\n if (!parsed.success) {\n const issueMap: FieldErrors = {}\n parsed.error.issues.forEach((issue) => {\n const path = issue.path[0]\n if (!path) return\n switch (path) {\n case 'email':\n issueMap.email = translate('onboarding.errors.emailInvalid', 'Enter a valid work email.')\n break\n case 'firstName':\n issueMap.firstName = translate('onboarding.errors.firstNameRequired', 'First name is required.')\n break\n case 'lastName':\n issueMap.lastName = translate('onboarding.errors.lastNameRequired', 'Last name is required.')\n break\n case 'organizationName':\n issueMap.organizationName = translate('onboarding.errors.organizationNameRequired', 'Organization name is required.')\n break\n case 'password':\n issueMap.password = translate(\n 'onboarding.errors.passwordRequired',\n 'Password must meet the requirements: {requirements}.',\n { requirements: passwordRequirements },\n )\n break\n case 'confirmPassword':\n issueMap.confirmPassword = translate('onboarding.errors.passwordMismatch', 'Passwords must match.')\n break\n case 'termsAccepted':\n issueMap.termsAccepted = translate('onboarding.form.termsRequired', 'Please accept the terms to continue.')\n break\n default:\n break\n }\n })\n if (!issueMap.termsAccepted && !termsAccepted) {\n issueMap.termsAccepted = translate('onboarding.form.termsRequired', 'Please accept the terms to continue.')\n }\n setFieldErrors(issueMap)\n setState('idle')\n return\n }\n\n try {\n const call = await apiCall<{ ok?: boolean; error?: string; email?: string; fieldErrors?: Record<string, string> }>(\n '/api/onboarding/onboarding',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...parsed.data, termsAccepted: true, marketingConsent }),\n },\n )\n const data = call.result ?? {}\n if (!call.ok || data.ok === false) {\n if (data.fieldErrors && typeof data.fieldErrors === 'object') {\n const mapped: FieldErrors = {}\n for (const key of Object.keys(data.fieldErrors)) {\n const value = data.fieldErrors[key]\n if (typeof value === 'string' && value.trim()) {\n mapped[key as keyof FieldErrors] = value\n }\n }\n setFieldErrors(mapped)\n }\n const message = typeof data.error === 'string' && data.error.trim()\n ? data.error\n : translate('onboarding.form.genericError', 'Something went wrong. Please try again.')\n setGlobalError(message)\n setState('idle')\n return\n }\n setEmailSubmitted(data.email ?? parsed.data.email)\n setState('success')\n } catch (err) {\n const message = err instanceof Error ? err.message : ''\n setGlobalError(message || translate('onboarding.form.genericError', 'Something went wrong. Please try again.'))\n setState('idle')\n }\n }\n\n const onboardingDisabled = !onboardingEnabled\n const submitting = state === 'loading'\n const disabled = onboardingDisabled || submitting || state === 'success'\n const verifyStatusMessages: Record<string, string> = {\n redirect_misconfigured: translate(\n 'onboarding.verifyStatus.redirectMisconfigured',\n 'The verification link cannot redirect safely because APP_URL does not match the URL that handled the request. Check APP_URL and APP_ALLOWED_ORIGINS, then open the verification link again.',\n ),\n origin_not_allowed: translate(\n 'onboarding.verifyStatus.originNotAllowed',\n 'The verification link was opened from an origin that is not allowed. Check APP_URL and APP_ALLOWED_ORIGINS, then open the verification link again.',\n ),\n url_not_configured: translate(\n 'onboarding.verifyStatus.urlNotConfigured',\n 'Onboarding verification is not configured. APP_URL must be set before verification links can be used.',\n ),\n already_exists: translate(\n 'onboarding.verifyStatus.alreadyExists',\n 'We already have an account with this email. Try signing in or resetting your password.',\n ),\n invalid: translate(\n 'onboarding.verifyStatus.invalid',\n 'The verification link is invalid or expired. Submit the onboarding form again to receive a new link.',\n ),\n error: translate(\n 'onboarding.verifyStatus.error',\n 'We could not complete verification. Please try again or contact support.',\n ),\n }\n const verifyStatus = searchParams.get('status') ?? ''\n const verifyStatusError = verifyStatusMessages[verifyStatus] ?? null\n\n return (\n <div className=\"relative flex min-h-svh items-center justify-center bg-muted/50 px-4 pb-24\">\n <Card className=\"relative w-full max-w-lg overflow-hidden shadow-lg\">\n {onboardingDisabled ? (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-background/80 p-6 backdrop-blur-[2px]\">\n <div\n className=\"max-w-sm rounded-xl border border-border/70 bg-background/95 px-5 py-4 text-center shadow-sm\"\n role=\"alert\"\n aria-live=\"polite\"\n >\n <p className=\"text-base font-semibold text-foreground\">\n {translate('onboarding.disabled.title', 'Self-service onboarding is currently unavailable')}\n </p>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {translate(\n 'onboarding.disabled.description',\n 'Workspace creation is currently handled manually. Contact the Open Mercato team or your administrator to request access before continuing.',\n )}\n </p>\n <div className=\"mt-4 flex justify-center\">\n <Button asChild variant=\"outline\">\n <Link href=\"/login\">\n {translate('onboarding.disabled.cta', 'Go to login')}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n ) : null}\n <CardHeader className=\"flex flex-col gap-4 p-10 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <Image alt=\"Open Mercato\" src=\"/open-mercato.svg\" width={120} height={120} priority />\n <CardTitle className=\"text-2xl font-semibold\">\n {translate('onboarding.title', 'Create your Open Mercato workspace')}\n </CardTitle>\n <CardDescription>\n {translate('onboarding.subtitle', 'Tell us a bit about you and we will set everything up.')}\n </CardDescription>\n </div>\n </CardHeader>\n <CardContent className={onboardingDisabled ? 'pb-10 opacity-45' : 'pb-10'}>\n {state === 'success' && emailSubmitted && (\n <div className=\"mb-6 rounded-md border border-status-success-border bg-status-success-bg px-4 py-3 text-sm text-status-success-text\" role=\"status\" aria-live=\"polite\">\n <strong className=\"block text-sm font-medium\">\n {translate('onboarding.form.successTitle', 'Check your inbox')}\n </strong>\n <p>\n {translate('onboarding.form.successBody', 'We sent a verification link to {email}. Confirm it within 24 hours to activate your workspace.', { email: emailSubmitted })}\n </p>\n </div>\n )}\n {state !== 'success' && (globalError || verifyStatusError) && (\n <Alert status=\"error\" style=\"lighter\" showIcon={false} className=\"mb-4 text-sm\" aria-live=\"assertive\">\n <AlertDescription>{globalError || verifyStatusError}</AlertDescription>\n </Alert>\n )}\n <form className=\"grid gap-4\" onSubmit={onSubmit} noValidate>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"email\">{translate('onboarding.form.email', 'Work email')}</Label>\n <EmailInput\n id=\"email\"\n name=\"email\"\n required\n disabled={disabled}\n aria-invalid={Boolean(fieldErrors.email)}\n aria-describedby={fieldErrors.email ? 'email-error' : undefined}\n />\n {fieldErrors.email && (\n <p id=\"email-error\" className=\"text-xs text-status-error-text\">{fieldErrors.email}</p>\n )}\n </div>\n <div className=\"grid gap-1 sm:grid-cols-2 sm:gap-4\">\n <div className=\"grid gap-1\">\n <Label htmlFor=\"firstName\">{translate('onboarding.form.firstName', 'First name')}</Label>\n <Input\n id=\"firstName\"\n name=\"firstName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"given-name\"\n aria-invalid={Boolean(fieldErrors.firstName)}\n aria-describedby={fieldErrors.firstName ? 'firstName-error' : undefined}\n />\n {fieldErrors.firstName && (\n <p id=\"firstName-error\" className=\"text-xs text-status-error-text\">{fieldErrors.firstName}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"lastName\">{translate('onboarding.form.lastName', 'Last name')}</Label>\n <Input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"family-name\"\n aria-invalid={Boolean(fieldErrors.lastName)}\n aria-describedby={fieldErrors.lastName ? 'lastName-error' : undefined}\n />\n {fieldErrors.lastName && (\n <p id=\"lastName-error\" className=\"text-xs text-status-error-text\">{fieldErrors.lastName}</p>\n )}\n </div>\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"organizationName\">{translate('onboarding.form.organizationName', 'Organization name')}</Label>\n <Input\n id=\"organizationName\"\n name=\"organizationName\"\n type=\"text\"\n required\n disabled={disabled}\n autoComplete=\"organization\"\n aria-invalid={Boolean(fieldErrors.organizationName)}\n aria-describedby={fieldErrors.organizationName ? 'organizationName-error' : undefined}\n />\n {fieldErrors.organizationName && (\n <p id=\"organizationName-error\" className=\"text-xs text-status-error-text\">{fieldErrors.organizationName}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"password\">{translate('onboarding.form.password', 'Password')}</Label>\n <PasswordInput\n id=\"password\"\n name=\"password\"\n required\n disabled={disabled}\n autoComplete=\"new-password\"\n minLength={passwordPolicy.minLength}\n aria-invalid={Boolean(fieldErrors.password)}\n aria-describedby={fieldErrors.password ? 'password-error' : undefined}\n />\n {passwordDescription ? (\n <p className=\"text-xs text-muted-foreground\">{passwordDescription}</p>\n ) : null}\n {fieldErrors.password && (\n <p id=\"password-error\" className=\"text-xs text-status-error-text\">{fieldErrors.password}</p>\n )}\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor=\"confirmPassword\">{translate('onboarding.form.confirmPassword', 'Confirm password')}</Label>\n <PasswordInput\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n required\n disabled={disabled}\n autoComplete=\"new-password\"\n aria-invalid={Boolean(fieldErrors.confirmPassword)}\n aria-describedby={fieldErrors.confirmPassword ? 'confirmPassword-error' : undefined}\n />\n {fieldErrors.confirmPassword && (\n <p id=\"confirmPassword-error\" className=\"text-xs text-status-error-text\">{fieldErrors.confirmPassword}</p>\n )}\n </div>\n <label className=\"flex items-start gap-3 text-sm text-muted-foreground\">\n <Checkbox\n id=\"terms\"\n checked={termsAccepted}\n disabled={disabled}\n onCheckedChange={(value: boolean | 'indeterminate') => {\n setTermsAccepted(value === true)\n if (value === true) {\n setFieldErrors((prev) => {\n const next = { ...prev }\n delete next.termsAccepted\n return next\n })\n }\n }}\n aria-invalid={Boolean(fieldErrors.termsAccepted)}\n />\n <span>\n {translate('onboarding.form.termsLabel', 'I have read and accept the ')}\n <a className=\"underline hover:text-foreground\" href=\"/terms\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.termsLink', 'Terms of Service')}\n </a>\n {translate('onboarding.form.termsAnd', ' and ')}\n <a className=\"underline hover:text-foreground\" href=\"/privacy\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.privacyLink', 'Privacy Policy')}\n </a>\n {fieldErrors.termsAccepted && (\n <span className=\"mt-1 block text-xs text-status-error-text\">{fieldErrors.termsAccepted}</span>\n )}\n </span>\n </label>\n <label className=\"flex items-start gap-3 text-sm text-muted-foreground\">\n <Checkbox\n id=\"marketingConsent\"\n checked={marketingConsent}\n disabled={disabled}\n onCheckedChange={(value: boolean | 'indeterminate') => {\n setMarketingConsent(value === true)\n }}\n />\n <span>\n {translate(\n 'onboarding.form.marketingLabel',\n \"I consent to receiving direct marketing from CT Tornado by email at the address I provide. I'm aware that I can withdraw my consent at any time (e.g., via the unsubscribe link), and that CT Tornado may also process my email address under its legitimate interests to manage its mailing list, keep proof of my consent, and record opt-outs. See our {termsLink} and {privacyLink}.\",\n )\n .split(/{termsLink}|{privacyLink}/)\n .map((part, index, parts) => (\n <span key={index}>\n {part}\n {index < parts.length - 1 && (\n index === 0 ? (\n <a className=\"underline hover:text-foreground\" href=\"/terms\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.termsLink', 'Terms of Service')}\n </a>\n ) : (\n <a className=\"underline hover:text-foreground\" href=\"/privacy\" target=\"_blank\" rel=\"noreferrer\">\n {translate('onboarding.form.privacyLink', 'Privacy Policy')}\n </a>\n )\n )}\n </span>\n ))}\n </span>\n </label>\n <Button\n type=\"submit\"\n disabled={disabled}\n className=\"mt-2 h-11 bg-foreground text-background transition hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {submitting\n ? translate('onboarding.form.loading', 'Sending...')\n : translate('onboarding.form.submit', 'Send verification email')}\n </Button>\n </form>\n </CardContent>\n </Card>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/onboarding",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6198.1.0822f97cc6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
73
|
+
"@open-mercato/shared": "0.6.6-develop.6198.1.0822f97cc6"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
76
|
+
"@open-mercato/shared": "0.6.6-develop.6198.1.0822f97cc6",
|
|
77
77
|
"@types/jest": "^30.0.0",
|
|
78
78
|
"jest": "^30.4.2",
|
|
79
79
|
"ts-jest": "^29.4.11"
|
|
@@ -16,6 +16,7 @@ import { Input } from '@open-mercato/ui/primitives/input'
|
|
|
16
16
|
import { EmailInput } from '@open-mercato/ui/primitives/email-input'
|
|
17
17
|
import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
|
|
18
18
|
import { Label } from '@open-mercato/ui/primitives/label'
|
|
19
|
+
import { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'
|
|
19
20
|
|
|
20
21
|
type SubmissionState = 'idle' | 'loading' | 'success'
|
|
21
22
|
type FieldErrors = Partial<Record<
|
|
@@ -225,7 +226,7 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
225
226
|
</CardHeader>
|
|
226
227
|
<CardContent className={onboardingDisabled ? 'pb-10 opacity-45' : 'pb-10'}>
|
|
227
228
|
{state === 'success' && emailSubmitted && (
|
|
228
|
-
<div className="mb-6 rounded-md border border-
|
|
229
|
+
<div className="mb-6 rounded-md border border-status-success-border bg-status-success-bg px-4 py-3 text-sm text-status-success-text" role="status" aria-live="polite">
|
|
229
230
|
<strong className="block text-sm font-medium">
|
|
230
231
|
{translate('onboarding.form.successTitle', 'Check your inbox')}
|
|
231
232
|
</strong>
|
|
@@ -235,9 +236,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
235
236
|
</div>
|
|
236
237
|
)}
|
|
237
238
|
{state !== 'success' && (globalError || verifyStatusError) && (
|
|
238
|
-
<
|
|
239
|
-
{globalError || verifyStatusError}
|
|
240
|
-
</
|
|
239
|
+
<Alert status="error" style="lighter" showIcon={false} className="mb-4 text-sm" aria-live="assertive">
|
|
240
|
+
<AlertDescription>{globalError || verifyStatusError}</AlertDescription>
|
|
241
|
+
</Alert>
|
|
241
242
|
)}
|
|
242
243
|
<form className="grid gap-4" onSubmit={onSubmit} noValidate>
|
|
243
244
|
<div className="grid gap-1">
|
|
@@ -249,10 +250,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
249
250
|
disabled={disabled}
|
|
250
251
|
aria-invalid={Boolean(fieldErrors.email)}
|
|
251
252
|
aria-describedby={fieldErrors.email ? 'email-error' : undefined}
|
|
252
|
-
className={fieldErrors.email ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
253
253
|
/>
|
|
254
254
|
{fieldErrors.email && (
|
|
255
|
-
<p id="email-error" className="text-xs text-
|
|
255
|
+
<p id="email-error" className="text-xs text-status-error-text">{fieldErrors.email}</p>
|
|
256
256
|
)}
|
|
257
257
|
</div>
|
|
258
258
|
<div className="grid gap-1 sm:grid-cols-2 sm:gap-4">
|
|
@@ -267,10 +267,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
267
267
|
autoComplete="given-name"
|
|
268
268
|
aria-invalid={Boolean(fieldErrors.firstName)}
|
|
269
269
|
aria-describedby={fieldErrors.firstName ? 'firstName-error' : undefined}
|
|
270
|
-
className={fieldErrors.firstName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
271
270
|
/>
|
|
272
271
|
{fieldErrors.firstName && (
|
|
273
|
-
<p id="firstName-error" className="text-xs text-
|
|
272
|
+
<p id="firstName-error" className="text-xs text-status-error-text">{fieldErrors.firstName}</p>
|
|
274
273
|
)}
|
|
275
274
|
</div>
|
|
276
275
|
<div className="grid gap-1">
|
|
@@ -284,10 +283,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
284
283
|
autoComplete="family-name"
|
|
285
284
|
aria-invalid={Boolean(fieldErrors.lastName)}
|
|
286
285
|
aria-describedby={fieldErrors.lastName ? 'lastName-error' : undefined}
|
|
287
|
-
className={fieldErrors.lastName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
288
286
|
/>
|
|
289
287
|
{fieldErrors.lastName && (
|
|
290
|
-
<p id="lastName-error" className="text-xs text-
|
|
288
|
+
<p id="lastName-error" className="text-xs text-status-error-text">{fieldErrors.lastName}</p>
|
|
291
289
|
)}
|
|
292
290
|
</div>
|
|
293
291
|
</div>
|
|
@@ -302,10 +300,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
302
300
|
autoComplete="organization"
|
|
303
301
|
aria-invalid={Boolean(fieldErrors.organizationName)}
|
|
304
302
|
aria-describedby={fieldErrors.organizationName ? 'organizationName-error' : undefined}
|
|
305
|
-
className={fieldErrors.organizationName ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
306
303
|
/>
|
|
307
304
|
{fieldErrors.organizationName && (
|
|
308
|
-
<p id="organizationName-error" className="text-xs text-
|
|
305
|
+
<p id="organizationName-error" className="text-xs text-status-error-text">{fieldErrors.organizationName}</p>
|
|
309
306
|
)}
|
|
310
307
|
</div>
|
|
311
308
|
<div className="grid gap-1">
|
|
@@ -319,13 +316,12 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
319
316
|
minLength={passwordPolicy.minLength}
|
|
320
317
|
aria-invalid={Boolean(fieldErrors.password)}
|
|
321
318
|
aria-describedby={fieldErrors.password ? 'password-error' : undefined}
|
|
322
|
-
className={fieldErrors.password ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
323
319
|
/>
|
|
324
320
|
{passwordDescription ? (
|
|
325
321
|
<p className="text-xs text-muted-foreground">{passwordDescription}</p>
|
|
326
322
|
) : null}
|
|
327
323
|
{fieldErrors.password && (
|
|
328
|
-
<p id="password-error" className="text-xs text-
|
|
324
|
+
<p id="password-error" className="text-xs text-status-error-text">{fieldErrors.password}</p>
|
|
329
325
|
)}
|
|
330
326
|
</div>
|
|
331
327
|
<div className="grid gap-1">
|
|
@@ -338,10 +334,9 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
338
334
|
autoComplete="new-password"
|
|
339
335
|
aria-invalid={Boolean(fieldErrors.confirmPassword)}
|
|
340
336
|
aria-describedby={fieldErrors.confirmPassword ? 'confirmPassword-error' : undefined}
|
|
341
|
-
className={fieldErrors.confirmPassword ? 'border-red-500 aria-invalid:ring-destructive' : undefined}
|
|
342
337
|
/>
|
|
343
338
|
{fieldErrors.confirmPassword && (
|
|
344
|
-
<p id="confirmPassword-error" className="text-xs text-
|
|
339
|
+
<p id="confirmPassword-error" className="text-xs text-status-error-text">{fieldErrors.confirmPassword}</p>
|
|
345
340
|
)}
|
|
346
341
|
</div>
|
|
347
342
|
<label className="flex items-start gap-3 text-sm text-muted-foreground">
|
|
@@ -371,7 +366,7 @@ export default function OnboardingPageClient({ onboardingEnabled }: Props) {
|
|
|
371
366
|
{translate('onboarding.form.privacyLink', 'Privacy Policy')}
|
|
372
367
|
</a>
|
|
373
368
|
{fieldErrors.termsAccepted && (
|
|
374
|
-
<span className="mt-1 block text-xs text-
|
|
369
|
+
<span className="mt-1 block text-xs text-status-error-text">{fieldErrors.termsAccepted}</span>
|
|
375
370
|
)}
|
|
376
371
|
</span>
|
|
377
372
|
</label>
|