feat: task problem/solution split + maintenance icons on rooms/calendar
- TechnicalPage: redesign task cards with separate "Задача" and "Решение" sections - "Задача": problem description + problem photos (editable before done) - "Решение": technician resolution comment + resolution photos (green section) - Resolution comment in CompletionModal now saves to resolution_notes field - Resolution photos save to resolution_photos (no longer mixed with problem photos) - RoomModal history tab: maintenance entries show separate problem/resolution sections - Backend: add resolution_notes, resolution_photos to PATCH allowed fields - Migration 029: resolution_notes TEXT + resolution_photos TEXT[] columns - RoomsPage: load active maintenance tasks, show 🔧 неисправность badge on room cards - CalendarPage: load active maintenance tasks, show 🔧 неиспр. label in calendar row - BookingCalendar: activeMaintenance prop, WS handler updates maintenance set on task changes - CalendarPage WS: handle housekeeping_task_created to update activeMaintenance set Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ export function RoomsPage() {
|
||||
const [rooms, setRooms] = useState<Room[]>([])
|
||||
const [categories, setCategories] = useState<RoomCategory[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [activeMaintenance, setActiveMaintenance] = useState<Set<string>>(new Set())
|
||||
const [search, setSearch] = useState('')
|
||||
const [floorFilter, setFloorFilter] = useState<number | 'all'>('all')
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
@@ -38,6 +39,16 @@ export function RoomsPage() {
|
||||
.then(([r, cats]) => { setRooms(r); setCategories(cats) })
|
||||
.catch(console.error)
|
||||
.finally(() => setLoading(false))
|
||||
|
||||
api.housekeeping.list(slug, { status: 'active', category: 'maintenance' })
|
||||
.then(tasks => {
|
||||
const ids = new Set(tasks.map(t => {
|
||||
const tAny = t as unknown as Record<string, string>
|
||||
return tAny.roomId ?? tAny.room_id ?? ''
|
||||
}).filter(Boolean))
|
||||
setActiveMaintenance(ids)
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [slug])
|
||||
|
||||
const floors = [...new Set(rooms.map(r => r.floor))].sort()
|
||||
@@ -228,6 +239,7 @@ export function RoomsPage() {
|
||||
<RoomCard
|
||||
key={room.id}
|
||||
room={room}
|
||||
hasActiveMaintenance={activeMaintenance.has(room.id)}
|
||||
onEdit={() => openEdit(room)}
|
||||
onContextMenu={(e) => { e.preventDefault(); setCtxMenu({ room, x: e.clientX, y: e.clientY }) }}
|
||||
/>
|
||||
@@ -273,7 +285,7 @@ export function RoomsPage() {
|
||||
)
|
||||
}
|
||||
|
||||
function RoomCard({ room, onEdit, onContextMenu }: { room: Room; onEdit: () => void; onContextMenu: (e: React.MouseEvent) => void }) {
|
||||
function RoomCard({ room, hasActiveMaintenance, onEdit, onContextMenu }: { room: Room; hasActiveMaintenance?: boolean; onEdit: () => void; onContextMenu: (e: React.MouseEvent) => void }) {
|
||||
return (
|
||||
<div
|
||||
className="card p-4 hover:shadow-card-hover transition-shadow cursor-pointer group relative"
|
||||
@@ -285,11 +297,18 @@ function RoomCard({ room, onEdit, onContextMenu }: { room: Room; onEdit: () => v
|
||||
>
|
||||
<Pencil size={13} />
|
||||
</button>
|
||||
{room.status === 'maintenance' && (
|
||||
<div className="absolute top-3 left-3">
|
||||
<span className="flex items-center gap-1 text-[10px] font-medium text-orange-600 dark:text-orange-400 bg-orange-50 dark:bg-orange-900/20 px-1.5 py-0.5 rounded-md border border-orange-200 dark:border-orange-800">
|
||||
<Wrench size={10} />ремонт
|
||||
</span>
|
||||
{(room.status === 'maintenance' || hasActiveMaintenance) && (
|
||||
<div className="absolute top-3 left-3 flex gap-1">
|
||||
{room.status === 'maintenance' && (
|
||||
<span className="flex items-center gap-1 text-[10px] font-medium text-orange-600 dark:text-orange-400 bg-orange-50 dark:bg-orange-900/20 px-1.5 py-0.5 rounded-md border border-orange-200 dark:border-orange-800">
|
||||
<Wrench size={10} />ремонт
|
||||
</span>
|
||||
)}
|
||||
{hasActiveMaintenance && room.status !== 'maintenance' && (
|
||||
<span title="Есть активная тех. задача" className="flex items-center gap-1 text-[10px] font-medium text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 px-1.5 py-0.5 rounded-md border border-red-200 dark:border-red-800">
|
||||
<Wrench size={10} />неисправность
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user