@open-mercato/core 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3287.1.450f4ffb56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/domain_mapping/index.js +31 -0
  3. package/dist/generated/entities/domain_mapping/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +16 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/auth.js +1 -1
  9. package/dist/helpers/integration/auth.js.map +2 -2
  10. package/dist/helpers/integration/authUi.js +1 -1
  11. package/dist/helpers/integration/authUi.js.map +2 -2
  12. package/dist/helpers/integration/salesUi.js +18 -17
  13. package/dist/helpers/integration/salesUi.js.map +2 -2
  14. package/dist/modules/auth/frontend/login.js +4 -4
  15. package/dist/modules/auth/frontend/login.js.map +2 -2
  16. package/dist/modules/auth/frontend/reset/[token]/page.js +5 -5
  17. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  18. package/dist/modules/auth/frontend/reset.js +2 -2
  19. package/dist/modules/auth/frontend/reset.js.map +2 -2
  20. package/dist/modules/customer_accounts/acl.js +2 -1
  21. package/dist/modules/customer_accounts/acl.js.map +2 -2
  22. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
  23. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
  24. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
  25. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
  26. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
  27. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
  28. package/dist/modules/customer_accounts/api/domain-check.js +70 -0
  29. package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
  30. package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
  31. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
  32. package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
  33. package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
  34. package/dist/modules/customer_accounts/api/login.js +14 -3
  35. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  36. package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
  37. package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
  38. package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
  39. package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
  40. package/dist/modules/customer_accounts/api/signup.js +32 -5
  41. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  42. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
  43. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
  44. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
  45. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
  46. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
  47. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
  48. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
  49. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
  50. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
  51. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
  52. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
  53. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
  55. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
  56. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
  57. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
  58. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
  59. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
  60. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
  61. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
  62. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
  63. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
  64. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
  65. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
  66. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +4 -3
  67. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  68. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +6 -5
  69. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  70. package/dist/modules/customer_accounts/data/enrichers.js +78 -12
  71. package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
  72. package/dist/modules/customer_accounts/data/entities.js +85 -2
  73. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  74. package/dist/modules/customer_accounts/data/guards.js +130 -0
  75. package/dist/modules/customer_accounts/data/guards.js.map +7 -0
  76. package/dist/modules/customer_accounts/data/validators.js +19 -0
  77. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  78. package/dist/modules/customer_accounts/di.js +23 -1
  79. package/dist/modules/customer_accounts/di.js.map +2 -2
  80. package/dist/modules/customer_accounts/events.js +9 -2
  81. package/dist/modules/customer_accounts/events.js.map +2 -2
  82. package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
  83. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  84. package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
  85. package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
  86. package/dist/modules/customer_accounts/lib/hostname.js +55 -0
  87. package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
  88. package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
  89. package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
  90. package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
  91. package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
  92. package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
  93. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
  94. package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
  95. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
  96. package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
  97. package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
  98. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
  99. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
  100. package/dist/modules/customer_accounts/notifications.client.js +76 -0
  101. package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
  102. package/dist/modules/customer_accounts/notifications.js +76 -0
  103. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  104. package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
  105. package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
  106. package/dist/modules/customer_accounts/setup.js +48 -1
  107. package/dist/modules/customer_accounts/setup.js.map +2 -2
  108. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
  109. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
  110. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
  111. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
  112. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
  113. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
  114. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
  115. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
  116. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
  117. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
  118. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
  119. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  120. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
  121. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
  122. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
  123. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
  124. package/dist/modules/customers/api/companies/route.js +4 -9
  125. package/dist/modules/customers/api/companies/route.js.map +2 -2
  126. package/dist/modules/customers/api/deals/route.js +4 -9
  127. package/dist/modules/customers/api/deals/route.js.map +2 -2
  128. package/dist/modules/customers/api/people/route.js +4 -9
  129. package/dist/modules/customers/api/people/route.js.map +2 -2
  130. package/dist/modules/customers/api/utils.js +0 -12
  131. package/dist/modules/customers/api/utils.js.map +2 -2
  132. package/dist/modules/customers/backend/customers/companies/page.js +361 -284
  133. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/deals/page.js +378 -473
  135. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  136. package/dist/modules/customers/backend/customers/people/page.js +368 -286
  137. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  138. package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
  139. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  140. package/dist/modules/customers/components/formConfig.js +2 -2
  141. package/dist/modules/customers/components/formConfig.js.map +2 -2
  142. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
  143. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  144. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
  145. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
  146. package/dist/modules/customers/widgets/injection-table.js +6 -0
  147. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  148. package/dist/modules/integrations/backend/integrations/[id]/page.js +2 -3
  149. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  150. package/dist/modules/perspectives/services/perspectiveService.js +16 -2
  151. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  152. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +5 -3
  153. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  154. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +3 -3
  155. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
  156. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +4 -2
  157. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
  158. package/dist/modules/query_index/lib/engine.js +31 -8
  159. package/dist/modules/query_index/lib/engine.js.map +2 -2
  160. package/dist/modules/sales/backend/sales/documents/[id]/page.js +3 -5
  161. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  162. package/dist/modules/sales/components/ProviderFieldInput.js +2 -2
  163. package/dist/modules/sales/components/ProviderFieldInput.js.map +2 -2
  164. package/dist/modules/sales/components/documents/SalesDocumentForm.js +3 -4
  165. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  166. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js +3 -4
  167. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js.map +2 -2
  168. package/dist/modules/workflows/backend/tasks/[id]/page.js +2 -2
  169. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  170. package/dist/modules/workflows/frontend/checkout-demo/page.js +2 -2
  171. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  172. package/generated/entities/domain_mapping/index.ts +14 -0
  173. package/generated/entities.ids.generated.ts +2 -1
  174. package/generated/entity-fields-registry.ts +16 -0
  175. package/package.json +5 -5
  176. package/src/helpers/integration/auth.ts +1 -1
  177. package/src/helpers/integration/authUi.ts +1 -1
  178. package/src/helpers/integration/salesUi.ts +27 -23
  179. package/src/modules/auth/frontend/login.tsx +4 -3
  180. package/src/modules/auth/frontend/reset/[token]/page.tsx +4 -4
  181. package/src/modules/auth/frontend/reset.tsx +2 -2
  182. package/src/modules/customer_accounts/acl.ts +1 -0
  183. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
  184. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
  185. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
  186. package/src/modules/customer_accounts/api/domain-check.ts +83 -0
  187. package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
  188. package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
  189. package/src/modules/customer_accounts/api/login.ts +14 -3
  190. package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
  191. package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
  192. package/src/modules/customer_accounts/api/signup.ts +36 -5
  193. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
  194. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
  195. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
  196. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
  197. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
  198. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
  199. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
  200. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
  201. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
  202. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
  203. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
  204. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
  205. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +3 -2
  206. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +5 -4
  207. package/src/modules/customer_accounts/data/enrichers.ts +95 -12
  208. package/src/modules/customer_accounts/data/entities.ts +80 -1
  209. package/src/modules/customer_accounts/data/guards.ts +155 -0
  210. package/src/modules/customer_accounts/data/validators.ts +26 -0
  211. package/src/modules/customer_accounts/di.ts +35 -1
  212. package/src/modules/customer_accounts/events.ts +8 -1
  213. package/src/modules/customer_accounts/i18n/de.json +78 -0
  214. package/src/modules/customer_accounts/i18n/en.json +78 -0
  215. package/src/modules/customer_accounts/i18n/es.json +78 -0
  216. package/src/modules/customer_accounts/i18n/pl.json +78 -0
  217. package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
  218. package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
  219. package/src/modules/customer_accounts/lib/hostname.ts +76 -0
  220. package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
  221. package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
  222. package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
  223. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
  224. package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
  225. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
  226. package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
  227. package/src/modules/customer_accounts/notifications.client.ts +76 -0
  228. package/src/modules/customer_accounts/notifications.ts +76 -0
  229. package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
  230. package/src/modules/customer_accounts/setup.ts +81 -1
  231. package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
  232. package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
  233. package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
  234. package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
  235. package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
  236. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
  237. package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
  238. package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
  239. package/src/modules/customers/api/companies/route.ts +4 -9
  240. package/src/modules/customers/api/deals/route.ts +4 -9
  241. package/src/modules/customers/api/people/route.ts +4 -9
  242. package/src/modules/customers/api/utils.ts +0 -14
  243. package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
  244. package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
  245. package/src/modules/customers/backend/customers/people/page.tsx +331 -240
  246. package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
  247. package/src/modules/customers/components/formConfig.tsx +2 -2
  248. package/src/modules/customers/i18n/de.json +31 -0
  249. package/src/modules/customers/i18n/en.json +31 -0
  250. package/src/modules/customers/i18n/es.json +31 -0
  251. package/src/modules/customers/i18n/pl.json +31 -0
  252. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
  253. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
  254. package/src/modules/customers/widgets/injection-table.ts +6 -0
  255. package/src/modules/integrations/backend/integrations/[id]/page.tsx +2 -2
  256. package/src/modules/perspectives/services/perspectiveService.ts +30 -2
  257. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +5 -3
  258. package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +3 -3
  259. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +4 -2
  260. package/src/modules/query_index/lib/engine.ts +46 -8
  261. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +3 -5
  262. package/src/modules/sales/components/ProviderFieldInput.tsx +2 -2
  263. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +2 -3
  264. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.tsx +3 -4
  265. package/src/modules/workflows/backend/tasks/[id]/page.tsx +2 -2
  266. package/src/modules/workflows/frontend/checkout-demo/page.tsx +2 -2
@@ -0,0 +1,56 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import { domainStatusMap } from './domainStatusMap'
7
+ import type { DomainMappingRow } from './types'
8
+
9
+ export type StatusBadgeSectionProps = {
10
+ mapping: DomainMappingRow
11
+ }
12
+
13
+ function formatTime(value: string | null, fallback: string): string {
14
+ if (!value) return fallback
15
+ const date = new Date(value)
16
+ if (Number.isNaN(date.getTime())) return fallback
17
+ return date.toLocaleString()
18
+ }
19
+
20
+ export function DefaultStatusBadge({ mapping }: StatusBadgeSectionProps) {
21
+ const t = useT()
22
+ const variant = domainStatusMap[mapping.status]
23
+ const label = t(
24
+ `customer_accounts.domainMapping.status.${mapping.status}`,
25
+ mapping.status,
26
+ )
27
+ const lastChecked = formatTime(
28
+ mapping.lastDnsCheckAt,
29
+ t('customer_accounts.domainMapping.autoVerify.checking', 'Automatically checking every 5 minutes'),
30
+ )
31
+ const showAutoCheck = mapping.status === 'pending' || mapping.status === 'dns_failed'
32
+
33
+ return (
34
+ <div className="flex flex-wrap items-center gap-3">
35
+ <StatusBadge variant={variant} dot>
36
+ {label}
37
+ </StatusBadge>
38
+ {mapping.lastDnsCheckAt ? (
39
+ <span className="text-xs text-muted-foreground">
40
+ {t('customer_accounts.domainMapping.autoVerify.lastChecked', 'Last checked: {time}', {
41
+ time: lastChecked,
42
+ })}
43
+ </span>
44
+ ) : null}
45
+ {showAutoCheck ? (
46
+ <span className="text-xs text-muted-foreground">
47
+ {t('customer_accounts.domainMapping.autoVerify.nextCheck', 'Next check in ~{minutes} minutes', {
48
+ minutes: '5',
49
+ })}
50
+ </span>
51
+ ) : null}
52
+ </div>
53
+ )
54
+ }
55
+
56
+ export default DefaultStatusBadge
@@ -0,0 +1,69 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { Check } from 'lucide-react'
5
+ import { cn } from '@open-mercato/shared/lib/utils'
6
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
7
+ import type { DomainStatus } from '@open-mercato/core/modules/customer_accounts/data/entities'
8
+
9
+ export type StepperProps = {
10
+ status: DomainStatus | null
11
+ }
12
+
13
+ function activeStep(status: DomainStatus | null): number {
14
+ if (!status) return 1
15
+ if (status === 'pending' || status === 'dns_failed') return 2
16
+ if (status === 'verified' || status === 'tls_failed') return 3
17
+ if (status === 'active') return 4
18
+ return 1
19
+ }
20
+
21
+ export function DefaultStepper({ status }: StepperProps) {
22
+ const t = useT()
23
+ const current = activeStep(status)
24
+ const steps: Array<{ idx: number; label: string }> = [
25
+ { idx: 1, label: t('customer_accounts.domainMapping.stepper.step1', 'Register Domain') },
26
+ { idx: 2, label: t('customer_accounts.domainMapping.stepper.step2', 'Configure DNS') },
27
+ { idx: 3, label: t('customer_accounts.domainMapping.stepper.step3', 'SSL Certificate') },
28
+ { idx: 4, label: t('customer_accounts.domainMapping.stepper.step4', 'Live') },
29
+ ]
30
+
31
+ return (
32
+ <ol className="flex flex-wrap items-center gap-3 text-sm">
33
+ {steps.map((step, index) => {
34
+ const isDone = step.idx < current
35
+ const isCurrent = step.idx === current
36
+ return (
37
+ <li key={step.idx} className="flex items-center gap-3">
38
+ <div className="flex items-center gap-2">
39
+ <span
40
+ className={cn(
41
+ 'flex h-7 w-7 items-center justify-center rounded-full border text-xs font-medium',
42
+ isDone && 'border-status-success-icon bg-status-success-icon text-status-success-foreground',
43
+ isCurrent && 'border-primary bg-primary/10 text-primary',
44
+ !isDone && !isCurrent && 'border-border bg-background text-muted-foreground',
45
+ )}
46
+ aria-current={isCurrent ? 'step' : undefined}
47
+ >
48
+ {isDone ? <Check className="h-4 w-4" aria-hidden /> : step.idx}
49
+ </span>
50
+ <span
51
+ className={cn(
52
+ 'whitespace-nowrap',
53
+ isCurrent ? 'font-medium text-foreground' : 'text-muted-foreground',
54
+ )}
55
+ >
56
+ {step.label}
57
+ </span>
58
+ </div>
59
+ {index < steps.length - 1 ? (
60
+ <span className="hidden h-px w-8 bg-border sm:block" aria-hidden />
61
+ ) : null}
62
+ </li>
63
+ )
64
+ })}
65
+ </ol>
66
+ )
67
+ }
68
+
69
+ export default DefaultStepper
@@ -0,0 +1,10 @@
1
+ import type { StatusMap } from '@open-mercato/ui/primitives/status-badge'
2
+ import type { DomainStatus } from '@open-mercato/core/modules/customer_accounts/data/entities'
3
+
4
+ export const domainStatusMap: StatusMap<DomainStatus> = {
5
+ pending: 'info',
6
+ verified: 'info',
7
+ active: 'success',
8
+ dns_failed: 'error',
9
+ tls_failed: 'warning',
10
+ }
@@ -0,0 +1,31 @@
1
+ import type { DomainStatus } from '@open-mercato/core/modules/customer_accounts/data/entities'
2
+
3
+ export type DomainMappingRow = {
4
+ id: string
5
+ hostname: string
6
+ organizationId: string
7
+ tenantId: string
8
+ provider: string
9
+ status: DomainStatus
10
+ verifiedAt: string | null
11
+ lastDnsCheckAt: string | null
12
+ dnsFailureReason: string | null
13
+ tlsFailureReason: string | null
14
+ tlsRetryCount: number
15
+ cnameTarget: string | null
16
+ aRecordTarget: string | null
17
+ createdAt: string
18
+ updatedAt: string | null
19
+ }
20
+
21
+ export type DomainConfig = {
22
+ cnameTarget: string | null
23
+ aRecordTarget: string | null
24
+ }
25
+
26
+ export type DomainListResponse = {
27
+ ok: boolean
28
+ domainMappings?: DomainMappingRow[]
29
+ config?: DomainConfig
30
+ error?: string
31
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+
3
+ const globeIcon = React.createElement(
4
+ 'svg',
5
+ { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
6
+ React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
7
+ React.createElement('path', { d: 'M2 12h20' }),
8
+ React.createElement('path', { d: 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z' }),
9
+ )
10
+
11
+ export const metadata = {
12
+ requireAuth: true,
13
+ requireFeatures: ['customer_accounts.domain.manage'],
14
+ pageTitle: 'Custom Domain',
15
+ pageTitleKey: 'customer_accounts.domainMapping.title',
16
+ pageGroup: 'Module Configs',
17
+ pageGroupKey: 'settings.sections.moduleConfigs',
18
+ pageOrder: 60,
19
+ icon: globeIcon,
20
+ pageContext: 'settings' as const,
21
+ breadcrumb: [
22
+ { label: 'Custom Domain', labelKey: 'customer_accounts.domainMapping.title' },
23
+ ],
24
+ }
25
+
26
+ export default metadata
@@ -0,0 +1,411 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { useRouter, useSearchParams } from 'next/navigation'
5
+ import { Globe } from 'lucide-react'
6
+ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
+ import { FormHeader } from '@open-mercato/ui/backend/forms'
8
+ import { EmptyState } from '@open-mercato/ui/backend/EmptyState'
9
+ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
10
+ import { Button } from '@open-mercato/ui/primitives/button'
11
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
13
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
14
+ import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
15
+ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
16
+ import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'
17
+ import { OrgSwitcher, type OrgOption } from './components/OrgSwitcher'
18
+ import { DomainPanel } from './components/DomainPanel'
19
+ import { RegisterDialog } from './components/RegisterDialog'
20
+ import type { DomainListResponse, DomainMappingRow, DomainConfig } from './components/types'
21
+
22
+ const STATE_LOADING = 'loading'
23
+ const STATE_LOADED = 'loaded'
24
+ const STATE_ERROR = 'error'
25
+ type LoadState = typeof STATE_LOADING | typeof STATE_LOADED | typeof STATE_ERROR
26
+
27
+ type OrgSwitcherNode = {
28
+ id: string
29
+ name: string
30
+ depth: number
31
+ selectable: boolean
32
+ children: OrgSwitcherNode[]
33
+ }
34
+
35
+ type OrgSwitcherResponse = {
36
+ items?: OrgSwitcherNode[]
37
+ selectedId?: string | null
38
+ }
39
+
40
+ function flattenOrgNodes(nodes: OrgSwitcherNode[] | undefined, acc: OrgOption[] = [], depth = 0): OrgOption[] {
41
+ if (!nodes) return acc
42
+ for (const node of nodes) {
43
+ if (node.selectable) {
44
+ acc.push({ id: node.id, label: node.name, depth })
45
+ }
46
+ flattenOrgNodes(node.children, acc, depth + 1)
47
+ }
48
+ return acc
49
+ }
50
+
51
+ export default function CustomerDomainSettingsPage() {
52
+ const t = useT()
53
+ const router = useRouter()
54
+ const searchParams = useSearchParams()
55
+
56
+ const [orgOptions, setOrgOptions] = React.useState<OrgOption[]>([])
57
+ const [defaultOrgId, setDefaultOrgId] = React.useState<string | null>(null)
58
+ const [orgsLoaded, setOrgsLoaded] = React.useState(false)
59
+
60
+ const urlOrgId = searchParams?.get('org') ?? null
61
+ const selectedOrgId = urlOrgId ?? defaultOrgId
62
+
63
+ const [mappings, setMappings] = React.useState<DomainMappingRow[]>([])
64
+ const [config, setConfig] = React.useState<DomainConfig>({ cnameTarget: null, aRecordTarget: null })
65
+ const [loadState, setLoadState] = React.useState<LoadState>(STATE_LOADING)
66
+ const [reloadToken, setReloadToken] = React.useState(0)
67
+ const [registerOpen, setRegisterOpen] = React.useState(false)
68
+ const [registerMode, setRegisterMode] = React.useState<'register' | 'change'>('register')
69
+ const [replaceTargetId, setReplaceTargetId] = React.useState<string | null>(null)
70
+ const [isMutating, setIsMutating] = React.useState(false)
71
+
72
+ const { confirm, ConfirmDialogElement } = useConfirmDialog()
73
+ const { runMutation } = useGuardedMutation<{ entityType: string; entityId?: string }>({
74
+ contextId: 'customer_accounts:domain-settings',
75
+ })
76
+
77
+ const runMutationWithContext = React.useCallback(
78
+ async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {
79
+ return runMutation({
80
+ operation,
81
+ mutationPayload,
82
+ context: { entityType: 'customer_accounts:domain_mapping' },
83
+ })
84
+ },
85
+ [runMutation],
86
+ )
87
+
88
+ const refresh = React.useCallback(() => {
89
+ setReloadToken((token) => token + 1)
90
+ }, [])
91
+
92
+ React.useEffect(() => {
93
+ let cancelled = false
94
+ async function loadOrgs() {
95
+ try {
96
+ const call = await apiCall<OrgSwitcherResponse>('/api/directory/organization-switcher')
97
+ if (cancelled) return
98
+ if (!call.ok || !call.result) {
99
+ setOrgOptions([])
100
+ setDefaultOrgId(null)
101
+ return
102
+ }
103
+ const flat = flattenOrgNodes(call.result.items, [], 0)
104
+ setOrgOptions(flat)
105
+ const fallback = call.result.selectedId ?? (flat[0]?.id ?? null)
106
+ setDefaultOrgId(fallback)
107
+ } catch {
108
+ if (!cancelled) {
109
+ setOrgOptions([])
110
+ setDefaultOrgId(null)
111
+ }
112
+ } finally {
113
+ if (!cancelled) setOrgsLoaded(true)
114
+ }
115
+ }
116
+ loadOrgs()
117
+ return () => {
118
+ cancelled = true
119
+ }
120
+ }, [])
121
+
122
+ React.useEffect(() => {
123
+ if (!orgsLoaded) return
124
+ if (!selectedOrgId) {
125
+ setMappings([])
126
+ setConfig({ cnameTarget: null, aRecordTarget: null })
127
+ setLoadState(STATE_LOADED)
128
+ return
129
+ }
130
+ let cancelled = false
131
+ async function load() {
132
+ setLoadState(STATE_LOADING)
133
+ try {
134
+ const call = await apiCall<DomainListResponse>(
135
+ `/api/customer_accounts/admin/domain-mappings?organizationId=${encodeURIComponent(selectedOrgId!)}`,
136
+ )
137
+ if (cancelled) return
138
+ if (!call.ok || !call.result?.ok) {
139
+ setLoadState(STATE_ERROR)
140
+ return
141
+ }
142
+ setMappings(Array.isArray(call.result.domainMappings) ? call.result.domainMappings : [])
143
+ setConfig(call.result.config ?? { cnameTarget: null, aRecordTarget: null })
144
+ setLoadState(STATE_LOADED)
145
+ } catch {
146
+ if (!cancelled) setLoadState(STATE_ERROR)
147
+ }
148
+ }
149
+ load()
150
+ return () => {
151
+ cancelled = true
152
+ }
153
+ }, [orgsLoaded, selectedOrgId, reloadToken])
154
+
155
+ useAppEvent('customer_accounts.domain_mapping.*', () => {
156
+ refresh()
157
+ }, [refresh])
158
+
159
+ const orderedMappings = React.useMemo(() => {
160
+ if (mappings.length === 0) return []
161
+ const sorted = [...mappings].sort((a, b) => {
162
+ const aActive = a.status === 'active' ? 0 : 1
163
+ const bActive = b.status === 'active' ? 0 : 1
164
+ if (aActive !== bActive) return aActive - bActive
165
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
166
+ })
167
+ return sorted
168
+ }, [mappings])
169
+
170
+ const primaryActiveId = React.useMemo(() => {
171
+ const active = orderedMappings.find((m) => m.status === 'active')
172
+ if (active) return active.id
173
+ return orderedMappings[0]?.id ?? null
174
+ }, [orderedMappings])
175
+
176
+ const handleOrgChange = React.useCallback(
177
+ (next: string) => {
178
+ const params = new URLSearchParams(searchParams?.toString() ?? '')
179
+ if (next) params.set('org', next)
180
+ else params.delete('org')
181
+ const qs = params.toString()
182
+ router.replace(qs ? `?${qs}` : '?', { scroll: false })
183
+ },
184
+ [router, searchParams],
185
+ )
186
+
187
+ const handleOpenRegister = React.useCallback(() => {
188
+ setRegisterMode('register')
189
+ setReplaceTargetId(null)
190
+ setRegisterOpen(true)
191
+ }, [])
192
+
193
+ const handleOpenChange = React.useCallback((mappingId: string) => {
194
+ setRegisterMode('change')
195
+ setReplaceTargetId(mappingId)
196
+ setRegisterOpen(true)
197
+ }, [])
198
+
199
+ const handleSubmitRegister = React.useCallback(
200
+ async (hostname: string) => {
201
+ if (!selectedOrgId) {
202
+ throw new Error(t('customer_accounts.domainMapping.error.load', 'Could not load custom-domain configuration'))
203
+ }
204
+ setIsMutating(true)
205
+ try {
206
+ await runMutationWithContext(async () => {
207
+ const body: Record<string, unknown> = {
208
+ hostname,
209
+ organizationId: selectedOrgId,
210
+ }
211
+ if (registerMode === 'change' && replaceTargetId) {
212
+ body.replacesDomainId = replaceTargetId
213
+ }
214
+ const call = await apiCall<{ ok: boolean; error?: string; domainMapping?: DomainMappingRow }>(
215
+ '/api/customer_accounts/admin/domain-mappings',
216
+ {
217
+ method: 'POST',
218
+ headers: { 'content-type': 'application/json' },
219
+ body: JSON.stringify(body),
220
+ },
221
+ )
222
+ if (!call.ok || !call.result?.ok) {
223
+ const message = call.result?.error
224
+ || t('customer_accounts.domainMapping.alreadyClaimed', 'This domain is not available. Please choose a different hostname.')
225
+ throw new Error(message)
226
+ }
227
+ flash(t('customer_accounts.domainMapping.registered', 'Domain registered successfully'), 'success')
228
+ setRegisterOpen(false)
229
+ refresh()
230
+ })
231
+ } finally {
232
+ setIsMutating(false)
233
+ }
234
+ },
235
+ [refresh, registerMode, replaceTargetId, runMutationWithContext, selectedOrgId, t],
236
+ )
237
+
238
+ const handleCheckNow = React.useCallback(
239
+ async (mapping: DomainMappingRow) => {
240
+ setIsMutating(true)
241
+ try {
242
+ await runMutationWithContext(async () => {
243
+ const call = await apiCall<{ ok: boolean; error?: string }>(
244
+ `/api/customer_accounts/admin/domain-mappings/${encodeURIComponent(mapping.id)}/verify`,
245
+ { method: 'POST' },
246
+ )
247
+ if (!call.ok || !call.result?.ok) {
248
+ const message = call.result?.error || t('customer_accounts.domainMapping.error.load', 'Could not load custom-domain configuration')
249
+ flash(message, 'error')
250
+ return
251
+ }
252
+ refresh()
253
+ })
254
+ } finally {
255
+ setIsMutating(false)
256
+ }
257
+ },
258
+ [refresh, runMutationWithContext, t],
259
+ )
260
+
261
+ const handleRetryTls = React.useCallback(
262
+ async (mapping: DomainMappingRow) => {
263
+ setIsMutating(true)
264
+ try {
265
+ await runMutationWithContext(async () => {
266
+ const call = await apiCall<{ ok: boolean; error?: string }>(
267
+ `/api/customer_accounts/admin/domain-mappings/${encodeURIComponent(mapping.id)}/health-check`,
268
+ { method: 'POST' },
269
+ )
270
+ if (!call.ok || !call.result?.ok) {
271
+ const message = call.result?.error || t('customer_accounts.domainMapping.error.load', 'Could not load custom-domain configuration')
272
+ flash(message, 'error')
273
+ return
274
+ }
275
+ refresh()
276
+ })
277
+ } finally {
278
+ setIsMutating(false)
279
+ }
280
+ },
281
+ [refresh, runMutationWithContext, t],
282
+ )
283
+
284
+ const handleRemove = React.useCallback(
285
+ async (mapping: DomainMappingRow) => {
286
+ const replacement = orderedMappings.find(
287
+ (m) => m.id !== mapping.id && m.status !== 'active',
288
+ )
289
+ const text = replacement
290
+ ? t('customer_accounts.domainMapping.removeConfirm.hasReplacement', 'This will also cancel the pending replacement domain ({hostname}).', { hostname: replacement.hostname })
291
+ : t('customer_accounts.domainMapping.removeConfirm', 'Are you sure? Customers using this domain will no longer be able to reach your portal.')
292
+ const confirmed = await confirm({
293
+ title: t('customer_accounts.domainMapping.removeConfirm.title', 'Remove custom domain?'),
294
+ text,
295
+ variant: 'destructive',
296
+ confirmText: t('customer_accounts.domainMapping.removeDomain', 'Remove Domain'),
297
+ })
298
+ if (!confirmed) return
299
+ setIsMutating(true)
300
+ try {
301
+ await runMutationWithContext(async () => {
302
+ const call = await apiCall<{ ok: boolean; error?: string }>(
303
+ `/api/customer_accounts/admin/domain-mappings?id=${encodeURIComponent(mapping.id)}`,
304
+ { method: 'DELETE' },
305
+ )
306
+ if (!call.ok || !call.result?.ok) {
307
+ const message = call.result?.error || t('customer_accounts.domainMapping.error.load', 'Could not load custom-domain configuration')
308
+ flash(message, 'error')
309
+ return
310
+ }
311
+ flash(t('customer_accounts.domainMapping.removed', 'Domain removed'), 'success')
312
+ refresh()
313
+ })
314
+ } finally {
315
+ setIsMutating(false)
316
+ }
317
+ },
318
+ [confirm, orderedMappings, refresh, runMutationWithContext, t],
319
+ )
320
+
321
+ const replaceTargetHostname = React.useMemo(() => {
322
+ if (registerMode !== 'change' || !replaceTargetId) return null
323
+ return orderedMappings.find((m) => m.id === replaceTargetId)?.hostname ?? null
324
+ }, [orderedMappings, registerMode, replaceTargetId])
325
+
326
+ return (
327
+ <Page>
328
+ <div className="px-3 py-3 md:px-6 md:py-4">
329
+ <FormHeader
330
+ mode="detail"
331
+ title={t('customer_accounts.domainMapping.title', 'Custom Domain')}
332
+ subtitle={t('customer_accounts.domainMapping.description', 'Map your own domain to the customer portal')}
333
+ backHref="/backend/customer_accounts/settings"
334
+ backLabel={t('customer_accounts.settings.title', 'Portal Settings')}
335
+ />
336
+ </div>
337
+
338
+ <PageBody className="space-y-6">
339
+ {orgOptions.length > 1 ? (
340
+ <div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border bg-card px-4 py-3">
341
+ <OrgSwitcher
342
+ options={orgOptions}
343
+ selectedId={selectedOrgId}
344
+ onChange={handleOrgChange}
345
+ />
346
+ </div>
347
+ ) : null}
348
+
349
+ {loadState === STATE_LOADING ? (
350
+ <LoadingMessage label={t('customer_accounts.domainMapping.loading', 'Loading domain configuration…')} />
351
+ ) : null}
352
+
353
+ {loadState === STATE_ERROR ? (
354
+ <ErrorMessage
355
+ label={t('customer_accounts.domainMapping.error.load', 'Could not load custom-domain configuration')}
356
+ action={
357
+ <Button type="button" variant="outline" size="sm" onClick={refresh}>
358
+ {t('customer_accounts.domainMapping.verifyNow', 'Check Now')}
359
+ </Button>
360
+ }
361
+ />
362
+ ) : null}
363
+
364
+ {loadState === STATE_LOADED && orderedMappings.length === 0 && selectedOrgId ? (
365
+ <EmptyState
366
+ icon={<Globe className="h-8 w-8" aria-hidden />}
367
+ title={t('customer_accounts.domainMapping.empty.title', 'No custom domain yet')}
368
+ description={t('customer_accounts.domainMapping.empty.description', 'Connect your own domain so customers can reach the portal at a branded URL.')}
369
+ action={{
370
+ label: t('customer_accounts.domainMapping.empty.cta', 'Register Domain'),
371
+ onClick: handleOpenRegister,
372
+ icon: <Globe className="h-4 w-4" aria-hidden />,
373
+ }}
374
+ />
375
+ ) : null}
376
+
377
+ {loadState === STATE_LOADED && orderedMappings.length > 0 ? (
378
+ <div className="space-y-4">
379
+ {orderedMappings.map((mapping) => {
380
+ const isReplacement = mapping.id !== primaryActiveId && mapping.status !== 'active'
381
+ return (
382
+ <DomainPanel
383
+ key={mapping.id}
384
+ mapping={mapping}
385
+ isReplacement={isReplacement}
386
+ cnameTarget={config.cnameTarget}
387
+ aRecordTarget={config.aRecordTarget}
388
+ busy={isMutating}
389
+ onCheckNow={() => handleCheckNow(mapping)}
390
+ onRetryTls={() => handleRetryTls(mapping)}
391
+ onChangeDomain={() => handleOpenChange(mapping.id)}
392
+ onRemove={() => handleRemove(mapping)}
393
+ />
394
+ )
395
+ })}
396
+ </div>
397
+ ) : null}
398
+ </PageBody>
399
+
400
+ <RegisterDialog
401
+ open={registerOpen}
402
+ onOpenChange={setRegisterOpen}
403
+ mode={registerMode}
404
+ currentHostname={replaceTargetHostname}
405
+ onSubmit={handleSubmitRegister}
406
+ />
407
+
408
+ {ConfirmDialogElement}
409
+ </Page>
410
+ )
411
+ }
@@ -7,6 +7,7 @@ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
7
  import { FormHeader } from '@open-mercato/ui/backend/forms'
8
8
  import { Button } from '@open-mercato/ui/primitives/button'
9
9
  import { Input } from '@open-mercato/ui/primitives/input'
10
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
10
11
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
11
12
  import { SwitchField } from '@open-mercato/ui/primitives/switch-field'
12
13
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
@@ -112,14 +113,14 @@ function ResetPasswordDialog({
112
113
  <label className="text-sm font-medium" htmlFor="reset-password">
113
114
  {t('customer_accounts.admin.detail.resetPassword.fields.newPassword', 'New Password')}
114
115
  </label>
115
- <Input
116
+ <PasswordInput
116
117
  id="reset-password"
117
- type="password"
118
118
  required
119
119
  minLength={8}
120
120
  value={newPassword}
121
121
  onChange={(event) => setNewPassword(event.target.value)}
122
122
  placeholder={t('customer_accounts.admin.detail.resetPassword.fields.placeholder', 'Min. 8 characters')}
123
+ autoComplete="new-password"
123
124
  />
124
125
  </div>
125
126
  <div className="flex justify-end gap-2 pt-2">
@@ -10,6 +10,8 @@ import type { ColumnDef } from '@tanstack/react-table'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
12
  import { Input } from '@open-mercato/ui/primitives/input'
13
+ import { EmailInput } from '@open-mercato/ui/primitives/email-input'
14
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
13
15
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
14
16
  import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
15
17
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
@@ -144,9 +146,8 @@ function CreateUserDialog({
144
146
  <label className="text-sm font-medium" htmlFor="create-email">
145
147
  {t('customer_accounts.admin.createUser.fields.email', 'Email')}
146
148
  </label>
147
- <Input
149
+ <EmailInput
148
150
  id="create-email"
149
- type="email"
150
151
  required
151
152
  value={email}
152
153
  onChange={(event) => setEmail(event.target.value)}
@@ -170,14 +171,14 @@ function CreateUserDialog({
170
171
  <label className="text-sm font-medium" htmlFor="create-password">
171
172
  {t('customer_accounts.admin.createUser.fields.password', 'Password')}
172
173
  </label>
173
- <Input
174
+ <PasswordInput
174
175
  id="create-password"
175
- type="password"
176
176
  required
177
177
  minLength={8}
178
178
  value={password}
179
179
  onChange={(event) => setPassword(event.target.value)}
180
180
  placeholder={t('customer_accounts.admin.createUser.fields.passwordPlaceholder', 'Min. 8 characters')}
181
+ autoComplete="new-password"
181
182
  />
182
183
  </div>
183
184
  {roleOptions.length > 0 && (