Files
pn-new-crm/src/lib/validators/tenancies.ts

82 lines
3.2 KiB
TypeScript
Raw Normal View History

import { z } from 'zod';
import { baseListQuerySchema } from '@/lib/api/list-query';
feat(tenancies-p2): rename berth_reservations → berth_tenancies (schema + perms + UI) 73-file atomic rename per docs/tenancies-design.md: - Migration 0085: rename table + indexes + FK constraints; rename documents.reservation_id → tenancy_id; migrate jsonb permission maps (reservations resource → tenancies; collapse create+activate → manage); rewrite historical audit_logs.entity_type='berth_reservation' → 'berth_tenancy'. FK renames wrapped in DO blocks so dev DBs that pre-date the FK additions don't abort. - Schema: berthReservations → berthTenancies; BerthReservation type → BerthTenancy; indexes idx_br_* / idx_brr_* → idx_bt_*. - RolePermissions: resource { view, create, activate, cancel } collapses to { view, manage, cancel }; all 8 default seed bundles + role-form + matrix updated. - Service: berth-reservations.service.ts → berth-tenancies.service.ts; endReservation → endTenancy; listReservations → listTenancies. - API: /api/v1/berth-reservations → /api/v1/tenancies (+ nested [id]); /api/v1/berths/[id]/reservations → /api/v1/berths/[id]/tenancies. - Validators: reservations.ts → tenancies.ts; RESERVATION_STATUSES → TENANCY_STATUSES; endReservationSchema → endTenancySchema. - Routes: /{portSlug}/berth-reservations → /{portSlug}/tenancies; /portal/my-reservations → /portal/my-tenancies. - Components: src/components/reservations/* → src/components/tenancies/*; BerthReservationsTab → BerthTenanciesTab; ClientReservationsTab → ClientTenanciesTab; ReservationList → TenancyList. - Socket events: berth_reservation:* → berth_tenancy:*; payload reservationId → tenancyId. - Webhook events: berth_reservation.* → berth_tenancy.*. - Portal: getPortalUserReservations → getPortalUserTenancies; PortalReservation → PortalTenancy; PortalDashboard.counts.activeReservations → activeTenancies; PortalNav label "Reservations" → "Tenancies". - Dossier: DossierReservation → DossierTenancy; reservationDecisions → tenancyDecisions across smart-archive-dialog + bulk-archive routes. - Documents schema: documents.reservationId → documents.tenancyId (TS + DB column + index + FK constraint). - Activity feed label berth_reservation → berth_tenancy (matched against migrated historical audit rows). KEPT (separate concepts): - Reservation Agreement document type (the contract sent to clients). - "Reservation" pipeline stage name. - {{reservation.*}} merge tokens in template authoring. - interest.reservationStatus / reservationDocStatus / dateReservationSent fields (track agreement signing on the deal). - reservation-agreement-context.ts service (builds merge context for the Reservation Agreement doc; only its DB imports were renamed). Verified: tsc clean, 1480/1480 vitest passing, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:09:35 +02:00
export const TENANCY_STATUSES = ['pending', 'active', 'ended', 'cancelled'] as const;
export const TENURE_TYPES = ['permanent', 'fixed_term', 'seasonal'] as const;
export const createPendingSchema = z.object({
berthId: z.string().min(1),
clientId: z.string().min(1),
yachtId: z.string().min(1),
interestId: z.string().optional(),
startDate: z.coerce.date(),
tenureType: z.enum(TENURE_TYPES).optional().default('permanent'),
notes: z.string().optional(),
});
export const activateSchema = z.object({
contractFileId: z.string().optional(),
effectiveDate: z.coerce.date().optional(),
});
feat(tenancies-p2): rename berth_reservations → berth_tenancies (schema + perms + UI) 73-file atomic rename per docs/tenancies-design.md: - Migration 0085: rename table + indexes + FK constraints; rename documents.reservation_id → tenancy_id; migrate jsonb permission maps (reservations resource → tenancies; collapse create+activate → manage); rewrite historical audit_logs.entity_type='berth_reservation' → 'berth_tenancy'. FK renames wrapped in DO blocks so dev DBs that pre-date the FK additions don't abort. - Schema: berthReservations → berthTenancies; BerthReservation type → BerthTenancy; indexes idx_br_* / idx_brr_* → idx_bt_*. - RolePermissions: resource { view, create, activate, cancel } collapses to { view, manage, cancel }; all 8 default seed bundles + role-form + matrix updated. - Service: berth-reservations.service.ts → berth-tenancies.service.ts; endReservation → endTenancy; listReservations → listTenancies. - API: /api/v1/berth-reservations → /api/v1/tenancies (+ nested [id]); /api/v1/berths/[id]/reservations → /api/v1/berths/[id]/tenancies. - Validators: reservations.ts → tenancies.ts; RESERVATION_STATUSES → TENANCY_STATUSES; endReservationSchema → endTenancySchema. - Routes: /{portSlug}/berth-reservations → /{portSlug}/tenancies; /portal/my-reservations → /portal/my-tenancies. - Components: src/components/reservations/* → src/components/tenancies/*; BerthReservationsTab → BerthTenanciesTab; ClientReservationsTab → ClientTenanciesTab; ReservationList → TenancyList. - Socket events: berth_reservation:* → berth_tenancy:*; payload reservationId → tenancyId. - Webhook events: berth_reservation.* → berth_tenancy.*. - Portal: getPortalUserReservations → getPortalUserTenancies; PortalReservation → PortalTenancy; PortalDashboard.counts.activeReservations → activeTenancies; PortalNav label "Reservations" → "Tenancies". - Dossier: DossierReservation → DossierTenancy; reservationDecisions → tenancyDecisions across smart-archive-dialog + bulk-archive routes. - Documents schema: documents.reservationId → documents.tenancyId (TS + DB column + index + FK constraint). - Activity feed label berth_reservation → berth_tenancy (matched against migrated historical audit rows). KEPT (separate concepts): - Reservation Agreement document type (the contract sent to clients). - "Reservation" pipeline stage name. - {{reservation.*}} merge tokens in template authoring. - interest.reservationStatus / reservationDocStatus / dateReservationSent fields (track agreement signing on the deal). - reservation-agreement-context.ts service (builds merge context for the Reservation Agreement doc; only its DB imports were renamed). Verified: tsc clean, 1480/1480 vitest passing, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:09:35 +02:00
export const endTenancySchema = z.object({
endDate: z.coerce.date(),
notes: z.string().optional(),
});
export const cancelSchema = z.object({
reason: z.string().optional(),
});
feat(tenancies-p6-followup): generic create dialog + edit dialog + self-FKs - Migration 0086: berth_tenancies.previous_tenancy_id + transferred_from_tenancy_id self-FKs + partial indexes. Per docs/tenancies-design.md these chain renewal / transfer successors to predecessors for fixed-term and seasonal lineage. Schema mirrored in tenancies.ts with AnyPgColumn typed-import. - POST /api/v1/tenancies (generic create): accepts berthId in the body so client + yacht tab entry points don't have to bounce through /api/v1/berths/[id]/tenancies. Same createPending service helper. - TenancyCreateDialog: <TenancyCreateDialog open clientId? yachtId? berthId? /> with all three pickers; pre-fills the carrier from the parent entity. POSTs to /api/v1/tenancies; "Create" and "Create and activate" CTAs both wire to the new endpoint. - Mounted on ClientTenanciesTab + YachtTenanciesTab behind <PermissionGate resource="tenancies" action="manage"> so reps can mint tenancies directly from those tabs without bouncing through the berth page. - TenancyEditDialog: edit metadata only (start/end dates, tenure type, notes) via the new action='update' branch on the [id] PATCH route. Status transitions stay on activate/end/cancel. Wired into the tenancy detail page header. Outer wrapper unmounts on close so the form re-initialises from current row data without setState-in-effect. - updateTenancy service helper + PATCH action='update' branch added. Audit-logged + emits berth_tenancy:activated to invalidate detail query caches. Renew + Transfer dialogs deferred — both need lineage UX decisions (tenure-aware mutate-in-place vs new-row spawn; client/yacht swap semantics) and the self-FK columns this commit lands are the underpinning. Next sub-task. Verified: tsc clean, 1493/1493 vitest, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 17:10:06 +02:00
/** PATCH body for the "edit metadata" idiom — touches notes / dates /
* tenure type without crossing a status boundary. Status transitions
* flow through activate / endTenancy / cancel; this is non-transition
* metadata only. */
export const updateTenancySchema = z
.object({
startDate: z.coerce.date().optional(),
endDate: z.coerce.date().nullable().optional(),
tenureType: z.enum(TENURE_TYPES).optional(),
notes: z.string().nullable().optional(),
})
.refine((d) => Object.keys(d).length > 0, { message: 'At least one field must be provided' });
export type UpdateTenancyInput = z.infer<typeof updateTenancySchema>;
/** Renewal input. For permanent-class tenancies the existing row is
* mutated in place; for fixed_term / seasonal the existing row is
* ended at `previousEndDate` and a fresh row is minted with
* previousTenancyId pointing back. */
export const renewTenancySchema = z.object({
/** New start date for the renewal window (or the mutated row when permanent). */
newStartDate: z.coerce.date(),
/** New end date — required for fixed_term / seasonal; optional when permanent. */
newEndDate: z.coerce.date().optional(),
/** Optional notes appended to the successor / mutated row. */
notes: z.string().optional(),
});
export type RenewTenancyInput = z.infer<typeof renewTenancySchema>;
/** Transfer input hand a berth's active tenancy to a new client/yacht
* pair. The current row is ended, a fresh active row is minted with
* transferredFromTenancyId pointing at the predecessor. */
export const transferTenancySchema = z.object({
newClientId: z.string().min(1),
newYachtId: z.string().min(1),
transferDate: z.coerce.date(),
notes: z.string().optional(),
});
export type TransferTenancyInput = z.infer<typeof transferTenancySchema>;
feat(tenancies-p2): rename berth_reservations → berth_tenancies (schema + perms + UI) 73-file atomic rename per docs/tenancies-design.md: - Migration 0085: rename table + indexes + FK constraints; rename documents.reservation_id → tenancy_id; migrate jsonb permission maps (reservations resource → tenancies; collapse create+activate → manage); rewrite historical audit_logs.entity_type='berth_reservation' → 'berth_tenancy'. FK renames wrapped in DO blocks so dev DBs that pre-date the FK additions don't abort. - Schema: berthReservations → berthTenancies; BerthReservation type → BerthTenancy; indexes idx_br_* / idx_brr_* → idx_bt_*. - RolePermissions: resource { view, create, activate, cancel } collapses to { view, manage, cancel }; all 8 default seed bundles + role-form + matrix updated. - Service: berth-reservations.service.ts → berth-tenancies.service.ts; endReservation → endTenancy; listReservations → listTenancies. - API: /api/v1/berth-reservations → /api/v1/tenancies (+ nested [id]); /api/v1/berths/[id]/reservations → /api/v1/berths/[id]/tenancies. - Validators: reservations.ts → tenancies.ts; RESERVATION_STATUSES → TENANCY_STATUSES; endReservationSchema → endTenancySchema. - Routes: /{portSlug}/berth-reservations → /{portSlug}/tenancies; /portal/my-reservations → /portal/my-tenancies. - Components: src/components/reservations/* → src/components/tenancies/*; BerthReservationsTab → BerthTenanciesTab; ClientReservationsTab → ClientTenanciesTab; ReservationList → TenancyList. - Socket events: berth_reservation:* → berth_tenancy:*; payload reservationId → tenancyId. - Webhook events: berth_reservation.* → berth_tenancy.*. - Portal: getPortalUserReservations → getPortalUserTenancies; PortalReservation → PortalTenancy; PortalDashboard.counts.activeReservations → activeTenancies; PortalNav label "Reservations" → "Tenancies". - Dossier: DossierReservation → DossierTenancy; reservationDecisions → tenancyDecisions across smart-archive-dialog + bulk-archive routes. - Documents schema: documents.reservationId → documents.tenancyId (TS + DB column + index + FK constraint). - Activity feed label berth_reservation → berth_tenancy (matched against migrated historical audit rows). KEPT (separate concepts): - Reservation Agreement document type (the contract sent to clients). - "Reservation" pipeline stage name. - {{reservation.*}} merge tokens in template authoring. - interest.reservationStatus / reservationDocStatus / dateReservationSent fields (track agreement signing on the deal). - reservation-agreement-context.ts service (builds merge context for the Reservation Agreement doc; only its DB imports were renamed). Verified: tsc clean, 1480/1480 vitest passing, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:09:35 +02:00
export const listTenanciesSchema = baseListQuerySchema.extend({
status: z.enum(TENANCY_STATUSES).optional(),
berthId: z.string().optional(),
clientId: z.string().optional(),
yachtId: z.string().optional(),
});
export type CreatePendingInput = z.infer<typeof createPendingSchema>;
export type ActivateInput = z.infer<typeof activateSchema>;
feat(tenancies-p2): rename berth_reservations → berth_tenancies (schema + perms + UI) 73-file atomic rename per docs/tenancies-design.md: - Migration 0085: rename table + indexes + FK constraints; rename documents.reservation_id → tenancy_id; migrate jsonb permission maps (reservations resource → tenancies; collapse create+activate → manage); rewrite historical audit_logs.entity_type='berth_reservation' → 'berth_tenancy'. FK renames wrapped in DO blocks so dev DBs that pre-date the FK additions don't abort. - Schema: berthReservations → berthTenancies; BerthReservation type → BerthTenancy; indexes idx_br_* / idx_brr_* → idx_bt_*. - RolePermissions: resource { view, create, activate, cancel } collapses to { view, manage, cancel }; all 8 default seed bundles + role-form + matrix updated. - Service: berth-reservations.service.ts → berth-tenancies.service.ts; endReservation → endTenancy; listReservations → listTenancies. - API: /api/v1/berth-reservations → /api/v1/tenancies (+ nested [id]); /api/v1/berths/[id]/reservations → /api/v1/berths/[id]/tenancies. - Validators: reservations.ts → tenancies.ts; RESERVATION_STATUSES → TENANCY_STATUSES; endReservationSchema → endTenancySchema. - Routes: /{portSlug}/berth-reservations → /{portSlug}/tenancies; /portal/my-reservations → /portal/my-tenancies. - Components: src/components/reservations/* → src/components/tenancies/*; BerthReservationsTab → BerthTenanciesTab; ClientReservationsTab → ClientTenanciesTab; ReservationList → TenancyList. - Socket events: berth_reservation:* → berth_tenancy:*; payload reservationId → tenancyId. - Webhook events: berth_reservation.* → berth_tenancy.*. - Portal: getPortalUserReservations → getPortalUserTenancies; PortalReservation → PortalTenancy; PortalDashboard.counts.activeReservations → activeTenancies; PortalNav label "Reservations" → "Tenancies". - Dossier: DossierReservation → DossierTenancy; reservationDecisions → tenancyDecisions across smart-archive-dialog + bulk-archive routes. - Documents schema: documents.reservationId → documents.tenancyId (TS + DB column + index + FK constraint). - Activity feed label berth_reservation → berth_tenancy (matched against migrated historical audit rows). KEPT (separate concepts): - Reservation Agreement document type (the contract sent to clients). - "Reservation" pipeline stage name. - {{reservation.*}} merge tokens in template authoring. - interest.reservationStatus / reservationDocStatus / dateReservationSent fields (track agreement signing on the deal). - reservation-agreement-context.ts service (builds merge context for the Reservation Agreement doc; only its DB imports were renamed). Verified: tsc clean, 1480/1480 vitest passing, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:09:35 +02:00
export type EndTenancyInput = z.infer<typeof endTenancySchema>;
export type CancelInput = z.infer<typeof cancelSchema>;
feat(tenancies-p2): rename berth_reservations → berth_tenancies (schema + perms + UI) 73-file atomic rename per docs/tenancies-design.md: - Migration 0085: rename table + indexes + FK constraints; rename documents.reservation_id → tenancy_id; migrate jsonb permission maps (reservations resource → tenancies; collapse create+activate → manage); rewrite historical audit_logs.entity_type='berth_reservation' → 'berth_tenancy'. FK renames wrapped in DO blocks so dev DBs that pre-date the FK additions don't abort. - Schema: berthReservations → berthTenancies; BerthReservation type → BerthTenancy; indexes idx_br_* / idx_brr_* → idx_bt_*. - RolePermissions: resource { view, create, activate, cancel } collapses to { view, manage, cancel }; all 8 default seed bundles + role-form + matrix updated. - Service: berth-reservations.service.ts → berth-tenancies.service.ts; endReservation → endTenancy; listReservations → listTenancies. - API: /api/v1/berth-reservations → /api/v1/tenancies (+ nested [id]); /api/v1/berths/[id]/reservations → /api/v1/berths/[id]/tenancies. - Validators: reservations.ts → tenancies.ts; RESERVATION_STATUSES → TENANCY_STATUSES; endReservationSchema → endTenancySchema. - Routes: /{portSlug}/berth-reservations → /{portSlug}/tenancies; /portal/my-reservations → /portal/my-tenancies. - Components: src/components/reservations/* → src/components/tenancies/*; BerthReservationsTab → BerthTenanciesTab; ClientReservationsTab → ClientTenanciesTab; ReservationList → TenancyList. - Socket events: berth_reservation:* → berth_tenancy:*; payload reservationId → tenancyId. - Webhook events: berth_reservation.* → berth_tenancy.*. - Portal: getPortalUserReservations → getPortalUserTenancies; PortalReservation → PortalTenancy; PortalDashboard.counts.activeReservations → activeTenancies; PortalNav label "Reservations" → "Tenancies". - Dossier: DossierReservation → DossierTenancy; reservationDecisions → tenancyDecisions across smart-archive-dialog + bulk-archive routes. - Documents schema: documents.reservationId → documents.tenancyId (TS + DB column + index + FK constraint). - Activity feed label berth_reservation → berth_tenancy (matched against migrated historical audit rows). KEPT (separate concepts): - Reservation Agreement document type (the contract sent to clients). - "Reservation" pipeline stage name. - {{reservation.*}} merge tokens in template authoring. - interest.reservationStatus / reservationDocStatus / dateReservationSent fields (track agreement signing on the deal). - reservation-agreement-context.ts service (builds merge context for the Reservation Agreement doc; only its DB imports were renamed). Verified: tsc clean, 1480/1480 vitest passing, migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:09:35 +02:00
export type ListTenanciesInput = z.infer<typeof listTenanciesSchema>;