diff --git a/src/lib/api.ts b/src/lib/api.ts index 77a2cdd..baddb59 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -317,6 +317,15 @@ export const api = { listObjects: (slug: string) => req('GET', `/api/hotels/${slug}/rental-objects`), + createObject: (slug: string, data: RentalObjectPayload) => + req('POST', `/api/hotels/${slug}/rental-objects`, data), + + updateObject: (slug: string, id: string, data: Partial) => + req('PATCH', `/api/hotels/${slug}/rental-objects/${id}`, data), + + deleteObject: (slug: string, id: string) => + req('DELETE', `/api/hotels/${slug}/rental-objects/${id}`), + listBookings: (slug: string, from?: string, to?: string) => { const qs = new URLSearchParams() if (from) qs.set('from', from) @@ -591,6 +600,20 @@ export interface RentalBookingPayload { notes?: string } +export interface RentalObjectPayload { + name: string + icon?: string + color?: string + text_color?: string + price_per_hour?: number + price_per_day?: number + open_hour?: number + close_hour?: number + max_hours_per_slot?: number | null + buffer_minutes?: number + sort_order?: number +} + function toHotelPayload(h: HotelPayload): Record { const out: Record = {} if (h.name !== undefined) out.name = h.name diff --git a/src/pages/RentalPage.tsx b/src/pages/RentalPage.tsx index dfc5a97..9d53e44 100644 --- a/src/pages/RentalPage.tsx +++ b/src/pages/RentalPage.tsx @@ -1,12 +1,14 @@ -import { useState } from 'react' -import { Plus, Pencil, Trash2, Clock, CalendarDays, X, Save } from 'lucide-react' +import { useState, useEffect } from 'react' +import { Plus, Pencil, Trash2, CalendarDays, X, Save } from 'lucide-react' import { format } from 'date-fns' import { ru } from 'date-fns/locale' import { cn, formatCurrency } from '../lib/utils' import { Badge } from '../components/ui/Badge' -import { RENTAL_OBJECTS, MOCK_RENTAL_BOOKINGS } from '../data/rentalData' import type { RentalObject, RentalBooking } from '../data/rentalData' import { RentalBookingModal } from '../components/rental/RentalBookingModal' +import { api } from '../lib/api' +import type { RentalObjectApi, RentalBookingApi } from '../lib/api' +import { useAuth } from '../contexts/AuthContext' // ── Object form ─────────────────────────────────────────────────────────────── @@ -257,9 +259,34 @@ function ObjectFormModal({ // ── Main page ───────────────────────────────────────────────────────────────── +// Convert API response to local type +function fromObjApi(o: RentalObjectApi): RentalObject { + return { + id: o.id, name: o.name, icon: o.icon, color: o.color, + textColor: o.textColor, pricePerHour: o.pricePerHour, pricePerDay: o.pricePerDay, + openHour: o.openHour, closeHour: o.closeHour, + maxHoursPerSlot: o.maxHoursPerSlot ?? undefined, + bufferMinutes: o.bufferMinutes ?? undefined, + } +} + +function fromBookingApi(b: RentalBookingApi): RentalBooking { + return { + id: b.id, objectId: b.objectId, date: (b.date as string).slice(0, 10), + isFullDay: b.isFullDay, startHour: b.startHour, endHour: b.endHour, + guestName: b.guestName, guestPhone: b.guestPhone ?? '', + linkedRoomId: b.linkedRoomId ?? undefined, + totalAmount: b.totalAmount, paidAmount: b.paidAmount, + status: b.status, notes: b.notes ?? undefined, + } +} + export function RentalPage() { - const [objects, setObjects] = useState(RENTAL_OBJECTS) - const [bookings, setBookings] = useState(MOCK_RENTAL_BOOKINGS) + const { user } = useAuth() + const slug = user?.hotelSlug ?? '' + + const [objects, setObjects] = useState([]) + const [bookings, setBookings] = useState([]) const [editObj, setEditObj] = useState(null) const [createModal, setCreateModal] = useState(false) const [selectedObj, setSelectedObj] = useState(null) @@ -267,25 +294,62 @@ export function RentalPage() { const today = format(new Date(), 'yyyy-MM-dd') - const handleSaveObject = (obj: RentalObject) => { - setObjects(prev => { - const exists = prev.find(o => o.id === obj.id) - return exists ? prev.map(o => o.id === obj.id ? obj : o) : [...prev, obj] - }) + // Load from API on mount + useEffect(() => { + if (!slug) return + Promise.all([ + api.rental.listObjects(slug), + api.rental.listBookings(slug), + ]).then(([objs, bks]) => { + setObjects(objs.map(fromObjApi)) + setBookings(bks.map(fromBookingApi)) + }).catch(console.error) + }, [slug]) + + const handleSaveObject = async (obj: RentalObject) => { + if (!slug) return + const payload = { + name: obj.name, icon: obj.icon, color: obj.color, text_color: obj.textColor, + price_per_hour: obj.pricePerHour, price_per_day: obj.pricePerDay, + open_hour: obj.openHour, close_hour: obj.closeHour, + max_hours_per_slot: obj.maxHoursPerSlot ?? null, + buffer_minutes: obj.bufferMinutes ?? 0, + } + const isNew = !objects.find(o => o.id === obj.id) + const saved = isNew + ? await api.rental.createObject(slug, payload) + : await api.rental.updateObject(slug, obj.id, payload) + const converted = fromObjApi(saved) + setObjects(prev => isNew ? [...prev, converted] : prev.map(o => o.id === obj.id ? converted : o)) + // Update selectedObj if it was the edited one + setSelectedObj(prev => prev?.id === obj.id ? converted : prev) setEditObj(null) setCreateModal(false) } - const handleDeleteObject = (id: string) => { + const handleDeleteObject = async (id: string) => { + if (!slug) return + await api.rental.deleteObject(slug, id) setObjects(prev => prev.filter(o => o.id !== id)) if (selectedObj?.id === id) setSelectedObj(null) } - const handleBookingSave = (b: RentalBooking) => { - setBookings(prev => { - const exists = prev.find(x => x.id === b.id) - return exists ? prev.map(x => x.id === b.id ? b : x) : [...prev, b] - }) + const handleBookingSave = async (b: RentalBooking) => { + if (!slug) return + const payload = { + object_id: b.objectId, date: b.date, is_full_day: b.isFullDay, + start_hour: b.startHour, end_hour: b.endHour, + guest_name: b.guestName, guest_phone: b.guestPhone || undefined, + linked_room_id: b.linkedRoomId || undefined, + total_amount: b.totalAmount, paid_amount: b.paidAmount ?? 0, + status: b.status, notes: b.notes || undefined, + } + const isNew = !bookings.find(x => x.id === b.id) + const saved = isNew + ? await api.rental.createBooking(slug, payload) + : await api.rental.updateBooking(slug, b.id, payload) + const converted = fromBookingApi(saved) + setBookings(prev => isNew ? [...prev, converted] : prev.map(x => x.id === b.id ? converted : x)) setRentalModal(null) }