@open-mercato/core 0.6.1-develop.3260.1.dc719cac84 → 0.6.1-develop.3278.1.a4d26475e4
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/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/domain_mapping/index.js +31 -0
- package/dist/generated/entities/domain_mapping/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +18 -17
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/customer_accounts/acl.js +2 -1
- package/dist/modules/customer_accounts/acl.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-check.js +70 -0
- package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
- package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
- package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
- package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
- package/dist/modules/customer_accounts/api/login.js +14 -3
- package/dist/modules/customer_accounts/api/login.js.map +2 -2
- package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
- package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
- package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
- package/dist/modules/customer_accounts/api/signup.js +32 -5
- package/dist/modules/customer_accounts/api/signup.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
- package/dist/modules/customer_accounts/data/enrichers.js +78 -12
- package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +85 -2
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/data/guards.js +130 -0
- package/dist/modules/customer_accounts/data/guards.js.map +7 -0
- package/dist/modules/customer_accounts/data/validators.js +19 -0
- package/dist/modules/customer_accounts/data/validators.js.map +2 -2
- package/dist/modules/customer_accounts/di.js +23 -1
- package/dist/modules/customer_accounts/di.js.map +2 -2
- package/dist/modules/customer_accounts/events.js +9 -2
- package/dist/modules/customer_accounts/events.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
- package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
- package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
- package/dist/modules/customer_accounts/lib/hostname.js +55 -0
- package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
- package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
- package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
- package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
- package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
- package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
- package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
- package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
- package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
- package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
- package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
- package/dist/modules/customer_accounts/notifications.client.js +76 -0
- package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
- package/dist/modules/customer_accounts/notifications.js +76 -0
- package/dist/modules/customer_accounts/notifications.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
- package/dist/modules/customer_accounts/setup.js +48 -1
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
- package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
- package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
- package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
- package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
- package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
- package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
- package/dist/modules/customers/api/companies/route.js +4 -9
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +4 -9
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +4 -9
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/api/utils.js +0 -12
- package/dist/modules/customers/api/utils.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +361 -284
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +378 -473
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +368 -286
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
- package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
- package/dist/modules/customers/widgets/injection-table.js +6 -0
- package/dist/modules/customers/widgets/injection-table.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +16 -2
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +1 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +31 -8
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/generated/entities/domain_mapping/index.ts +14 -0
- package/generated/entities.ids.generated.ts +2 -1
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +5 -5
- package/src/helpers/integration/salesUi.ts +27 -23
- package/src/modules/customer_accounts/acl.ts +1 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
- package/src/modules/customer_accounts/api/domain-check.ts +83 -0
- package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
- package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
- package/src/modules/customer_accounts/api/login.ts +14 -3
- package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
- package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
- package/src/modules/customer_accounts/api/signup.ts +36 -5
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
- package/src/modules/customer_accounts/data/enrichers.ts +95 -12
- package/src/modules/customer_accounts/data/entities.ts +80 -1
- package/src/modules/customer_accounts/data/guards.ts +155 -0
- package/src/modules/customer_accounts/data/validators.ts +26 -0
- package/src/modules/customer_accounts/di.ts +35 -1
- package/src/modules/customer_accounts/events.ts +8 -1
- package/src/modules/customer_accounts/i18n/de.json +78 -0
- package/src/modules/customer_accounts/i18n/en.json +78 -0
- package/src/modules/customer_accounts/i18n/es.json +78 -0
- package/src/modules/customer_accounts/i18n/pl.json +78 -0
- package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
- package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
- package/src/modules/customer_accounts/lib/hostname.ts +76 -0
- package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
- package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
- package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
- package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
- package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
- package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
- package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
- package/src/modules/customer_accounts/notifications.client.ts +76 -0
- package/src/modules/customer_accounts/notifications.ts +76 -0
- package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
- package/src/modules/customer_accounts/setup.ts +81 -1
- package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
- package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
- package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
- package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
- package/src/modules/customers/api/companies/route.ts +4 -9
- package/src/modules/customers/api/deals/route.ts +4 -9
- package/src/modules/customers/api/people/route.ts +4 -9
- package/src/modules/customers/api/utils.ts +0 -14
- package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
- package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
- package/src/modules/customers/backend/customers/people/page.tsx +331 -240
- package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
- package/src/modules/customers/i18n/de.json +31 -0
- package/src/modules/customers/i18n/en.json +31 -0
- package/src/modules/customers/i18n/es.json +31 -0
- package/src/modules/customers/i18n/pl.json +31 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
- package/src/modules/customers/widgets/injection-table.ts +6 -0
- package/src/modules/perspectives/services/perspectiveService.ts +30 -2
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +1 -0
- package/src/modules/query_index/lib/engine.ts +46 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customer_accounts/api/signup.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { compare as bcryptCompare } from 'bcryptjs'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport { signupSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerTokenService } from '@open-mercato/core/modules/customer_accounts/services/customerTokenService'\nimport { CustomerRole, CustomerUserRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport CustomerSignupVerificationEmail from '@open-mercato/core/modules/customer_accounts/emails/CustomerSignupVerificationEmail'\nimport CustomerExistingAccountEmail from '@open-mercato/core/modules/customer_accounts/emails/CustomerExistingAccountEmail'\nimport { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'\nimport {\n checkAuthRateLimit,\n customerSignupRateLimitConfig,\n customerSignupIpRateLimitConfig,\n} from '@open-mercato/core/modules/customer_accounts/lib/rateLimiter'\nimport { readNormalizedEmailFromJsonRequest } from '@open-mercato/core/modules/customer_accounts/lib/rateLimitIdentifier'\nimport { findOrganizationInTenant } from '@open-mercato/core/modules/customer_accounts/lib/organizationLookup'\nimport { getSecurityEmailBaseUrl, mapSecurityEmailUrlError } from '@open-mercato/shared/lib/url'\n\nexport const metadata: { path?: string; requireAuth?: boolean } = { requireAuth: false }\n\n// Precomputed bcrypt cost-10 hash of an unknowable random 32-byte input; used to equalize\n// response latency between the existing-user and new-user signup branches so the endpoint's\n// 202-for-both contract is not undone by a timing side channel.\nconst TIMING_EQUALIZATION_HASH = '$2b$10$.F2A6UHFzk.d8trNdfqt4OLz05Nf3IOuMmN6VJKflhD4.rz.prR8i'\nfunction resolvePortalLoginUrl(baseUrl: string, organizationSlug?: string | null): string {\n return organizationSlug\n ? `${baseUrl}/${organizationSlug}/portal/login`\n : `${baseUrl}/portal/login`\n}\n\nfunction resolvePortalVerifyUrl(baseUrl: string, token: string, organizationSlug?: string | null): string {\n const route = organizationSlug\n ? `${baseUrl}/${organizationSlug}/portal/verify`\n : `${baseUrl}/portal/verify`\n return `${route}?token=${encodeURIComponent(token)}`\n}\n\nexport async function POST(req: Request) {\n const rateLimitEmail = await readNormalizedEmailFromJsonRequest(req)\n const { error: rateLimitError } = await checkAuthRateLimit({\n req,\n ipConfig: customerSignupIpRateLimitConfig,\n compoundConfig: customerSignupRateLimitConfig,\n compoundIdentifier: rateLimitEmail,\n })\n if (rateLimitError) return rateLimitError\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = signupSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const { email, password, displayName, tenantId, organizationId } = parsed.data\n if (!tenantId || !organizationId) {\n return NextResponse.json({ ok: false, error: 'tenantId and organizationId are required' }, { status: 400 })\n }\n\n let baseUrl: string\n try {\n baseUrl = getSecurityEmailBaseUrl(req)\n } catch (error) {\n const mapped = mapSecurityEmailUrlError(error, {\n scope: 'customer_accounts.signup',\n configMessage: 'Customer signup is not configured',\n })\n if (mapped) return NextResponse.json(mapped.body, { status: mapped.status })\n throw error\n }\n\n const container = await createRequestContainer()\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n const customerTokenService = container.resolve('customerTokenService') as CustomerTokenService\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const { translate } = await resolveTranslations()\n\n const orgRow = await findOrganizationInTenant(em, organizationId, tenantId)\n if (!orgRow) {\n return NextResponse.json({ ok: false, error: 'Registration could not be completed' }, { status: 400 })\n }\n\n const existing = await customerUserService.findByEmail(email, tenantId)\n if (existing) {\n await bcryptCompare(password, TIMING_EQUALIZATION_HASH)\n const existingOrg = await findOrganizationInTenant(em, existing.organizationId, tenantId)\n const loginUrl = resolvePortalLoginUrl(baseUrl, existingOrg?.slug ?? null)\n const subject = translate('customer_accounts.signup.existing.subject', 'You already have a portal account')\n const copy = {\n preview: translate('customer_accounts.signup.existing.preview', 'A sign-up attempt was made for an email that already has a portal account.'),\n title: translate('customer_accounts.signup.existing.title', 'You already have a portal account'),\n body: translate(\n 'customer_accounts.signup.existing.body',\n 'A sign-up request was made for this email address. You can sign in with your existing account. If you forgot your password, use the password reset option on the sign-in page.',\n ),\n cta: translate('customer_accounts.signup.existing.cta', 'Open sign-in page'),\n hint: translate(\n 'customer_accounts.signup.existing.hint',\n 'If this was not you, you can ignore this message. No new portal account was created.',\n ),\n }\n\n void sendEmail({\n to: existing.email,\n subject,\n react: CustomerExistingAccountEmail({ loginUrl, copy }),\n }).catch((error) => {\n console.error('[customer_accounts.signup] existing-account email failed', error)\n })\n\n return NextResponse.json({ ok: true }, { status: 202 })\n }\n\n const user = await customerUserService.createUser(email, password, displayName, { tenantId, organizationId })\n\n const defaultRole = await em.findOne(CustomerRole, {\n tenantId,\n isDefault: true,\n deletedAt: null,\n })\n if (defaultRole) {\n const userRole = em.create(CustomerUserRole, {\n user,\n role: defaultRole,\n createdAt: new Date(),\n } as any)\n em.persist(userRole)\n }\n\n await em.persist(user).flush()\n\n const verificationToken = await customerTokenService.createEmailVerification(user.id, tenantId)\n const verifyUrl = resolvePortalVerifyUrl(baseUrl, verificationToken, orgRow.slug)\n const subject = translate('customer_accounts.signup.created.subject', 'Verify your portal account')\n const copy = {\n preview: translate('customer_accounts.signup.created.preview', 'Verify your portal account to finish sign-up.'),\n title: translate('customer_accounts.signup.created.title', 'Verify your portal account'),\n body: translate(\n 'customer_accounts.signup.created.body',\n 'Your account request was accepted. Confirm your email address to finish setting up portal access.',\n ),\n cta: translate('customer_accounts.signup.created.cta', 'Verify email address'),\n hint: translate(\n 'customer_accounts.signup.created.hint',\n 'This verification link expires in 24 hours. If you did not request this, you can ignore this email.',\n ),\n }\n\n void sendEmail({\n to: user.email,\n subject,\n react: CustomerSignupVerificationEmail({ verifyUrl, copy }),\n }).catch((error) => {\n console.error('[customer_accounts.signup] verification email failed', error)\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId,\n organizationId,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true }, { status: 202 })\n}\n\nconst signupAcceptedSchema = z.object({ ok: z.literal(true) })\n\nconst errorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst methodDoc: OpenApiMethodDoc = {\n summary: 'Register a new customer account',\n description: 'Accepts a signup request and always returns 202 to prevent account enumeration.',\n tags: ['Customer Authentication'],\n requestBody: {\n schema: signupSchema,\n description: 'Signup payload with email, password, and display name.',\n },\n responses: [\n { status: 202, description: 'Signup accepted', schema: signupAcceptedSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or invalid request origin', schema: errorSchema },\n { status: 429, description: 'Too many signup attempts', schema: rateLimitErrorSchema },\n { status: 500, description: 'Signup email origin is not configured', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer account registration',\n description: 'Handles customer self-registration without revealing whether the email already exists.',\n methods: { POST: methodDoc },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,WAAW,qBAAqB;AACzC,SAAS,SAAS;AAElB,SAAS,2BAA2B;AACpC,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AAGvC,SAAS,cAAc,wBAAwB;AAC/C,SAAS,iCAAiC;AAC1C,OAAO,qCAAqC;AAC5C,OAAO,kCAAkC;AACzC,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AACnD,SAAS,gCAAgC;AACzC,SAAS,yBAAyB,gCAAgC;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { compare as bcryptCompare } from 'bcryptjs'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport { signupSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerTokenService } from '@open-mercato/core/modules/customer_accounts/services/customerTokenService'\nimport { CustomerRole, CustomerUserRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport CustomerSignupVerificationEmail from '@open-mercato/core/modules/customer_accounts/emails/CustomerSignupVerificationEmail'\nimport CustomerExistingAccountEmail from '@open-mercato/core/modules/customer_accounts/emails/CustomerExistingAccountEmail'\nimport { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'\nimport {\n checkAuthRateLimit,\n customerSignupRateLimitConfig,\n customerSignupIpRateLimitConfig,\n} from '@open-mercato/core/modules/customer_accounts/lib/rateLimiter'\nimport { readNormalizedEmailFromJsonRequest } from '@open-mercato/core/modules/customer_accounts/lib/rateLimitIdentifier'\nimport { findOrganizationInTenant } from '@open-mercato/core/modules/customer_accounts/lib/organizationLookup'\nimport { getSecurityEmailBaseUrl, mapSecurityEmailUrlError } from '@open-mercato/shared/lib/url'\nimport {\n resolveTenantContext,\n TenantResolutionError,\n} from '@open-mercato/core/modules/customer_accounts/lib/resolveTenantContext'\nimport { urlForCustomerOrg } from '@open-mercato/core/modules/customer_accounts/lib/customerUrl'\n\nexport const metadata: { path?: string; requireAuth?: boolean } = { requireAuth: false }\n\n// Precomputed bcrypt cost-10 hash of an unknowable random 32-byte input; used to equalize\n// response latency between the existing-user and new-user signup branches so the endpoint's\n// 202-for-both contract is not undone by a timing side channel.\nconst TIMING_EQUALIZATION_HASH = '$2b$10$.F2A6UHFzk.d8trNdfqt4OLz05Nf3IOuMmN6VJKflhD4.rz.prR8i'\nfunction resolvePortalLoginUrl(baseUrl: string, organizationSlug?: string | null): string {\n return organizationSlug\n ? `${baseUrl}/${organizationSlug}/portal/login`\n : `${baseUrl}/portal/login`\n}\n\nfunction resolvePortalVerifyUrl(baseUrl: string, token: string, organizationSlug?: string | null): string {\n const route = organizationSlug\n ? `${baseUrl}/${organizationSlug}/portal/verify`\n : `${baseUrl}/portal/verify`\n return `${route}?token=${encodeURIComponent(token)}`\n}\n\nexport async function POST(req: Request) {\n const rateLimitEmail = await readNormalizedEmailFromJsonRequest(req)\n const { error: rateLimitError } = await checkAuthRateLimit({\n req,\n ipConfig: customerSignupIpRateLimitConfig,\n compoundConfig: customerSignupRateLimitConfig,\n compoundIdentifier: rateLimitEmail,\n })\n if (rateLimitError) return rateLimitError\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = signupSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const { email, password, displayName } = parsed.data\n let tenantId: string\n let organizationId: string | null\n try {\n const context = await resolveTenantContext(req, parsed.data.tenantId)\n tenantId = context.tenantId\n organizationId = context.organizationId ?? parsed.data.organizationId ?? null\n } catch (err) {\n if (err instanceof TenantResolutionError) {\n return NextResponse.json({ ok: false, error: err.message }, { status: err.status })\n }\n throw err\n }\n if (!organizationId) {\n return NextResponse.json({ ok: false, error: 'organizationId is required' }, { status: 400 })\n }\n\n let baseUrl: string\n try {\n baseUrl = getSecurityEmailBaseUrl(req)\n } catch (error) {\n const mapped = mapSecurityEmailUrlError(error, {\n scope: 'customer_accounts.signup',\n configMessage: 'Customer signup is not configured',\n })\n if (mapped) return NextResponse.json(mapped.body, { status: mapped.status })\n throw error\n }\n\n const container = await createRequestContainer()\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n const customerTokenService = container.resolve('customerTokenService') as CustomerTokenService\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const { translate } = await resolveTranslations()\n\n const orgRow = await findOrganizationInTenant(em, organizationId, tenantId)\n if (!orgRow) {\n return NextResponse.json({ ok: false, error: 'Registration could not be completed' }, { status: 400 })\n }\n\n const existing = await customerUserService.findByEmail(email, tenantId)\n if (existing) {\n await bcryptCompare(password, TIMING_EQUALIZATION_HASH)\n const existingOrg = await findOrganizationInTenant(em, existing.organizationId, tenantId)\n // Prefer the org's active custom domain when available; fall back to the\n // platform login URL preserved by `resolvePortalLoginUrl`.\n let loginUrl = resolvePortalLoginUrl(baseUrl, existingOrg?.slug ?? null)\n try {\n loginUrl = await urlForCustomerOrg(existing.organizationId, '/login', { container })\n } catch {\n // Fall back to platform URL on any resolution issue.\n }\n const subject = translate('customer_accounts.signup.existing.subject', 'You already have a portal account')\n const copy = {\n preview: translate('customer_accounts.signup.existing.preview', 'A sign-up attempt was made for an email that already has a portal account.'),\n title: translate('customer_accounts.signup.existing.title', 'You already have a portal account'),\n body: translate(\n 'customer_accounts.signup.existing.body',\n 'A sign-up request was made for this email address. You can sign in with your existing account. If you forgot your password, use the password reset option on the sign-in page.',\n ),\n cta: translate('customer_accounts.signup.existing.cta', 'Open sign-in page'),\n hint: translate(\n 'customer_accounts.signup.existing.hint',\n 'If this was not you, you can ignore this message. No new portal account was created.',\n ),\n }\n\n void sendEmail({\n to: existing.email,\n subject,\n react: CustomerExistingAccountEmail({ loginUrl, copy }),\n }).catch((error) => {\n console.error('[customer_accounts.signup] existing-account email failed', error)\n })\n\n return NextResponse.json({ ok: true }, { status: 202 })\n }\n\n const user = await customerUserService.createUser(email, password, displayName, { tenantId, organizationId })\n\n const defaultRole = await em.findOne(CustomerRole, {\n tenantId,\n isDefault: true,\n deletedAt: null,\n })\n if (defaultRole) {\n const userRole = em.create(CustomerUserRole, {\n user,\n role: defaultRole,\n createdAt: new Date(),\n } as any)\n em.persist(userRole)\n }\n\n await em.persist(user).flush()\n\n const verificationToken = await customerTokenService.createEmailVerification(user.id, tenantId)\n let verifyUrl = resolvePortalVerifyUrl(baseUrl, verificationToken, orgRow.slug)\n try {\n verifyUrl = await urlForCustomerOrg(organizationId, `/verify?token=${encodeURIComponent(verificationToken)}`, {\n container,\n })\n } catch {\n // Fall back to the platform-derived URL on any resolution issue.\n }\n const subject = translate('customer_accounts.signup.created.subject', 'Verify your portal account')\n const copy = {\n preview: translate('customer_accounts.signup.created.preview', 'Verify your portal account to finish sign-up.'),\n title: translate('customer_accounts.signup.created.title', 'Verify your portal account'),\n body: translate(\n 'customer_accounts.signup.created.body',\n 'Your account request was accepted. Confirm your email address to finish setting up portal access.',\n ),\n cta: translate('customer_accounts.signup.created.cta', 'Verify email address'),\n hint: translate(\n 'customer_accounts.signup.created.hint',\n 'This verification link expires in 24 hours. If you did not request this, you can ignore this email.',\n ),\n }\n\n void sendEmail({\n to: user.email,\n subject,\n react: CustomerSignupVerificationEmail({ verifyUrl, copy }),\n }).catch((error) => {\n console.error('[customer_accounts.signup] verification email failed', error)\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId,\n organizationId,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true }, { status: 202 })\n}\n\nconst signupAcceptedSchema = z.object({ ok: z.literal(true) })\n\nconst errorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst methodDoc: OpenApiMethodDoc = {\n summary: 'Register a new customer account',\n description: 'Accepts a signup request and always returns 202 to prevent account enumeration.',\n tags: ['Customer Authentication'],\n requestBody: {\n schema: signupSchema,\n description: 'Signup payload with email, password, and display name.',\n },\n responses: [\n { status: 202, description: 'Signup accepted', schema: signupAcceptedSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or invalid request origin', schema: errorSchema },\n { status: 429, description: 'Too many signup attempts', schema: rateLimitErrorSchema },\n { status: 500, description: 'Signup email origin is not configured', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer account registration',\n description: 'Handles customer self-registration without revealing whether the email already exists.',\n methods: { POST: methodDoc },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["subject", "copy"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ExternalLink, RefreshCw, Replace, Trash2 } from "lucide-react";
|
|
4
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
function DefaultActions({
|
|
7
|
+
mapping,
|
|
8
|
+
isReplacement,
|
|
9
|
+
onCheckNow,
|
|
10
|
+
onRetryTls,
|
|
11
|
+
onChangeDomain,
|
|
12
|
+
onRemove,
|
|
13
|
+
busy
|
|
14
|
+
}) {
|
|
15
|
+
const t = useT();
|
|
16
|
+
const showCheckNow = mapping.status === "pending" || mapping.status === "dns_failed";
|
|
17
|
+
const showRetryTls = mapping.status === "tls_failed" || mapping.status === "verified";
|
|
18
|
+
const showTestDomain = mapping.status === "active";
|
|
19
|
+
const showChangeDomain = mapping.status === "active" && !isReplacement;
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
21
|
+
showCheckNow ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onCheckNow, disabled: busy, children: [
|
|
22
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
|
|
23
|
+
t("customer_accounts.domainMapping.verifyNow", "Check Now")
|
|
24
|
+
] }) : null,
|
|
25
|
+
showRetryTls ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetryTls, disabled: busy, children: [
|
|
26
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
|
|
27
|
+
t("customer_accounts.domainMapping.tls.retryButton", "Retry SSL")
|
|
28
|
+
] }) : null,
|
|
29
|
+
showTestDomain ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", asChild: true, children: /* @__PURE__ */ jsxs("a", { href: `https://${mapping.hostname}`, target: "_blank", rel: "noopener noreferrer", children: [
|
|
30
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
|
|
31
|
+
t("customer_accounts.domainMapping.preview", "Test Domain")
|
|
32
|
+
] }) }) : null,
|
|
33
|
+
showChangeDomain ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onChangeDomain, disabled: busy, children: [
|
|
34
|
+
/* @__PURE__ */ jsx(Replace, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
|
|
35
|
+
t("customer_accounts.domainMapping.changeDomain", "Change Domain")
|
|
36
|
+
] }) : null,
|
|
37
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onRemove, disabled: busy, children: [
|
|
38
|
+
/* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
|
|
39
|
+
t("customer_accounts.domainMapping.removeDomain", "Remove Domain")
|
|
40
|
+
] })
|
|
41
|
+
] });
|
|
42
|
+
}
|
|
43
|
+
var Actions_default = DefaultActions;
|
|
44
|
+
export {
|
|
45
|
+
DefaultActions,
|
|
46
|
+
Actions_default as default
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=Actions.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ExternalLink, RefreshCw, Replace, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DomainMappingRow } from './types'\n\nexport type ActionsProps = {\n mapping: DomainMappingRow\n isReplacement: boolean\n onCheckNow: () => void\n onRetryTls: () => void\n onChangeDomain: () => void\n onRemove: () => void\n busy?: boolean\n}\n\nexport function DefaultActions({\n mapping,\n isReplacement,\n onCheckNow,\n onRetryTls,\n onChangeDomain,\n onRemove,\n busy,\n}: ActionsProps) {\n const t = useT()\n const showCheckNow = mapping.status === 'pending' || mapping.status === 'dns_failed'\n const showRetryTls = mapping.status === 'tls_failed' || mapping.status === 'verified'\n const showTestDomain = mapping.status === 'active'\n const showChangeDomain = mapping.status === 'active' && !isReplacement\n\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n {showCheckNow ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onCheckNow} disabled={busy}>\n <RefreshCw className=\"mr-1.5 h-4 w-4\" aria-hidden />\n {t('customer_accounts.domainMapping.verifyNow', 'Check Now')}\n </Button>\n ) : null}\n {showRetryTls ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onRetryTls} disabled={busy}>\n <RefreshCw className=\"mr-1.5 h-4 w-4\" aria-hidden />\n {t('customer_accounts.domainMapping.tls.retryButton', 'Retry SSL')}\n </Button>\n ) : null}\n {showTestDomain ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" asChild>\n <a href={`https://${mapping.hostname}`} target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"mr-1.5 h-4 w-4\" aria-hidden />\n {t('customer_accounts.domainMapping.preview', 'Test Domain')}\n </a>\n </Button>\n ) : null}\n {showChangeDomain ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onChangeDomain} disabled={busy}>\n <Replace className=\"mr-1.5 h-4 w-4\" aria-hidden />\n {t('customer_accounts.domainMapping.changeDomain', 'Change Domain')}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onRemove} disabled={busy}>\n <Trash2 className=\"mr-1.5 h-4 w-4\" aria-hidden />\n {t('customer_accounts.domainMapping.removeDomain', 'Remove Domain')}\n </Button>\n </div>\n )\n}\n\nexport default DefaultActions\n"],
|
|
5
|
+
"mappings": ";AAoCQ,SACE,KADF;AAjCR,SAAS,cAAc,WAAW,SAAS,cAAc;AACzD,SAAS,cAAc;AACvB,SAAS,YAAY;AAad,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiB;AACf,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,QAAQ,WAAW,aAAa,QAAQ,WAAW;AACxE,QAAM,eAAe,QAAQ,WAAW,gBAAgB,QAAQ,WAAW;AAC3E,QAAM,iBAAiB,QAAQ,WAAW;AAC1C,QAAM,mBAAmB,QAAQ,WAAW,YAAY,CAAC;AAEzD,SACE,qBAAC,SAAI,WAAU,qCACZ;AAAA,mBACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,YAAY,UAAU,MAC/E;AAAA,0BAAC,aAAU,WAAU,kBAAiB,eAAW,MAAC;AAAA,MACjD,EAAE,6CAA6C,WAAW;AAAA,OAC7D,IACE;AAAA,IACH,eACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,YAAY,UAAU,MAC/E;AAAA,0BAAC,aAAU,WAAU,kBAAiB,eAAW,MAAC;AAAA,MACjD,EAAE,mDAAmD,WAAW;AAAA,OACnE,IACE;AAAA,IACH,iBACC,oBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAO,MACvD,+BAAC,OAAE,MAAM,WAAW,QAAQ,QAAQ,IAAI,QAAO,UAAS,KAAI,uBAC1D;AAAA,0BAAC,gBAAa,WAAU,kBAAiB,eAAW,MAAC;AAAA,MACpD,EAAE,2CAA2C,aAAa;AAAA,OAC7D,GACF,IACE;AAAA,IACH,mBACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,gBAAgB,UAAU,MACnF;AAAA,0BAAC,WAAQ,WAAU,kBAAiB,eAAW,MAAC;AAAA,MAC/C,EAAE,gDAAgD,eAAe;AAAA,OACpE,IACE;AAAA,IACJ,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,UAAU,UAAU,MAC7E;AAAA,0BAAC,UAAO,WAAU,kBAAiB,eAAW,MAAC;AAAA,MAC9C,EAAE,gDAAgD,eAAe;AAAA,OACpE;AAAA,KACF;AAEJ;AAEA,IAAO,kBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertTriangle } from "lucide-react";
|
|
4
|
+
import { Alert, AlertTitle, AlertDescription } from "@open-mercato/ui/primitives/alert";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
function DnsDiagnostics({ mapping }) {
|
|
7
|
+
const t = useT();
|
|
8
|
+
if (mapping.status !== "dns_failed") return null;
|
|
9
|
+
return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
|
|
10
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4", "aria-hidden": true }),
|
|
11
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: t("customer_accounts.domainMapping.dns.diagnostics.title", "DNS configuration issue") }),
|
|
12
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
13
|
+
mapping.dnsFailureReason ? /* @__PURE__ */ jsx("p", { children: mapping.dnsFailureReason }) : null,
|
|
14
|
+
mapping.cnameTarget ? /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs", children: [
|
|
15
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: t("customer_accounts.domainMapping.dns.diagnostics.expected", "Expected target") }),
|
|
16
|
+
/* @__PURE__ */ jsx("dd", { className: "font-mono", children: mapping.cnameTarget })
|
|
17
|
+
] }) : null
|
|
18
|
+
] }) })
|
|
19
|
+
] });
|
|
20
|
+
}
|
|
21
|
+
function TlsDiagnostics({ mapping }) {
|
|
22
|
+
const t = useT();
|
|
23
|
+
if (mapping.status !== "tls_failed") return null;
|
|
24
|
+
return /* @__PURE__ */ jsxs(Alert, { variant: "warning", children: [
|
|
25
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4", "aria-hidden": true }),
|
|
26
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: t("customer_accounts.domainMapping.tls.diagnostics.title", "SSL certificate issue") }),
|
|
27
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
28
|
+
mapping.tlsFailureReason ? /* @__PURE__ */ jsx("p", { children: mapping.tlsFailureReason }) : null,
|
|
29
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("customer_accounts.domainMapping.tls.diagnostics.retryCount", "Retry attempts: {count}", {
|
|
30
|
+
count: String(mapping.tlsRetryCount)
|
|
31
|
+
}) }),
|
|
32
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
|
|
33
|
+
"customer_accounts.domainMapping.tls.diagnostics.operatorNote",
|
|
34
|
+
"We are retrying automatically. If this persists, contact platform support \u2014 your DNS is fine, this is on our side."
|
|
35
|
+
) })
|
|
36
|
+
] }) })
|
|
37
|
+
] });
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
DnsDiagnostics,
|
|
41
|
+
TlsDiagnostics
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=Diagnostics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { AlertTriangle } from 'lucide-react'\nimport { Alert, AlertTitle, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DomainMappingRow } from './types'\n\nexport type DiagnosticsProps = {\n mapping: DomainMappingRow\n}\n\nexport function DnsDiagnostics({ mapping }: DiagnosticsProps) {\n const t = useT()\n if (mapping.status !== 'dns_failed') return null\n return (\n <Alert variant=\"destructive\">\n <AlertTriangle className=\"h-4 w-4\" aria-hidden />\n <AlertTitle>\n {t('customer_accounts.domainMapping.dns.diagnostics.title', 'DNS configuration issue')}\n </AlertTitle>\n <AlertDescription>\n <div className=\"space-y-2\">\n {mapping.dnsFailureReason ? <p>{mapping.dnsFailureReason}</p> : null}\n {mapping.cnameTarget ? (\n <dl className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs\">\n <dt className=\"text-muted-foreground\">\n {t('customer_accounts.domainMapping.dns.diagnostics.expected', 'Expected target')}\n </dt>\n <dd className=\"font-mono\">{mapping.cnameTarget}</dd>\n </dl>\n ) : null}\n </div>\n </AlertDescription>\n </Alert>\n )\n}\n\nexport function TlsDiagnostics({ mapping }: DiagnosticsProps) {\n const t = useT()\n if (mapping.status !== 'tls_failed') return null\n return (\n <Alert variant=\"warning\">\n <AlertTriangle className=\"h-4 w-4\" aria-hidden />\n <AlertTitle>\n {t('customer_accounts.domainMapping.tls.diagnostics.title', 'SSL certificate issue')}\n </AlertTitle>\n <AlertDescription>\n <div className=\"space-y-2\">\n {mapping.tlsFailureReason ? <p>{mapping.tlsFailureReason}</p> : null}\n <p className=\"text-xs text-muted-foreground\">\n {t('customer_accounts.domainMapping.tls.diagnostics.retryCount', 'Retry attempts: {count}', {\n count: String(mapping.tlsRetryCount),\n })}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'customer_accounts.domainMapping.tls.diagnostics.operatorNote',\n 'We are retrying automatically. If this persists, contact platform support \u2014 your DNS is fine, this is on our side.',\n )}\n </p>\n </div>\n </AlertDescription>\n </Alert>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAiBM,cAQM,YARN;AAdN,SAAS,qBAAqB;AAC9B,SAAS,OAAO,YAAY,wBAAwB;AACpD,SAAS,YAAY;AAOd,SAAS,eAAe,EAAE,QAAQ,GAAqB;AAC5D,QAAM,IAAI,KAAK;AACf,MAAI,QAAQ,WAAW,aAAc,QAAO;AAC5C,SACE,qBAAC,SAAM,SAAQ,eACb;AAAA,wBAAC,iBAAc,WAAU,WAAU,eAAW,MAAC;AAAA,IAC/C,oBAAC,cACE,YAAE,yDAAyD,yBAAyB,GACvF;AAAA,IACA,oBAAC,oBACC,+BAAC,SAAI,WAAU,aACZ;AAAA,cAAQ,mBAAmB,oBAAC,OAAG,kBAAQ,kBAAiB,IAAO;AAAA,MAC/D,QAAQ,cACP,qBAAC,QAAG,WAAU,4DACZ;AAAA,4BAAC,QAAG,WAAU,yBACX,YAAE,4DAA4D,iBAAiB,GAClF;AAAA,QACA,oBAAC,QAAG,WAAU,aAAa,kBAAQ,aAAY;AAAA,SACjD,IACE;AAAA,OACN,GACF;AAAA,KACF;AAEJ;AAEO,SAAS,eAAe,EAAE,QAAQ,GAAqB;AAC5D,QAAM,IAAI,KAAK;AACf,MAAI,QAAQ,WAAW,aAAc,QAAO;AAC5C,SACE,qBAAC,SAAM,SAAQ,WACb;AAAA,wBAAC,iBAAc,WAAU,WAAU,eAAW,MAAC;AAAA,IAC/C,oBAAC,cACE,YAAE,yDAAyD,uBAAuB,GACrF;AAAA,IACA,oBAAC,oBACC,+BAAC,SAAI,WAAU,aACZ;AAAA,cAAQ,mBAAmB,oBAAC,OAAG,kBAAQ,kBAAiB,IAAO;AAAA,MAChE,oBAAC,OAAE,WAAU,iCACV,YAAE,8DAA8D,2BAA2B;AAAA,QAC1F,OAAO,OAAO,QAAQ,aAAa;AAAA,MACrC,CAAC,GACH;AAAA,MACA,oBAAC,OAAE,WAAU,iCACV;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Copy, Check } from "lucide-react";
|
|
5
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
7
|
+
function CopyButton({ value, label }) {
|
|
8
|
+
const t = useT();
|
|
9
|
+
const [copied, setCopied] = React.useState(false);
|
|
10
|
+
const handleCopy = React.useCallback(async () => {
|
|
11
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) return;
|
|
12
|
+
await navigator.clipboard.writeText(value);
|
|
13
|
+
setCopied(true);
|
|
14
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
15
|
+
}, [value]);
|
|
16
|
+
return /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleCopy, "aria-label": label, children: [
|
|
17
|
+
copied ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4", "aria-hidden": true }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4", "aria-hidden": true }),
|
|
18
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1.5 text-xs", children: copied ? t("customer_accounts.domainMapping.copyTarget.copied", "Copied") : t("customer_accounts.domainMapping.copyTarget", "Copy") })
|
|
19
|
+
] });
|
|
20
|
+
}
|
|
21
|
+
function DefaultDnsConfig({ hostname, cnameTarget, aRecordTarget }) {
|
|
22
|
+
const t = useT();
|
|
23
|
+
const exampleHost = hostname ?? t("customer_accounts.domainMapping.hostname.placeholder", "e.g., shop.yourdomain.com");
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 rounded-lg border bg-muted/20 p-4", children: [
|
|
25
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
26
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: t("customer_accounts.domainMapping.cnameInstruction", "Add a CNAME record for {hostname} pointing to {target} in your DNS provider", {
|
|
27
|
+
hostname: exampleHost,
|
|
28
|
+
target: cnameTarget ?? "\u2014"
|
|
29
|
+
}) }),
|
|
30
|
+
cnameTarget ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border bg-background px-3 py-2", children: [
|
|
31
|
+
/* @__PURE__ */ jsx("code", { className: "flex-1 truncate font-mono text-sm", children: cnameTarget }),
|
|
32
|
+
/* @__PURE__ */ jsx(CopyButton, { value: cnameTarget, label: t("customer_accounts.domainMapping.copyTarget", "Copy") })
|
|
33
|
+
] }) : null
|
|
34
|
+
] }),
|
|
35
|
+
aRecordTarget ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
36
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("customer_accounts.domainMapping.aRecordInstruction", "For an apex domain, add an A record for {hostname} pointing to {target}", {
|
|
37
|
+
hostname: exampleHost,
|
|
38
|
+
target: aRecordTarget
|
|
39
|
+
}) }),
|
|
40
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border bg-background px-3 py-2", children: [
|
|
41
|
+
/* @__PURE__ */ jsx("code", { className: "flex-1 truncate font-mono text-sm", children: aRecordTarget }),
|
|
42
|
+
/* @__PURE__ */ jsx(CopyButton, { value: aRecordTarget, label: t("customer_accounts.domainMapping.copyTarget", "Copy") })
|
|
43
|
+
] })
|
|
44
|
+
] }) : null,
|
|
45
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("customer_accounts.domainMapping.propagationNote", "DNS changes can take up to 48 hours to propagate. The system checks automatically \u2014 you'll be notified when it's ready.") })
|
|
46
|
+
] });
|
|
47
|
+
}
|
|
48
|
+
var DnsConfig_default = DefaultDnsConfig;
|
|
49
|
+
export {
|
|
50
|
+
DefaultDnsConfig,
|
|
51
|
+
DnsConfig_default as default
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=DnsConfig.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Copy, Check } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type DnsConfigProps = {\n hostname: string | null\n cnameTarget: string | null\n aRecordTarget: string | null\n}\n\nfunction CopyButton({ value, label }: { value: string; label: string }) {\n const t = useT()\n const [copied, setCopied] = React.useState(false)\n\n const handleCopy = React.useCallback(async () => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return\n await navigator.clipboard.writeText(value)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 1500)\n }, [value])\n\n return (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={handleCopy} aria-label={label}>\n {copied ? <Check className=\"h-4 w-4\" aria-hidden /> : <Copy className=\"h-4 w-4\" aria-hidden />}\n <span className=\"ml-1.5 text-xs\">\n {copied\n ? t('customer_accounts.domainMapping.copyTarget.copied', 'Copied')\n : t('customer_accounts.domainMapping.copyTarget', 'Copy')}\n </span>\n </Button>\n )\n}\n\nexport function DefaultDnsConfig({ hostname, cnameTarget, aRecordTarget }: DnsConfigProps) {\n const t = useT()\n const exampleHost = hostname ?? t('customer_accounts.domainMapping.hostname.placeholder', 'e.g., shop.yourdomain.com')\n\n return (\n <div className=\"space-y-4 rounded-lg border bg-muted/20 p-4\">\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-foreground\">\n {t('customer_accounts.domainMapping.cnameInstruction', 'Add a CNAME record for {hostname} pointing to {target} in your DNS provider', {\n hostname: exampleHost,\n target: cnameTarget ?? '\u2014',\n })}\n </p>\n {cnameTarget ? (\n <div className=\"flex items-center gap-2 rounded-md border bg-background px-3 py-2\">\n <code className=\"flex-1 truncate font-mono text-sm\">{cnameTarget}</code>\n <CopyButton value={cnameTarget} label={t('customer_accounts.domainMapping.copyTarget', 'Copy')} />\n </div>\n ) : null}\n </div>\n {aRecordTarget ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {t('customer_accounts.domainMapping.aRecordInstruction', 'For an apex domain, add an A record for {hostname} pointing to {target}', {\n hostname: exampleHost,\n target: aRecordTarget,\n })}\n </p>\n <div className=\"flex items-center gap-2 rounded-md border bg-background px-3 py-2\">\n <code className=\"flex-1 truncate font-mono text-sm\">{aRecordTarget}</code>\n <CopyButton value={aRecordTarget} label={t('customer_accounts.domainMapping.copyTarget', 'Copy')} />\n </div>\n </div>\n ) : null}\n <p className=\"text-xs text-muted-foreground\">\n {t('customer_accounts.domainMapping.propagationNote', 'DNS changes can take up to 48 hours to propagate. The system checks automatically \u2014 you\\'ll be notified when it\\'s ready.')}\n </p>\n </div>\n )\n}\n\nexport default DefaultDnsConfig\n"],
|
|
5
|
+
"mappings": ";AAyBI,SACY,KADZ;AAvBJ,YAAY,WAAW;AACvB,SAAS,MAAM,aAAa;AAC5B,SAAS,cAAc;AACvB,SAAS,YAAY;AAQrB,SAAS,WAAW,EAAE,OAAO,MAAM,GAAqC;AACtE,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAEhD,QAAM,aAAa,MAAM,YAAY,YAAY;AAC/C,QAAI,OAAO,cAAc,eAAe,CAAC,UAAU,UAAW;AAC9D,UAAM,UAAU,UAAU,UAAU,KAAK;AACzC,cAAU,IAAI;AACd,WAAO,WAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,EAChD,GAAG,CAAC,KAAK,CAAC;AAEV,SACE,qBAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,MAAK,MAAK,SAAS,YAAY,cAAY,OAC9E;AAAA,aAAS,oBAAC,SAAM,WAAU,WAAU,eAAW,MAAC,IAAK,oBAAC,QAAK,WAAU,WAAU,eAAW,MAAC;AAAA,IAC5F,oBAAC,UAAK,WAAU,kBACb,mBACG,EAAE,qDAAqD,QAAQ,IAC/D,EAAE,8CAA8C,MAAM,GAC5D;AAAA,KACF;AAEJ;AAEO,SAAS,iBAAiB,EAAE,UAAU,aAAa,cAAc,GAAmB;AACzF,QAAM,IAAI,KAAK;AACf,QAAM,cAAc,YAAY,EAAE,wDAAwD,2BAA2B;AAErH,SACE,qBAAC,SAAI,WAAU,+CACb;AAAA,yBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,OAAE,WAAU,uCACV,YAAE,oDAAoD,+EAA+E;AAAA,QACpI,UAAU;AAAA,QACV,QAAQ,eAAe;AAAA,MACzB,CAAC,GACH;AAAA,MACC,cACC,qBAAC,SAAI,WAAU,qEACb;AAAA,4BAAC,UAAK,WAAU,qCAAqC,uBAAY;AAAA,QACjE,oBAAC,cAAW,OAAO,aAAa,OAAO,EAAE,8CAA8C,MAAM,GAAG;AAAA,SAClG,IACE;AAAA,OACN;AAAA,IACC,gBACC,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,OAAE,WAAU,iCACV,YAAE,sDAAsD,2EAA2E;AAAA,QAClI,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC,GACH;AAAA,MACA,qBAAC,SAAI,WAAU,qEACb;AAAA,4BAAC,UAAK,WAAU,qCAAqC,yBAAc;AAAA,QACnE,oBAAC,cAAW,OAAO,eAAe,OAAO,EAAE,8CAA8C,MAAM,GAAG;AAAA,SACpG;AAAA,OACF,IACE;AAAA,IACJ,oBAAC,OAAE,WAAU,iCACV,YAAE,mDAAmD,8HAA2H,GACnL;AAAA,KACF;AAEJ;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRegisteredComponent } from "@open-mercato/ui/backend/injection/useRegisteredComponent";
|
|
4
|
+
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
import { DefaultStepper } from "./Stepper.js";
|
|
7
|
+
import { DefaultStatusBadge } from "./StatusBadgeSection.js";
|
|
8
|
+
import { DefaultDnsConfig } from "./DnsConfig.js";
|
|
9
|
+
import { DefaultActions } from "./Actions.js";
|
|
10
|
+
import { DnsDiagnostics, TlsDiagnostics } from "./Diagnostics.js";
|
|
11
|
+
function DomainPanel(props) {
|
|
12
|
+
const t = useT();
|
|
13
|
+
const Stepper = useRegisteredComponent(
|
|
14
|
+
"section:customer_accounts.domain-settings:stepper",
|
|
15
|
+
DefaultStepper
|
|
16
|
+
);
|
|
17
|
+
const StatusBadgeSection = useRegisteredComponent(
|
|
18
|
+
"section:customer_accounts.domain-settings:status",
|
|
19
|
+
DefaultStatusBadge
|
|
20
|
+
);
|
|
21
|
+
const DnsConfig = useRegisteredComponent(
|
|
22
|
+
"section:customer_accounts.domain-settings:dns-config",
|
|
23
|
+
DefaultDnsConfig
|
|
24
|
+
);
|
|
25
|
+
const Actions = useRegisteredComponent(
|
|
26
|
+
"section:customer_accounts.domain-settings:actions",
|
|
27
|
+
DefaultActions
|
|
28
|
+
);
|
|
29
|
+
const { mapping, isReplacement, cnameTarget, aRecordTarget } = props;
|
|
30
|
+
const showDnsConfig = mapping.status !== "active";
|
|
31
|
+
return /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-xl border bg-card p-6 shadow-sm", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-start justify-between gap-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold tracking-tight", children: mapping.hostname }),
|
|
35
|
+
isReplacement ? /* @__PURE__ */ jsx(Tag, { variant: "info", dot: true, children: t("customer_accounts.domainMapping.replacement.label", "Replacement (waiting to activate)") }) : null
|
|
36
|
+
] }),
|
|
37
|
+
/* @__PURE__ */ jsx(StatusBadgeSection, { mapping })
|
|
38
|
+
] }) }),
|
|
39
|
+
/* @__PURE__ */ jsx(Stepper, { status: mapping.status }),
|
|
40
|
+
/* @__PURE__ */ jsx(DnsDiagnostics, { mapping }),
|
|
41
|
+
/* @__PURE__ */ jsx(TlsDiagnostics, { mapping }),
|
|
42
|
+
showDnsConfig ? /* @__PURE__ */ jsx(
|
|
43
|
+
DnsConfig,
|
|
44
|
+
{
|
|
45
|
+
hostname: mapping.hostname,
|
|
46
|
+
cnameTarget: mapping.cnameTarget ?? cnameTarget,
|
|
47
|
+
aRecordTarget: mapping.aRecordTarget ?? aRecordTarget
|
|
48
|
+
}
|
|
49
|
+
) : null,
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Actions,
|
|
52
|
+
{
|
|
53
|
+
mapping,
|
|
54
|
+
isReplacement,
|
|
55
|
+
busy: props.busy,
|
|
56
|
+
onCheckNow: props.onCheckNow,
|
|
57
|
+
onRetryTls: props.onRetryTls,
|
|
58
|
+
onChangeDomain: props.onChangeDomain,
|
|
59
|
+
onRemove: props.onRemove
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
64
|
+
var DomainPanel_default = DomainPanel;
|
|
65
|
+
export {
|
|
66
|
+
DomainPanel,
|
|
67
|
+
DomainPanel_default as default
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=DomainPanel.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRegisteredComponent } from '@open-mercato/ui/backend/injection/useRegisteredComponent'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DefaultStepper, type StepperProps } from './Stepper'\nimport { DefaultStatusBadge, type StatusBadgeSectionProps } from './StatusBadgeSection'\nimport { DefaultDnsConfig, type DnsConfigProps } from './DnsConfig'\nimport { DefaultActions, type ActionsProps } from './Actions'\nimport { DnsDiagnostics, TlsDiagnostics } from './Diagnostics'\nimport type { DomainMappingRow } from './types'\n\nexport type DomainPanelProps = {\n mapping: DomainMappingRow\n isReplacement: boolean\n cnameTarget: string | null\n aRecordTarget: string | null\n busy?: boolean\n onCheckNow: () => void\n onRetryTls: () => void\n onChangeDomain: () => void\n onRemove: () => void\n}\n\nexport function DomainPanel(props: DomainPanelProps) {\n const t = useT()\n const Stepper = useRegisteredComponent<StepperProps>(\n 'section:customer_accounts.domain-settings:stepper',\n DefaultStepper,\n )\n const StatusBadgeSection = useRegisteredComponent<StatusBadgeSectionProps>(\n 'section:customer_accounts.domain-settings:status',\n DefaultStatusBadge,\n )\n const DnsConfig = useRegisteredComponent<DnsConfigProps>(\n 'section:customer_accounts.domain-settings:dns-config',\n DefaultDnsConfig,\n )\n const Actions = useRegisteredComponent<ActionsProps>(\n 'section:customer_accounts.domain-settings:actions',\n DefaultActions,\n )\n\n const { mapping, isReplacement, cnameTarget, aRecordTarget } = props\n const showDnsConfig = mapping.status !== 'active'\n\n return (\n <section className=\"space-y-4 rounded-xl border bg-card p-6 shadow-sm\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-lg font-semibold tracking-tight\">{mapping.hostname}</h3>\n {isReplacement ? (\n <Tag variant=\"info\" dot>\n {t('customer_accounts.domainMapping.replacement.label', 'Replacement (waiting to activate)')}\n </Tag>\n ) : null}\n </div>\n <StatusBadgeSection mapping={mapping} />\n </div>\n </div>\n\n <Stepper status={mapping.status} />\n\n <DnsDiagnostics mapping={mapping} />\n <TlsDiagnostics mapping={mapping} />\n\n {showDnsConfig ? (\n <DnsConfig\n hostname={mapping.hostname}\n cnameTarget={mapping.cnameTarget ?? cnameTarget}\n aRecordTarget={mapping.aRecordTarget ?? aRecordTarget}\n />\n ) : null}\n\n <Actions\n mapping={mapping}\n isReplacement={isReplacement}\n busy={props.busy}\n onCheckNow={props.onCheckNow}\n onRetryTls={props.onRetryTls}\n onChangeDomain={props.onChangeDomain}\n onRemove={props.onRemove}\n />\n </section>\n )\n}\n\nexport default DomainPanel\n"],
|
|
5
|
+
"mappings": ";AAmDU,SACE,KADF;AAhDV,SAAS,8BAA8B;AACvC,SAAS,WAAW;AACpB,SAAS,YAAY;AACrB,SAAS,sBAAyC;AAClD,SAAS,0BAAwD;AACjE,SAAS,wBAA6C;AACtD,SAAS,sBAAyC;AAClD,SAAS,gBAAgB,sBAAsB;AAexC,SAAS,YAAY,OAAyB;AACnD,QAAM,IAAI,KAAK;AACf,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACA,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,eAAe,aAAa,cAAc,IAAI;AAC/D,QAAM,gBAAgB,QAAQ,WAAW;AAEzC,SACE,qBAAC,aAAQ,WAAU,qDACjB;AAAA,wBAAC,SAAI,WAAU,oDACb,+BAAC,SAAI,WAAU,aACb;AAAA,2BAAC,SAAI,WAAU,qCACb;AAAA,4BAAC,QAAG,WAAU,wCAAwC,kBAAQ,UAAS;AAAA,QACtE,gBACC,oBAAC,OAAI,SAAQ,QAAO,KAAG,MACpB,YAAE,qDAAqD,mCAAmC,GAC7F,IACE;AAAA,SACN;AAAA,MACA,oBAAC,sBAAmB,SAAkB;AAAA,OACxC,GACF;AAAA,IAEA,oBAAC,WAAQ,QAAQ,QAAQ,QAAQ;AAAA,IAEjC,oBAAC,kBAAe,SAAkB;AAAA,IAClC,oBAAC,kBAAe,SAAkB;AAAA,IAEjC,gBACC;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ,eAAe;AAAA,QACpC,eAAe,QAAQ,iBAAiB;AAAA;AAAA,IAC1C,IACE;AAAA,IAEJ;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM;AAAA,QAClB,YAAY,MAAM;AAAA,QAClB,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA;AAAA,IAClB;AAAA,KACF;AAEJ;AAEA,IAAO,sBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
|
+
function OrgSwitcher({ options, selectedId, onChange }) {
|
|
5
|
+
const t = useT();
|
|
6
|
+
if (options.length <= 1) return null;
|
|
7
|
+
return /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
8
|
+
/* @__PURE__ */ jsx("span", { children: t("customer_accounts.domainMapping.orgSwitcher.label", "Organization") }),
|
|
9
|
+
/* @__PURE__ */ jsx(
|
|
10
|
+
"select",
|
|
11
|
+
{
|
|
12
|
+
className: "h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground shadow-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
13
|
+
value: selectedId ?? "",
|
|
14
|
+
onChange: (event) => onChange(event.target.value),
|
|
15
|
+
children: options.map((opt) => {
|
|
16
|
+
const indent = opt.depth > 0 ? "\xA0\xA0".repeat(opt.depth) : "";
|
|
17
|
+
return /* @__PURE__ */ jsxs("option", { value: opt.id, children: [
|
|
18
|
+
indent,
|
|
19
|
+
opt.label
|
|
20
|
+
] }, opt.id);
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
] });
|
|
25
|
+
}
|
|
26
|
+
var OrgSwitcher_default = OrgSwitcher;
|
|
27
|
+
export {
|
|
28
|
+
OrgSwitcher,
|
|
29
|
+
OrgSwitcher_default as default
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=OrgSwitcher.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type OrgOption = {\n id: string\n label: string\n depth: number\n}\n\nexport type OrgSwitcherProps = {\n options: OrgOption[]\n selectedId: string | null\n onChange: (next: string) => void\n}\n\nexport function OrgSwitcher({ options, selectedId, onChange }: OrgSwitcherProps) {\n const t = useT()\n if (options.length <= 1) return null\n return (\n <label className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <span>{t('customer_accounts.domainMapping.orgSwitcher.label', 'Organization')}</span>\n <select\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground shadow-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40\"\n value={selectedId ?? ''}\n onChange={(event) => onChange(event.target.value)}\n >\n {options.map((opt) => {\n const indent = opt.depth > 0 ? '\u00A0\u00A0'.repeat(opt.depth) : ''\n return (\n <option key={opt.id} value={opt.id}>\n {indent}\n {opt.label}\n </option>\n )\n })}\n </select>\n </label>\n )\n}\n\nexport default OrgSwitcher\n"],
|
|
5
|
+
"mappings": ";AAsBM,cASM,YATN;AAnBN,SAAS,YAAY;AAcd,SAAS,YAAY,EAAE,SAAS,YAAY,SAAS,GAAqB;AAC/E,QAAM,IAAI,KAAK;AACf,MAAI,QAAQ,UAAU,EAAG,QAAO;AAChC,SACE,qBAAC,WAAM,WAAU,yDACf;AAAA,wBAAC,UAAM,YAAE,qDAAqD,cAAc,GAAE;AAAA,IAC9E;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,cAAc;AAAA,QACrB,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QAE/C,kBAAQ,IAAI,CAAC,QAAQ;AACpB,gBAAM,SAAS,IAAI,QAAQ,IAAI,WAAK,OAAO,IAAI,KAAK,IAAI;AACxD,iBACE,qBAAC,YAAoB,OAAO,IAAI,IAC7B;AAAA;AAAA,YACA,IAAI;AAAA,eAFM,IAAI,EAGjB;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,sBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogDescription,
|
|
8
|
+
DialogFooter,
|
|
9
|
+
DialogHeader,
|
|
10
|
+
DialogTitle
|
|
11
|
+
} from "@open-mercato/ui/primitives/dialog";
|
|
12
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
13
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
14
|
+
import { Label } from "@open-mercato/ui/primitives/label";
|
|
15
|
+
import { KbdShortcut } from "@open-mercato/ui/primitives/kbd";
|
|
16
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
17
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
18
|
+
const HOSTNAME_BASIC = /^[a-z0-9.-]+$/i;
|
|
19
|
+
function RegisterDialog({
|
|
20
|
+
open,
|
|
21
|
+
onOpenChange,
|
|
22
|
+
mode,
|
|
23
|
+
currentHostname,
|
|
24
|
+
onSubmit,
|
|
25
|
+
initialError
|
|
26
|
+
}) {
|
|
27
|
+
const t = useT();
|
|
28
|
+
const [hostname, setHostname] = React.useState("");
|
|
29
|
+
const [error, setError] = React.useState(initialError ?? null);
|
|
30
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
31
|
+
const inputRef = React.useRef(null);
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
if (!open) {
|
|
34
|
+
setHostname("");
|
|
35
|
+
setError(null);
|
|
36
|
+
setSubmitting(false);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
setError(initialError ?? null);
|
|
40
|
+
window.setTimeout(() => inputRef.current?.focus(), 0);
|
|
41
|
+
}, [open, initialError]);
|
|
42
|
+
const validate = React.useCallback(
|
|
43
|
+
(value) => {
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
if (!trimmed) return t("customer_accounts.domainMapping.errors.required", "Domain is required");
|
|
46
|
+
if (trimmed.length > 253) return t("customer_accounts.domainMapping.hostname.validation.invalid", "Enter a valid domain name (no protocol, path, or port)");
|
|
47
|
+
if (trimmed.includes("://") || trimmed.includes("/") || trimmed.includes(":")) {
|
|
48
|
+
return t("customer_accounts.domainMapping.hostname.validation.invalid", "Enter a valid domain name (no protocol, path, or port)");
|
|
49
|
+
}
|
|
50
|
+
if (!HOSTNAME_BASIC.test(trimmed) && !/^xn--/i.test(trimmed)) {
|
|
51
|
+
return t("customer_accounts.domainMapping.hostname.validation.invalid", "Enter a valid domain name (no protocol, path, or port)");
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
},
|
|
55
|
+
[t]
|
|
56
|
+
);
|
|
57
|
+
const handleSubmit = React.useCallback(
|
|
58
|
+
async (event) => {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
const validation = validate(hostname);
|
|
61
|
+
if (validation) {
|
|
62
|
+
setError(validation);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
setSubmitting(true);
|
|
66
|
+
setError(null);
|
|
67
|
+
try {
|
|
68
|
+
await onSubmit(hostname.trim());
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : "Submission failed";
|
|
71
|
+
setError(message);
|
|
72
|
+
} finally {
|
|
73
|
+
setSubmitting(false);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
[hostname, onSubmit, validate]
|
|
77
|
+
);
|
|
78
|
+
const handleKeyDown = React.useCallback(
|
|
79
|
+
(event) => {
|
|
80
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
handleSubmit(event);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[handleSubmit]
|
|
86
|
+
);
|
|
87
|
+
const title = mode === "change" ? t("customer_accounts.domainMapping.dialog.change.title", "Change custom domain") : t("customer_accounts.domainMapping.dialog.register.title", "Register custom domain");
|
|
88
|
+
const description = mode === "change" && currentHostname ? t(
|
|
89
|
+
"customer_accounts.domainMapping.changeDomain.description",
|
|
90
|
+
"Your current domain ({hostname}) will stay active until the new domain is fully set up. No downtime for your customers.",
|
|
91
|
+
{ hostname: currentHostname }
|
|
92
|
+
) : t("customer_accounts.domainMapping.description", "Map your own domain to the customer portal");
|
|
93
|
+
const submitLabel = mode === "change" ? t("customer_accounts.domainMapping.dialog.change.submit", "Start replacement") : t("customer_accounts.domainMapping.dialog.register.submit", "Register");
|
|
94
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
95
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
96
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: title }),
|
|
97
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: description })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsxs("form", { className: "space-y-4", onSubmit: handleSubmit, onKeyDown: handleKeyDown, children: [
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
101
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "custom-domain-hostname", children: t("customer_accounts.domainMapping.hostname.label", "Domain") }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
Input,
|
|
104
|
+
{
|
|
105
|
+
id: "custom-domain-hostname",
|
|
106
|
+
ref: inputRef,
|
|
107
|
+
value: hostname,
|
|
108
|
+
autoComplete: "off",
|
|
109
|
+
spellCheck: false,
|
|
110
|
+
placeholder: t("customer_accounts.domainMapping.hostname.placeholder", "e.g., shop.yourdomain.com"),
|
|
111
|
+
onChange: (event) => {
|
|
112
|
+
setHostname(event.target.value);
|
|
113
|
+
if (error) setError(null);
|
|
114
|
+
},
|
|
115
|
+
"aria-invalid": error ? true : void 0,
|
|
116
|
+
disabled: submitting
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-status-error-text", role: "alert", children: error }) : null
|
|
120
|
+
] }),
|
|
121
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: "items-center", children: [
|
|
122
|
+
/* @__PURE__ */ jsx("span", { className: "hidden text-xs text-muted-foreground sm:inline-flex sm:items-center sm:gap-1", children: /* @__PURE__ */ jsx(KbdShortcut, { keys: ["\u2318", "Enter"] }) }),
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
Button,
|
|
125
|
+
{
|
|
126
|
+
type: "button",
|
|
127
|
+
variant: "ghost",
|
|
128
|
+
size: "default",
|
|
129
|
+
onClick: () => onOpenChange(false),
|
|
130
|
+
disabled: submitting,
|
|
131
|
+
children: t("customer_accounts.domainMapping.dialog.cancel", "Cancel")
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
/* @__PURE__ */ jsxs(Button, { type: "submit", size: "default", disabled: submitting, children: [
|
|
135
|
+
submitting ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : null,
|
|
136
|
+
submitLabel
|
|
137
|
+
] })
|
|
138
|
+
] })
|
|
139
|
+
] })
|
|
140
|
+
] }) });
|
|
141
|
+
}
|
|
142
|
+
var RegisterDialog_default = RegisterDialog;
|
|
143
|
+
export {
|
|
144
|
+
RegisterDialog,
|
|
145
|
+
RegisterDialog_default as default
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=RegisterDialog.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { KbdShortcut } from '@open-mercato/ui/primitives/kbd'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type RegisterDialogProps = {\n open: boolean\n onOpenChange: (next: boolean) => void\n mode: 'register' | 'change'\n currentHostname?: string | null\n onSubmit: (hostname: string) => Promise<void>\n initialError?: string | null\n}\n\nconst HOSTNAME_BASIC = /^[a-z0-9.-]+$/i\n\nexport function RegisterDialog({\n open,\n onOpenChange,\n mode,\n currentHostname,\n onSubmit,\n initialError,\n}: RegisterDialogProps) {\n const t = useT()\n const [hostname, setHostname] = React.useState('')\n const [error, setError] = React.useState<string | null>(initialError ?? null)\n const [submitting, setSubmitting] = React.useState(false)\n const inputRef = React.useRef<HTMLInputElement>(null)\n\n React.useEffect(() => {\n if (!open) {\n setHostname('')\n setError(null)\n setSubmitting(false)\n return\n }\n setError(initialError ?? null)\n window.setTimeout(() => inputRef.current?.focus(), 0)\n }, [open, initialError])\n\n const validate = React.useCallback(\n (value: string): string | null => {\n const trimmed = value.trim()\n if (!trimmed) return t('customer_accounts.domainMapping.errors.required', 'Domain is required')\n if (trimmed.length > 253) return t('customer_accounts.domainMapping.hostname.validation.invalid', 'Enter a valid domain name (no protocol, path, or port)')\n if (trimmed.includes('://') || trimmed.includes('/') || trimmed.includes(':')) {\n return t('customer_accounts.domainMapping.hostname.validation.invalid', 'Enter a valid domain name (no protocol, path, or port)')\n }\n if (!HOSTNAME_BASIC.test(trimmed) && !/^xn--/i.test(trimmed)) {\n return t('customer_accounts.domainMapping.hostname.validation.invalid', 'Enter a valid domain name (no protocol, path, or port)')\n }\n return null\n },\n [t],\n )\n\n const handleSubmit = React.useCallback(\n async (event: React.FormEvent) => {\n event.preventDefault()\n const validation = validate(hostname)\n if (validation) {\n setError(validation)\n return\n }\n setSubmitting(true)\n setError(null)\n try {\n await onSubmit(hostname.trim())\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Submission failed'\n setError(message)\n } finally {\n setSubmitting(false)\n }\n },\n [hostname, onSubmit, validate],\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSubmit(event)\n }\n },\n [handleSubmit],\n )\n\n const title =\n mode === 'change'\n ? t('customer_accounts.domainMapping.dialog.change.title', 'Change custom domain')\n : t('customer_accounts.domainMapping.dialog.register.title', 'Register custom domain')\n\n const description =\n mode === 'change' && currentHostname\n ? t(\n 'customer_accounts.domainMapping.changeDomain.description',\n 'Your current domain ({hostname}) will stay active until the new domain is fully set up. No downtime for your customers.',\n { hostname: currentHostname },\n )\n : t('customer_accounts.domainMapping.description', 'Map your own domain to the customer portal')\n\n const submitLabel =\n mode === 'change'\n ? t('customer_accounts.domainMapping.dialog.change.submit', 'Start replacement')\n : t('customer_accounts.domainMapping.dialog.register.submit', 'Register')\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <form className=\"space-y-4\" onSubmit={handleSubmit} onKeyDown={handleKeyDown}>\n <div className=\"space-y-2\">\n <Label htmlFor=\"custom-domain-hostname\">\n {t('customer_accounts.domainMapping.hostname.label', 'Domain')}\n </Label>\n <Input\n id=\"custom-domain-hostname\"\n ref={inputRef}\n value={hostname}\n autoComplete=\"off\"\n spellCheck={false}\n placeholder={t('customer_accounts.domainMapping.hostname.placeholder', 'e.g., shop.yourdomain.com')}\n onChange={(event) => {\n setHostname(event.target.value)\n if (error) setError(null)\n }}\n aria-invalid={error ? true : undefined}\n disabled={submitting}\n />\n {error ? (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n <DialogFooter className=\"items-center\">\n <span className=\"hidden text-xs text-muted-foreground sm:inline-flex sm:items-center sm:gap-1\">\n <KbdShortcut keys={['\u2318', 'Enter']} />\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"default\"\n onClick={() => onOpenChange(false)}\n disabled={submitting}\n >\n {t('customer_accounts.domainMapping.dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" size=\"default\" disabled={submitting}>\n {submitting ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {submitLabel}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default RegisterDialog\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|