-
Reservations
-
+ Tenancies
+
- {/* Active reservation card */}
+ {/* Active tenancy card */}
- Active reservation
+ Active tenancy
{active ? (
-
+
) : (
- No active reservation.
+ No active tenancy.
)}
@@ -77,9 +77,9 @@ export function BerthReservationsTab({ berthId }: BerthReservationsTabProps) {
{isLoading ? (
Loading…
) : history.length === 0 ? (
-
+
) : (
-
+
)}
diff --git a/src/components/clients/bulk-archive-wizard.tsx b/src/components/clients/bulk-archive-wizard.tsx
index 814a170e..54ab030c 100644
--- a/src/components/clients/bulk-archive-wizard.tsx
+++ b/src/components/clients/bulk-archive-wizard.tsx
@@ -26,7 +26,7 @@ interface PreflightItem {
stakeLevel: 'low' | 'high';
highStakesStage: string | null;
blockers: string[];
- summary: { berths: number; yachts: number; reservations: number; signedDocs: number };
+ summary: { berths: number; yachts: number; tenancies: number; signedDocs: number };
}
interface Props {
@@ -215,8 +215,8 @@ function BulkArchiveWizardBody({ open, onOpenChange, clientIds, onSuccess }: Pro
{currentHighStakes.summary.signedDocs > 0
? `${currentHighStakes.summary.signedDocs} signed doc(s), `
: ''}
- {currentHighStakes.summary.reservations > 0
- ? `${currentHighStakes.summary.reservations} reservation(s)`
+ {currentHighStakes.summary.tenancies > 0
+ ? `${currentHighStakes.summary.tenancies} tenancy(ies)`
: ''}
diff --git a/src/components/clients/client-detail.tsx b/src/components/clients/client-detail.tsx
index 37270ab3..50e10e5b 100644
--- a/src/components/clients/client-detail.tsx
+++ b/src/components/clients/client-detail.tsx
@@ -64,7 +64,7 @@ interface ClientData {
status: string;
};
}>;
- activeReservations: Array<{
+ activeTenancies: Array<{
id: string;
berthId: string;
yachtId: string;
@@ -113,9 +113,9 @@ export function ClientDetail({ clientId, currentUserId }: ClientDetailProps) {
'yacht:ownership_transferred': [['clients', clientId]],
'company_membership:added': [['clients', clientId]],
'company_membership:ended': [['clients', clientId]],
- 'berth_reservation:activated': [['clients', clientId]],
- 'berth_reservation:ended': [['clients', clientId]],
- 'berth_reservation:cancelled': [['clients', clientId]],
+ 'berth_tenancy:activated': [['clients', clientId]],
+ 'berth_tenancy:ended': [['clients', clientId]],
+ 'berth_tenancy:cancelled': [['clients', clientId]],
});
if (error && !isLoading) {
diff --git a/src/components/clients/client-tabs.tsx b/src/components/clients/client-tabs.tsx
index dccd672e..0e41ed7d 100644
--- a/src/components/clients/client-tabs.tsx
+++ b/src/components/clients/client-tabs.tsx
@@ -16,7 +16,7 @@ import { ClientInterestsTab } from '@/components/clients/client-interests-tab';
import { ClientPipelineSummary } from '@/components/clients/client-pipeline-summary';
import { ClientYachtsTab } from '@/components/clients/client-yachts-tab';
import { ClientCompaniesTab } from '@/components/clients/client-companies-tab';
-import { ClientReservationsTab } from '@/components/clients/client-reservations-tab';
+import { ClientTenanciesTab } from '@/components/clients/client-tenancies-tab';
import { ClientFilesTab } from '@/components/clients/client-files-tab';
import { ContactsEditor } from '@/components/clients/contacts-editor';
import { AddressesEditor, type Address } from '@/components/shared/addresses-editor';
@@ -123,7 +123,7 @@ interface ClientTabsOptions {
status: string;
};
}>;
- activeReservations: Array<{
+ activeTenancies: Array<{
id: string;
berthId: string;
yachtId: string;
@@ -276,12 +276,10 @@ export function getClientTabs({ clientId, currentUserId, client }: ClientTabsOpt
content:
,
},
{
- id: 'reservations',
- label: 'Reservations',
- badge: client.activeReservations.length,
- content: (
-
- ),
+ id: 'tenancies',
+ label: 'Tenancies',
+ badge: client.activeTenancies.length,
+ content:
,
},
{
id: 'addresses',
diff --git a/src/components/clients/client-reservations-tab.tsx b/src/components/clients/client-tenancies-tab.tsx
similarity index 64%
rename from src/components/clients/client-reservations-tab.tsx
rename to src/components/clients/client-tenancies-tab.tsx
index d852f984..1811d9ef 100644
--- a/src/components/clients/client-reservations-tab.tsx
+++ b/src/components/clients/client-tenancies-tab.tsx
@@ -3,13 +3,13 @@
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
-import { ReservationList, type ReservationRow } from '@/components/reservations/reservation-list';
+import { TenancyList, type TenancyRow } from '@/components/tenancies/tenancy-list';
import { Button } from '@/components/ui/button';
import { apiFetch } from '@/lib/api/client';
-interface ClientReservationsTabProps {
+interface ClientTenanciesTabProps {
clientId: string;
- activeReservations: Array<{
+ activeTenancies: Array<{
id: string;
berthId: string;
yachtId: string;
@@ -19,24 +19,21 @@ interface ClientReservationsTabProps {
}>;
}
-interface ReservationListResponse {
- data: ReservationRow[];
+interface TenancyListResponse {
+ data: TenancyRow[];
pagination?: { total: number };
}
-export function ClientReservationsTab({
- clientId,
- activeReservations,
-}: ClientReservationsTabProps) {
+export function ClientTenanciesTab({ clientId, activeTenancies }: ClientTenanciesTabProps) {
const [showHistory, setShowHistory] = useState(false);
- const activeRows: ReservationRow[] = activeReservations.map((r) => ({
+ const activeRows: TenancyRow[] = activeTenancies.map((r) => ({
id: r.id,
berthId: r.berthId,
portId: '',
clientId,
yachtId: r.yachtId,
- status: r.status as ReservationRow['status'],
+ status: r.status as TenancyRow['status'],
startDate: typeof r.startDate === 'string' ? r.startDate : r.startDate.toISOString(),
endDate: null,
tenureType: r.tenureType,
@@ -48,23 +45,23 @@ export function ClientReservationsTab({
// Lazy-load history (ended + cancelled). Two parallel queries because
// the API takes one status at a time; combining once both resolve.
const endedQuery = useQuery({
- queryKey: ['reservations', { clientId, status: 'ended' }],
+ queryKey: ['tenancies', { clientId, status: 'ended' }],
queryFn: () =>
- apiFetch
(
- `/api/v1/berth-reservations?clientId=${encodeURIComponent(clientId)}&status=ended&pageSize=50`,
+ apiFetch(
+ `/api/v1/tenancies?clientId=${encodeURIComponent(clientId)}&status=ended&pageSize=50`,
),
enabled: showHistory,
});
const cancelledQuery = useQuery({
- queryKey: ['reservations', { clientId, status: 'cancelled' }],
+ queryKey: ['tenancies', { clientId, status: 'cancelled' }],
queryFn: () =>
- apiFetch(
- `/api/v1/berth-reservations?clientId=${encodeURIComponent(clientId)}&status=cancelled&pageSize=50`,
+ apiFetch(
+ `/api/v1/tenancies?clientId=${encodeURIComponent(clientId)}&status=cancelled&pageSize=50`,
),
enabled: showHistory,
});
- const historyRows: ReservationRow[] = [
+ const historyRows: TenancyRow[] = [
...(endedQuery.data?.data ?? []),
...(cancelledQuery.data?.data ?? []),
].sort((a, b) => (a.startDate < b.startDate ? 1 : -1));
@@ -75,12 +72,12 @@ export function ClientReservationsTab({
-
Active reservations
+ Active tenancies
-
@@ -100,15 +97,15 @@ export function ClientReservationsTab({
isHistoryLoading ? (
Loading…
) : (
-
)
) : (
- Click “Show history” to load ended and cancelled reservations.
+ Click “Show history” to load ended and cancelled tenancies.
)}
diff --git a/src/components/clients/smart-archive-dialog.tsx b/src/components/clients/smart-archive-dialog.tsx
index b73de3e7..cd98aea2 100644
--- a/src/components/clients/smart-archive-dialog.tsx
+++ b/src/components/clients/smart-archive-dialog.tsx
@@ -47,8 +47,8 @@ interface DossierYacht {
hullNumber: string | null;
status: string;
}
-interface DossierReservation {
- reservationId: string;
+interface DossierTenancy {
+ tenancyId: string;
berthId: string;
mooringNumber: string;
status: string;
@@ -75,7 +75,7 @@ interface ArchiveDossier {
berths: DossierBerth[];
yachts: DossierYacht[];
companies: Array<{ companyId: string; name: string; membershipRole: string | null }>;
- reservations: DossierReservation[];
+ tenancies: DossierTenancy[];
invoices: DossierInvoice[];
documents: DossierDocument[];
hasPortalUser: boolean;
@@ -84,7 +84,7 @@ interface ArchiveDossier {
type BerthAction = 'release' | 'retain';
type YachtAction = 'transfer' | 'mark_sold_away' | 'retain';
-type ReservationAction = 'cancel' | 'transfer';
+type TenancyAction = 'cancel' | 'transfer';
type InvoiceAction = 'void' | 'write_off' | 'leave';
type DocumentAction = 'void_documenso' | 'leave';
@@ -168,13 +168,9 @@ function SmartArchiveDialogBody({
? Object.fromEntries(dossier.yachts.map((y) => [y.yachtId, 'retain' as YachtAction]))
: {},
);
- const [reservationDecisions, setReservationDecisions] = useState<
- Record
- >(() =>
+ const [tenancyDecisions, setTenancyDecisions] = useState>(() =>
dossier
- ? Object.fromEntries(
- dossier.reservations.map((r) => [r.reservationId, 'cancel' as ReservationAction]),
- )
+ ? Object.fromEntries(dossier.tenancies.map((r) => [r.tenancyId, 'cancel' as TenancyAction]))
: {},
);
const [invoiceDecisions, setInvoiceDecisions] = useState>(() =>
@@ -233,9 +229,9 @@ function SmartArchiveDialogBody({
yachtId: y.yachtId,
action: yachtDecisions[y.yachtId] ?? 'retain',
})),
- reservationDecisions: dossier.reservations.map((r) => ({
- reservationId: r.reservationId,
- action: reservationDecisions[r.reservationId] ?? 'cancel',
+ tenancyDecisions: dossier.tenancies.map((r) => ({
+ tenancyId: r.tenancyId,
+ action: tenancyDecisions[r.tenancyId] ?? 'cancel',
})),
invoiceDecisions: dossier.invoices.map((i) => ({
invoiceId: i.invoiceId,
@@ -459,33 +455,30 @@ function SmartArchiveDialogBody({
)}
- {/* Reservations */}
- {dossier.reservations.length > 0 && (
+ {/* Tenancies */}
+ {dossier.tenancies.length > 0 && (
- Active reservations (
- {dossier.reservations.length})
+ Active tenancies (
+ {dossier.tenancies.length})
- {dossier.reservations.map((r) => (
-
+ {dossier.tenancies.map((r) => (
+
Berth {r.mooringNumber}
))}
diff --git a/src/components/dashboard/activity-feed.tsx b/src/components/dashboard/activity-feed.tsx
index 912c68f1..261c9e32 100644
--- a/src/components/dashboard/activity-feed.tsx
+++ b/src/components/dashboard/activity-feed.tsx
@@ -58,7 +58,7 @@ function humanizeFieldName(name: string): string {
const ENTITY_TYPE_LABELS: Record
= {
residential_client: 'Residential client',
residential_interest: 'Residential interest',
- berth_reservation: 'Berth reservation',
+ berth_tenancy: 'Berth tenancy',
berth_maintenance_log: 'Berth maintenance',
berth_recommendation: 'Berth recommendation',
client_note: 'Client note',
diff --git a/src/components/documents/create-document-wizard.tsx b/src/components/documents/create-document-wizard.tsx
index 826b444d..07c72778 100644
--- a/src/components/documents/create-document-wizard.tsx
+++ b/src/components/documents/create-document-wizard.tsx
@@ -45,7 +45,7 @@ const SIGNER_ROLES = ['client', 'sales', 'approver', 'developer', 'other'] as co
const SUBJECT_TYPES = [
{ key: 'interest', label: 'Interest', field: 'interestId' as const },
- { key: 'reservation', label: 'Reservation', field: 'reservationId' as const },
+ { key: 'tenancy', label: 'Tenancy', field: 'tenancyId' as const },
{ key: 'client', label: 'Client', field: 'clientId' as const },
{ key: 'company', label: 'Company', field: 'companyId' as const },
{ key: 'yacht', label: 'Yacht', field: 'yachtId' as const },
@@ -364,7 +364,7 @@ export function CreateDocumentWizard({ portSlug }: CreateDocumentWizardProps) {
setSubjectId(e.target.value)}
- placeholder="Reservation id"
+ placeholder="Tenancy id"
/>
)}
diff --git a/src/components/documents/document-detail.tsx b/src/components/documents/document-detail.tsx
index a4f898ec..d0bbf2cb 100644
--- a/src/components/documents/document-detail.tsx
+++ b/src/components/documents/document-detail.tsx
@@ -57,7 +57,7 @@ interface DetailDoc {
documentType: string;
documensoId: string | null;
signedFileId: string | null;
- reservationId: string | null;
+ tenancyId: string | null;
interestId: string | null;
clientId: string | null;
yachtId: string | null;
@@ -232,10 +232,10 @@ export function DocumentDetail({ documentId, portSlug }: DocumentDetailProps) {
// render as a chip row; nothing renders when there's nothing to
// link.
const linkedRows: Array<{ href: string; label: string; sub: string | null }> = [];
- if (doc.reservationId) {
+ if (doc.tenancyId) {
linkedRows.push({
- href: `/${portSlug}/berth-reservations/${doc.reservationId}`,
- label: 'Reservation',
+ href: `/${portSlug}/tenancies/${doc.tenancyId}`,
+ label: 'Tenancy',
sub: null,
});
}
diff --git a/src/components/interests/interest-detail.tsx b/src/components/interests/interest-detail.tsx
index 4e5acead..a00864a3 100644
--- a/src/components/interests/interest-detail.tsx
+++ b/src/components/interests/interest-detail.tsx
@@ -75,7 +75,7 @@ interface InterestData {
reminderDays: number | null;
reminderLastFired: string | null;
/** Phase 2 risk-signal dates derived in getInterestById from event
- * tables (document_events, berth_reservations, conflicting won
+ * tables (document_events, berth_tenancies, conflicting won
* interests). Feed DealPulseChip; null when no matching event. */
dateDocumentDeclined: string | null;
dateReservationCancelled: string | null;
diff --git a/src/components/portal/portal-nav.tsx b/src/components/portal/portal-nav.tsx
index 0c62e6fb..531d0494 100644
--- a/src/components/portal/portal-nav.tsx
+++ b/src/components/portal/portal-nav.tsx
@@ -17,7 +17,7 @@ const navItems = [
{ label: 'Dashboard', href: '/portal/dashboard', icon: LayoutDashboard },
{ label: 'Interests', href: '/portal/interests', icon: Anchor },
{ label: 'My Yachts', href: '/portal/my-yachts', icon: Sailboat },
- { label: 'Reservations', href: '/portal/my-reservations', icon: CalendarCheck },
+ { label: 'Tenancies', href: '/portal/my-tenancies', icon: CalendarCheck },
{ label: 'Documents', href: '/portal/documents', icon: FileText },
{ label: 'Invoices', href: '/portal/invoices', icon: Receipt },
{ label: 'Profile', href: '/portal/profile', icon: User },
diff --git a/src/components/reservations/berth-reserve-dialog.tsx b/src/components/tenancies/berth-reserve-dialog.tsx
similarity index 93%
rename from src/components/reservations/berth-reserve-dialog.tsx
rename to src/components/tenancies/berth-reserve-dialog.tsx
index ae9d783f..ce405208 100644
--- a/src/components/reservations/berth-reserve-dialog.tsx
+++ b/src/components/tenancies/berth-reserve-dialog.tsx
@@ -102,7 +102,7 @@ export function BerthReserveDialog({ open, onOpenChange, berthId }: BerthReserve
}
async function createPending(data: FormValues): Promise<{ id: string }> {
- const res = await apiFetch<{ data: { id: string } }>(`/api/v1/berths/${berthId}/reservations`, {
+ const res = await apiFetch<{ data: { id: string } }>(`/api/v1/berths/${berthId}/tenancies`, {
method: 'POST',
body: {
clientId: data.clientId!,
@@ -122,9 +122,9 @@ export function BerthReserveDialog({ open, onOpenChange, berthId }: BerthReserve
await createPending(data);
},
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['berths', berthId, 'reservations'] });
- queryClient.invalidateQueries({ queryKey: ['berth-reservations'] });
- toast.success('Reservation created');
+ queryClient.invalidateQueries({ queryKey: ['berths', berthId, 'tenancies'] });
+ queryClient.invalidateQueries({ queryKey: ['tenancies'] });
+ toast.success('Tenancy created');
onOpenChange(false);
},
onError: (err: unknown) => {
@@ -139,22 +139,22 @@ export function BerthReserveDialog({ open, onOpenChange, berthId }: BerthReserve
if (err) throw new Error(err);
const pending = await createPending(data);
// Immediately activate
- await apiFetch(`/api/v1/berth-reservations/${pending.id}`, {
+ await apiFetch(`/api/v1/tenancies/${pending.id}`, {
method: 'PATCH',
body: { action: 'activate' },
});
},
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['berths', berthId, 'reservations'] });
- queryClient.invalidateQueries({ queryKey: ['berth-reservations'] });
- toast.success('Reservation created and activated');
+ queryClient.invalidateQueries({ queryKey: ['berths', berthId, 'tenancies'] });
+ queryClient.invalidateQueries({ queryKey: ['tenancies'] });
+ toast.success('Tenancy created and activated');
onOpenChange(false);
},
onError: (err: unknown) => {
const msg = err instanceof Error ? err.message : 'Failed to activate';
- if (/active reservation|conflict|409/i.test(msg)) {
+ if (/active tenancy|active reservation|conflict|409/i.test(msg)) {
setFormError(
- 'This berth already has an active reservation. The pending record was created - activate it manually once the other reservation ends.',
+ 'This berth already has an active tenancy. The pending record was created - activate it manually once the other tenancy ends.',
);
} else {
setFormError(msg);
diff --git a/src/components/reservations/berth-reservations-list.tsx b/src/components/tenancies/tenancies-list-page.tsx
similarity index 61%
rename from src/components/reservations/berth-reservations-list.tsx
rename to src/components/tenancies/tenancies-list-page.tsx
index e9668e25..b208730d 100644
--- a/src/components/reservations/berth-reservations-list.tsx
+++ b/src/components/tenancies/tenancies-list-page.tsx
@@ -5,30 +5,30 @@ import { useParams } from 'next/navigation';
import { useQuery } from '@tanstack/react-query';
import { PageHeader } from '@/components/shared/page-header';
-import { ReservationList, type ReservationRow } from '@/components/reservations/reservation-list';
+import { TenancyList, type TenancyRow } from '@/components/tenancies/tenancy-list';
import { TableSkeleton } from '@/components/shared/loading-skeleton';
import { apiFetch } from '@/lib/api/client';
-interface ReservationsApiResponse {
- data: ReservationRow[];
+interface TenanciesApiResponse {
+ data: TenancyRow[];
pagination: { total: number; page: number; pageSize: number };
}
-export function BerthReservationsList() {
+export function TenanciesListPage() {
const params = useParams<{ portSlug: string }>();
const portSlug = params?.portSlug ?? '';
- const { data, isLoading } = useQuery({
- queryKey: ['berth-reservations', 'list'],
- queryFn: () => apiFetch('/api/v1/berth-reservations?page=1&limit=100&order=desc'),
+ const { data, isLoading } = useQuery({
+ queryKey: ['tenancies', 'list'],
+ queryFn: () => apiFetch('/api/v1/tenancies?page=1&limit=100&order=desc'),
});
return (
diff --git a/src/components/reservations/reservation-detail.tsx b/src/components/tenancies/tenancy-detail.tsx
similarity index 81%
rename from src/components/reservations/reservation-detail.tsx
rename to src/components/tenancies/tenancy-detail.tsx
index af470e88..9eb0936b 100644
--- a/src/components/reservations/reservation-detail.tsx
+++ b/src/components/tenancies/tenancy-detail.tsx
@@ -23,9 +23,9 @@ import { EmptyState } from '@/components/ui/empty-state';
import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
-import { ClientLink, YachtLink, BerthLink } from '@/components/reservations/reservation-list';
+import { ClientLink, YachtLink, BerthLink } from '@/components/tenancies/tenancy-list';
-interface ReservationDoc {
+interface TenancyDoc {
id: string;
title: string;
status: string;
@@ -34,7 +34,7 @@ interface ReservationDoc {
signers: Array<{ id: string; status: string; signerName: string }>;
}
-interface ReservationData {
+interface TenancyData {
id: string;
status: string;
startDate: string;
@@ -47,7 +47,7 @@ interface ReservationData {
notes: string | null;
}
-const RESERVATION_PILL: Record = {
+const TENANCY_PILL: Record = {
pending: 'pending',
active: 'active',
ended: 'archived',
@@ -58,13 +58,13 @@ function todayIso(): string {
return new Date().toISOString().slice(0, 10);
}
-interface EndReservationDialogProps {
- reservationId: string;
+interface EndTenancyDialogProps {
+ tenancyId: string;
open: boolean;
onOpenChange: (open: boolean) => void;
}
-function EndReservationDialog({ reservationId, open, onOpenChange }: EndReservationDialogProps) {
+function EndTenancyDialog({ tenancyId, open, onOpenChange }: EndTenancyDialogProps) {
const qc = useQueryClient();
const [endDate, setEndDate] = useState(todayIso);
const [submitting, setSubmitting] = useState(false);
@@ -73,12 +73,12 @@ function EndReservationDialog({ reservationId, open, onOpenChange }: EndReservat
e.preventDefault();
setSubmitting(true);
try {
- await apiFetch(`/api/v1/berth-reservations/${reservationId}`, {
+ await apiFetch(`/api/v1/tenancies/${tenancyId}`, {
method: 'PATCH',
body: { action: 'end', endDate },
});
- qc.invalidateQueries({ queryKey: ['reservation', reservationId] });
- toast.success('Reservation ended');
+ qc.invalidateQueries({ queryKey: ['tenancy', tenancyId] });
+ toast.success('Tenancy ended');
onOpenChange(false);
} catch (err) {
toastError(err);
@@ -91,7 +91,7 @@ function EndReservationDialog({ reservationId, open, onOpenChange }: EndReservat
);
}
@@ -254,13 +252,13 @@ export function ReservationDetail({ reservationId, portSlug }: ReservationDetail
return (