feat(berths): inline spec-PDF preview, manual-pin badge, maintenance module toggle, under-offer popover
All checks were successful
Build & Push Docker Images / lint (push) Successful in 2m45s
Build & Push Docker Images / build-and-push (push) Successful in 8m11s

Post-cutover UAT batch #3:
- #62 Spec tab renders the current berth spec PDF inline (lazy PdfViewer,
  toggleable, default-open) + explicit download. Interest Documents tab
  already previews/downloads linked deal docs inline (verified).
- #57 Surface berths.status_override_mode through the interest-berths API;
  linked-berth rows show an amber "Pin overrides pitch" badge + corrected
  consequence copy when a berth is specifically-pitched but manually pinned
  (the soft-pin wins on the public map).
- #63 New maintenance-module gate (maintenance_module_enabled, default on):
  registry + admin Settings toggle, maintenance-module.service, port-provider
  useMaintenanceModuleEnabled, layout wiring, buildBerthTabs hides the
  Maintenance tab when off, and both maintenance log routes assert the gate.
- #66 BerthOccupancyChip: >1 competing interest opens a popover listing every
  deal (name + stage + in-EOI/primary + link); single stays a direct link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 19:15:04 +02:00
parent 2a7f922a01
commit 1750e265e7
13 changed files with 301 additions and 59 deletions

View File

@@ -16,6 +16,10 @@ interface PortContextValue {
* Resolved server-side in the dashboard layout. Consumers read via
* `useTenanciesModuleEnabled()`. */
tenanciesModuleByPort: Record<string, boolean>;
/** Per-port Maintenance-module flag. Resolved server-side in the
* dashboard layout; consumers read via `useMaintenanceModuleEnabled()`.
* Defaults to enabled (unset port => true). */
maintenanceModuleByPort: Record<string, boolean>;
}
const PortContext = createContext<PortContextValue>({
@@ -24,6 +28,7 @@ const PortContext = createContext<PortContextValue>({
currentPortId: null,
currentPortSlug: null,
tenanciesModuleByPort: {},
maintenanceModuleByPort: {},
});
interface PortProviderProps {
@@ -31,6 +36,7 @@ interface PortProviderProps {
ports: Port[];
defaultPortId: string | null;
tenanciesModuleByPort?: Record<string, boolean>;
maintenanceModuleByPort?: Record<string, boolean>;
}
export function PortProvider({
@@ -38,6 +44,7 @@ export function PortProvider({
ports,
defaultPortId,
tenanciesModuleByPort = {},
maintenanceModuleByPort = {},
}: PortProviderProps) {
const params = useParams();
const portSlugFromUrl = params?.portSlug as string | undefined;
@@ -87,6 +94,7 @@ export function PortProvider({
currentPortId: currentPort?.id ?? null,
currentPortSlug: currentPort?.slug ?? null,
tenanciesModuleByPort,
maintenanceModuleByPort,
}}
>
{children}
@@ -106,3 +114,13 @@ export function useTenanciesModuleEnabled(): boolean {
if (!currentPortId) return false;
return tenanciesModuleByPort[currentPortId] ?? false;
}
/** Read the maintenance-module-enabled flag for the currently-active port.
* Defaults to ENABLED (true) when unset so the feature stays visible
* unless an admin has explicitly turned it off. Server-side resolved in
* the dashboard layout — synchronous read, no fetch latency/flicker. */
export function useMaintenanceModuleEnabled(): boolean {
const { currentPortId, maintenanceModuleByPort } = useContext(PortContext);
if (!currentPortId) return true;
return maintenanceModuleByPort[currentPortId] ?? true;
}