'use client'; import { useEffect, useState } from 'react'; import { useSearchParams, useRouter, useParams } from 'next/navigation'; import { useQuery } from '@tanstack/react-query'; import { DetailLayout } from '@/components/shared/detail-layout'; import { DetailNotFound } from '@/components/shared/detail-not-found'; import { useMobileChrome } from '@/components/layout/mobile/mobile-layout-provider'; import { apiFetch } from '@/lib/api/client'; import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation'; import { BerthDetailHeader, type BerthDetailData } from './berth-detail-header'; import { BerthForm } from './berth-form'; import { buildBerthTabs } from './berth-tabs'; interface BerthDetailProps { berthId: string; } export function BerthDetail({ berthId }: BerthDetailProps) { const params = useParams<{ portSlug: string }>(); const portSlug = params?.portSlug ?? ''; const { data, isLoading, error } = useQuery({ queryKey: ['berth', berthId], queryFn: () => apiFetch<{ data: BerthDetailData }>(`/api/v1/berths/${berthId}`).then((r) => r.data), retry: (failureCount, err) => { const status = (err as { status?: number } | null | undefined)?.status; if (status === 404 || status === 403) return false; return failureCount < 2; }, }); useRealtimeInvalidation({ 'berth:updated': [['berth', berthId]], 'berth:statusChanged': [['berth', berthId]], }); const { setChrome } = useMobileChrome(); const titleForChrome: string | null = data?.mooringNumber ? `Berth ${data.mooringNumber}` : null; useEffect(() => { setChrome({ title: titleForChrome, showBackButton: true }); return () => setChrome({ title: null, showBackButton: false }); }, [titleForChrome, setChrome]); // Auto-open edit sheet when ?edit=true is present in the URL const searchParams = useSearchParams(); const router = useRouter(); const [editOpen, setEditOpen] = useState(false); useEffect(() => { if (searchParams.get('edit') === 'true') { // setState in effect is the right shape here - the URL is an // external store and the trigger is a query-param change, not a // prop in the React tree. // eslint-disable-next-line react-hooks/set-state-in-effect setEditOpen(true); // Strip the param without adding a history entry const params = new URLSearchParams(searchParams.toString()); params.delete('edit'); const newUrl = params.toString() ? `?${params.toString()}` : window.location.pathname; router.replace(newUrl as never); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams]); if (error && !isLoading) { const status = (error as { status?: number } | null | undefined)?.status; return ( ); } const berth = data; return ( <> : null} tabs={berth ? buildBerthTabs(berth) : []} defaultTab="overview" /> {berth ? : null} ); }