feat: agent status badge + auto workstation detection in BookingDetailPanel
- Check localhost:9000 on panel open via getIdentity() - If local agent is running → use its workstation_id directly, no picker - If agent is offline → show subtle badge 'Агент не запущен · ключи недоступны' under tabs - If agent offline but multiple server-side encoders online → still show picker Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
|||||||
} from '../../lib/utils'
|
} from '../../lib/utils'
|
||||||
import type { Booking, Room } from '../../types'
|
import type { Booking, Room } from '../../types'
|
||||||
import { api, type BookingGuest, type BookingGuestPayload, type GuestApiType } from '../../lib/api'
|
import { api, type BookingGuest, type BookingGuestPayload, type GuestApiType } from '../../lib/api'
|
||||||
|
import { getIdentity, type AgentIdentity } from '../../lib/agent'
|
||||||
|
|
||||||
const fmtDate = (iso: string) =>
|
const fmtDate = (iso: string) =>
|
||||||
new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long' }).format(new Date(iso))
|
new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long' }).format(new Date(iso))
|
||||||
@@ -146,6 +147,12 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
|||||||
setEarlyOpen(false)
|
setEarlyOpen(false)
|
||||||
}, [booking.id])
|
}, [booking.id])
|
||||||
|
|
||||||
|
// Проверяем локальный агент (localhost:9000) при открытии панели
|
||||||
|
useEffect(() => {
|
||||||
|
setAgentIdentity('loading')
|
||||||
|
getIdentity().then(identity => setAgentIdentity(identity))
|
||||||
|
}, [booking.id])
|
||||||
|
|
||||||
// Parse ФИО string into form fields
|
// Parse ФИО string into form fields
|
||||||
const parseFio = (val: string) => {
|
const parseFio = (val: string) => {
|
||||||
const parts = val.trim().split(/\s+/)
|
const parts = val.trim().split(/\s+/)
|
||||||
@@ -306,6 +313,7 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
|||||||
const [cardEncoders, setCardEncoders] = useState<Array<{ id: string; name: string }>>([])
|
const [cardEncoders, setCardEncoders] = useState<Array<{ id: string; name: string }>>([])
|
||||||
const [cardEncoderPickerOpen, setCardEncoderPickerOpen] = useState(false)
|
const [cardEncoderPickerOpen, setCardEncoderPickerOpen] = useState(false)
|
||||||
const [selectedEncoderId, setSelectedEncoderId] = useState('')
|
const [selectedEncoderId, setSelectedEncoderId] = useState('')
|
||||||
|
const [agentIdentity, setAgentIdentity] = useState<AgentIdentity | null | 'loading'>('loading')
|
||||||
|
|
||||||
const freeRoomsForRelocate = (rooms ?? []).filter(r =>
|
const freeRoomsForRelocate = (rooms ?? []).filter(r =>
|
||||||
r.id !== booking.roomId &&
|
r.id !== booking.roomId &&
|
||||||
@@ -400,7 +408,13 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
|||||||
const handleIssueCard = async () => {
|
const handleIssueCard = async () => {
|
||||||
if (!slug || cardIssuing) return
|
if (!slug || cardIssuing) return
|
||||||
|
|
||||||
// Load available encoders
|
// Если локальный агент запущен — используем его рабочее место напрямую
|
||||||
|
if (agentIdentity && agentIdentity !== 'loading' && agentIdentity.workstation_id) {
|
||||||
|
await doIssueCard(agentIdentity.workstation_id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Агент не запущен — ищем онлайн-рабочие места с энкодером через сервер
|
||||||
let encoders: Array<{ id: string; name: string }> = []
|
let encoders: Array<{ id: string; name: string }> = []
|
||||||
try {
|
try {
|
||||||
const wsList = await api.workstations.list(slug)
|
const wsList = await api.workstations.list(slug)
|
||||||
@@ -415,12 +429,11 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (encoders.length === 1) {
|
if (encoders.length === 1) {
|
||||||
// Auto-issue
|
|
||||||
await doIssueCard(encoders[0].id)
|
await doIssueCard(encoders[0].id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple encoders — show picker
|
// Несколько энкодеров — показываем выбор
|
||||||
setCardEncoders(encoders)
|
setCardEncoders(encoders)
|
||||||
setSelectedEncoderId(encoders[0].id)
|
setSelectedEncoderId(encoders[0].id)
|
||||||
setCardEncoderPickerOpen(true)
|
setCardEncoderPickerOpen(true)
|
||||||
@@ -552,6 +565,16 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Статус агента — показываем только если агент не запущен */}
|
||||||
|
{agentIdentity === null && (
|
||||||
|
<div className="flex items-center gap-1.5 mt-2 px-1">
|
||||||
|
<span className="w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-slate-500 shrink-0" />
|
||||||
|
<span className="text-[11px] text-slate-400 dark:text-slate-500">
|
||||||
|
Агент не запущен · ключи недоступны
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tab content */}
|
{/* Tab content */}
|
||||||
|
|||||||
Reference in New Issue
Block a user