feat: cleaning tooltip, report photos, fix tech task creation, completion photos
- Calendar: hover tooltip on '🧹 убирается' shows who is cleaning (assignee name) fetched from active HK tasks on mount, updated via WS housekeeping_updated events - HousekeepingPage: 'Сообщить о неисправности' now creates a real maintenance task in the DB (category=maintenance) + sends WS so TechnicalPage receives it instantly; photo upload added to the report form - RoomContextMenu: photo upload added to tech task creation form (camera button, preview thumbnails); photos passed through to API on task creation - TechnicalPage: selecting 'Выполнено' now opens CompletionModal with photo upload; technician can attach completion photos before confirming; if require_completion_photo setting is on — at least one photo is required - Settings (HousekeepingPage plans tab): new toggle 'Обязательное фото при завершении тех. задачи' saved to housekeeping_settings.require_completion_photo (migration 027) - Backend: housekeeping-settings updated with require_completion_photo field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@ export function CalendarPage() {
|
||||
const [rentalBookings, setRentalBookings] = useState<RentalBookingApi[]>([])
|
||||
const [locks, setLocks] = useState<Map<string, BookingLock>>(new Map())
|
||||
const [priceOverrides, setPriceOverrides] = useState<Record<string, Record<string, number>>>({})
|
||||
// roomId → assigneeName for "убирается" tooltip
|
||||
const [cleaningAssignees, setCleaningAssignees] = useState<Record<string, string>>({})
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) return
|
||||
@@ -52,6 +54,20 @@ export function CalendarPage() {
|
||||
setRentalBookings(rb as RentalBookingApi[])
|
||||
}
|
||||
}).catch(console.error)
|
||||
|
||||
// Fetch active HK tasks to build "who is cleaning" tooltip map
|
||||
api.housekeeping.list(slug, { status: 'active', category: 'housekeeping' })
|
||||
.then(tasks => {
|
||||
const assignees: Record<string, string> = {}
|
||||
for (const t of tasks) {
|
||||
const tAny = t as unknown as Record<string, string>
|
||||
if (tAny.status === 'in_progress' && tAny.roomId && tAny.assigneeName) {
|
||||
assignees[tAny.roomId] = tAny.assigneeName
|
||||
}
|
||||
}
|
||||
setCleaningAssignees(assignees)
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [slug, isRentalActive])
|
||||
|
||||
const handleWsMessage = useCallback((msg: WsMessage) => {
|
||||
@@ -70,6 +86,19 @@ export function CalendarPage() {
|
||||
setRooms(prev => prev.map(r =>
|
||||
r.id === msg.roomId ? { ...r, housekeepingStatus: msg.roomStatus as Room['housekeepingStatus'] } : r
|
||||
))
|
||||
// If room is no longer being cleaned, remove from tooltip map
|
||||
if (msg.roomStatus !== 'cleaning') {
|
||||
setCleaningAssignees(prev => { const n = { ...prev }; delete n[msg.roomId]; return n })
|
||||
}
|
||||
} else if (msg.type === 'housekeeping_updated') {
|
||||
const t = msg.task as Record<string, string>
|
||||
if (t.roomId) {
|
||||
if (t.status === 'in_progress' && t.assigneeName) {
|
||||
setCleaningAssignees(prev => ({ ...prev, [t.roomId]: t.assigneeName }))
|
||||
} else if (t.status !== 'in_progress') {
|
||||
setCleaningAssignees(prev => { const n = { ...prev }; delete n[t.roomId]; return n })
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -174,7 +203,7 @@ export function CalendarPage() {
|
||||
}
|
||||
}, [slug, send])
|
||||
|
||||
const handleMaintenanceTaskCreate = useCallback(async (roomId: string, description: string, priority: HkPriority) => {
|
||||
const handleMaintenanceTaskCreate = useCallback(async (roomId: string, description: string, priority: HkPriority, photos: string[] = []) => {
|
||||
try {
|
||||
const today = new Date().toISOString().slice(0, 10)
|
||||
const task = await api.housekeeping.create(slug, {
|
||||
@@ -184,6 +213,7 @@ export function CalendarPage() {
|
||||
notes: description,
|
||||
due_date: today,
|
||||
category: 'maintenance',
|
||||
photos,
|
||||
})
|
||||
send({ type: 'housekeeping_task_created', task: task as unknown as Record<string, unknown> })
|
||||
} catch (err) {
|
||||
@@ -227,6 +257,7 @@ export function CalendarPage() {
|
||||
onBookingBulkUpdate={handleBulkUpdate}
|
||||
onRoomUpdate={handleRoomUpdate}
|
||||
onMaintenanceTaskCreate={handleMaintenanceTaskCreate}
|
||||
cleaningAssignees={cleaningAssignees}
|
||||
fadingBookingIds={fadingBookings}
|
||||
rentalObjects={isRentalActive ? rentalObjects as unknown as import('../data/rentalData').RentalObject[] : undefined}
|
||||
rentalBookings={isRentalActive ? rentalBookings as unknown as RentalBooking[] : undefined}
|
||||
|
||||
Reference in New Issue
Block a user