import { useId } from 'react' import { MapPin, Ticket, Check, Paperclip } from 'lucide-react' import type { Place, Reservation, Day, TripFile } from '../../types' import type { FileManagerState } from './useFileManager' import { TRANSPORT_TYPES } from './FileManager.constants' import { transportIcon } from './FileManager.helpers' import { planFileLinkToggle, runFileLinkRecordStep, type FileLinkField } from './fileActions' import { DialogHeader, DialogShell, DialogTile, NEUTRAL_TINT } from '../shared/DialogShell' import { EditorField, INPUT } from '../shared/dialogParts' export function AssignModal(S: FileManagerState) { const { files, assignFileId, setAssignFileId, t, days, assignments, places, reservations, tripId, handleAssign, refreshFiles } = S const labelId = useId() const close = () => setAssignFileId(null) // The file's own column goes through handleAssign, which toasts a failure. const toggleLink = async (file: TripFile, field: FileLinkField, targetId: number) => { const step = planFileLinkToggle(file, field, targetId) if (step.kind === 'update') { await handleAssign(file.id, step.data) return } try { await runFileLinkRecordStep(tripId, file.id, field, targetId, step.kind) refreshFiles() } catch { // A failed link record change stays quiet. } } return ( } tint={NEUTRAL_TINT} labelId={labelId} onClose={close} eyebrow={t('files.assignTitle')} title={files.find(f => f.id === assignFileId)?.original_name || ''} /> )} > f.id === assignFileId)?.description || ''} onBlur={e => { const val = e.target.value.trim() const file = files.find(f => f.id === assignFileId) if (file && val !== (file.description || '')) { void handleAssign(file.id, { description: val } as any) } }} onKeyDown={e => { if (e.key === 'Enter') (e.target as HTMLInputElement).blur() }} className={INPUT} />
{(() => { const file = files.find(f => f.id === assignFileId) if (!file) return null const assignedPlaceIds = new Set() const dayGroups: { day: Day; dayPlaces: Place[] }[] = [] for (const day of days) { const da = assignments[String(day.id)] || [] const dayPlaces = da.map(a => places.find(p => p.id === a.place?.id || p.id === a.place_id)).filter(Boolean) as Place[] if (dayPlaces.length > 0) { dayGroups.push({ day, dayPlaces }) dayPlaces.forEach(p => assignedPlaceIds.add(p.id)) } } const unassigned = places.filter(p => !assignedPlaceIds.has(p.id)) const placeBtn = (p: Place, idx: number) => { const isLinked = file.place_id === p.id || (file.linked_place_ids || []).includes(p.id) return ( ) } const placesSection = places.length > 0 && (
{t('files.assignPlace')}
{dayGroups.map(({ day, dayPlaces }) => (
{day.title || t('dayplan.dayN', { n: day.day_number })} {(() => { const badge = day.date || (day.title ? t('dayplan.dayN', { n: day.day_number }) : null) return badge ? ( {badge} ) : null })()}
{dayPlaces.map((p, i) => placeBtn(p, i))}
))} {unassigned.length > 0 && (
{dayGroups.length > 0 &&
{t('files.unassigned')}
} {unassigned.map((p, i) => placeBtn(p, i))}
)}
) const bookingReservations = reservations.filter(r => !TRANSPORT_TYPES.has(r.type)) const transportReservations = reservations.filter(r => TRANSPORT_TYPES.has(r.type)) const reservationBtn = (r: Reservation) => { const isLinked = file.reservation_id === r.id || (file.linked_reservation_ids || []).includes(r.id) const Icon = TRANSPORT_TYPES.has(r.type) ? transportIcon(r.type) : Ticket return ( ) } const bookingsSection = reservations.length > 0 && (
{bookingReservations.length > 0 && ( <>
{t('files.assignBooking')}
{bookingReservations.map(reservationBtn)} )} {transportReservations.length > 0 && ( <>
0 ? 4 : 0 }}> {t('files.assignTransport')}
{transportReservations.map(reservationBtn)} )}
) const hasBoth = placesSection && bookingsSection return (
{placesSection}
{hasBoth &&
} {hasBoth &&
}
{bookingsSection}
) })()}
) }