import { useState } from 'react' import { GripVertical, ArrowUp, ArrowDown, Plus, Trash2 } from 'lucide-react' import Modal from '../shared/Modal' import Tooltip from '../shared/Tooltip' import { useNetworkMode } from '../../hooks/useNetworkMode' import { dayLabel } from '../../utils/dayLabel' import { deleteDayBlockedReason } from '../../utils/dayDeleteImpact' import type { Day } from '../../types' interface DayReorderPopupProps { isOpen: boolean days: Day[] t: (key: string, params?: Record) => string locale: string onReorder: (orderedIds: number[]) => void onAddDay: () => void /** Asks to delete a day; the planner opens the question. Without it rows have no delete button. */ onDeleteDay?: (dayId: number) => void onClose: () => void } /** * Modal for moving whole days around: drag a row by its grip or use the up/down * arrows, add a day at the end, or delete one. Day headers stay untouched, so * this is the single surface for ordering. Reorders are applied optimistically * by the store, so the list reflects each move immediately. A delete only asks: * the planner shows what goes with the day before anything happens. */ export function DayReorderPopup({ isOpen, days, t, locale, onReorder, onAddDay, onDeleteDay, onClose }: DayReorderPopupProps) { const [dragIndex, setDragIndex] = useState(null) const [overIndex, setOverIndex] = useState(null) const { offline } = useNetworkMode() const ordered = [...days].sort((a, b) => (a.day_number ?? 0) - (b.day_number ?? 0)) const deleteBlocked = deleteDayBlockedReason(ordered.length, offline, t) const move = (from: number, to: number) => { if (to < 0 || to >= ordered.length || from === to) return const ids = ordered.map(d => d.id) const [moved] = ids.splice(from, 1) ids.splice(to, 0, moved) onReorder(ids) } const cellBtn = { display: 'grid', placeItems: 'center', width: 28, height: 28, border: '1px solid var(--border-faint)', borderRadius: 7, background: 'none', cursor: 'pointer', color: 'var(--text-muted)', padding: 0, } as const return ( } >

{t('dayplan.reorderHint')}

{/* The popup is a modal, so it portals out of the planner and has to opt into the long-press drag itself (#1616). Without this a finger only selects the row text. */}
{ordered.map((day, index) => (
setDragIndex(index)} onDragEnd={() => { setDragIndex(null); setOverIndex(null) }} onDragOver={e => { e.preventDefault(); if (overIndex !== index) setOverIndex(index) }} onDrop={e => { e.preventDefault() if (dragIndex !== null && dragIndex !== index) move(dragIndex, index) setDragIndex(null); setOverIndex(null) }} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', borderRadius: 9, border: '1px solid var(--border-faint)', background: overIndex === index && dragIndex !== null && dragIndex !== index ? 'var(--bg-hover)' : 'var(--bg-card, white)', opacity: dragIndex === index ? 0.5 : 1, outline: overIndex === index && dragIndex !== null && dragIndex !== index ? '2px dashed var(--border-primary)' : 'none', outlineOffset: -2, }} > {index + 1} {dayLabel(day, index, t, locale)} {onDeleteDay && ( <>
))}
) }