@open-mercato/ui 0.4.2-canary-c02407ff85
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/build.mjs +62 -0
- package/dist/backend/AppShell.js +902 -0
- package/dist/backend/AppShell.js.map +7 -0
- package/dist/backend/ConfirmDialog.js +17 -0
- package/dist/backend/ConfirmDialog.js.map +7 -0
- package/dist/backend/ContextHelp.js +31 -0
- package/dist/backend/ContextHelp.js.map +7 -0
- package/dist/backend/CrudForm.js +2028 -0
- package/dist/backend/CrudForm.js.map +7 -0
- package/dist/backend/DataTable.js +1363 -0
- package/dist/backend/DataTable.js.map +7 -0
- package/dist/backend/EmptyState.js +52 -0
- package/dist/backend/EmptyState.js.map +7 -0
- package/dist/backend/FilterBar.js +140 -0
- package/dist/backend/FilterBar.js.map +7 -0
- package/dist/backend/FilterOverlay.js +279 -0
- package/dist/backend/FilterOverlay.js.map +7 -0
- package/dist/backend/FlashMessages.js +66 -0
- package/dist/backend/FlashMessages.js.map +7 -0
- package/dist/backend/JsonBuilder.js +322 -0
- package/dist/backend/JsonBuilder.js.map +7 -0
- package/dist/backend/JsonDisplay.js +203 -0
- package/dist/backend/JsonDisplay.js.map +7 -0
- package/dist/backend/Page.js +27 -0
- package/dist/backend/Page.js.map +7 -0
- package/dist/backend/PerspectiveSidebar.js +282 -0
- package/dist/backend/PerspectiveSidebar.js.map +7 -0
- package/dist/backend/RowActions.js +148 -0
- package/dist/backend/RowActions.js.map +7 -0
- package/dist/backend/TruncatedCell.js +92 -0
- package/dist/backend/TruncatedCell.js.map +7 -0
- package/dist/backend/UserMenu.js +107 -0
- package/dist/backend/UserMenu.js.map +7 -0
- package/dist/backend/ValueIcons.js +34 -0
- package/dist/backend/ValueIcons.js.map +7 -0
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js +1264 -0
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +7 -0
- package/dist/backend/custom-fields/FieldDefinitionsManager.js +332 -0
- package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +7 -0
- package/dist/backend/dashboard/DashboardScreen.js +578 -0
- package/dist/backend/dashboard/DashboardScreen.js.map +7 -0
- package/dist/backend/dashboard/index.js +5 -0
- package/dist/backend/dashboard/index.js.map +7 -0
- package/dist/backend/dashboard/widgetRegistry.js +55 -0
- package/dist/backend/dashboard/widgetRegistry.js.map +7 -0
- package/dist/backend/detail/ActivitiesSection.js +962 -0
- package/dist/backend/detail/ActivitiesSection.js.map +7 -0
- package/dist/backend/detail/AddressEditor.js +413 -0
- package/dist/backend/detail/AddressEditor.js.map +7 -0
- package/dist/backend/detail/AddressTiles.js +437 -0
- package/dist/backend/detail/AddressTiles.js.map +7 -0
- package/dist/backend/detail/AddressesSection.js +264 -0
- package/dist/backend/detail/AddressesSection.js.map +7 -0
- package/dist/backend/detail/AttachmentDeleteDialog.js +41 -0
- package/dist/backend/detail/AttachmentDeleteDialog.js.map +7 -0
- package/dist/backend/detail/AttachmentMetadataDialog.js +517 -0
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +7 -0
- package/dist/backend/detail/AttachmentsSection.js +367 -0
- package/dist/backend/detail/AttachmentsSection.js.map +7 -0
- package/dist/backend/detail/CustomDataSection.js +433 -0
- package/dist/backend/detail/CustomDataSection.js.map +7 -0
- package/dist/backend/detail/DetailFieldsSection.js +75 -0
- package/dist/backend/detail/DetailFieldsSection.js.map +7 -0
- package/dist/backend/detail/ErrorMessage.js +28 -0
- package/dist/backend/detail/ErrorMessage.js.map +7 -0
- package/dist/backend/detail/InlineEditors.js +681 -0
- package/dist/backend/detail/InlineEditors.js.map +7 -0
- package/dist/backend/detail/LoadingMessage.js +14 -0
- package/dist/backend/detail/LoadingMessage.js.map +7 -0
- package/dist/backend/detail/NotesSection.js +1032 -0
- package/dist/backend/detail/NotesSection.js.map +7 -0
- package/dist/backend/detail/TabEmptyState.js +25 -0
- package/dist/backend/detail/TabEmptyState.js.map +7 -0
- package/dist/backend/detail/TagsSection.js +254 -0
- package/dist/backend/detail/TagsSection.js.map +7 -0
- package/dist/backend/detail/addressFormat.js +77 -0
- package/dist/backend/detail/addressFormat.js.map +7 -0
- package/dist/backend/detail/index.js +34 -0
- package/dist/backend/detail/index.js.map +7 -0
- package/dist/backend/fields/registry.generated.js +8 -0
- package/dist/backend/fields/registry.generated.js.map +7 -0
- package/dist/backend/fields/registry.js +29 -0
- package/dist/backend/fields/registry.js.map +7 -0
- package/dist/backend/indexes/PartialIndexBanner.js +58 -0
- package/dist/backend/indexes/PartialIndexBanner.js.map +7 -0
- package/dist/backend/indexes/store.js +62 -0
- package/dist/backend/indexes/store.js.map +7 -0
- package/dist/backend/injection/InjectionSpot.js +179 -0
- package/dist/backend/injection/InjectionSpot.js.map +7 -0
- package/dist/backend/injection/PageInjectionBoundary.js +26 -0
- package/dist/backend/injection/PageInjectionBoundary.js.map +7 -0
- package/dist/backend/injection/helpers.js +26 -0
- package/dist/backend/injection/helpers.js.map +7 -0
- package/dist/backend/injection/widgetRegistry.js +55 -0
- package/dist/backend/injection/widgetRegistry.js.map +7 -0
- package/dist/backend/inputs/ComboboxInput.js +225 -0
- package/dist/backend/inputs/ComboboxInput.js.map +7 -0
- package/dist/backend/inputs/LookupSelect.js +191 -0
- package/dist/backend/inputs/LookupSelect.js.map +7 -0
- package/dist/backend/inputs/PhoneNumberField.js +100 -0
- package/dist/backend/inputs/PhoneNumberField.js.map +7 -0
- package/dist/backend/inputs/SwitchableMarkdownInput.js +92 -0
- package/dist/backend/inputs/SwitchableMarkdownInput.js.map +7 -0
- package/dist/backend/inputs/TagsInput.js +222 -0
- package/dist/backend/inputs/TagsInput.js.map +7 -0
- package/dist/backend/inputs/index.js +6 -0
- package/dist/backend/inputs/index.js.map +7 -0
- package/dist/backend/operations/LastOperationBanner.js +80 -0
- package/dist/backend/operations/LastOperationBanner.js.map +7 -0
- package/dist/backend/operations/store.js +183 -0
- package/dist/backend/operations/store.js.map +7 -0
- package/dist/backend/schedule/ScheduleAgenda.js +107 -0
- package/dist/backend/schedule/ScheduleAgenda.js.map +7 -0
- package/dist/backend/schedule/ScheduleGrid.js +107 -0
- package/dist/backend/schedule/ScheduleGrid.js.map +7 -0
- package/dist/backend/schedule/ScheduleToolbar.js +166 -0
- package/dist/backend/schedule/ScheduleToolbar.js.map +7 -0
- package/dist/backend/schedule/ScheduleView.js +165 -0
- package/dist/backend/schedule/ScheduleView.js.map +7 -0
- package/dist/backend/schedule/index.js +6 -0
- package/dist/backend/schedule/index.js.map +7 -0
- package/dist/backend/schedule/recurrence.js +83 -0
- package/dist/backend/schedule/recurrence.js.map +7 -0
- package/dist/backend/schedule/types.js +1 -0
- package/dist/backend/schedule/types.js.map +7 -0
- package/dist/backend/upgrades/UpgradeActionBanner.js +91 -0
- package/dist/backend/upgrades/UpgradeActionBanner.js.map +7 -0
- package/dist/backend/utils/api.js +127 -0
- package/dist/backend/utils/api.js.map +7 -0
- package/dist/backend/utils/apiCall.js +48 -0
- package/dist/backend/utils/apiCall.js.map +7 -0
- package/dist/backend/utils/crud.js +126 -0
- package/dist/backend/utils/crud.js.map +7 -0
- package/dist/backend/utils/customFieldColumns.js +56 -0
- package/dist/backend/utils/customFieldColumns.js.map +7 -0
- package/dist/backend/utils/customFieldDefs.js +143 -0
- package/dist/backend/utils/customFieldDefs.js.map +7 -0
- package/dist/backend/utils/customFieldFilters.js +126 -0
- package/dist/backend/utils/customFieldFilters.js.map +7 -0
- package/dist/backend/utils/customFieldForms.js +162 -0
- package/dist/backend/utils/customFieldForms.js.map +7 -0
- package/dist/backend/utils/customFieldValues.js +26 -0
- package/dist/backend/utils/customFieldValues.js.map +7 -0
- package/dist/backend/utils/flash.js +16 -0
- package/dist/backend/utils/flash.js.map +7 -0
- package/dist/backend/utils/nav.js +185 -0
- package/dist/backend/utils/nav.js.map +7 -0
- package/dist/backend/utils/serverErrors.js +230 -0
- package/dist/backend/utils/serverErrors.js.map +7 -0
- package/dist/frontend/AuthFooter.js +23 -0
- package/dist/frontend/AuthFooter.js.map +7 -0
- package/dist/frontend/LanguageSwitcher.js +57 -0
- package/dist/frontend/LanguageSwitcher.js.map +7 -0
- package/dist/frontend/Layout.js +14 -0
- package/dist/frontend/Layout.js.map +7 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +7 -0
- package/dist/primitives/DataLoader.js +67 -0
- package/dist/primitives/DataLoader.js.map +7 -0
- package/dist/primitives/ErrorNotice.js +20 -0
- package/dist/primitives/ErrorNotice.js.map +7 -0
- package/dist/primitives/alert.js +38 -0
- package/dist/primitives/alert.js.map +7 -0
- package/dist/primitives/badge.js +28 -0
- package/dist/primitives/badge.js.map +7 -0
- package/dist/primitives/button.js +44 -0
- package/dist/primitives/button.js.map +7 -0
- package/dist/primitives/card.js +91 -0
- package/dist/primitives/card.js.map +7 -0
- package/dist/primitives/checkbox.js +28 -0
- package/dist/primitives/checkbox.js.map +7 -0
- package/dist/primitives/dialog.js +90 -0
- package/dist/primitives/dialog.js.map +7 -0
- package/dist/primitives/input.js +22 -0
- package/dist/primitives/input.js.map +7 -0
- package/dist/primitives/label.js +21 -0
- package/dist/primitives/label.js.map +7 -0
- package/dist/primitives/separator.js +9 -0
- package/dist/primitives/separator.js.map +7 -0
- package/dist/primitives/spinner.js +24 -0
- package/dist/primitives/spinner.js.map +7 -0
- package/dist/primitives/switch.js +80 -0
- package/dist/primitives/switch.js.map +7 -0
- package/dist/primitives/table.js +29 -0
- package/dist/primitives/table.js.map +7 -0
- package/dist/primitives/tabs.js +87 -0
- package/dist/primitives/tabs.js.map +7 -0
- package/dist/primitives/textarea.js +21 -0
- package/dist/primitives/textarea.js.map +7 -0
- package/dist/primitives/tooltip.js +60 -0
- package/dist/primitives/tooltip.js.map +7 -0
- package/dist/theme/QueryProvider.js +44 -0
- package/dist/theme/QueryProvider.js.map +7 -0
- package/dist/theme/ThemeProvider.js +95 -0
- package/dist/theme/ThemeProvider.js.map +7 -0
- package/dist/theme/ThemeToggle.js +88 -0
- package/dist/theme/ThemeToggle.js.map +7 -0
- package/dist/theme/index.js +10 -0
- package/dist/theme/index.js.map +7 -0
- package/dist/types/react-big-calendar.d.js +1 -0
- package/dist/types/react-big-calendar.d.js.map +7 -0
- package/jest.config.cjs +23 -0
- package/jest.setup.ts +55 -0
- package/package.json +105 -0
- package/src/backend/AppShell.tsx +1096 -0
- package/src/backend/ConfirmDialog.tsx +19 -0
- package/src/backend/ContextHelp.tsx +38 -0
- package/src/backend/CrudForm.tsx +2503 -0
- package/src/backend/DataTable.tsx +1730 -0
- package/src/backend/EmptyState.tsx +65 -0
- package/src/backend/FilterBar.tsx +161 -0
- package/src/backend/FilterOverlay.tsx +328 -0
- package/src/backend/FlashMessages.tsx +82 -0
- package/src/backend/JsonBuilder.tsx +362 -0
- package/src/backend/JsonDisplay.tsx +254 -0
- package/src/backend/Page.tsx +30 -0
- package/src/backend/PerspectiveSidebar.tsx +337 -0
- package/src/backend/RowActions.tsx +151 -0
- package/src/backend/TruncatedCell.tsx +133 -0
- package/src/backend/UserMenu.tsx +118 -0
- package/src/backend/ValueIcons.tsx +48 -0
- package/src/backend/__tests__/AppShell.test.tsx +115 -0
- package/src/backend/__tests__/CrudForm.render.test.tsx +30 -0
- package/src/backend/__tests__/DataTable.render.test.tsx +48 -0
- package/src/backend/__tests__/custom-field-filters.test.ts +72 -0
- package/src/backend/__tests__/custom-field-forms.test.ts +54 -0
- package/src/backend/__tests__/serverErrors.test.ts +83 -0
- package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +1292 -0
- package/src/backend/custom-fields/FieldDefinitionsManager.tsx +381 -0
- package/src/backend/dashboard/DashboardScreen.tsx +684 -0
- package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +112 -0
- package/src/backend/dashboard/index.ts +1 -0
- package/src/backend/dashboard/widgetRegistry.ts +68 -0
- package/src/backend/detail/ActivitiesSection.tsx +1284 -0
- package/src/backend/detail/AddressEditor.tsx +472 -0
- package/src/backend/detail/AddressTiles.tsx +587 -0
- package/src/backend/detail/AddressesSection.tsx +346 -0
- package/src/backend/detail/AttachmentDeleteDialog.tsx +56 -0
- package/src/backend/detail/AttachmentMetadataDialog.tsx +672 -0
- package/src/backend/detail/AttachmentsSection.tsx +414 -0
- package/src/backend/detail/CustomDataSection.tsx +530 -0
- package/src/backend/detail/DetailFieldsSection.tsx +147 -0
- package/src/backend/detail/ErrorMessage.tsx +32 -0
- package/src/backend/detail/InlineEditors.tsx +877 -0
- package/src/backend/detail/LoadingMessage.tsx +14 -0
- package/src/backend/detail/NotesSection.tsx +1275 -0
- package/src/backend/detail/TabEmptyState.tsx +48 -0
- package/src/backend/detail/TagsSection.tsx +314 -0
- package/src/backend/detail/addressFormat.tsx +121 -0
- package/src/backend/detail/index.ts +44 -0
- package/src/backend/fields/registry.generated.ts +8 -0
- package/src/backend/fields/registry.ts +38 -0
- package/src/backend/indexes/PartialIndexBanner.tsx +68 -0
- package/src/backend/indexes/store.ts +88 -0
- package/src/backend/injection/InjectionSpot.tsx +236 -0
- package/src/backend/injection/PageInjectionBoundary.tsx +31 -0
- package/src/backend/injection/helpers.ts +35 -0
- package/src/backend/injection/widgetRegistry.ts +68 -0
- package/src/backend/inputs/ComboboxInput.tsx +269 -0
- package/src/backend/inputs/LookupSelect.tsx +247 -0
- package/src/backend/inputs/PhoneNumberField.tsx +129 -0
- package/src/backend/inputs/SwitchableMarkdownInput.tsx +128 -0
- package/src/backend/inputs/TagsInput.tsx +259 -0
- package/src/backend/inputs/index.ts +5 -0
- package/src/backend/operations/LastOperationBanner.tsx +85 -0
- package/src/backend/operations/__tests__/LastOperationBanner.test.tsx +99 -0
- package/src/backend/operations/store.ts +230 -0
- package/src/backend/schedule/ScheduleAgenda.tsx +136 -0
- package/src/backend/schedule/ScheduleGrid.tsx +136 -0
- package/src/backend/schedule/ScheduleToolbar.tsx +178 -0
- package/src/backend/schedule/ScheduleView.tsx +198 -0
- package/src/backend/schedule/index.ts +5 -0
- package/src/backend/schedule/recurrence.ts +99 -0
- package/src/backend/schedule/types.ts +26 -0
- package/src/backend/upgrades/UpgradeActionBanner.tsx +128 -0
- package/src/backend/utils/__tests__/apiCall.test.ts +109 -0
- package/src/backend/utils/__tests__/crud.test.ts +87 -0
- package/src/backend/utils/__tests__/customFieldDefs.test.ts +25 -0
- package/src/backend/utils/__tests__/customFieldValues.test.ts +35 -0
- package/src/backend/utils/api.ts +149 -0
- package/src/backend/utils/apiCall.ts +96 -0
- package/src/backend/utils/crud.ts +174 -0
- package/src/backend/utils/customFieldColumns.ts +71 -0
- package/src/backend/utils/customFieldDefs.ts +245 -0
- package/src/backend/utils/customFieldFilters.ts +145 -0
- package/src/backend/utils/customFieldForms.ts +196 -0
- package/src/backend/utils/customFieldValues.ts +41 -0
- package/src/backend/utils/flash.ts +17 -0
- package/src/backend/utils/nav.ts +238 -0
- package/src/backend/utils/serverErrors.ts +302 -0
- package/src/frontend/AuthFooter.tsx +29 -0
- package/src/frontend/LanguageSwitcher.tsx +66 -0
- package/src/frontend/Layout.tsx +13 -0
- package/src/index.ts +32 -0
- package/src/primitives/DataLoader.tsx +92 -0
- package/src/primitives/ErrorNotice.tsx +26 -0
- package/src/primitives/alert.tsx +52 -0
- package/src/primitives/badge.tsx +31 -0
- package/src/primitives/button.tsx +47 -0
- package/src/primitives/card.tsx +92 -0
- package/src/primitives/checkbox.tsx +28 -0
- package/src/primitives/dialog.tsx +110 -0
- package/src/primitives/input.tsx +20 -0
- package/src/primitives/label.tsx +18 -0
- package/src/primitives/separator.tsx +7 -0
- package/src/primitives/spinner.tsx +27 -0
- package/src/primitives/switch.tsx +86 -0
- package/src/primitives/table.tsx +27 -0
- package/src/primitives/tabs.tsx +128 -0
- package/src/primitives/textarea.tsx +20 -0
- package/src/primitives/tooltip.tsx +85 -0
- package/src/theme/QueryProvider.tsx +46 -0
- package/src/theme/ThemeProvider.tsx +120 -0
- package/src/theme/ThemeToggle.tsx +88 -0
- package/src/theme/index.ts +3 -0
- package/src/types/react-big-calendar.d.ts +16 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/backend/detail/AddressEditor.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Plus, Settings } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@open-mercato/ui/primitives/dialog'\nimport { buildCountryOptions } from '@open-mercato/shared/lib/location/countries'\nimport type { AddressFormatStrategy } from './addressFormat'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nexport type AddressTypeOption = {\n value: string\n label: string\n}\n\nexport type AddressTypesAdapter<C = unknown> = {\n list: (context?: C) => Promise<AddressTypeOption[]>\n create?: (value: string, context?: C) => Promise<AddressTypeOption | null>\n manageHref?: string\n}\n\nexport type AddressEditorDraft = {\n name: string\n purpose: string\n companyName: string\n addressLine1: string\n addressLine2: string\n buildingNumber: string\n flatNumber: string\n city: string\n region: string\n postalCode: string\n country: string\n isPrimary: boolean\n}\n\nexport type AddressEditorField =\n | 'name'\n | 'purpose'\n | 'companyName'\n | 'addressLine1'\n | 'addressLine2'\n | 'buildingNumber'\n | 'flatNumber'\n | 'city'\n | 'region'\n | 'postalCode'\n | 'country'\n | 'isPrimary'\n\ntype AddressEditorProps<C = unknown> = {\n value: AddressEditorDraft\n onChange: (next: AddressEditorDraft) => void\n format: AddressFormatStrategy\n t: Translator\n labelPrefix?: string\n disabled?: boolean\n errors?: Partial<Record<AddressEditorField, string>>\n hidePrimaryToggle?: boolean\n showFormatHint?: boolean\n addressTypesAdapter?: AddressTypesAdapter<C>\n addressTypesContext?: C\n}\n\nexport function AddressEditor<C = unknown>({\n value,\n onChange,\n format,\n t,\n labelPrefix = 'customers.people.detail.addresses',\n disabled = false,\n errors = {},\n hidePrimaryToggle = false,\n showFormatHint = true,\n addressTypesAdapter,\n addressTypesContext,\n}: AddressEditorProps<C>) {\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n\n const [addressTypes, setAddressTypes] = React.useState<AddressTypeOption[]>([])\n const [addressTypesLoading, setAddressTypesLoading] = React.useState(false)\n const [addressTypeError, setAddressTypeError] = React.useState<string | null>(null)\n\n const [typeDialogOpen, setTypeDialogOpen] = React.useState(false)\n const [typeValue, setTypeValue] = React.useState('')\n const [typeFormError, setTypeFormError] = React.useState<string | null>(null)\n const [countryDialogOpen, setCountryDialogOpen] = React.useState(false)\n const [countryQuery, setCountryQuery] = React.useState('')\n\n const countryOptions = React.useMemo(\n () =>\n buildCountryOptions({\n transformLabel: (code, fallback) => t(`customers.countries.${code.toLowerCase()}`, fallback ?? code),\n }),\n [t],\n )\n\n React.useEffect(() => {\n let cancelled = false\n const load = async () => {\n if (!addressTypesAdapter) {\n setAddressTypes([])\n setAddressTypeError(null)\n return\n }\n setAddressTypesLoading(true)\n try {\n const result = await addressTypesAdapter.list(addressTypesContext)\n if (!cancelled) {\n setAddressTypes(Array.isArray(result) ? result : [])\n setAddressTypeError(null)\n }\n } catch (err) {\n if (!cancelled) {\n setAddressTypes([])\n setAddressTypeError(label('types.loadError', 'Failed to load address types'))\n }\n } finally {\n if (!cancelled) setAddressTypesLoading(false)\n }\n }\n load().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [addressTypesAdapter, addressTypesContext, label])\n\n const current: AddressEditorDraft = {\n name: value.name ?? '',\n purpose: value.purpose ?? '',\n companyName: value.companyName ?? '',\n addressLine1: value.addressLine1 ?? '',\n addressLine2: value.addressLine2 ?? '',\n buildingNumber: value.buildingNumber ?? '',\n flatNumber: value.flatNumber ?? '',\n city: value.city ?? '',\n region: value.region ?? '',\n postalCode: value.postalCode ?? '',\n country: value.country ?? '',\n isPrimary: value.isPrimary ?? false,\n }\n\n const update = React.useCallback(\n (key: keyof AddressEditorDraft, nextValue: string | boolean) => {\n onChange({ ...current, [key]: nextValue })\n },\n [current, onChange],\n )\n\n const filteredCountryOptions = React.useMemo(() => {\n const query = countryQuery.trim().toLowerCase()\n if (!query.length) return countryOptions\n return countryOptions.filter(\n (option) => option.label.toLowerCase().includes(query) || option.code.toLowerCase().includes(query),\n )\n }, [countryOptions, countryQuery])\n\n const selectedCountry = React.useMemo(() => {\n const code = (current.country ?? '').toUpperCase()\n if (!code.length) return null\n return countryOptions.find((option) => option.code === code) ?? null\n }, [countryOptions, current.country])\n\n const handleTypeSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const trimmed = typeValue.trim()\n if (!trimmed.length) {\n setTypeFormError(label('types.emptyError', 'Please provide a value'))\n return\n }\n if (!addressTypesAdapter?.create) return\n setTypeFormError(null)\n const created = await addressTypesAdapter.create(trimmed, addressTypesContext)\n if (created) {\n setAddressTypes((prev) => {\n const map = new Map(prev.map((entry) => [entry.value, entry]))\n map.set(created.value, created)\n return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label))\n })\n }\n setTypeDialogOpen(false)\n setTypeValue('')\n },\n [addressTypesAdapter, addressTypesContext, label, typeValue],\n )\n\n const inputClass = (field: AddressEditorField) =>\n [\n 'w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring',\n errors[field] ? 'border-red-500 focus:ring-red-500' : 'border-input bg-background',\n ].join(' ')\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('name')}\n placeholder={label('fields.label', 'Label')}\n value={current.name}\n onChange={(evt) => update('name', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.name ? 'true' : undefined}\n />\n <div className=\"flex gap-2\">\n <select\n className={inputClass('purpose')}\n value={current.purpose}\n onChange={(evt) => update('purpose', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.purpose ? 'true' : undefined}\n >\n <option value=\"\">\n {addressTypesLoading\n ? label('types.loading', 'Loading\u2026')\n : label('types.placeholder', 'Address type')}\n </option>\n {addressTypes.map((entry) => (\n <option key={entry.value} value={entry.value}>\n {entry.label}\n </option>\n ))}\n </select>\n {addressTypesAdapter?.create ? (\n <Dialog open={typeDialogOpen} onOpenChange={setTypeDialogOpen}>\n <DialogTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"icon\" className=\"shrink-0\" disabled={disabled}>\n <Plus className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{label('types.add', 'Add address type')}</DialogTitle>\n <DialogDescription>\n {label('types.addHint', 'Create a new address type for reuse.')}\n </DialogDescription>\n </DialogHeader>\n <form className=\"space-y-3\" onSubmit={handleTypeSubmit}>\n <Input\n autoFocus\n value={typeValue}\n onChange={(evt) => {\n setTypeValue(evt.target.value)\n if (typeFormError) setTypeFormError(null)\n }}\n placeholder={label('types.placeholder', 'Address type')}\n disabled={disabled}\n aria-invalid={typeFormError ? 'true' : undefined}\n />\n {typeFormError ? <p className=\"text-sm text-destructive\">{typeFormError}</p> : null}\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setTypeDialogOpen(false)} disabled={disabled}>\n {label('types.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={disabled || !typeValue.trim()}>\n {label('types.save', 'Save')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n ) : null}\n <Button\n asChild\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n disabled={disabled}\n title={label('types.manage', 'Manage address types')}\n >\n <Link\n href={addressTypesAdapter?.manageHref ?? '/backend/config/dictionaries'}\n aria-label={label('types.manage', 'Manage address types')}\n >\n <Settings className=\"h-4 w-4\" />\n </Link>\n </Button>\n </div>\n </div>\n {errors.purpose ? <p className=\"text-xs text-destructive\">{errors.purpose}</p> : null}\n {addressTypeError ? <p className=\"text-xs text-destructive\">{addressTypeError}</p> : null}\n <Input\n className={inputClass('companyName')}\n placeholder={label('fields.companyName', 'Company name')}\n value={current.companyName}\n onChange={(evt) => update('companyName', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.companyName ? 'true' : undefined}\n />\n\n {format === 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.street', 'Street')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : (\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.line1', 'Address line 1')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n )}\n\n <Input\n className={inputClass('addressLine2')}\n placeholder={label('fields.line2', 'Address line 2')}\n value={current.addressLine2}\n onChange={(evt) => update('addressLine2', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine2 ? 'true' : undefined}\n />\n\n {format !== 'street_first' ? (\n <div className=\"grid gap-2 sm:grid-cols-[1.5fr,0.7fr,0.7fr]\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={label('fields.street', 'Street')}\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n <Input\n className={inputClass('buildingNumber')}\n placeholder={label('fields.buildingNumber', 'Building number')}\n value={current.buildingNumber}\n onChange={(evt) => update('buildingNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.buildingNumber ? 'true' : undefined}\n />\n <Input\n className={inputClass('flatNumber')}\n placeholder={label('fields.flatNumber', 'Flat number')}\n value={current.flatNumber}\n onChange={(evt) => update('flatNumber', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.flatNumber ? 'true' : undefined}\n />\n </div>\n ) : null}\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('city')}\n placeholder={label('fields.city', 'City')}\n value={current.city}\n onChange={(evt) => update('city', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.city ? 'true' : undefined}\n />\n <Input\n className={inputClass('region')}\n placeholder={label('fields.region', 'Region')}\n value={current.region}\n onChange={(evt) => update('region', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.region ? 'true' : undefined}\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('postalCode')}\n placeholder={label('fields.postalCode', 'Postal code')}\n value={current.postalCode}\n onChange={(evt) => update('postalCode', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.postalCode ? 'true' : undefined}\n />\n <Dialog open={countryDialogOpen} onOpenChange={setCountryDialogOpen}>\n <DialogTrigger asChild>\n <button type=\"button\" className={inputClass('country')} disabled={disabled}>\n {selectedCountry?.label ?? label('fields.country', 'Country')}\n </button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{label('country.title', 'Choose a country')}</DialogTitle>\n <DialogDescription>{label('country.subtitle', 'Search for a country')}</DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <Input\n placeholder={label('country.search', 'Search countries')}\n value={countryQuery}\n onChange={(evt) => setCountryQuery(evt.target.value)}\n />\n <div className=\"max-h-64 overflow-auto rounded-md border border-border/60\">\n <ul className=\"divide-y divide-border/50\">\n {filteredCountryOptions.map((option) => (\n <li key={option.code}>\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between px-3 py-2 text-left text-sm hover:bg-muted\"\n onClick={() => {\n update('country', option.code)\n setCountryDialogOpen(false)\n }}\n >\n <span>{option.label}</span>\n <span className=\"text-xs text-muted-foreground\">{option.code}</span>\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n\n {showFormatHint ? (\n <p className=\"text-xs text-muted-foreground\">\n {label('formatHint', 'Format based on address settings')}\n </p>\n ) : null}\n\n {!hidePrimaryToggle ? (\n <label className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <input\n type=\"checkbox\"\n checked={current.isPrimary}\n onChange={(evt) => update('isPrimary', evt.target.checked)}\n disabled={disabled}\n />\n {label('fields.primary', 'Primary address')}\n </label>\n ) : null}\n </div>\n )\n}\n\nexport default AddressEditor\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Loader2, Pencil, Plus, Trash2, X } from "lucide-react";
|
|
5
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
7
|
+
import { TabEmptyState } from "@open-mercato/ui/backend/detail";
|
|
8
|
+
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
9
|
+
import { AddressView, formatAddressString } from "./addressFormat.js";
|
|
10
|
+
import AddressEditor from "./AddressEditor.js";
|
|
11
|
+
const defaultDraft = {
|
|
12
|
+
name: "",
|
|
13
|
+
purpose: "",
|
|
14
|
+
companyName: "",
|
|
15
|
+
addressLine1: "",
|
|
16
|
+
addressLine2: "",
|
|
17
|
+
buildingNumber: "",
|
|
18
|
+
flatNumber: "",
|
|
19
|
+
city: "",
|
|
20
|
+
region: "",
|
|
21
|
+
postalCode: "",
|
|
22
|
+
country: "",
|
|
23
|
+
isPrimary: false
|
|
24
|
+
};
|
|
25
|
+
const serverFieldMap = {
|
|
26
|
+
name: "name",
|
|
27
|
+
purpose: "purpose",
|
|
28
|
+
companyName: "companyName",
|
|
29
|
+
addressLine1: "addressLine1",
|
|
30
|
+
addressLine2: "addressLine2",
|
|
31
|
+
buildingNumber: "buildingNumber",
|
|
32
|
+
flatNumber: "flatNumber",
|
|
33
|
+
city: "city",
|
|
34
|
+
region: "region",
|
|
35
|
+
postalCode: "postalCode",
|
|
36
|
+
country: "country",
|
|
37
|
+
isPrimary: "isPrimary"
|
|
38
|
+
};
|
|
39
|
+
function normalizeOptional(value) {
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
return trimmed.length ? trimmed : void 0;
|
|
42
|
+
}
|
|
43
|
+
function extractValidationDetails(error) {
|
|
44
|
+
if (!error || typeof error !== "object") return [];
|
|
45
|
+
const candidate = error.details;
|
|
46
|
+
if (!Array.isArray(candidate)) return [];
|
|
47
|
+
return candidate.map((entry) => entry && typeof entry === "object" ? entry : null).filter((entry) => entry !== null);
|
|
48
|
+
}
|
|
49
|
+
function resolveFieldMessage(detail, fieldLabel, t, prefix) {
|
|
50
|
+
const label = (suffix, fallback) => t(`${prefix}.${suffix}`, fallback);
|
|
51
|
+
switch (detail.code) {
|
|
52
|
+
case "invalid_type":
|
|
53
|
+
return label("validation.invalid", "Invalid value for {{field}}").replace("{{field}}", fieldLabel);
|
|
54
|
+
case "too_small":
|
|
55
|
+
if (detail.minimum === 1 && detail.type === "string") {
|
|
56
|
+
return label("validation.required", "{{field}} is required").replace("{{field}}", fieldLabel);
|
|
57
|
+
}
|
|
58
|
+
return label("validation.generic", "Invalid value for {{field}}").replace("{{field}}", fieldLabel);
|
|
59
|
+
case "too_big":
|
|
60
|
+
if (typeof detail.maximum === "number") {
|
|
61
|
+
return label("validation.tooLong", "{{field}} is too long").replace("{{field}}", fieldLabel).replace("{{max}}", `${detail.maximum}`);
|
|
62
|
+
}
|
|
63
|
+
return label("validation.generic", "Invalid value for {{field}}").replace("{{field}}", fieldLabel);
|
|
64
|
+
default:
|
|
65
|
+
return label("validation.generic", "Invalid value for {{field}}").replace("{{field}}", fieldLabel);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function AddressTiles({
|
|
69
|
+
addresses,
|
|
70
|
+
onCreate,
|
|
71
|
+
onUpdate,
|
|
72
|
+
onDelete,
|
|
73
|
+
t,
|
|
74
|
+
emptyLabel,
|
|
75
|
+
isSubmitting = false,
|
|
76
|
+
gridClassName = "grid gap-4 min-[480px]:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4",
|
|
77
|
+
hideAddButton = false,
|
|
78
|
+
onAddActionChange,
|
|
79
|
+
emptyStateTitle,
|
|
80
|
+
emptyStateActionLabel,
|
|
81
|
+
labelPrefix = "customers.people.detail.addresses",
|
|
82
|
+
addressTypesAdapter,
|
|
83
|
+
addressTypesContext,
|
|
84
|
+
loadFormat,
|
|
85
|
+
formatContext
|
|
86
|
+
}) {
|
|
87
|
+
const scopeVersion = useOrganizationScopeVersion();
|
|
88
|
+
const [isFormOpen, setIsFormOpen] = React.useState(false);
|
|
89
|
+
const [editingId, setEditingId] = React.useState(null);
|
|
90
|
+
const [draft, setDraft] = React.useState(defaultDraft);
|
|
91
|
+
const [saving, setSaving] = React.useState(false);
|
|
92
|
+
const [deletingId, setDeletingId] = React.useState(null);
|
|
93
|
+
const [generalError, setGeneralError] = React.useState(null);
|
|
94
|
+
const [fieldErrors, setFieldErrors] = React.useState({});
|
|
95
|
+
const [format, setFormat] = React.useState("line_first");
|
|
96
|
+
const [formatLoading, setFormatLoading] = React.useState(false);
|
|
97
|
+
const label = React.useCallback(
|
|
98
|
+
(suffix, fallback, params) => t(`${labelPrefix}.${suffix}`, fallback, params),
|
|
99
|
+
[labelPrefix, t]
|
|
100
|
+
);
|
|
101
|
+
const fieldLabels = React.useMemo(
|
|
102
|
+
() => ({
|
|
103
|
+
name: label("fields.label", "Label"),
|
|
104
|
+
purpose: label("fields.type", "Address type"),
|
|
105
|
+
companyName: label("fields.companyName", "Company name"),
|
|
106
|
+
addressLine1: label("fields.line1", "Address line 1"),
|
|
107
|
+
addressLine2: label("fields.line2", "Address line 2"),
|
|
108
|
+
street: label("fields.street", "Street"),
|
|
109
|
+
buildingNumber: label("fields.buildingNumber", "Building number"),
|
|
110
|
+
flatNumber: label("fields.flatNumber", "Flat number"),
|
|
111
|
+
city: label("fields.city", "City"),
|
|
112
|
+
region: label("fields.region", "Region"),
|
|
113
|
+
postalCode: label("fields.postalCode", "Postal code"),
|
|
114
|
+
country: label("fields.country", "Country"),
|
|
115
|
+
isPrimary: label("fields.primary", "Primary address")
|
|
116
|
+
}),
|
|
117
|
+
[label]
|
|
118
|
+
);
|
|
119
|
+
const resetForm = React.useCallback(() => {
|
|
120
|
+
setDraft(defaultDraft);
|
|
121
|
+
setFieldErrors({});
|
|
122
|
+
setGeneralError(null);
|
|
123
|
+
setEditingId(null);
|
|
124
|
+
}, []);
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
let cancelled = false;
|
|
127
|
+
async function loadFormatValue() {
|
|
128
|
+
if (!loadFormat) {
|
|
129
|
+
setFormat("line_first");
|
|
130
|
+
setFormatLoading(false);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
setFormatLoading(true);
|
|
134
|
+
try {
|
|
135
|
+
const value = await loadFormat(formatContext);
|
|
136
|
+
if (!cancelled && (value === "street_first" || value === "line_first")) {
|
|
137
|
+
setFormat(value);
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (!cancelled) {
|
|
141
|
+
const message = err instanceof Error && err.message ? err.message : label("formatLoadError", "Failed to load address configuration");
|
|
142
|
+
flash(message, "error");
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
if (!cancelled) setFormatLoading(false);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
void loadFormatValue();
|
|
149
|
+
return () => {
|
|
150
|
+
cancelled = true;
|
|
151
|
+
};
|
|
152
|
+
}, [formatContext, label, loadFormat, scopeVersion]);
|
|
153
|
+
const openCreateForm = React.useCallback(() => {
|
|
154
|
+
resetForm();
|
|
155
|
+
setIsFormOpen(true);
|
|
156
|
+
}, [resetForm]);
|
|
157
|
+
const handleCancel = React.useCallback(() => {
|
|
158
|
+
resetForm();
|
|
159
|
+
setIsFormOpen(false);
|
|
160
|
+
}, [resetForm]);
|
|
161
|
+
const handleEdit = React.useCallback((value) => {
|
|
162
|
+
setDraft({
|
|
163
|
+
name: value.name ?? "",
|
|
164
|
+
purpose: value.purpose ?? "",
|
|
165
|
+
companyName: value.companyName ?? "",
|
|
166
|
+
addressLine1: value.addressLine1 ?? "",
|
|
167
|
+
addressLine2: value.addressLine2 ?? "",
|
|
168
|
+
buildingNumber: value.buildingNumber ?? "",
|
|
169
|
+
flatNumber: value.flatNumber ?? "",
|
|
170
|
+
city: value.city ?? "",
|
|
171
|
+
region: value.region ?? "",
|
|
172
|
+
postalCode: value.postalCode ?? "",
|
|
173
|
+
country: value.country ?? "",
|
|
174
|
+
isPrimary: value.isPrimary ?? false
|
|
175
|
+
});
|
|
176
|
+
setEditingId(value.id);
|
|
177
|
+
setIsFormOpen(true);
|
|
178
|
+
setFieldErrors({});
|
|
179
|
+
setGeneralError(null);
|
|
180
|
+
}, []);
|
|
181
|
+
const validate = React.useCallback(() => {
|
|
182
|
+
const errors = {};
|
|
183
|
+
if (!draft.addressLine1.trim()) {
|
|
184
|
+
errors.addressLine1 = label("validation.required", "{{field}} is required").replace("{{field}}", fieldLabels.addressLine1);
|
|
185
|
+
}
|
|
186
|
+
if (Object.keys(errors).length > 0) {
|
|
187
|
+
setFieldErrors(errors);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}, [draft.addressLine1, fieldLabels.addressLine1, label]);
|
|
192
|
+
const handleSave = React.useCallback(async () => {
|
|
193
|
+
if (!validate()) return;
|
|
194
|
+
setSaving(true);
|
|
195
|
+
setGeneralError(null);
|
|
196
|
+
try {
|
|
197
|
+
const payload = {
|
|
198
|
+
name: normalizeOptional(draft.name),
|
|
199
|
+
purpose: normalizeOptional(draft.purpose),
|
|
200
|
+
companyName: normalizeOptional(draft.companyName),
|
|
201
|
+
addressLine1: draft.addressLine1.trim(),
|
|
202
|
+
addressLine2: normalizeOptional(draft.addressLine2),
|
|
203
|
+
buildingNumber: normalizeOptional(draft.buildingNumber),
|
|
204
|
+
flatNumber: normalizeOptional(draft.flatNumber),
|
|
205
|
+
city: normalizeOptional(draft.city),
|
|
206
|
+
region: normalizeOptional(draft.region),
|
|
207
|
+
postalCode: normalizeOptional(draft.postalCode),
|
|
208
|
+
country: normalizeOptional(draft.country)?.toUpperCase(),
|
|
209
|
+
isPrimary: draft.isPrimary
|
|
210
|
+
};
|
|
211
|
+
if (editingId && onUpdate) {
|
|
212
|
+
await onUpdate(editingId, payload);
|
|
213
|
+
} else {
|
|
214
|
+
await onCreate(payload);
|
|
215
|
+
}
|
|
216
|
+
resetForm();
|
|
217
|
+
setIsFormOpen(false);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
const details = extractValidationDetails(err);
|
|
220
|
+
if (details.length) {
|
|
221
|
+
const nextErrors = {};
|
|
222
|
+
details.forEach((detail) => {
|
|
223
|
+
const path = Array.isArray(detail.path) ? detail.path : [];
|
|
224
|
+
const key = typeof path[0] === "string" ? path[0] : void 0;
|
|
225
|
+
if (!key) return;
|
|
226
|
+
const fieldKey = serverFieldMap[key];
|
|
227
|
+
if (!fieldKey) return;
|
|
228
|
+
const fieldLabel = fieldLabels[fieldKey] ?? key;
|
|
229
|
+
nextErrors[fieldKey] = resolveFieldMessage(detail, fieldLabel, t, labelPrefix);
|
|
230
|
+
});
|
|
231
|
+
setFieldErrors(nextErrors);
|
|
232
|
+
setGeneralError(label("validation.summary", "Please fix the highlighted fields."));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const message = err instanceof Error && err.message ? err.message : label("error", "Failed to save address");
|
|
236
|
+
setGeneralError(message);
|
|
237
|
+
flash(message, "error");
|
|
238
|
+
} finally {
|
|
239
|
+
setSaving(false);
|
|
240
|
+
}
|
|
241
|
+
}, [draft, editingId, fieldLabels, label, labelPrefix, onCreate, onUpdate, resetForm, t, validate]);
|
|
242
|
+
const handleDelete = React.useCallback(
|
|
243
|
+
async (id) => {
|
|
244
|
+
if (!onDelete) return;
|
|
245
|
+
setDeletingId(id);
|
|
246
|
+
try {
|
|
247
|
+
await onDelete(id);
|
|
248
|
+
if (editingId === id) {
|
|
249
|
+
resetForm();
|
|
250
|
+
setIsFormOpen(false);
|
|
251
|
+
}
|
|
252
|
+
} catch (err) {
|
|
253
|
+
const message = err instanceof Error && err.message ? err.message : label("error", "Failed to delete address");
|
|
254
|
+
flash(message, "error");
|
|
255
|
+
} finally {
|
|
256
|
+
setDeletingId(null);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
[editingId, label, onDelete, resetForm]
|
|
260
|
+
);
|
|
261
|
+
const disableActions = saving || isSubmitting || deletingId !== null;
|
|
262
|
+
const isEditing = editingId !== null;
|
|
263
|
+
const addDisabled = disableActions || isEditing;
|
|
264
|
+
const hasAddresses = addresses.length > 0;
|
|
265
|
+
const emptyTitle = emptyStateTitle ?? emptyLabel;
|
|
266
|
+
const emptyActionLabel = emptyStateActionLabel ?? label("add", "Add address");
|
|
267
|
+
React.useEffect(() => {
|
|
268
|
+
if (!onAddActionChange) return;
|
|
269
|
+
onAddActionChange({ openCreateForm, addDisabled });
|
|
270
|
+
}, [onAddActionChange, openCreateForm, addDisabled]);
|
|
271
|
+
React.useEffect(
|
|
272
|
+
() => () => {
|
|
273
|
+
if (onAddActionChange) onAddActionChange(null);
|
|
274
|
+
},
|
|
275
|
+
[onAddActionChange]
|
|
276
|
+
);
|
|
277
|
+
const renderFormTile = React.useCallback(
|
|
278
|
+
(key) => /* @__PURE__ */ jsxs(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
className: "rounded-lg border-2 border-dashed border-muted-foreground/50 bg-muted/20 p-4 text-sm",
|
|
282
|
+
onKeyDown: (event) => {
|
|
283
|
+
if (!(event.metaKey || event.ctrlKey)) return;
|
|
284
|
+
if (event.key !== "Enter") return;
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
if (disableActions) return;
|
|
287
|
+
void handleSave();
|
|
288
|
+
},
|
|
289
|
+
children: [
|
|
290
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
291
|
+
/* @__PURE__ */ jsx("span", { children: editingId ? label("editTitle", "Edit address") : label("addTitle", "Add address") }),
|
|
292
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: handleCancel, disabled: disableActions, children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
|
|
293
|
+
] }),
|
|
294
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-3", children: [
|
|
295
|
+
formatLoading ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: label("formatLoading", "Loading address preferences\u2026") }) : null,
|
|
296
|
+
/* @__PURE__ */ jsx(
|
|
297
|
+
AddressEditor,
|
|
298
|
+
{
|
|
299
|
+
value: draft,
|
|
300
|
+
onChange: (next) => {
|
|
301
|
+
setDraft(next);
|
|
302
|
+
if (Object.keys(fieldErrors).length) {
|
|
303
|
+
const nextErrors = { ...fieldErrors };
|
|
304
|
+
Object.keys(nextErrors).forEach((key2) => {
|
|
305
|
+
const candidate = next[key2];
|
|
306
|
+
if (candidate !== void 0 && candidate !== null && `${candidate}`.length) {
|
|
307
|
+
delete nextErrors[key2];
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
setFieldErrors(nextErrors);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
format,
|
|
314
|
+
t,
|
|
315
|
+
disabled: disableActions,
|
|
316
|
+
errors: fieldErrors,
|
|
317
|
+
showFormatHint: !formatLoading,
|
|
318
|
+
labelPrefix,
|
|
319
|
+
addressTypesAdapter,
|
|
320
|
+
addressTypesContext
|
|
321
|
+
}
|
|
322
|
+
),
|
|
323
|
+
generalError ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-600", children: generalError }) : null,
|
|
324
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
|
|
325
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleCancel, disabled: disableActions, children: label("cancel", "Cancel") }),
|
|
326
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: handleSave, disabled: disableActions, children: saving ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
327
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
328
|
+
editingId ? label("updating", "Updating\u2026") : label("saving", "Saving\u2026")
|
|
329
|
+
] }) : editingId ? label("update", "Update address") : label("save", "Save address") })
|
|
330
|
+
] })
|
|
331
|
+
] })
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
key
|
|
335
|
+
),
|
|
336
|
+
[
|
|
337
|
+
addressTypesAdapter,
|
|
338
|
+
addressTypesContext,
|
|
339
|
+
disableActions,
|
|
340
|
+
draft,
|
|
341
|
+
editingId,
|
|
342
|
+
fieldErrors,
|
|
343
|
+
format,
|
|
344
|
+
formatLoading,
|
|
345
|
+
handleCancel,
|
|
346
|
+
handleSave,
|
|
347
|
+
generalError,
|
|
348
|
+
label,
|
|
349
|
+
labelPrefix,
|
|
350
|
+
saving,
|
|
351
|
+
t
|
|
352
|
+
]
|
|
353
|
+
);
|
|
354
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
355
|
+
!hideAddButton ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
|
|
356
|
+
Button,
|
|
357
|
+
{
|
|
358
|
+
type: "button",
|
|
359
|
+
variant: "outline",
|
|
360
|
+
size: "sm",
|
|
361
|
+
onClick: openCreateForm,
|
|
362
|
+
disabled: addDisabled,
|
|
363
|
+
children: [
|
|
364
|
+
/* @__PURE__ */ jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
365
|
+
label("add", "Add address")
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
) }) : null,
|
|
369
|
+
hasAddresses ? /* @__PURE__ */ jsxs("div", { className: gridClassName, children: [
|
|
370
|
+
addresses.map((address) => {
|
|
371
|
+
if (isFormOpen && editingId === address.id) {
|
|
372
|
+
return renderFormTile(`form-${address.id}`);
|
|
373
|
+
}
|
|
374
|
+
const isDeleting = deletingId === address.id;
|
|
375
|
+
return /* @__PURE__ */ jsx(
|
|
376
|
+
"div",
|
|
377
|
+
{
|
|
378
|
+
className: "group rounded-lg border border-border/60 bg-card p-4 text-sm transition hover:border-border",
|
|
379
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
380
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
381
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
382
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: address.name ?? label("labelFallback", "Address") }),
|
|
383
|
+
address.isPrimary ? /* @__PURE__ */ jsx("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-semibold uppercase", children: label("primaryBadge", "Primary") }) : null
|
|
384
|
+
] }),
|
|
385
|
+
address.purpose ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: address.purpose }) : null,
|
|
386
|
+
/* @__PURE__ */ jsx(AddressView, { address, format, className: "text-sm text-foreground" }),
|
|
387
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: formatAddressString(address, format) })
|
|
388
|
+
] }),
|
|
389
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100", children: [
|
|
390
|
+
/* @__PURE__ */ jsx(
|
|
391
|
+
Button,
|
|
392
|
+
{
|
|
393
|
+
type: "button",
|
|
394
|
+
variant: "ghost",
|
|
395
|
+
size: "icon",
|
|
396
|
+
onClick: () => handleEdit(address),
|
|
397
|
+
disabled: disableActions,
|
|
398
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
|
|
399
|
+
}
|
|
400
|
+
),
|
|
401
|
+
/* @__PURE__ */ jsx(
|
|
402
|
+
Button,
|
|
403
|
+
{
|
|
404
|
+
type: "button",
|
|
405
|
+
variant: "ghost",
|
|
406
|
+
size: "icon",
|
|
407
|
+
onClick: () => handleDelete(address.id),
|
|
408
|
+
disabled: disableActions,
|
|
409
|
+
children: isDeleting ? /* @__PURE__ */ jsx("span", { className: "relative flex h-4 w-4 items-center justify-center text-destructive", children: /* @__PURE__ */ jsx("span", { className: "absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent" }) }) : /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
] })
|
|
413
|
+
] })
|
|
414
|
+
},
|
|
415
|
+
address.id
|
|
416
|
+
);
|
|
417
|
+
}),
|
|
418
|
+
isFormOpen && !editingId ? renderFormTile("create") : null
|
|
419
|
+
] }) : isFormOpen ? /* @__PURE__ */ jsx("div", { className: gridClassName, children: renderFormTile("create") }) : /* @__PURE__ */ jsx(
|
|
420
|
+
TabEmptyState,
|
|
421
|
+
{
|
|
422
|
+
title: emptyTitle,
|
|
423
|
+
action: {
|
|
424
|
+
label: emptyActionLabel,
|
|
425
|
+
onClick: openCreateForm,
|
|
426
|
+
disabled: addDisabled
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
] });
|
|
431
|
+
}
|
|
432
|
+
var AddressTiles_default = AddressTiles;
|
|
433
|
+
export {
|
|
434
|
+
AddressTiles,
|
|
435
|
+
AddressTiles_default as default
|
|
436
|
+
};
|
|
437
|
+
//# sourceMappingURL=AddressTiles.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/backend/detail/AddressTiles.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, Pencil, Plus, Trash2, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { AddressView, formatAddressString, type AddressFormatStrategy } from './addressFormat'\nimport AddressEditor, { type AddressTypesAdapter } from './AddressEditor'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@open-mercato/ui/primitives/dialog'\n\nexport type Translator = (\n key: string,\n fallback?: string,\n params?: Record<string, string | number>,\n) => string\n\nexport type AddressInput = {\n name?: string\n purpose?: string\n companyName?: string\n addressLine1: string\n addressLine2?: string\n buildingNumber?: string\n flatNumber?: string\n city?: string\n region?: string\n postalCode?: string\n country?: string\n isPrimary?: boolean\n}\n\nexport type AddressValue = AddressInput & {\n id: string\n purpose?: string | null\n companyName?: string | null\n}\n\ntype AddressTilesProps<C = unknown> = {\n addresses: AddressValue[]\n onCreate: (payload: AddressInput) => Promise<void> | void\n onUpdate?: (id: string, payload: AddressInput) => Promise<void> | void\n onDelete?: (id: string) => Promise<void> | void\n t: Translator\n emptyLabel: string\n isSubmitting?: boolean\n gridClassName?: string\n hideAddButton?: boolean\n onAddActionChange?: (action: { openCreateForm: () => void; addDisabled: boolean } | null) => void\n emptyStateTitle?: string\n emptyStateActionLabel?: string\n labelPrefix?: string\n addressTypesAdapter?: AddressTypesAdapter<C>\n addressTypesContext?: C\n loadFormat?: (context?: C) => Promise<AddressFormatStrategy>\n formatContext?: C\n}\n\ntype DraftAddressState = {\n name: string\n purpose: string\n companyName: string\n addressLine1: string\n addressLine2: string\n buildingNumber: string\n flatNumber: string\n city: string\n region: string\n postalCode: string\n country: string\n isPrimary: boolean\n}\n\ntype DraftFieldKey = keyof DraftAddressState\n\ntype AddressValidationDetail = {\n path?: Array<string | number>\n code?: string\n message?: string\n minimum?: number\n maximum?: number\n type?: string\n}\n\nconst defaultDraft: DraftAddressState = {\n name: '',\n purpose: '',\n companyName: '',\n addressLine1: '',\n addressLine2: '',\n buildingNumber: '',\n flatNumber: '',\n city: '',\n region: '',\n postalCode: '',\n country: '',\n isPrimary: false,\n}\n\nconst serverFieldMap: Record<string, DraftFieldKey> = {\n name: 'name',\n purpose: 'purpose',\n companyName: 'companyName',\n addressLine1: 'addressLine1',\n addressLine2: 'addressLine2',\n buildingNumber: 'buildingNumber',\n flatNumber: 'flatNumber',\n city: 'city',\n region: 'region',\n postalCode: 'postalCode',\n country: 'country',\n isPrimary: 'isPrimary',\n}\n\nfunction normalizeOptional(value: string): string | undefined {\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nfunction extractValidationDetails(error: unknown): AddressValidationDetail[] {\n if (!error || typeof error !== 'object') return []\n const candidate = (error as { details?: unknown }).details\n if (!Array.isArray(candidate)) return []\n return candidate\n .map((entry) => (entry && typeof entry === 'object' ? (entry as AddressValidationDetail) : null))\n .filter((entry): entry is AddressValidationDetail => entry !== null)\n}\n\nfunction resolveFieldMessage(detail: AddressValidationDetail, fieldLabel: string, t: Translator, prefix: string): string {\n const label = (suffix: string, fallback: string) => t(`${prefix}.${suffix}`, fallback)\n switch (detail.code) {\n case 'invalid_type':\n return label('validation.invalid', 'Invalid value for {{field}}').replace('{{field}}', fieldLabel)\n case 'too_small':\n if (detail.minimum === 1 && detail.type === 'string') {\n return label('validation.required', '{{field}} is required').replace('{{field}}', fieldLabel)\n }\n return label('validation.generic', 'Invalid value for {{field}}').replace('{{field}}', fieldLabel)\n case 'too_big':\n if (typeof detail.maximum === 'number') {\n return label('validation.tooLong', '{{field}} is too long').replace('{{field}}', fieldLabel)\n .replace('{{max}}', `${detail.maximum}`)\n }\n return label('validation.generic', 'Invalid value for {{field}}').replace('{{field}}', fieldLabel)\n default:\n return label('validation.generic', 'Invalid value for {{field}}').replace('{{field}}', fieldLabel)\n }\n}\n\nexport function AddressTiles<C = unknown>({\n addresses,\n onCreate,\n onUpdate,\n onDelete,\n t,\n emptyLabel,\n isSubmitting = false,\n gridClassName = 'grid gap-4 min-[480px]:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4',\n hideAddButton = false,\n onAddActionChange,\n emptyStateTitle,\n emptyStateActionLabel,\n labelPrefix = 'customers.people.detail.addresses',\n addressTypesAdapter,\n addressTypesContext,\n loadFormat,\n formatContext,\n}: AddressTilesProps<C>) {\n const scopeVersion = useOrganizationScopeVersion()\n const [isFormOpen, setIsFormOpen] = React.useState(false)\n const [editingId, setEditingId] = React.useState<string | null>(null)\n const [draft, setDraft] = React.useState<DraftAddressState>(defaultDraft)\n const [saving, setSaving] = React.useState(false)\n const [deletingId, setDeletingId] = React.useState<string | null>(null)\n const [generalError, setGeneralError] = React.useState<string | null>(null)\n const [fieldErrors, setFieldErrors] = React.useState<Partial<Record<DraftFieldKey, string>>>({})\n const [format, setFormat] = React.useState<AddressFormatStrategy>('line_first')\n const [formatLoading, setFormatLoading] = React.useState(false)\n\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n\n const fieldLabels = React.useMemo(\n () => ({\n name: label('fields.label', 'Label'),\n purpose: label('fields.type', 'Address type'),\n companyName: label('fields.companyName', 'Company name'),\n addressLine1: label('fields.line1', 'Address line 1'),\n addressLine2: label('fields.line2', 'Address line 2'),\n street: label('fields.street', 'Street'),\n buildingNumber: label('fields.buildingNumber', 'Building number'),\n flatNumber: label('fields.flatNumber', 'Flat number'),\n city: label('fields.city', 'City'),\n region: label('fields.region', 'Region'),\n postalCode: label('fields.postalCode', 'Postal code'),\n country: label('fields.country', 'Country'),\n isPrimary: label('fields.primary', 'Primary address'),\n }),\n [label],\n )\n\n const resetForm = React.useCallback(() => {\n setDraft(defaultDraft)\n setFieldErrors({})\n setGeneralError(null)\n setEditingId(null)\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadFormatValue() {\n if (!loadFormat) {\n setFormat('line_first')\n setFormatLoading(false)\n return\n }\n setFormatLoading(true)\n try {\n const value = await loadFormat(formatContext)\n if (!cancelled && (value === 'street_first' || value === 'line_first')) {\n setFormat(value)\n }\n } catch (err) {\n if (!cancelled) {\n const message =\n err instanceof Error && err.message\n ? err.message\n : label('formatLoadError', 'Failed to load address configuration')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setFormatLoading(false)\n }\n }\n void loadFormatValue()\n return () => {\n cancelled = true\n }\n }, [formatContext, label, loadFormat, scopeVersion])\n\n const openCreateForm = React.useCallback(() => {\n resetForm()\n setIsFormOpen(true)\n }, [resetForm])\n\n const handleCancel = React.useCallback(() => {\n resetForm()\n setIsFormOpen(false)\n }, [resetForm])\n\n const handleEdit = React.useCallback((value: AddressValue) => {\n setDraft({\n name: value.name ?? '',\n purpose: value.purpose ?? '',\n companyName: value.companyName ?? '',\n addressLine1: value.addressLine1 ?? '',\n addressLine2: value.addressLine2 ?? '',\n buildingNumber: value.buildingNumber ?? '',\n flatNumber: value.flatNumber ?? '',\n city: value.city ?? '',\n region: value.region ?? '',\n postalCode: value.postalCode ?? '',\n country: value.country ?? '',\n isPrimary: value.isPrimary ?? false,\n })\n setEditingId(value.id)\n setIsFormOpen(true)\n setFieldErrors({})\n setGeneralError(null)\n }, [])\n\n const validate = React.useCallback((): boolean => {\n const errors: Partial<Record<DraftFieldKey, string>> = {}\n if (!draft.addressLine1.trim()) {\n errors.addressLine1 = label('validation.required', '{{field}} is required').replace('{{field}}', fieldLabels.addressLine1)\n }\n if (Object.keys(errors).length > 0) {\n setFieldErrors(errors)\n return false\n }\n return true\n }, [draft.addressLine1, fieldLabels.addressLine1, label])\n\n const handleSave = React.useCallback(async () => {\n if (!validate()) return\n setSaving(true)\n setGeneralError(null)\n try {\n const payload: AddressInput = {\n name: normalizeOptional(draft.name),\n purpose: normalizeOptional(draft.purpose),\n companyName: normalizeOptional(draft.companyName),\n addressLine1: draft.addressLine1.trim(),\n addressLine2: normalizeOptional(draft.addressLine2),\n buildingNumber: normalizeOptional(draft.buildingNumber),\n flatNumber: normalizeOptional(draft.flatNumber),\n city: normalizeOptional(draft.city),\n region: normalizeOptional(draft.region),\n postalCode: normalizeOptional(draft.postalCode),\n country: normalizeOptional(draft.country)?.toUpperCase(),\n isPrimary: draft.isPrimary,\n }\n if (editingId && onUpdate) {\n await onUpdate(editingId, payload)\n } else {\n await onCreate(payload)\n }\n resetForm()\n setIsFormOpen(false)\n } catch (err) {\n const details = extractValidationDetails(err)\n if (details.length) {\n const nextErrors: Partial<Record<DraftFieldKey, string>> = {}\n details.forEach((detail) => {\n const path = Array.isArray(detail.path) ? detail.path : []\n const key = typeof path[0] === 'string' ? path[0] : undefined\n if (!key) return\n const fieldKey = serverFieldMap[key]\n if (!fieldKey) return\n const fieldLabel = fieldLabels[fieldKey] ?? key\n nextErrors[fieldKey] = resolveFieldMessage(detail, fieldLabel, t, labelPrefix)\n })\n setFieldErrors(nextErrors)\n setGeneralError(label('validation.summary', 'Please fix the highlighted fields.'))\n return\n }\n const message =\n err instanceof Error && err.message\n ? err.message\n : label('error', 'Failed to save address')\n setGeneralError(message)\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n }, [draft, editingId, fieldLabels, label, labelPrefix, onCreate, onUpdate, resetForm, t, validate])\n\n const handleDelete = React.useCallback(\n async (id: string) => {\n if (!onDelete) return\n setDeletingId(id)\n try {\n await onDelete(id)\n if (editingId === id) {\n resetForm()\n setIsFormOpen(false)\n }\n } catch (err) {\n const message =\n err instanceof Error && err.message\n ? err.message\n : label('error', 'Failed to delete address')\n flash(message, 'error')\n } finally {\n setDeletingId(null)\n }\n },\n [editingId, label, onDelete, resetForm]\n )\n\n const disableActions = saving || isSubmitting || deletingId !== null\n const isEditing = editingId !== null\n const addDisabled = disableActions || isEditing\n const hasAddresses = addresses.length > 0\n const emptyTitle = emptyStateTitle ?? emptyLabel\n const emptyActionLabel = emptyStateActionLabel ?? label('add', 'Add address')\n\n React.useEffect(() => {\n if (!onAddActionChange) return\n onAddActionChange({ openCreateForm, addDisabled })\n }, [onAddActionChange, openCreateForm, addDisabled])\n\n React.useEffect(\n () => () => {\n if (onAddActionChange) onAddActionChange(null)\n },\n [onAddActionChange]\n )\n\n const renderFormTile = React.useCallback(\n (key: string) => (\n <div\n key={key}\n className=\"rounded-lg border-2 border-dashed border-muted-foreground/50 bg-muted/20 p-4 text-sm\"\n onKeyDown={(event) => {\n if (!(event.metaKey || event.ctrlKey)) return\n if (event.key !== 'Enter') return\n event.preventDefault()\n if (disableActions) return\n void handleSave()\n }}\n >\n <div className=\"flex items-center justify-between text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n <span>\n {editingId\n ? label('editTitle', 'Edit address')\n : label('addTitle', 'Add address')}\n </span>\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" onClick={handleCancel} disabled={disableActions}>\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n <div className=\"mt-3 space-y-3\">\n {formatLoading ? (\n <p className=\"text-xs text-muted-foreground\">\n {label('formatLoading', 'Loading address preferences\u2026')}\n </p>\n ) : null}\n <AddressEditor\n value={draft}\n onChange={(next) => {\n setDraft(next)\n if (Object.keys(fieldErrors).length) {\n const nextErrors = { ...fieldErrors }\n ;(Object.keys(nextErrors) as DraftFieldKey[]).forEach((key) => {\n const candidate = (next as Record<string, unknown>)[key]\n if (candidate !== undefined && candidate !== null && `${candidate}`.length) {\n delete nextErrors[key]\n }\n })\n setFieldErrors(nextErrors)\n }\n }}\n format={format}\n t={t}\n disabled={disableActions}\n errors={fieldErrors}\n showFormatHint={!formatLoading}\n labelPrefix={labelPrefix}\n addressTypesAdapter={addressTypesAdapter}\n addressTypesContext={addressTypesContext}\n />\n {generalError ? <p className=\"text-xs text-red-600\">{generalError}</p> : null}\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={handleCancel} disabled={disableActions}>\n {label('cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={disableActions}>\n {saving ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {editingId\n ? label('updating', 'Updating\u2026')\n : label('saving', 'Saving\u2026')}\n </>\n ) : editingId ? (\n label('update', 'Update address')\n ) : (\n label('save', 'Save address')\n )}\n </Button>\n </div>\n </div>\n </div>\n ),\n [\n addressTypesAdapter,\n addressTypesContext,\n disableActions,\n draft,\n editingId,\n fieldErrors,\n format,\n formatLoading,\n handleCancel,\n handleSave,\n generalError,\n label,\n labelPrefix,\n saving,\n t,\n ]\n )\n\n return (\n <div className=\"space-y-4\">\n {!hideAddButton ? (\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={openCreateForm}\n disabled={addDisabled}\n >\n <Plus className=\"mr-2 h-4 w-4\" />\n {label('add', 'Add address')}\n </Button>\n </div>\n ) : null}\n {hasAddresses ? (\n <div className={gridClassName}>\n {addresses.map((address) => {\n if (isFormOpen && editingId === address.id) {\n return renderFormTile(`form-${address.id}`)\n }\n const isDeleting = deletingId === address.id\n return (\n <div\n key={address.id}\n className=\"group rounded-lg border border-border/60 bg-card p-4 text-sm transition hover:border-border\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"text-sm font-semibold text-foreground\">\n {address.name ?? label('labelFallback', 'Address')}\n </p>\n {address.isPrimary ? (\n <span className=\"rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-semibold uppercase\">\n {label('primaryBadge', 'Primary')}\n </span>\n ) : null}\n </div>\n {address.purpose ? (\n <p className=\"text-xs text-muted-foreground\">\n {address.purpose}\n </p>\n ) : null}\n <AddressView address={address} format={format} className=\"text-sm text-foreground\" />\n <p className=\"text-xs text-muted-foreground\">\n {formatAddressString(address, format)}\n </p>\n </div>\n <div className=\"flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => handleEdit(address)}\n disabled={disableActions}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => handleDelete(address.id)}\n disabled={disableActions}\n >\n {isDeleting ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n </div>\n )\n })}\n {isFormOpen && !editingId ? renderFormTile('create') : null}\n </div>\n ) : isFormOpen ? (\n <div className={gridClassName}>\n {renderFormTile('create')}\n </div>\n ) : (\n <TabEmptyState\n title={emptyTitle}\n action={{\n label: emptyActionLabel,\n onClick: openCreateForm,\n disabled: addDisabled,\n }}\n />\n )}\n </div>\n )\n}\n\nexport default AddressTiles\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["key"]
|
|
7
|
+
}
|