@@ -23,13 +23,13 @@ export function SensorWorkspace({transport,enabled=true,onDetailChange}:{transpo
const editingCurrent = inventory ? . items . find ( v = > v . id === editing ? . id ) ;
const editingCurrent = inventory ? . items . find ( v = > v . id === editing ? . id ) ;
useEffect ( ( ) = > { onDetailChange ? . ( ! ! device ) ; } , [ ! ! device , onDetailChange ] ) ;
useEffect ( ( ) = > { onDetailChange ? . ( ! ! device ) ; } , [ ! ! device , onDetailChange ] ) ;
return < div className = "sensor-workspace" > { device ? < SensorDetail enabled = { enabled && fresh } device = { device } transport = { transport } back = { ( ) = > setSelected ( null ) } refresh = { refresh } failure = { failure } / > : < >
return < div className = "sensor-workspace" > { device ? < SensorDetail enabled = { enabled && fresh } device = { device } transport = { transport } back = { ( ) = > setSelected ( null ) } refresh = { refresh } failure = { failure } / > : < >
< div className = "sensor-actions" > < StatusBadge tone = { fresh && enabled ? 'success' : 'neutral' } > { ! enabled ? 'БК недоступен' : fresh ? 'Сведения с БК' : 'Нет свежих сведений' } < / StatusBadge > < IconButton label = "Обновить устройства" disabled = { ! enabled } onClick = { ( ) = > void refresh ( ) } > < Icon name = "refresh" / > < / IconButton > < / div >
< div className = "sensor-actions sensor-inventory-toolbar " > < StatusBadge tone = { fresh && enabled ? 'success' : 'neutral' } > { ! enabled ? 'БК недоступен' : fresh ? 'Сведения с БК' : 'Нет свежих сведений' } < / StatusBadge > < IconButton label = "Обновить устройства" disabled = { ! enabled } onClick = { ( ) = > void refresh ( ) } > < Icon name = "refresh" / > < / IconButton > < / div >
{ ! inventory ? < ActivityIndicator label = "Получаем устройства БК" / > : connected . length === 0 ? < SettingsCard title = "Поддерживаемые устройства не обнаружены" description = "Подключите камеру к бортовому компьютеру." / > : < ResourceList aria-label = "Устройства БК" > { connected . map ( item = > {
{ ! inventory ? < ActivityIndicator label = "Получаем устройства БК" / > : connected . length === 0 ? < SettingsCard title = "Поддерживаемые устройства не обнаружены" description = "Подключите камеру к бортовому компьютеру." / > : < ResourceList aria-label = "Устройства БК" > { connected . map ( item = > {
const operation = inventory . operations ? . find ( v = > v . device_id === item . id && v . state === 'running' ) ; const busy = ! ! operation || localBusy === item . id ;
const operation = inventory . operations ? . find ( v = > v . device_id === item . id && v . state === 'running' ) ; const busy = ! ! operation || localBusy === item . id ;
const configured = item . configured ? ? item . snapshot . enrollment === 'enrolled' ;
const configured = item . configured ? ? item . snapshot . enrollment === 'enrolled' ;
const prep = operation ? . action_id === 'prepare' && inventory . preparation && ( inventory . preparation . started_at * 1000 >= Date . parse ( operation . requested_at ) - 1000 ) ? inventory.preparation :undefined ;
const prep = operation ? . action_id === 'prepare' && inventory . preparation && ( inventory . preparation . started_at * 1000 >= Date . parse ( operation . requested_at ) - 1000 ) ? inventory.preparation :undefined ;
const status = sensorStatus ( item , enabled && fresh ) ; const label = busy ? 'Подготовка или команда выполняется' : status . label ;
const status = sensorStatus ( item , enabled && fresh ) ; const label = busy ? 'Подготовка или команда выполняется' : status . label ;
return < li key = { item . id } > < ResourceRow icon = { < Icon name = "camera" / > } title = { item . name } description = { item . model } metadata = { < span > USB { item . usb } < / span > } aria-busy = { busy } progress = { busy ? { label , value :prep?.state === 'running' ? prep . steps . filter ( s = > s . state === 'complete' ) . length / ( prep . steps . length + 1 ) : prep ? . state === 'complete' ? 5 / 6 :undefined , valueText :prep?.steps.find ( s = > s . state === 'running' ) ? . label ? ? 'Проверка кадров камеры' } : undefined } status = { < StatusBadge variant = { configured && item . online ? 'indicator' : 'badge' } tone = { status . tone } aria-label = { label } title = { label } > { configured && item.online?null :label } < / StatusBadge > } actions = { < > { ! configured && < IconButton label = { ` Подготовить и проверить: ${ item . name } ` } disabled = { ! enabled || ! fresh || busy || ! item . online || item . snapshot . acquisition === 'streaming' || item . snapshot . acquisition === 'stopping' } onClick = { ( ) = > void action ( item , 'prepare' ) } > < Icon name = "download" / > < / IconButton > } < IconButton label = { ` Настройки: ${ item . name } ` } disabled = { ! enabled || ! fresh || busy } onClick = { ( ) = > { setEditing ( item ) ; setName ( item . name ) ; } } > < Icon name = "settings" / > < / IconButton > < IconButton label = { ` Просмотр: ${ item . name } ` } disabled = { ! item . prepared || busy } onClick = { ( ) = > setSelected ( item . id ) } > < Icon name = "eye" / > < / IconButton > < / > } / > < / li > ; } ) } < / ResourceList > }
return < li key = { item . id } > < ResourceRow icon = { < Icon name = "camera" / > } title = { item . name } description = { item . model } metadata = { < span > USB { item . usb } < / span > } statusPlacement = { configured ? 'leading' : 'trailing' } aria-busy = { busy } progress = { busy ? { label , value :prep?.state === 'running' ? prep . steps . filter ( s = > s . state === 'complete' ) . length / ( prep . steps . length + 1 ) : prep ? . state === 'complete' ? 5 / 6 :undefined , valueText :prep?.steps.find ( s = > s . state === 'running' ) ? . label ? ? 'Проверка кадров камеры' } : undefined } status = { < StatusBadge variant = { configured && item . online ? 'indicator' : 'badge' } tone = { status . tone } aria-label = { label } title = { label } > { configured && item.online?null :label } < / StatusBadge > } actions = { < > { ! configured && < IconButton label = { ` Подготовить и проверить: ${ item . name } ` } disabled = { ! enabled || ! fresh || busy || ! item . online || item . snapshot . acquisition === 'streaming' || item . snapshot . acquisition === 'stopping' } onClick = { ( ) = > void action ( item , 'prepare' ) } > < Icon name = "download" / > < / IconButton > } < IconButton label = { ` Настройки: ${ item . name } ` } disabled = { ! enabled || ! fresh || busy } onClick = { ( ) = > { setEditing ( item ) ; setName ( item . name ) ; } } > < Icon name = "settings" / > < / IconButton > < IconButton label = { ` Просмотр: ${ item . name } ` } disabled = { ! item . prepared || busy } onClick = { ( ) = > setSelected ( item . id ) } > < Icon name = "eye" / > < / IconButton > < / > } / > < / li > ; } ) } < / ResourceList > }
{ ( inventory ? . operations ? . some ( v = > v . state === 'running' && v . action_id === 'prepare' && ! ! inventory . preparation && inventory . preparation . started_at * 1000 >= Date . parse ( v . requested_at ) - 1000 ) ) && inventory ? . preparation && < SettingsCard title = "Подготовка устройства" > { inventory . preparation . steps . map ( step = > < ResourceRow key = { step . id } title = { step . label } description = { step . message } status = { step . state === 'complete' ? < Icon name = "check" label = "Выполнено" / > : step . state === 'running' ? < ActivityIndicator label = "Выполняется" / > : < StatusBadge > { step . state === 'error' ? 'Ошибка' : step . state === 'blocked' ? 'Не выполнено' : 'Ожидает' } < / StatusBadge > } / > ) } < ResourceRow title = "Проверка кадров камеры" status = { inventory . preparation . state === 'complete' ? < ActivityIndicator label = "Проверяем потоки" / > : < StatusBadge > О ж и д а е т < / StatusBadge > } / > < / SettingsCard > }
{ ( inventory ? . operations ? . some ( v = > v . state === 'running' && v . action_id === 'prepare' && ! ! inventory . preparation && inventory . preparation . started_at * 1000 >= Date . parse ( v . requested_at ) - 1000 ) ) && inventory ? . preparation && < SettingsCard title = "Подготовка устройства" > { inventory . preparation . steps . map ( step = > < ResourceRow key = { step . id } title = { step . label } description = { step . message } status = { step . state === 'complete' ? < Icon name = "check" label = "Выполнено" / > : step . state === 'running' ? < ActivityIndicator label = "Выполняется" / > : < StatusBadge > { step . state === 'error' ? 'Ошибка' : step . state === 'blocked' ? 'Не выполнено' : 'Ожидает' } < / StatusBadge > } / > ) } < ResourceRow title = "Проверка кадров камеры" status = { inventory . preparation . state === 'complete' ? < ActivityIndicator label = "Проверяем потоки" / > : < StatusBadge > О ж и д а е т < / StatusBadge > } / > < / SettingsCard > }
< / > }
< / > }
< Window open = { editing !== null } title = "Настройки устройства" onClose = { ( ) = > setEditing ( null ) } footer = { < WindowFooterActions > < Button disabled = { ! ! localBusy } onClick = { ( ) = > setEditing ( null ) } > О т м е н а < / Button > < Button disabled = { ! ! localBusy || ! name . trim ( ) || ! enabled || ! fresh } onClick = { ( ) = > { if ( editing ) void action ( editing , 'rename' , { name } ) ; } } > С о х р а н и т ь < / Button > < / WindowFooterActions > } > < div className = "sensor-content" > < TextField label = "Название устройства" value = { name } maxLength = { 80 } onChange = { e = > setName ( e . target . value ) } disabled = { ! ! localBusy } / > { editing && ( editing . configured ? ? editing . snapshot . enrollment === 'enrolled' ) && < ResourceRow title = "Конфигурация на БК" description = "Повторно развернуть и проверить встроенный драйвер устройства." actions = { < Button disabled = { ! ! localBusy || ! enabled || ! fresh || ! editingCurrent ? . online || [ 'streaming' , 'starting' , 'stopping' ] . includes ( editingCurrent ? . snapshot . acquisition ? ? 'offline' ) } onClick = { ( ) = > { const target = editing ; setEditing ( null ) ; void action ( target , 'prepare' ) ; } } > О б н о в и т ь < / Button > } / > } < / div > < / Window >
< Window open = { editing !== null } title = "Настройки устройства" onClose = { ( ) = > setEditing ( null ) } footer = { < WindowFooterActions > < Button disabled = { ! ! localBusy } onClick = { ( ) = > setEditing ( null ) } > О т м е н а < / Button > < Button disabled = { ! ! localBusy || ! name . trim ( ) || ! enabled || ! fresh } onClick = { ( ) = > { if ( editing ) void action ( editing , 'rename' , { name } ) ; } } > С о х р а н и т ь < / Button > < / WindowFooterActions > } > < div className = "sensor-content" > < TextField label = "Название устройства" value = { name } maxLength = { 80 } onChange = { e = > setName ( e . target . value ) } disabled = { ! ! localBusy } / > { editing && ( editing . configured ? ? editing . snapshot . enrollment === 'enrolled' ) && < ResourceRow title = "Конфигурация на БК" description = "Повторно развернуть и проверить встроенный драйвер устройства." actions = { < Button disabled = { ! ! localBusy || ! enabled || ! fresh || ! editingCurrent ? . online || [ 'streaming' , 'starting' , 'stopping' ] . includes ( editingCurrent ? . snapshot . acquisition ? ? 'offline' ) } onClick = { ( ) = > { const target = editing ; setEditing ( null ) ; void action ( target , 'prepare' ) ; } } > О б н о в и т ь < / Button > } / > } < / div > < / Window >