+ Задачи оператора
+
+ } onClick={() => refetch()} loading={isFetching} />
+ {/* Явно указываем Generic для Select, чтобы val имел правильный тип */}
+
+
+
+
+
+
setPage(p),
+ }}
+ />
+
+
+ setSelectedTask(null)}
+ />
+
+ );
+};
+
+export default TasksPage;
\ No newline at end of file
diff --git a/src/types/api.ts b/src/types/api.ts
new file mode 100644
index 0000000..0d7b64f
--- /dev/null
+++ b/src/types/api.ts
@@ -0,0 +1,81 @@
+// Общий конверт ответа API
+export interface ApiResponse {
+ status: 'success' | 'error';
+ data: T;
+ meta?: PaginationMeta;
+ error?: {
+ error: string;
+ };
+}
+
+export interface PaginationMeta {
+ total: number;
+ limit: number;
+ offset: number;
+ has_next: boolean;
+ has_prev: boolean;
+}
+
+// --- Common Entities ---
+export interface CompanyOwner {
+ uuid: string;
+ external_uuid: string | null;
+ name: string;
+ address: string;
+ active_contract: boolean;
+ parent_info?: { uuid: string; name: string };
+}
+
+// Тип для статуса бейджа Ant Design
+export type AntBadgeStatus = 'success' | 'processing' | 'error' | 'default' | 'warning';
+
+export interface EntityData {
+ uuid: string;
+ device_name?: string;
+ ip?: string; // Для серверов
+ rn_kkt?: string; // Для ФР
+ serial_number?: string;
+ operational_status?: 'active' | 'offline' | 'unknown';
+ health_status?: 'ok' | 'attention_required' | 'locked';
+ // Используем unknown вместо any для безопасной работы с динамическими полями
+ [key: string]: unknown;
+}
+
+// --- Search DTO ---
+export interface SearchFoundEntity {
+ entity_type: 'Server' | 'Workstation' | 'FiscalRegister';
+ data: EntityData;
+}
+
+export interface SearchResultGroup {
+ owner: CompanyOwner;
+ found_entities: SearchFoundEntity[];
+}
+
+export interface SearchResponseData {
+ search_results: SearchResultGroup[];
+}
+
+// --- Tasks DTO ---
+export type TaskStatus = 'new' | 'resolved' | 'rejected' | 'pending_sd_action' | 'sd_error';
+export type TaskType = 'add_equipment' | 'conflict' | 'offline_alert';
+
+export interface TaskDTO {
+ id: number;
+ task_type: TaskType;
+ entity_type: string;
+ status: TaskStatus;
+ created_at: string;
+ // Детали могут быть любой структуры, но мы знаем, что это объект
+ details: Record;
+}
+
+export interface TaskResolutionPayload {
+ status: 'resolved' | 'rejected';
+ comment?: string;
+ resolution_payload?: {
+ action: string;
+ new_owner_id?: string;
+ [key: string]: unknown;
+ };
+}
\ No newline at end of file
diff --git a/src/utils/mappers.tsx b/src/utils/mappers.tsx
new file mode 100644
index 0000000..31f8749
--- /dev/null
+++ b/src/utils/mappers.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import {
+ DatabaseOutlined,
+ DesktopOutlined,
+ CalculatorOutlined,
+ BankOutlined,
+ QuestionCircleOutlined,
+} from '@ant-design/icons';
+import { AntBadgeStatus } from '@/types/api';
+
+export const getEntityIcon = (type: string): React.ReactNode => {
+ switch (type) {
+ case 'Server': return ;
+ case 'Workstation': return ;
+ case 'FiscalRegister': return ;
+ case 'Company': return ;
+ default: return ;
+ }
+};
+
+export const getEntityLabel = (type: string): string => {
+ const map: Record = {
+ Server: 'Сервер',
+ Workstation: 'Рабочая станция',
+ FiscalRegister: 'Фискальный регистратор',
+ };
+ return map[type] || type;
+};
+
+export const getStatusColor = (status?: unknown): AntBadgeStatus => {
+ // Приведение строки к валидному статусу Badge
+ const s = String(status);
+ switch (s) {
+ case 'active':
+ case 'ok':
+ return 'success';
+ case 'offline':
+ case 'locked':
+ return 'error';
+ case 'attention_required':
+ case 'unknown':
+ return 'warning';
+ default:
+ return 'default';
+ }
+};
\ No newline at end of file