@open-mercato/core 0.7.1-develop.7123.1.25365e718d → 0.7.1-develop.7131.1.20abf9e885
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +5 -0
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +13 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +5 -0
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +5 -0
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +5 -0
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +5 -0
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +13 -0
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +5 -0
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +5 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +5 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +13 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +5 -117
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -3
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +33 -9
- package/dist/modules/customers/commands/deals.js.map +3 -3
- package/dist/modules/customers/components/AddressEditor.js +71 -1
- package/dist/modules/customers/components/AddressEditor.js.map +2 -2
- package/dist/modules/customers/utils/addressFormat.js +18 -2
- package/dist/modules/customers/utils/addressFormat.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +5 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +5 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +5 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +9 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/token/[token]/route.js +5 -0
- package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +5 -0
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +5 -0
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +17 -2
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +7 -24
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js +29 -0
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js.map +7 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +5 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +5 -0
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +5 -0
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +5 -0
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +5 -0
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +5 -0
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +5 -0
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +5 -0
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +5 -0
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
- package/src/modules/auth/api/profile/route.ts +5 -0
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
- package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
- package/src/modules/customers/api/interactions/complete/route.ts +5 -0
- package/src/modules/customers/api/labels/assign/route.ts +5 -0
- package/src/modules/customers/api/labels/route.ts +5 -0
- package/src/modules/customers/api/labels/unassign/route.ts +5 -0
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
- package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
- package/src/modules/customers/api/pipelines/route.ts +13 -0
- package/src/modules/customers/api/settings/address-format/route.ts +5 -0
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
- package/src/modules/customers/api/tags/assign/route.ts +5 -0
- package/src/modules/customers/api/tags/unassign/route.ts +5 -0
- package/src/modules/customers/api/todos/route.ts +13 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +0 -23
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +11 -172
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -1
- package/src/modules/customers/commands/deals.ts +84 -9
- package/src/modules/customers/components/AddressEditor.tsx +116 -1
- package/src/modules/customers/i18n/de.json +6 -0
- package/src/modules/customers/i18n/en.json +6 -0
- package/src/modules/customers/i18n/es.json +6 -0
- package/src/modules/customers/i18n/ko.json +6 -0
- package/src/modules/customers/i18n/pl.json +6 -0
- package/src/modules/customers/utils/addressFormat.tsx +59 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -0
- package/src/modules/eudr/api/plots/import/route.ts +7 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
- package/src/modules/messages/api/[id]/forward/route.ts +5 -0
- package/src/modules/messages/api/[id]/reply/route.ts +5 -0
- package/src/modules/messages/api/[id]/route.ts +9 -0
- package/src/modules/messages/api/token/[token]/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
- package/src/modules/sales/api/quotes/accept/route.ts +5 -0
- package/src/modules/sales/api/quotes/convert/route.ts +5 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
- package/src/modules/sales/components/documents/AddressesSection.tsx +48 -5
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +5 -23
- package/src/modules/sales/components/documents/normalizeAddressDraft.ts +29 -0
- package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
- package/src/modules/staff/api/team-members/self/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
- package/src/modules/warranty_claims/api/ai/assess/route.ts +5 -0
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +5 -0
- package/src/modules/warranty_claims/api/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/receiving/route.ts +5 -0
- package/src/modules/warranty_claims/api/replacement-order/route.ts +5 -0
- package/src/modules/warranty_claims/api/return-label/route.ts +5 -0
- package/src/modules/warranty_claims/api/sales-return/route.ts +5 -0
- package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
- package/src/modules/warranty_claims/api/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/transition/route.ts +5 -0
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
- package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/components/AddressEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Plus, Settings } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\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 { buildHrefWithReturnTo } from '@open-mercato/shared/lib/navigation/returnTo'\nimport type { AddressFormatStrategy } from '../utils/addressFormat'\nimport { useAddressTypes } from './detail/hooks/useAddressTypes'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\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 latitude?: string\n longitude?: 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 | 'latitude'\n | 'longitude'\n | 'isPrimary'\n\ntype AddressEditorProps = {\n value: AddressEditorDraft\n onChange: (next: AddressEditorDraft) => void\n format: AddressFormatStrategy\n t: Translator\n disabled?: boolean\n errors?: Partial<Record<AddressEditorField, string>>\n hidePrimaryToggle?: boolean\n showFormatHint?: boolean\n showCoordinateFields?: boolean\n}\n\nexport function AddressEditor({\n value,\n onChange,\n format,\n t,\n disabled = false,\n errors = {},\n hidePrimaryToggle = false,\n showFormatHint = true,\n showCoordinateFields = false,\n}: AddressEditorProps) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const { options: addressTypes, loading: addressTypesLoading, error: addressTypeError, createType } = useAddressTypes(t)\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 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 ...(showCoordinateFields\n ? { latitude: value.latitude ?? '', longitude: value.longitude ?? '' }\n : {}),\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 const returnTo = React.useMemo(() => {\n const query = searchParams?.toString() ?? ''\n if (!pathname) return null\n return query.length ? `${pathname}?${query}` : pathname\n }, [pathname, searchParams])\n const manageAddressTypesHref = React.useMemo(\n () => buildHrefWithReturnTo('/backend/config/customers', returnTo),\n [returnTo],\n )\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(t('customers.people.detail.addresses.types.emptyError', 'Please provide a value'))\n return\n }\n setTypeFormError(null)\n await createType(trimmed)\n setTypeDialogOpen(false)\n setTypeValue('')\n },\n [createType, t, 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-status-error-border focus:ring-status-error-border' : '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={t('customers.people.detail.addresses.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 value={current.purpose || undefined}\n onValueChange={(next) => update('purpose', next ?? '')}\n disabled={disabled}\n >\n <SelectTrigger\n className={errors.purpose ? 'border-destructive' : undefined}\n aria-invalid={errors.purpose ? 'true' : undefined}\n >\n <SelectValue\n placeholder={\n addressTypesLoading\n ? t('customers.people.detail.addresses.types.loading', 'Loading\u2026')\n : t('customers.people.detail.addresses.types.placeholder', 'Address type')\n }\n />\n </SelectTrigger>\n <SelectContent>\n {addressTypes.map((entry) => (\n <SelectItem key={entry.value} value={entry.value}>\n {entry.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\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>{t('customers.people.detail.addresses.types.add', 'Add address type')}</DialogTitle>\n <DialogDescription>\n {t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 {t('customers.people.detail.addresses.types.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={disabled || !typeValue.trim()}>\n {t('customers.people.detail.addresses.types.save', 'Save')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n <Button\n asChild\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n disabled={disabled}\n title={t('customers.people.detail.addresses.types.manage', 'Manage address types')}\n >\n <Link\n href={manageAddressTypesHref}\n aria-label={t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 {showFormatHint ? (\n <p className=\"text-xs text-muted-foreground\">\n {format === 'street_first'\n ? t('customers.people.detail.addresses.streetFormatHint', 'Street-first layout is active.')\n : t('customers.people.detail.addresses.lineFormatHint', 'Address-line layout is active.')}\n </p>\n ) : null}\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={\n format === 'street_first'\n ? t('customers.people.detail.addresses.fields.street', 'Street')\n : t('customers.people.detail.addresses.fields.line1', 'Address line 1')\n }\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n {errors.addressLine1 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine1}</p> : null}\n {format === 'street_first' ? (\n <>\n <Input\n className={inputClass('buildingNumber')}\n placeholder={t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 <Input\n className={inputClass('addressLine2')}\n placeholder={t('customers.people.detail.addresses.fields.streetExtra', '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 {errors.addressLine2 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine2}</p> : null}\n </>\n ) : (\n <>\n <Input\n className={inputClass('addressLine2')}\n placeholder={t('customers.people.detail.addresses.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 {errors.addressLine2 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine2}</p> : null}\n </>\n )}\n <Input\n className={inputClass('city')}\n placeholder={t('customers.people.detail.addresses.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 {errors.city ? <p className=\"text-xs text-destructive\">{errors.city}</p> : null}\n <Input\n className={inputClass('region')}\n placeholder={t('customers.people.detail.addresses.fields.region', 'Region/state')}\n value={current.region}\n onChange={(evt) => update('region', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.region ? 'true' : undefined}\n />\n {errors.region ? <p className=\"text-xs text-destructive\">{errors.region}</p> : null}\n <Input\n className={inputClass('postalCode')}\n placeholder={t('customers.people.detail.addresses.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 {errors.postalCode ? <p className=\"text-xs text-destructive\">{errors.postalCode}</p> : null}\n <Dialog\n open={countryDialogOpen}\n onOpenChange={(open) => {\n setCountryDialogOpen(open)\n if (!open) setCountryQuery('')\n }}\n >\n <DialogTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n className={`${inputClass('country')} h-10 w-full justify-between`}\n disabled={disabled}\n aria-invalid={errors.country ? 'true' : undefined}\n >\n <span className=\"truncate text-left\">\n {selectedCountry\n ? `${selectedCountry.label}`\n : t('customers.people.detail.addresses.countryPlaceholder', 'Select country')}\n </span>\n <span className=\"ml-2 text-xs text-muted-foreground\">\n {selectedCountry ? selectedCountry.code : null}\n </span>\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{t('customers.people.detail.addresses.countryDialogTitle', 'Select country')}</DialogTitle>\n <DialogDescription>\n {t('customers.people.detail.addresses.countryDialogDescription', 'Search and choose an ISO country code.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <Input\n placeholder={t('customers.people.detail.addresses.countrySearch', 'Search country')}\n value={countryQuery}\n onChange={(evt) => setCountryQuery(evt.target.value)}\n />\n <div className=\"max-h-64 overflow-y-auto rounded border divide-y\">\n {filteredCountryOptions.length === 0 ? (\n <p className=\"px-3 py-2 text-sm text-muted-foreground\">\n {t('customers.people.detail.addresses.countryEmpty', 'No matches found')}\n </p>\n ) : (\n filteredCountryOptions.map((option) => (\n <Button\n key={option.code}\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-between rounded-none font-normal\"\n onClick={() => {\n update('country', option.code)\n setCountryDialogOpen(false)\n setCountryQuery('')\n }}\n >\n <span className=\"truncate\">{option.label}</span>\n <span className=\"text-xs text-muted-foreground\">{option.code}</span>\n </Button>\n ))\n )}\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n update('country', '')\n setCountryDialogOpen(false)\n setCountryQuery('')\n }}\n disabled={disabled}\n >\n {t('customers.people.detail.addresses.countryClear', 'Clear')}\n </Button>\n <Button type=\"button\" onClick={() => setCountryDialogOpen(false)}>\n {t('customers.people.detail.addresses.countryClose', 'Done')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n {errors.country ? <p className=\"text-xs text-destructive\">{errors.country}</p> : null}\n {showCoordinateFields ? (\n <>\n <Input\n className={inputClass('latitude')}\n placeholder={t('customers.people.detail.addresses.fields.latitude', 'Latitude')}\n aria-label={t('customers.people.detail.addresses.fields.latitude', 'Latitude')}\n inputMode=\"decimal\"\n value={current.latitude ?? ''}\n onChange={(evt) => update('latitude', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.latitude ? 'true' : undefined}\n />\n {errors.latitude ? <p className=\"text-xs text-destructive\">{errors.latitude}</p> : null}\n <Input\n className={inputClass('longitude')}\n placeholder={t('customers.people.detail.addresses.fields.longitude', 'Longitude')}\n aria-label={t('customers.people.detail.addresses.fields.longitude', 'Longitude')}\n inputMode=\"decimal\"\n value={current.longitude ?? ''}\n onChange={(evt) => update('longitude', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.longitude ? 'true' : undefined}\n />\n {errors.longitude ? <p className=\"text-xs text-destructive\">{errors.longitude}</p> : null}\n </>\n ) : null}\n </div>\n {!hidePrimaryToggle ? (\n <label className=\"inline-flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={current.isPrimary}\n onChange={(evt) => update('isPrimary', evt.target.checked)}\n disabled={disabled}\n aria-invalid={errors.isPrimary ? 'true' : undefined}\n />\n <span>{t('customers.people.detail.addresses.fields.primary', 'Set as primary')}</span>\n </label>\n ) : null}\n {errors.isPrimary ? <p className=\"text-xs text-destructive\">{errors.isPrimary}</p> : null}\n </div>\n )\n}\n\nexport default AddressEditor\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Plus, Settings } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\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 { buildHrefWithReturnTo } from '@open-mercato/shared/lib/navigation/returnTo'\nimport { resolveTaxIdLabel, type AddressFormatStrategy } from '../utils/addressFormat'\nimport { useAddressTypes } from './detail/hooks/useAddressTypes'\n\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\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 /**\n * Contact details that belong to the ADDRESS rather than to the customer: the tax identifier it was\n * invoiced under, and the phone a carrier calls about a delivery. Optional so every existing caller\n * keeps compiling; callers must opt in with the matching `show*Field` props before they render.\n */\n taxId?: string\n /**\n * Which scheme the identifier belongs to, in Stripe's `{country}_{kind}` vocabulary. Chosen, not\n * inferred: `PL1234567890` and `1234567890` are the same business written two ways, and a rule\n * that reads the form of the value is guessing \u2014 the more schemes the vocabulary carries, the more\n * often it guesses wrong, and a wrong type is worse than none because it names the number.\n */\n taxIdType?: string\n phone?: string\n latitude?: string\n longitude?: 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 | 'taxId'\n | 'taxIdType'\n | 'phone'\n | 'latitude'\n | 'longitude'\n | 'isPrimary'\n\ntype AddressEditorProps = {\n value: AddressEditorDraft\n onChange: (next: AddressEditorDraft) => void\n format: AddressFormatStrategy\n t: Translator\n disabled?: boolean\n errors?: Partial<Record<AddressEditorField, string>>\n hidePrimaryToggle?: boolean\n showFormatHint?: boolean\n showCoordinateFields?: boolean\n /**\n * Render the phone. Off by default, opt-in for the same reason `showCoordinateFields` is: only a\n * caller whose storage can hold a field should offer it.\n */\n showPhoneField?: boolean\n /**\n * Render the tax identifier and its scheme. Separate from the phone, and not for symmetry \u2014 a\n * phone is a contact detail and a tax identifier is not, and the two stop travelling together at\n * the next phase: `CustomerAddress` gains a `phone` column and no tax id, so the address book will\n * offer one and not the other.\n *\n * Both gate the CALLER, not the field. Inside a tile that opts in, the input renders whether or not\n * it carries a value and takes the same `disabled` as every neighbour.\n */\n showTaxIdField?: boolean\n}\n\n/**\n * The schemes offered in the picker, in the order a Polish deployment meets them. The vocabulary is\n * additive under the backward-compatibility contract, so a new scheme is a new entry here rather\n * than a new branch in a rule that has to guess.\n *\n * The labels are the ones `resolveTaxIdLabel` resolves, so the picker and the marker beside the\n * filled field always read the same.\n */\nconst TAX_ID_TYPES = ['pl_nip', 'eu_vat', 'other'] as const\n\nexport function AddressEditor({\n value,\n onChange,\n format,\n t,\n disabled = false,\n errors = {},\n hidePrimaryToggle = false,\n showFormatHint = true,\n showCoordinateFields = false,\n showPhoneField = false,\n showTaxIdField = false,\n}: AddressEditorProps) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const { options: addressTypes, loading: addressTypesLoading, error: addressTypeError, createType } = useAddressTypes(t)\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 const taxIdLabels = {\n plNip: t('customers.people.detail.addresses.fields.taxId.plNip', 'Tax ID'),\n euVat: t('customers.people.detail.addresses.fields.taxId.euVat', 'EU VAT'),\n other: t('customers.people.detail.addresses.fields.taxId.other', 'Tax number'),\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 taxId: value.taxId ?? '',\n taxIdType: value.taxIdType ?? '',\n phone: value.phone ?? '',\n ...(showCoordinateFields\n ? { latitude: value.latitude ?? '', longitude: value.longitude ?? '' }\n : {}),\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 const returnTo = React.useMemo(() => {\n const query = searchParams?.toString() ?? ''\n if (!pathname) return null\n return query.length ? `${pathname}?${query}` : pathname\n }, [pathname, searchParams])\n const manageAddressTypesHref = React.useMemo(\n () => buildHrefWithReturnTo('/backend/config/customers', returnTo),\n [returnTo],\n )\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(t('customers.people.detail.addresses.types.emptyError', 'Please provide a value'))\n return\n }\n setTypeFormError(null)\n await createType(trimmed)\n setTypeDialogOpen(false)\n setTypeValue('')\n },\n [createType, t, 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-status-error-border focus:ring-status-error-border' : '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={t('customers.people.detail.addresses.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 value={current.purpose || undefined}\n onValueChange={(next) => update('purpose', next ?? '')}\n disabled={disabled}\n >\n <SelectTrigger\n className={errors.purpose ? 'border-destructive' : undefined}\n aria-invalid={errors.purpose ? 'true' : undefined}\n >\n <SelectValue\n placeholder={\n addressTypesLoading\n ? t('customers.people.detail.addresses.types.loading', 'Loading\u2026')\n : t('customers.people.detail.addresses.types.placeholder', 'Address type')\n }\n />\n </SelectTrigger>\n <SelectContent>\n {addressTypes.map((entry) => (\n <SelectItem key={entry.value} value={entry.value}>\n {entry.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\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>{t('customers.people.detail.addresses.types.add', 'Add address type')}</DialogTitle>\n <DialogDescription>\n {t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 {t('customers.people.detail.addresses.types.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={disabled || !typeValue.trim()}>\n {t('customers.people.detail.addresses.types.save', 'Save')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n <Button\n asChild\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n disabled={disabled}\n title={t('customers.people.detail.addresses.types.manage', 'Manage address types')}\n >\n <Link\n href={manageAddressTypesHref}\n aria-label={t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 {showFormatHint ? (\n <p className=\"text-xs text-muted-foreground\">\n {format === 'street_first'\n ? t('customers.people.detail.addresses.streetFormatHint', 'Street-first layout is active.')\n : t('customers.people.detail.addresses.lineFormatHint', 'Address-line layout is active.')}\n </p>\n ) : null}\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n <Input\n className={inputClass('addressLine1')}\n placeholder={\n format === 'street_first'\n ? t('customers.people.detail.addresses.fields.street', 'Street')\n : t('customers.people.detail.addresses.fields.line1', 'Address line 1')\n }\n value={current.addressLine1}\n onChange={(evt) => update('addressLine1', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.addressLine1 ? 'true' : undefined}\n />\n {errors.addressLine1 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine1}</p> : null}\n {format === 'street_first' ? (\n <>\n <Input\n className={inputClass('buildingNumber')}\n placeholder={t('customers.people.detail.addresses.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={t('customers.people.detail.addresses.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 <Input\n className={inputClass('addressLine2')}\n placeholder={t('customers.people.detail.addresses.fields.streetExtra', '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 {errors.addressLine2 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine2}</p> : null}\n </>\n ) : (\n <>\n <Input\n className={inputClass('addressLine2')}\n placeholder={t('customers.people.detail.addresses.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 {errors.addressLine2 ? <p className=\"text-xs text-destructive sm:col-span-2\">{errors.addressLine2}</p> : null}\n </>\n )}\n <Input\n className={inputClass('city')}\n placeholder={t('customers.people.detail.addresses.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 {errors.city ? <p className=\"text-xs text-destructive\">{errors.city}</p> : null}\n <Input\n className={inputClass('region')}\n placeholder={t('customers.people.detail.addresses.fields.region', 'Region/state')}\n value={current.region}\n onChange={(evt) => update('region', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.region ? 'true' : undefined}\n />\n {errors.region ? <p className=\"text-xs text-destructive\">{errors.region}</p> : null}\n <Input\n className={inputClass('postalCode')}\n placeholder={t('customers.people.detail.addresses.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 {errors.postalCode ? <p className=\"text-xs text-destructive\">{errors.postalCode}</p> : null}\n <Dialog\n open={countryDialogOpen}\n onOpenChange={(open) => {\n setCountryDialogOpen(open)\n if (!open) setCountryQuery('')\n }}\n >\n <DialogTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n className={`${inputClass('country')} h-10 w-full justify-between`}\n disabled={disabled}\n aria-invalid={errors.country ? 'true' : undefined}\n >\n <span className=\"truncate text-left\">\n {selectedCountry\n ? `${selectedCountry.label}`\n : t('customers.people.detail.addresses.countryPlaceholder', 'Select country')}\n </span>\n <span className=\"ml-2 text-xs text-muted-foreground\">\n {selectedCountry ? selectedCountry.code : null}\n </span>\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{t('customers.people.detail.addresses.countryDialogTitle', 'Select country')}</DialogTitle>\n <DialogDescription>\n {t('customers.people.detail.addresses.countryDialogDescription', 'Search and choose an ISO country code.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <Input\n placeholder={t('customers.people.detail.addresses.countrySearch', 'Search country')}\n value={countryQuery}\n onChange={(evt) => setCountryQuery(evt.target.value)}\n />\n <div className=\"max-h-64 overflow-y-auto rounded border divide-y\">\n {filteredCountryOptions.length === 0 ? (\n <p className=\"px-3 py-2 text-sm text-muted-foreground\">\n {t('customers.people.detail.addresses.countryEmpty', 'No matches found')}\n </p>\n ) : (\n filteredCountryOptions.map((option) => (\n <Button\n key={option.code}\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-between rounded-none font-normal\"\n onClick={() => {\n update('country', option.code)\n setCountryDialogOpen(false)\n setCountryQuery('')\n }}\n >\n <span className=\"truncate\">{option.label}</span>\n <span className=\"text-xs text-muted-foreground\">{option.code}</span>\n </Button>\n ))\n )}\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n update('country', '')\n setCountryDialogOpen(false)\n setCountryQuery('')\n }}\n disabled={disabled}\n >\n {t('customers.people.detail.addresses.countryClear', 'Clear')}\n </Button>\n <Button type=\"button\" onClick={() => setCountryDialogOpen(false)}>\n {t('customers.people.detail.addresses.countryClose', 'Done')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n {errors.country ? <p className=\"text-xs text-destructive\">{errors.country}</p> : null}\n {showCoordinateFields ? (\n <>\n <Input\n className={inputClass('latitude')}\n placeholder={t('customers.people.detail.addresses.fields.latitude', 'Latitude')}\n aria-label={t('customers.people.detail.addresses.fields.latitude', 'Latitude')}\n inputMode=\"decimal\"\n value={current.latitude ?? ''}\n onChange={(evt) => update('latitude', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.latitude ? 'true' : undefined}\n />\n {errors.latitude ? <p className=\"text-xs text-destructive\">{errors.latitude}</p> : null}\n <Input\n className={inputClass('longitude')}\n placeholder={t('customers.people.detail.addresses.fields.longitude', 'Longitude')}\n aria-label={t('customers.people.detail.addresses.fields.longitude', 'Longitude')}\n inputMode=\"decimal\"\n value={current.longitude ?? ''}\n onChange={(evt) => update('longitude', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.longitude ? 'true' : undefined}\n />\n {errors.longitude ? <p className=\"text-xs text-destructive\">{errors.longitude}</p> : null}\n </>\n ) : null}\n {showTaxIdField ? (\n <>\n <Select\n value={current.taxIdType || undefined}\n onValueChange={(next) => update('taxIdType', next ?? '')}\n disabled={disabled}\n >\n <SelectTrigger\n className={errors.taxIdType ? 'border-destructive' : undefined}\n aria-label={t('customers.people.detail.addresses.fields.taxIdType', 'Tax id type')}\n aria-invalid={errors.taxIdType ? 'true' : undefined}\n >\n <SelectValue\n placeholder={t('customers.people.detail.addresses.fields.taxIdType', 'Tax id type')}\n />\n </SelectTrigger>\n <SelectContent>\n {TAX_ID_TYPES.map((type) => (\n <SelectItem key={type} value={type}>\n {resolveTaxIdLabel(taxIdLabels, type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Input\n className={inputClass('taxId')}\n placeholder={t('customers.people.detail.addresses.fields.taxId', 'Tax number')}\n aria-label={t('customers.people.detail.addresses.fields.taxId', 'Tax number')}\n rightIcon={\n current.taxId ? (\n <span className=\"text-xs\">{resolveTaxIdLabel(taxIdLabels, current.taxIdType)}</span>\n ) : null\n }\n value={current.taxId ?? ''}\n onChange={(evt) => update('taxId', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.taxId ? 'true' : undefined}\n />\n {errors.taxId ? <p className=\"text-xs text-destructive\">{errors.taxId}</p> : null}\n </>\n ) : null}\n {showPhoneField ? (\n <>\n <Input\n className={inputClass('phone')}\n placeholder={t('customers.people.detail.addresses.fields.phone', 'Phone')}\n aria-label={t('customers.people.detail.addresses.fields.phone', 'Phone')}\n rightIcon={\n current.phone ? (\n <span className=\"text-xs\">\n {t('customers.people.detail.addresses.fields.phone', 'Phone')}\n </span>\n ) : null\n }\n inputMode=\"tel\"\n value={current.phone ?? ''}\n onChange={(evt) => update('phone', evt.target.value)}\n disabled={disabled}\n aria-invalid={errors.phone ? 'true' : undefined}\n />\n {errors.phone ? <p className=\"text-xs text-destructive\">{errors.phone}</p> : null}\n </>\n ) : null}\n </div>\n {!hidePrimaryToggle ? (\n <label className=\"inline-flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={current.isPrimary}\n onChange={(evt) => update('isPrimary', evt.target.checked)}\n disabled={disabled}\n aria-invalid={errors.isPrimary ? 'true' : undefined}\n />\n <span>{t('customers.people.detail.addresses.fields.primary', 'Set as primary')}</span>\n </label>\n ) : null}\n {errors.isPrimary ? <p className=\"text-xs text-destructive\">{errors.isPrimary}</p> : null}\n </div>\n )\n}\n\nexport default AddressEditor\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -63,7 +63,22 @@ function formatAddressLines(address, format) {
|
|
|
63
63
|
function formatAddressString(address, format, separator = ", ") {
|
|
64
64
|
return formatAddressLines(address, format).filter(Boolean).join(separator);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
const TAX_ID_LABEL_KEY_BY_TYPE = {
|
|
67
|
+
pl_nip: "plNip",
|
|
68
|
+
eu_vat: "euVat"
|
|
69
|
+
};
|
|
70
|
+
function resolveTaxIdLabel(label, taxIdType) {
|
|
71
|
+
if (!label) return void 0;
|
|
72
|
+
if (typeof label === "string") return label;
|
|
73
|
+
const key = TAX_ID_LABEL_KEY_BY_TYPE[typeof taxIdType === "string" ? taxIdType : ""] ?? "other";
|
|
74
|
+
return label[key];
|
|
75
|
+
}
|
|
76
|
+
function AddressView({
|
|
77
|
+
address,
|
|
78
|
+
format,
|
|
79
|
+
className,
|
|
80
|
+
lineClassName
|
|
81
|
+
}) {
|
|
67
82
|
const lines = formatAddressLines(address, format);
|
|
68
83
|
if (!lines.length) return null;
|
|
69
84
|
return /* @__PURE__ */ jsx("div", { className, children: lines.map((line, index) => /* @__PURE__ */ jsx("div", { className: lineClassName, children: line }, `${index}-${line}`)) });
|
|
@@ -72,6 +87,7 @@ export {
|
|
|
72
87
|
AddressView,
|
|
73
88
|
formatAddressJson,
|
|
74
89
|
formatAddressLines,
|
|
75
|
-
formatAddressString
|
|
90
|
+
formatAddressString,
|
|
91
|
+
resolveTaxIdLabel
|
|
76
92
|
};
|
|
77
93
|
//# sourceMappingURL=addressFormat.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/utils/addressFormat.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport type { CustomerAddressFormat } from '../data/entities'\n\nexport type AddressFormatStrategy = CustomerAddressFormat\n\nexport type AddressValue = {\n addressLine1: string | null | undefined\n addressLine2?: string | null\n buildingNumber?: string | null\n flatNumber?: string | null\n city?: string | null\n region?: string | null\n postalCode?: string | null\n country?: string | null\n companyName?: string | null\n}\n\nexport type AddressJsonShape = {\n format: AddressFormatStrategy\n companyName: string | null\n addressLine1: string | null\n addressLine2: string | null\n buildingNumber: string | null\n flatNumber: string | null\n postalCode: string | null\n city: string | null\n region: string | null\n country: string | null\n}\n\nfunction normalize(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n}\n\nfunction mergeStreetLine(address: AddressValue): string | null {\n const street = normalize(address.addressLine1)\n const building = normalize(address.buildingNumber)\n const flat = normalize(address.flatNumber)\n if (!street && !building && !flat) return null\n let line = street ?? ''\n if (building) line = line ? `${line} ${building}` : building\n if (flat) line = line ? `${line}/${flat}` : flat\n return line.length ? line : null\n}\n\nexport function formatAddressJson(address: AddressValue, format: AddressFormatStrategy): AddressJsonShape {\n return {\n format,\n companyName: normalize(address.companyName),\n addressLine1: normalize(address.addressLine1),\n addressLine2: normalize(address.addressLine2),\n buildingNumber: normalize(address.buildingNumber),\n flatNumber: normalize(address.flatNumber),\n postalCode: normalize(address.postalCode),\n city: normalize(address.city),\n region: normalize(address.region),\n country: normalize(address.country),\n }\n}\n\nexport function formatAddressLines(address: AddressValue, format: AddressFormatStrategy): string[] {\n const json = formatAddressJson(address, format)\n const lines: string[] = []\n\n if (json.companyName) lines.push(json.companyName)\n\n if (format === 'street_first') {\n const streetLine = mergeStreetLine(address)\n if (streetLine) lines.push(streetLine)\n const supplemental = normalize(address.addressLine2)\n if (supplemental) lines.push(supplemental)\n const postalCity = [json.postalCode, json.city].filter(Boolean).join(' ')\n if (postalCity.length) lines.push(postalCity)\n if (json.region) lines.push(json.region)\n if (json.country) lines.push(json.country)\n } else {\n if (json.addressLine1) {\n const baseLine1 = json.addressLine1\n const appended = mergeStreetLine(address)\n if (!json.buildingNumber && !json.flatNumber) {\n lines.push(baseLine1)\n } else {\n const composite = appended ?? baseLine1\n lines.push(composite)\n }\n }\n if (json.addressLine2) lines.push(json.addressLine2)\n const postalCity = [json.postalCode, json.city].filter(Boolean).join(' ')\n if (postalCity.length) lines.push(postalCity)\n if (json.region) lines.push(json.region)\n if (json.country) lines.push(json.country)\n }\n\n return lines\n}\n\nexport function formatAddressString(address: AddressValue, format: AddressFormatStrategy, separator = ', '): string {\n return formatAddressLines(address, format).filter(Boolean).join(separator)\n}\n\ntype AddressViewProps = {\n address: AddressValue\n format: AddressFormatStrategy\n className?: string\n lineClassName?: string\n}\n\nexport function AddressView({
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import * as React from 'react'\nimport type { CustomerAddressFormat } from '../data/entities'\n\nexport type AddressFormatStrategy = CustomerAddressFormat\n\nexport type AddressValue = {\n addressLine1: string | null | undefined\n addressLine2?: string | null\n buildingNumber?: string | null\n flatNumber?: string | null\n city?: string | null\n region?: string | null\n postalCode?: string | null\n country?: string | null\n companyName?: string | null\n /**\n * Contact details that belong to the ADDRESS rather than to the customer: who to call about this\n * delivery, and the tax identifier this invoice address was billed under. They remain available to\n * address editors and snapshot payloads, but are deliberately excluded from `formatAddressLines`\n * and `AddressView`, whose existing contract remains postal-only.\n *\n * `taxIdType` interprets the value in Stripe's `{country}_{kind}` vocabulary (`pl_nip`, `eu_vat`,\n * `other`, widened additively): `1234567890` and `PL1234567890` are the same business, and only the\n * type tells a domestic identifier from an EU VAT number. It is metadata about `taxId`, never a\n * displayed field of its own.\n */\n phone?: string | null\n taxId?: string | null\n taxIdType?: string | null\n}\n\n/**\n * Tax-id labels keyed by `taxIdType`, for a caller that wants the identifier named correctly rather\n * than generically.\n *\n * The stored scheme is chosen explicitly rather than inferred from the identifier. `other` also\n * covers an address written before `taxIdType` existed. An unrecognised type takes the `other` route\n * instead of guessing a domestic scheme.\n */\nexport type TaxIdLabelByType = {\n plNip: string\n euVat: string\n other: string\n}\n\nexport type AddressJsonShape = {\n format: AddressFormatStrategy\n companyName: string | null\n addressLine1: string | null\n addressLine2: string | null\n buildingNumber: string | null\n flatNumber: string | null\n postalCode: string | null\n city: string | null\n region: string | null\n country: string | null\n}\n\nfunction normalize(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n}\n\nfunction mergeStreetLine(address: AddressValue): string | null {\n const street = normalize(address.addressLine1)\n const building = normalize(address.buildingNumber)\n const flat = normalize(address.flatNumber)\n if (!street && !building && !flat) return null\n let line = street ?? ''\n if (building) line = line ? `${line} ${building}` : building\n if (flat) line = line ? `${line}/${flat}` : flat\n return line.length ? line : null\n}\n\nexport function formatAddressJson(address: AddressValue, format: AddressFormatStrategy): AddressJsonShape {\n return {\n format,\n companyName: normalize(address.companyName),\n addressLine1: normalize(address.addressLine1),\n addressLine2: normalize(address.addressLine2),\n buildingNumber: normalize(address.buildingNumber),\n flatNumber: normalize(address.flatNumber),\n postalCode: normalize(address.postalCode),\n city: normalize(address.city),\n region: normalize(address.region),\n country: normalize(address.country),\n }\n}\n\nexport function formatAddressLines(address: AddressValue, format: AddressFormatStrategy): string[] {\n const json = formatAddressJson(address, format)\n const lines: string[] = []\n\n if (json.companyName) lines.push(json.companyName)\n\n if (format === 'street_first') {\n const streetLine = mergeStreetLine(address)\n if (streetLine) lines.push(streetLine)\n const supplemental = normalize(address.addressLine2)\n if (supplemental) lines.push(supplemental)\n const postalCity = [json.postalCode, json.city].filter(Boolean).join(' ')\n if (postalCity.length) lines.push(postalCity)\n if (json.region) lines.push(json.region)\n if (json.country) lines.push(json.country)\n } else {\n if (json.addressLine1) {\n const baseLine1 = json.addressLine1\n const appended = mergeStreetLine(address)\n if (!json.buildingNumber && !json.flatNumber) {\n lines.push(baseLine1)\n } else {\n const composite = appended ?? baseLine1\n lines.push(composite)\n }\n }\n if (json.addressLine2) lines.push(json.addressLine2)\n const postalCity = [json.postalCode, json.city].filter(Boolean).join(' ')\n if (postalCity.length) lines.push(postalCity)\n if (json.region) lines.push(json.region)\n if (json.country) lines.push(json.country)\n }\n\n return lines\n}\n\nexport function formatAddressString(address: AddressValue, format: AddressFormatStrategy, separator = ', '): string {\n return formatAddressLines(address, format).filter(Boolean).join(separator)\n}\n\n/**\n * Which member of a label map names which scheme. Private, and deliberately not exhaustive: an\n * unrecognised type resolves to `other`, so the vocabulary can widen without every caller being\n * updated in the same release.\n */\nconst TAX_ID_LABEL_KEY_BY_TYPE: Record<string, keyof TaxIdLabelByType> = {\n pl_nip: 'plNip',\n eu_vat: 'euVat',\n}\n\n/**\n * The label a tax identifier should carry, given its type. Exported because the editor renders the\n * same identifier as an input and must name it the same way this formatter does \u2014 two copies of the\n * mapping is exactly how a foreign number ends up under a domestic scheme's name.\n */\nexport function resolveTaxIdLabel(\n label: string | TaxIdLabelByType | undefined,\n taxIdType: string | null | undefined,\n): string | undefined {\n if (!label) return undefined\n if (typeof label === 'string') return label\n const key = TAX_ID_LABEL_KEY_BY_TYPE[typeof taxIdType === 'string' ? taxIdType : ''] ?? 'other'\n return label[key]\n}\n\ntype AddressViewProps = {\n address: AddressValue\n format: AddressFormatStrategy\n className?: string\n lineClassName?: string\n}\n\nexport function AddressView({\n address,\n format,\n className,\n lineClassName,\n}: AddressViewProps): React.ReactElement | null {\n const lines = formatAddressLines(address, format)\n if (!lines.length) return null\n return (\n <div className={className}>\n {lines.map((line, index) => (\n <div key={`${index}-${line}`} className={lineClassName}>\n {line}\n </div>\n ))}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
updateDictionaryEntrySchema as updateEntryDocSchema
|
|
21
21
|
} from "../../../openapi.js";
|
|
22
22
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
23
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
23
24
|
const logger = createLogger("dictionaries").child({ component: "entries-api" });
|
|
24
25
|
const paramsSchema = z.object({
|
|
25
26
|
dictionaryId: z.string().uuid(),
|
|
@@ -155,6 +156,10 @@ async function PATCH(req, ctx) {
|
|
|
155
156
|
if (isCrudHttpError(err)) {
|
|
156
157
|
return NextResponse.json(err.body, { status: err.status });
|
|
157
158
|
}
|
|
159
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
160
|
+
if (interceptorRejection) {
|
|
161
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
162
|
+
}
|
|
158
163
|
if (err instanceof z.ZodError) {
|
|
159
164
|
return NextResponse.json({ error: "Invalid input", details: err.issues }, { status: 400 });
|
|
160
165
|
}
|
|
@@ -230,6 +235,10 @@ async function DELETE(req, ctx) {
|
|
|
230
235
|
if (isCrudHttpError(err)) {
|
|
231
236
|
return NextResponse.json(err.body, { status: err.status });
|
|
232
237
|
}
|
|
238
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
239
|
+
if (interceptorRejection) {
|
|
240
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
241
|
+
}
|
|
233
242
|
if (err instanceof z.ZodError) {
|
|
234
243
|
return NextResponse.json({ error: "Invalid input", details: err.issues }, { status: 400 });
|
|
235
244
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/dictionaries/api/%5BdictionaryId%5D/entries/%5BentryId%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { updateDictionaryEntrySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n dictionaryEntryParamsSchema,\n dictionaryEntryResponseSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n updateDictionaryEntrySchema as updateEntryDocSchema,\n} from '../../../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('dictionaries').child({ component: 'entries-api' })\nconst paramsSchema = z.object({\n dictionaryId: z.string().uuid(),\n entryId: z.string().uuid(),\n})\n\nasync function loadDictionary(context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>, id: string) {\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionary = await context.em.findOne(Dictionary, {\n id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nasync function loadEntry(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n dictionary: Dictionary,\n entryId: string,\n) {\n const entry = await context.em.findOne(DictionaryEntry, {\n id: entryId,\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: context.tenantId,\n })\n if (!entry) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.entry_not_found', 'Dictionary entry not found') })\n }\n return entry\n}\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n const rawBody = await req.json().catch(() => ({}))\n const payload = updateDictionaryEntrySchema.parse(rawBody)\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n // These nested routes don't use the CRUD factory, so invoke the command bus explicitly.\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const input = { ...(payload as Record<string, unknown>), id: entryId }\n const { result, logEntry } = await commandBus.execute('dictionaries.entries.update', {\n input,\n ctx: context.ctx,\n })\n const updateResult = (result ?? {}) as { entryId?: string | null }\n const updatedEntryId = typeof updateResult.entryId === 'string' ? updateResult.entryId : null\n if (!updatedEntryId) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: updatedEntryId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const updated = await findOneWithDecryption(\n context.em.fork(),\n DictionaryEntry,\n updatedEntryId,\n { populate: ['dictionary'] },\n { tenantId: context.auth.tenantId ?? null, organizationId: context.auth.orgId ?? null },\n )\n if (!updated) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n const response = NextResponse.json({\n id: updated.id,\n value: updated.value,\n label: updated.label,\n color: updated.color,\n icon: updated.icon,\n position: updated.position ?? 0,\n isDefault: updated.isDefault ?? false,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: updatedEntryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to update dictionary entry', { err })\n return NextResponse.json({ error: 'Failed to update dictionary entry' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute('dictionaries.entries.delete', {\n input: { body: { id: entry.id } },\n ctx: context.ctx,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: entry.id,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to delete dictionary entry', { err })\n return NextResponse.json({ error: 'Failed to delete dictionary entry' }, { status: 500 })\n }\n}\n\nconst dictionaryEntryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary entry',\n description: 'Updates the specified dictionary entry using the command bus pipeline.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: updateEntryDocSchema,\n description: 'Fields to update on the dictionary entry.',\n },\n responses: [\n { status: 200, description: 'Dictionary entry updated.', schema: dictionaryEntryResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update entry', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryEntryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary entry',\n description: 'Deletes the specified dictionary entry via the command bus.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Entry deleted.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete entry', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary entry resource',\n pathParams: dictionaryEntryParamsSchema,\n methods: {\n PATCH: dictionaryEntryPatchDoc,\n DELETE: dictionaryEntryDeleteDoc,\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,YAAY,uBAAuB;AAC5C,SAAS,iCAAiC,gCAAgC;AAC1E,SAAS,mCAAmC;AAC5C,SAAS,eAAe,uBAAuB;AAC/C,SAAS,8CAA8C;AACvD;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kCAAkC;AAE3C,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,+BAA+B;AAAA,OAC1B;AACP,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { updateDictionaryEntrySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n dictionaryEntryParamsSchema,\n dictionaryEntryResponseSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n updateDictionaryEntrySchema as updateEntryDocSchema,\n} from '../../../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('dictionaries').child({ component: 'entries-api' })\nconst paramsSchema = z.object({\n dictionaryId: z.string().uuid(),\n entryId: z.string().uuid(),\n})\n\nasync function loadDictionary(context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>, id: string) {\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionary = await context.em.findOne(Dictionary, {\n id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nasync function loadEntry(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n dictionary: Dictionary,\n entryId: string,\n) {\n const entry = await context.em.findOne(DictionaryEntry, {\n id: entryId,\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: context.tenantId,\n })\n if (!entry) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.entry_not_found', 'Dictionary entry not found') })\n }\n return entry\n}\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n const rawBody = await req.json().catch(() => ({}))\n const payload = updateDictionaryEntrySchema.parse(rawBody)\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n // These nested routes don't use the CRUD factory, so invoke the command bus explicitly.\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const input = { ...(payload as Record<string, unknown>), id: entryId }\n const { result, logEntry } = await commandBus.execute('dictionaries.entries.update', {\n input,\n ctx: context.ctx,\n })\n const updateResult = (result ?? {}) as { entryId?: string | null }\n const updatedEntryId = typeof updateResult.entryId === 'string' ? updateResult.entryId : null\n if (!updatedEntryId) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: updatedEntryId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const updated = await findOneWithDecryption(\n context.em.fork(),\n DictionaryEntry,\n updatedEntryId,\n { populate: ['dictionary'] },\n { tenantId: context.auth.tenantId ?? null, organizationId: context.auth.orgId ?? null },\n )\n if (!updated) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n const response = NextResponse.json({\n id: updated.id,\n value: updated.value,\n label: updated.label,\n color: updated.color,\n icon: updated.icon,\n position: updated.position ?? 0,\n isDefault: updated.isDefault ?? false,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: updatedEntryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to update dictionary entry', { err })\n return NextResponse.json({ error: 'Failed to update dictionary entry' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute('dictionaries.entries.delete', {\n input: { body: { id: entry.id } },\n ctx: context.ctx,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: entry.id,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to delete dictionary entry', { err })\n return NextResponse.json({ error: 'Failed to delete dictionary entry' }, { status: 500 })\n }\n}\n\nconst dictionaryEntryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary entry',\n description: 'Updates the specified dictionary entry using the command bus pipeline.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: updateEntryDocSchema,\n description: 'Fields to update on the dictionary entry.',\n },\n responses: [\n { status: 200, description: 'Dictionary entry updated.', schema: dictionaryEntryResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update entry', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryEntryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary entry',\n description: 'Deletes the specified dictionary entry via the command bus.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Entry deleted.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete entry', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary entry resource',\n pathParams: dictionaryEntryParamsSchema,\n methods: {\n PATCH: dictionaryEntryPatchDoc,\n DELETE: dictionaryEntryDeleteDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../../../openapi.js";
|
|
24
24
|
import { resolveDictionaryActorId } from "../../../context.js";
|
|
25
25
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
26
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
26
27
|
const logger = createLogger("dictionaries").child({ component: "entries-api" });
|
|
27
28
|
const paramsSchema = z.object({ dictionaryId: z.string().uuid() });
|
|
28
29
|
const metadata = {
|
|
@@ -117,6 +118,10 @@ async function POST(req, ctx) {
|
|
|
117
118
|
if (isCrudHttpError(err)) {
|
|
118
119
|
return NextResponse.json(err.body, { status: err.status });
|
|
119
120
|
}
|
|
121
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
122
|
+
if (interceptorRejection) {
|
|
123
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
124
|
+
}
|
|
120
125
|
if (err instanceof z.ZodError) {
|
|
121
126
|
return NextResponse.json({ error: "Invalid input", details: err.issues }, { status: 400 });
|
|
122
127
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/dictionaries/api/%5BdictionaryId%5D/entries/reorder/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'\nimport {\n reorderDictionaryEntriesCommandSchema,\n reorderDictionaryEntriesSchema,\n type ReorderDictionaryEntriesCommandInput,\n} from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n dictionaryIdParamsSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n reorderEntriesRequestSchema,\n} from '../../../openapi'\nimport { resolveDictionaryActorId } from '../../../context'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('dictionaries').child({ component: 'entries-api' })\n\nconst paramsSchema = z.object({ dictionaryId: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function POST(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionaryEm = context.em.fork()\n const dictionary = await findOneWithDecryption(\n dictionaryEm,\n Dictionary,\n {\n id: dictionaryId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n },\n undefined,\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n\n const payload = reorderDictionaryEntriesSchema.parse(await readJsonSafe(req, {}))\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = reorderDictionaryEntriesCommandSchema.parse({\n dictionaryId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n entries: payload.entries,\n } satisfies ReorderDictionaryEntriesCommandInput)\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<ReorderDictionaryEntriesCommandInput, { dictionaryId: string; updatedIds: string[] }>(\n 'dictionaries.entries.reorder',\n {\n input: commandInput,\n ctx: context.ctx,\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.dictionary',\n resourceId: logEntry.resourceId ?? dictionaryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to reorder dictionary entries', { err })\n return NextResponse.json({ error: 'Failed to reorder dictionary entries' }, { status: 500 })\n }\n}\n\nconst reorderPostDoc: OpenApiMethodDoc = {\n summary: 'Reorder dictionary entries',\n description: 'Updates the position of dictionary entries for drag-and-drop reordering.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: reorderEntriesRequestSchema,\n description: 'Array of entry IDs with their new positions.',\n },\n responses: [\n { status: 200, description: 'Entries reordered.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to reorder entries', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Reorder dictionary entries',\n pathParams: dictionaryIdParamsSchema,\n methods: {\n POST: reorderPostDoc,\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,uCAAuC;AAChD;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,eAAe,uBAAuB;AAG/C,SAAS,kCAAkC;AAC3C,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'\nimport {\n reorderDictionaryEntriesCommandSchema,\n reorderDictionaryEntriesSchema,\n type ReorderDictionaryEntriesCommandInput,\n} from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n dictionaryIdParamsSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n reorderEntriesRequestSchema,\n} from '../../../openapi'\nimport { resolveDictionaryActorId } from '../../../context'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('dictionaries').child({ component: 'entries-api' })\n\nconst paramsSchema = z.object({ dictionaryId: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function POST(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionaryEm = context.em.fork()\n const dictionary = await findOneWithDecryption(\n dictionaryEm,\n Dictionary,\n {\n id: dictionaryId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n },\n undefined,\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n\n const payload = reorderDictionaryEntriesSchema.parse(await readJsonSafe(req, {}))\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = reorderDictionaryEntriesCommandSchema.parse({\n dictionaryId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n entries: payload.entries,\n } satisfies ReorderDictionaryEntriesCommandInput)\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<ReorderDictionaryEntriesCommandInput, { dictionaryId: string; updatedIds: string[] }>(\n 'dictionaries.entries.reorder',\n {\n input: commandInput,\n ctx: context.ctx,\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.dictionary',\n resourceId: logEntry.resourceId ?? dictionaryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid input', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to reorder dictionary entries', { err })\n return NextResponse.json({ error: 'Failed to reorder dictionary entries' }, { status: 500 })\n }\n}\n\nconst reorderPostDoc: OpenApiMethodDoc = {\n summary: 'Reorder dictionary entries',\n description: 'Updates the position of dictionary entries for drag-and-drop reordering.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: reorderEntriesRequestSchema,\n description: 'Array of entry IDs with their new positions.',\n },\n responses: [\n { status: 200, description: 'Entries reordered.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to reorder entries', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Reorder dictionary entries',\n pathParams: dictionaryIdParamsSchema,\n methods: {\n POST: reorderPostDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
sortDictionaryEntries
|
|
36
36
|
} from "@open-mercato/core/modules/dictionaries/lib/entrySort";
|
|
37
37
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
38
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
38
39
|
const logger = createLogger("dictionaries").child({ component: "entries-api" });
|
|
39
40
|
const paramsSchema = z.object({ dictionaryId: z.string().uuid() });
|
|
40
41
|
const DICTIONARY_ENTRY_RESOURCE = "dictionaries.entry";
|
|
@@ -243,6 +244,10 @@ async function POST(req, ctx) {
|
|
|
243
244
|
if (isCrudHttpError(err)) {
|
|
244
245
|
return NextResponse.json(err.body, { status: err.status });
|
|
245
246
|
}
|
|
247
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
248
|
+
if (interceptorRejection) {
|
|
249
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
250
|
+
}
|
|
246
251
|
if (err instanceof z.ZodError) {
|
|
247
252
|
return NextResponse.json({ error: "Invalid input", details: err.issues }, { status: 400 });
|
|
248
253
|
}
|