Add date editing with conflict detection and rental booking editing

- BookingDetailPanel: edit dates/room with inline conflict detection; options to force-save or swap conflicting booking's room
- BookingsPage + CalendarPage: pass rooms/allBookings/onBulkUpdate to panel
- RentalBookingModal: accept editBooking prop to pre-populate fields for editing
- RentalPage: add edit (pencil) button per booking row, unify create/update handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 15:13:49 +03:00
parent 84c90bcab1
commit 913ddeedc9
6 changed files with 224 additions and 38 deletions

View File

@@ -7,6 +7,7 @@ interface RentalBookingModalProps {
obj: RentalObject
date: string // 'yyyy-MM-dd'
existingBookings: RentalBooking[]
editBooking?: RentalBooking // pass to edit an existing booking
onClose: () => void
onSave: (b: RentalBooking) => void
}
@@ -19,14 +20,15 @@ function formatHour(h: number): string {
return `${h}:00`
}
export function RentalBookingModal({ obj, date, existingBookings, onClose, onSave }: RentalBookingModalProps) {
const [isFullDay, setIsFullDay] = useState(false)
const [startHour, setStartHour] = useState(obj.openHour)
const [endHour, setEndHour] = useState(Math.min(obj.openHour + 2, obj.closeHour))
const [guestName, setGuestName] = useState('')
const [guestPhone, setGuestPhone] = useState('')
const [notes, setNotes] = useState('')
const [paidAmount, setPaidAmount] = useState<string>('')
export function RentalBookingModal({ obj, date, existingBookings, editBooking, onClose, onSave }: RentalBookingModalProps) {
const isEdit = !!editBooking
const [isFullDay, setIsFullDay] = useState(editBooking?.isFullDay ?? false)
const [startHour, setStartHour] = useState(editBooking?.startHour ?? obj.openHour)
const [endHour, setEndHour] = useState(editBooking?.endHour ?? Math.min(obj.openHour + 2, obj.closeHour))
const [guestName, setGuestName] = useState(editBooking?.guestName ?? '')
const [guestPhone, setGuestPhone] = useState(editBooking?.guestPhone ?? '')
const [notes, setNotes] = useState(editBooking?.notes ?? '')
const [paidAmount, setPaidAmount] = useState<string>(editBooking?.paidAmount ? String(editBooking.paidAmount) : '')
const [payMethod, setPayMethod] = useState<'cash' | 'card' | 'transfer'>('cash')
const hours = isFullDay
@@ -39,18 +41,19 @@ export function RentalBookingModal({ obj, date, existingBookings, onClose, onSav
const maxHoursOk = !obj.maxHoursPerSlot || hours <= obj.maxHoursPerSlot
const isTimeConflict = !isFullDay && existingBookings.some(b =>
const otherBookings = existingBookings.filter(b => b.id !== editBooking?.id)
const isTimeConflict = !isFullDay && otherBookings.some(b =>
!b.isFullDay && b.status !== 'cancelled' &&
b.startHour < endHour && b.endHour > startHour,
)
const hasFullDayConflict = existingBookings.some(b => b.isFullDay && b.status !== 'cancelled')
const hasFullDayConflict = otherBookings.some(b => b.isFullDay && b.status !== 'cancelled')
const canSave = guestName.trim() !== '' && hours > 0 && maxHoursOk && !isTimeConflict && !hasFullDayConflict
const handleSave = () => {
if (!canSave) return
onSave({
id: `rb-${Date.now()}`,
id: editBooking?.id ?? `rb-${Date.now()}`,
objectId: obj.id,
date,
isFullDay,
@@ -284,7 +287,7 @@ export function RentalBookingModal({ obj, date, existingBookings, onClose, onSav
disabled={!canSave}
className="btn-primary disabled:opacity-50 disabled:cursor-not-allowed"
>
Забронировать
{isEdit ? 'Сохранить' : 'Забронировать'}
</button>
</div>
</div>