feat: add category field to housekeeping tasks + TechnicalPage for maintenance tasks

- Add migration 025 to add `category` column (housekeeping/maintenance) to housekeeping_tasks
- Backend: filter by ?category= in GET, store category in POST, allow patching category
- API client: HkPayload + housekeeping.list() now support category param
- HousekeepingPage: filters by category=housekeeping so maintenance tasks don't appear
- RoomContextMenu: priority sub-form for dirty/cleaning status, tech task creation inline form
- BookingCalendar: accepts onMaintenanceTaskCreate prop, passes priority to onRoomUpdate
- CalendarPage: handleRoomUpdate creates task for dirty+cleaning (with priority), new handleMaintenanceTaskCreate for category=maintenance tasks
- TechnicalPage: new page for viewing/creating/updating maintenance tasks
- App.tsx: /technical route added
- Sidebar: Тех. задачи nav item added under Управление

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 23:28:42 +03:00
parent 9be51e5d07
commit 3bb7262b59
10 changed files with 508 additions and 48 deletions

View File

@@ -7,6 +7,7 @@ import type { RentalObjectApi, RentalBookingApi } from '../lib/api'
import { useHotelSocket } from '../hooks/useHotelSocket'
import type { WsMessage } from '../hooks/useHotelSocket'
import type { Room, Booking } from '../types'
import type { HkPriority } from '../components/rooms/RoomContextMenu'
import type { RentalBooking } from '../data/rentalData'
export type BookingLock = { checkIn: string; checkOut: string; lockedBy: string }
@@ -153,18 +154,18 @@ export function CalendarPage() {
send({ type: 'unlock', roomId })
}, [send])
const handleRoomUpdate = useCallback(async (roomId: string, patch: Partial<Room>) => {
const handleRoomUpdate = useCallback(async (roomId: string, patch: Partial<Room>, priority?: HkPriority) => {
try {
const updated = await api.rooms.update(slug, roomId, patch)
setRooms(prev => prev.map(r => r.id === updated.id ? updated : r))
// When manually marking room as dirty → auto-create a housekeeping task
if (patch.housekeepingStatus === 'dirty') {
if (patch.housekeepingStatus === 'dirty' || patch.housekeepingStatus === 'cleaning') {
const today = new Date().toISOString().slice(0, 10)
const task = await api.housekeeping.create(slug, {
room_id: roomId,
type: 'regular',
priority: 'medium',
priority: priority ?? 'medium',
due_date: today,
category: 'housekeeping',
})
send({ type: 'housekeeping_task_created', task: task as unknown as Record<string, unknown> })
}
@@ -173,6 +174,23 @@ export function CalendarPage() {
}
}, [slug, send])
const handleMaintenanceTaskCreate = useCallback(async (roomId: string, description: string, priority: HkPriority) => {
try {
const today = new Date().toISOString().slice(0, 10)
const task = await api.housekeeping.create(slug, {
room_id: roomId,
type: 'maintenance',
priority,
notes: description,
due_date: today,
category: 'maintenance',
})
send({ type: 'housekeeping_task_created', task: task as unknown as Record<string, unknown> })
} catch (err) {
console.error('Failed to create maintenance task:', err)
}
}, [slug, send])
const handleRentalBookingCreate = useCallback(async (b: RentalBooking) => {
try {
const created = await api.rental.createBooking(slug, {
@@ -208,6 +226,7 @@ export function CalendarPage() {
onBookingUpdate={handleUpdate}
onBookingBulkUpdate={handleBulkUpdate}
onRoomUpdate={handleRoomUpdate}
onMaintenanceTaskCreate={handleMaintenanceTaskCreate}
fadingBookingIds={fadingBookings}
rentalObjects={isRentalActive ? rentalObjects as unknown as import('../data/rentalData').RentalObject[] : undefined}
rentalBookings={isRentalActive ? rentalBookings as unknown as RentalBooking[] : undefined}