feat(addresses): full CRUD UI for client + company multi-address
Client and company detail pages each gain an Addresses tab with click-to-edit fields wired to the existing CountryCombobox/SubdivisionCombobox primitives. Adds a primary toggle that demotes the previous primary inside one transaction so the partial unique index never trips. - New service helpers: list/add/update/remove ClientAddress + CompanyAddress - New routes: /api/v1/clients/[id]/addresses[/addressId], same under companies/ - New shared component: <AddressesEditor> reused by both detail surfaces - Integration tests cover happy path, primary demotion, and tenant scoping Tests: 747/747 vitest (was 741, +6 address tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { CompanyDetailHeader } from '@/components/companies/company-detail-heade
|
||||
import { getCompanyTabs } from '@/components/companies/company-tabs';
|
||||
import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import type { Address } from '@/components/shared/addresses-editor';
|
||||
|
||||
export interface CompanyData {
|
||||
id: string;
|
||||
@@ -25,6 +26,8 @@ export interface CompanyData {
|
||||
archivedAt: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
tags?: Array<{ id: string; name: string; color: string }>;
|
||||
addresses?: Address[];
|
||||
}
|
||||
|
||||
interface CompanyDetailProps {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { InlineTagEditor } from '@/components/shared/inline-tag-editor';
|
||||
import { NotesList } from '@/components/shared/notes-list';
|
||||
import { CompanyMembersTab } from '@/components/companies/company-members-tab';
|
||||
import { CompanyOwnedYachtsTab } from '@/components/companies/company-owned-yachts-tab';
|
||||
import { AddressesEditor, type Address } from '@/components/shared/addresses-editor';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import type { CountryCode } from '@/lib/i18n/countries';
|
||||
|
||||
@@ -45,6 +46,7 @@ interface CompanyTabsCompany {
|
||||
billingEmail: string | null;
|
||||
notes: string | null;
|
||||
tags?: Array<{ id: string; name: string; color: string }>;
|
||||
addresses?: Address[];
|
||||
}
|
||||
|
||||
interface CompanyTabsOptions {
|
||||
@@ -211,10 +213,12 @@ export function getCompanyTabs({
|
||||
{
|
||||
id: 'addresses',
|
||||
label: 'Addresses',
|
||||
badge: company.addresses?.length ?? 0,
|
||||
content: (
|
||||
<EmptyState
|
||||
title="Addresses"
|
||||
description="Company addresses coming soon — the addresses endpoint is pending wiring."
|
||||
<AddressesEditor
|
||||
endpoint={`/api/v1/companies/${companyId}/addresses`}
|
||||
invalidateKey={['companies', companyId]}
|
||||
addresses={company.addresses ?? []}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user