feat: TTLock integration + fix agent offline status
- Add TTLock electronic lock module: settings page, room-lock mapping, card issuance via agent command (ttlock:write_card) - Fix agent offline: EquipmentPage now polls every 30s + reacts to workstation:online/offline WebSocket events instantly - Backend broadcasts workstation online/offline events to hotel clients via existing WebSocket (agent-ws.ts) - New DB migration 048_ttlock.sql: hotel_ttlock_config, room_lock_mappings, card_issuances tables - New backend routes: GET/PATCH ttlock/config, POST ttlock/token, GET ttlock/locks, POST/DELETE ttlock/room-locks, POST issue-card - "Выдать ключ (TTLock)" button in BookingDetailPanel for confirmed/checked_in - TTLock page added to sidebar under Настройки Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
import { cn } from '../lib/utils'
|
||||
import { api, type Workstation, type WorkstationDevice } from '../lib/api'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { useHotelSocket } from '../hooks/useHotelSocket'
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1024,7 +1025,7 @@ function WorkstationCard({
|
||||
// ─── Main Page ────────────────────────────────────────────────────────────────
|
||||
|
||||
export function EquipmentPage() {
|
||||
const { user } = useAuth()
|
||||
const { user, session } = useAuth()
|
||||
const slug = user?.hotelSlug ?? ''
|
||||
|
||||
const [workstations, setWorkstations] = useState<Workstation[]>([])
|
||||
@@ -1051,6 +1052,23 @@ export function EquipmentPage() {
|
||||
|
||||
useEffect(() => { load() }, [load])
|
||||
|
||||
// Auto-refresh every 30 s so online/offline status stays current
|
||||
useEffect(() => {
|
||||
const timer = setInterval(load, 30_000)
|
||||
return () => clearInterval(timer)
|
||||
}, [load])
|
||||
|
||||
// Instant update when agent connects or disconnects
|
||||
useHotelSocket({
|
||||
slug,
|
||||
token: session?.token ?? null,
|
||||
onMessage: (msg) => {
|
||||
if (msg.type === 'workstation:online' || msg.type === 'workstation:offline') {
|
||||
load()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
api.agents.getLatestRelease().then(setLatestRelease).catch(() => {})
|
||||
}, [])
|
||||
|
||||
Reference in New Issue
Block a user