ывыфвфы
This commit is contained in:
parent
b618bdf0da
commit
0c6b949424
3 changed files with 52 additions and 14 deletions
|
|
@ -1,12 +1,9 @@
|
||||||
"""Global devices endpoint — cross-object device listing with filters."""
|
"""Global devices endpoint — cross-object device listing with filters."""
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
|
|
@ -37,8 +34,6 @@ async def list_all_devices(
|
||||||
- All bank terminals for a client: ?category=bank_terminal&client=Авангард
|
- All bank terminals for a client: ?category=bank_terminal&client=Авангард
|
||||||
- All devices in a city: ?city=Санкт-Петербург
|
- All devices in a city: ?city=Санкт-Петербург
|
||||||
"""
|
"""
|
||||||
logger.info("list_all_devices called: category=%r city=%r client=%r object_id=%r status=%r", category, city, client, object_id, status)
|
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
select(Device)
|
select(Device)
|
||||||
.join(Object, Device.object_id == Object.id)
|
.join(Object, Device.object_id == Object.id)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]:
|
||||||
scope_type = scope.get("type")
|
scope_type = scope.get("type")
|
||||||
if scope_type == "device":
|
if scope_type == "device":
|
||||||
query = query.where(Device.id == scope["id"])
|
query = query.where(Device.id == scope["id"])
|
||||||
|
elif scope_type == "device_list":
|
||||||
|
query = query.where(Device.id.in_(scope["ids"]))
|
||||||
elif scope_type == "object":
|
elif scope_type == "object":
|
||||||
query = query.where(Device.object_id == scope["id"])
|
query = query.where(Device.object_id == scope["id"])
|
||||||
elif scope_type == "rack":
|
elif scope_type == "rack":
|
||||||
|
|
@ -63,17 +65,37 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]:
|
||||||
return []
|
return []
|
||||||
query = query.where(Device.rack_id.in_(rack_ids))
|
query = query.where(Device.rack_id.in_(rack_ids))
|
||||||
elif scope_type == "category":
|
elif scope_type == "category":
|
||||||
query = query.where(Device.category == scope["value"])
|
# support both "value" (legacy) and "category" key
|
||||||
|
cat = scope.get("category") or scope.get("value")
|
||||||
|
if cat:
|
||||||
|
query = query.where(Device.category == cat)
|
||||||
if "object_ids" in scope:
|
if "object_ids" in scope:
|
||||||
query = query.where(Device.object_id.in_(scope["object_ids"]))
|
query = query.where(Device.object_id.in_(scope["object_ids"]))
|
||||||
elif "object_id" in scope:
|
elif "object_id" in scope:
|
||||||
query = query.where(Device.object_id == scope["object_id"])
|
query = query.where(Device.object_id == scope["object_id"])
|
||||||
elif scope_type == "role":
|
elif scope_type == "role":
|
||||||
query = query.where(Device.role == scope["value"])
|
role = scope.get("role") or scope.get("value")
|
||||||
|
if role:
|
||||||
|
query = query.where(Device.role == role)
|
||||||
if "object_ids" in scope:
|
if "object_ids" in scope:
|
||||||
query = query.where(Device.object_id.in_(scope["object_ids"]))
|
query = query.where(Device.object_id.in_(scope["object_ids"]))
|
||||||
elif "object_id" in scope:
|
elif "object_id" in scope:
|
||||||
query = query.where(Device.object_id == scope["object_id"])
|
query = query.where(Device.object_id == scope["object_id"])
|
||||||
|
elif scope_type == "filter":
|
||||||
|
# Cross-object filter scope (from WorkDevices global page)
|
||||||
|
from app.models.object import Object as Obj
|
||||||
|
if scope.get("category"):
|
||||||
|
cats = scope["category"]
|
||||||
|
if isinstance(cats, list):
|
||||||
|
query = query.where(Device.category.in_(cats))
|
||||||
|
else:
|
||||||
|
query = query.where(Device.category == cats)
|
||||||
|
if scope.get("object_id"):
|
||||||
|
query = query.where(Device.object_id == scope["object_id"])
|
||||||
|
if scope.get("city"):
|
||||||
|
query = query.where(Obj.city == scope["city"])
|
||||||
|
if scope.get("client"):
|
||||||
|
query = query.where(Obj.client == scope["client"])
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ export function WorkDevicesPage() {
|
||||||
const [filterClient, setFilterClient] = useState<string | undefined>()
|
const [filterClient, setFilterClient] = useState<string | undefined>()
|
||||||
const [filterObjectId, setFilterObjectId] = useState<number | undefined>()
|
const [filterObjectId, setFilterObjectId] = useState<number | undefined>()
|
||||||
const [filterStatus, setFilterStatus] = useState<string | undefined>()
|
const [filterStatus, setFilterStatus] = useState<string | undefined>()
|
||||||
|
const [searchText, setSearchText] = useState('')
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([])
|
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([])
|
||||||
|
|
||||||
const [taskModal, setTaskModal] = useState(false)
|
const [taskModal, setTaskModal] = useState(false)
|
||||||
|
|
@ -68,6 +69,14 @@ export function WorkDevicesPage() {
|
||||||
|
|
||||||
const currentAction = actions?.find((a) => a.name === selectedAction)
|
const currentAction = actions?.find((a) => a.name === selectedAction)
|
||||||
|
|
||||||
|
const displayedDevices = useMemo(() => {
|
||||||
|
if (!searchText) return devices ?? []
|
||||||
|
const q = searchText.toLowerCase()
|
||||||
|
return (devices ?? []).filter(
|
||||||
|
(d) => d.ip.toLowerCase().includes(q) || (d.hostname ?? '').toLowerCase().includes(q)
|
||||||
|
)
|
||||||
|
}, [devices, searchText])
|
||||||
|
|
||||||
// Object dropdown filtered by active city/client selectors
|
// Object dropdown filtered by active city/client selectors
|
||||||
const objectOptions = useMemo(() => {
|
const objectOptions = useMemo(() => {
|
||||||
return (allObjects ?? [])
|
return (allObjects ?? [])
|
||||||
|
|
@ -275,15 +284,27 @@ export function WorkDevicesPage() {
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
<Row gutter={12} style={{ marginBottom: 12 }} align="middle">
|
||||||
|
<Col flex="auto">
|
||||||
|
<Input.Search
|
||||||
|
placeholder="Поиск по IP или hostname..."
|
||||||
|
allowClear
|
||||||
|
style={{ maxWidth: 360 }}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
{devices && (
|
{devices && (
|
||||||
<Typography.Text type="secondary" style={{ marginBottom: 8, display: 'block' }}>
|
<Typography.Text type="secondary">
|
||||||
Найдено: {devices.length} устройств
|
{searchText ? `${displayedDevices.length} из ${devices.length}` : `${devices.length} устройств`}
|
||||||
{selectedRowKeys.length > 0 && ` · Выбрано: ${selectedRowKeys.length}`}
|
{selectedRowKeys.length > 0 && ` · Выбрано: ${selectedRowKeys.length}`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
)}
|
)}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
dataSource={devices ?? []}
|
dataSource={displayedDevices}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue