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:
2026-03-24 11:07:28 +03:00
parent 66009d345f
commit 2ebef940b9
8 changed files with 310 additions and 37 deletions

View File

@@ -0,0 +1,2 @@
ALTER TABLE housekeeping_settings
ADD COLUMN IF NOT EXISTS require_completion_photo BOOLEAN NOT NULL DEFAULT false;

View File

@@ -7,17 +7,19 @@ export interface HkSettings {
checkout_auto: boolean
checkout_priority: string
inspection_after_clean: boolean
require_completion_photo: boolean
}
const DEFAULT_SETTINGS: HkSettings = {
checkout_auto: true,
checkout_priority: 'high',
inspection_after_clean: true,
require_completion_photo: false,
}
export async function getHkSettings(hotelId: string): Promise<HkSettings> {
const { rows } = await db.query(
'SELECT checkout_auto, checkout_priority, inspection_after_clean FROM housekeeping_settings WHERE hotel_id = $1',
'SELECT checkout_auto, checkout_priority, inspection_after_clean, require_completion_photo FROM housekeeping_settings WHERE hotel_id = $1',
[hotelId],
)
return rows[0] ?? DEFAULT_SETTINGS
@@ -57,20 +59,22 @@ const housekeepingSettings: FastifyPluginAsync = async (fastify) => {
const hotelId = await getHotelId(slug)
if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' })
const { checkout_auto, checkout_priority, inspection_after_clean } = request.body
const { checkout_auto, checkout_priority, inspection_after_clean, require_completion_photo } = request.body
await db.query(
`INSERT INTO housekeeping_settings (hotel_id, checkout_auto, checkout_priority, inspection_after_clean)
VALUES ($1, $2, $3, $4)
`INSERT INTO housekeeping_settings (hotel_id, checkout_auto, checkout_priority, inspection_after_clean, require_completion_photo)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (hotel_id) DO UPDATE SET
checkout_auto = COALESCE(EXCLUDED.checkout_auto, housekeeping_settings.checkout_auto),
checkout_priority = COALESCE(EXCLUDED.checkout_priority, housekeeping_settings.checkout_priority),
inspection_after_clean = COALESCE(EXCLUDED.inspection_after_clean, housekeeping_settings.inspection_after_clean),
updated_at = NOW()`,
checkout_auto = COALESCE(EXCLUDED.checkout_auto, housekeeping_settings.checkout_auto),
checkout_priority = COALESCE(EXCLUDED.checkout_priority, housekeeping_settings.checkout_priority),
inspection_after_clean = COALESCE(EXCLUDED.inspection_after_clean, housekeeping_settings.inspection_after_clean),
require_completion_photo = COALESCE(EXCLUDED.require_completion_photo, housekeeping_settings.require_completion_photo),
updated_at = NOW()`,
[
hotelId,
checkout_auto ?? DEFAULT_SETTINGS.checkout_auto,
checkout_priority ?? DEFAULT_SETTINGS.checkout_priority,
inspection_after_clean ?? DEFAULT_SETTINGS.inspection_after_clean,
checkout_auto ?? DEFAULT_SETTINGS.checkout_auto,
checkout_priority ?? DEFAULT_SETTINGS.checkout_priority,
inspection_after_clean ?? DEFAULT_SETTINGS.inspection_after_clean,
require_completion_photo ?? DEFAULT_SETTINGS.require_completion_photo,
],
)
return getHkSettings(hotelId)