From 6753bb02b8ffa6650442dd00508f3266955e280c Mon Sep 17 00:00:00 2001 From: SERTY Date: Mon, 15 Dec 2025 08:17:22 +0300 Subject: [PATCH] =?UTF-8?q?15.12.25=20-=20=D0=AD=D1=82=D0=B0=D0=BF=203.=20?= =?UTF-8?q?=D0=94=D0=B5=D1=82=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20--2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/equipment.ts | 28 ++++-- src/pages/equipment/FiscalDetails.tsx | 64 +++++++----- src/pages/equipment/ServerDetails.tsx | 51 +++++----- src/pages/equipment/WorkstationDetails.tsx | 26 ++--- src/types/api.ts | 107 +++++++++++++++++++++ 5 files changed, 205 insertions(+), 71 deletions(-) diff --git a/src/api/equipment.ts b/src/api/equipment.ts index ca1ee93..495b768 100644 --- a/src/api/equipment.ts +++ b/src/api/equipment.ts @@ -1,15 +1,23 @@ import apiClient from './axios'; -import { ApiResponse, ServerEntity, WorkstationEntity, FiscalEntity, UpdateServerDTO, UpdateWorkstationDTO, UpdateFiscalDTO } from '@/types/api'; +import { + ApiResponse, + ServerDetailDTO, + WorkstationDetailDTO, + FiscalDetailDTO, + UpdateServerPayload, + UpdateWorkstationPayload, + UpdateFiscalPayload +} from '@/types/api'; export const equipmentApi = { // --- Servers --- getServer: async (uuid: string) => { - const response = await apiClient.get>(`/servers/${uuid}`); + const response = await apiClient.get>(`/servers/${uuid}`); return response.data; }, - updateServer: async (uuid: string, data: UpdateServerDTO) => { - const response = await apiClient.put>(`/servers/${uuid}`, data); + updateServer: async (uuid: string, data: UpdateServerPayload) => { + const response = await apiClient.put>(`/servers/${uuid}`, data); return response.data; }, @@ -20,23 +28,23 @@ export const equipmentApi = { // --- Workstations --- getWorkstation: async (uuid: string) => { - const response = await apiClient.get>(`/workstations/${uuid}`); + const response = await apiClient.get>(`/workstations/${uuid}`); return response.data; }, - updateWorkstation: async (uuid: string, data: UpdateWorkstationDTO) => { - const response = await apiClient.put>(`/workstations/${uuid}`, data); + updateWorkstation: async (uuid: string, data: UpdateWorkstationPayload) => { + const response = await apiClient.put>(`/workstations/${uuid}`, data); return response.data; }, // --- Fiscals --- getFiscal: async (uuid: string) => { - const response = await apiClient.get>(`/fiscals/${uuid}`); + const response = await apiClient.get>(`/fiscals/${uuid}`); return response.data; }, - updateFiscal: async (uuid: string, data: UpdateFiscalDTO) => { - const response = await apiClient.put>(`/fiscals/${uuid}`, data); + updateFiscal: async (uuid: string, data: UpdateFiscalPayload) => { + const response = await apiClient.put>(`/fiscals/${uuid}`, data); return response.data; }, }; \ No newline at end of file diff --git a/src/pages/equipment/FiscalDetails.tsx b/src/pages/equipment/FiscalDetails.tsx index aa3e311..916c6e3 100644 --- a/src/pages/equipment/FiscalDetails.tsx +++ b/src/pages/equipment/FiscalDetails.tsx @@ -1,12 +1,12 @@ import React, { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { Card, Descriptions, Button, Space, Typography, Spin, Badge, Modal, Form, Input, message } from 'antd'; +import { Card, Descriptions, Button, Space, Typography, Spin, Badge, Modal, Form, Input, message, Table } from 'antd'; import { ArrowLeftOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { equipmentApi } from '@/api/equipment'; import { getEntityIcon, getStatusColor } from '@/utils/mappers'; import { formatRnm } from '@/utils/formatters'; -import { UpdateFiscalDTO } from '@/types/api'; +import { UpdateFiscalPayload } from '@/types/api'; import dayjs from 'dayjs'; const { Title, Text } = Typography; @@ -25,7 +25,7 @@ const FiscalDetails: React.FC = () => { }); const updateMutation = useMutation({ - mutationFn: (values: UpdateFiscalDTO) => equipmentApi.updateFiscal(id!, values), + mutationFn: (values: UpdateFiscalPayload) => equipmentApi.updateFiscal(id!, values), onSuccess: () => { message.success('Данные обновлены'); queryClient.invalidateQueries({ queryKey: ['fiscal', id] }); @@ -39,7 +39,6 @@ const FiscalDetails: React.FC = () => { const fiscal = fiscalRes.data; - // Логика цвета даты окончания ФН const getFnDateColor = (dateStr?: string) => { if (!dateStr) return undefined; const diff = dayjs(dateStr).diff(dayjs(), 'day'); @@ -50,11 +49,22 @@ const FiscalDetails: React.FC = () => { const handleEdit = () => { form.setFieldsValue({ - description: fiscal.description, + description: fiscal.Description, }); setIsEditModalOpen(true); }; + // Преобразуем лицензии из объекта в массив для таблицы + const licensesData = fiscal.Licenses + ? Object.entries(fiscal.Licenses).map(([id, data]) => ({ id, ...data })) + : []; + + const licenseColumns = [ + { title: 'ID', dataIndex: 'id', width: 60 }, + { title: 'Название', dataIndex: 'name' }, + { title: 'До', dataIndex: 'dateUntil', render: (d: string) => d ? d.split(' ')[0] : '-' }, + ]; + return (
@@ -63,11 +73,11 @@ const FiscalDetails: React.FC = () => {
{getEntityIcon('FiscalRegister')}
- {fiscal.model_kkt || 'ККТ'} - {fiscal.serial_number} + {fiscal.ModelKKT || 'ККТ'} + {fiscal.FRSerialNumber}
- + @@ -78,24 +88,22 @@ const FiscalDetails: React.FC = () => { - {/* Main Info */} - {formatRnm(fiscal.rn_kkt)} + {formatRnm(fiscal.RNKKT)} - {fiscal.serial_number} - {fiscal.model_kkt} - {fiscal.description || '-'} + {fiscal.FRSerialNumber} + {fiscal.ModelKKT} + {fiscal.Description || '-'} - {/* FN Info */} - {fiscal.fn_number || '-'} + {fiscal.FNNumber || '-'} - {fiscal.fn_registration_date ? dayjs(fiscal.fn_registration_date).format('DD.MM.YYYY') : '-'} + {fiscal.kkt_reg_date ? dayjs(fiscal.kkt_reg_date).format('DD.MM.YYYY') : '-'} @@ -105,23 +113,33 @@ const FiscalDetails: React.FC = () => { - {/* Firmware Info */} - {fiscal.fr_firmware || '-'} - {fiscal.fr_downloader || '-'} - {fiscal.driver_version || '-'} + {fiscal.FRFirmware || '-'} + {fiscal.FRDownloader || '-'} + {fiscal.DriverVersion || '-'} - {/* Legal Info */} - {fiscal.organization_name || '-'} - {fiscal.inn || '-'} + {fiscal.LegalName || '-'} + {fiscal.INN || '-'} {fiscal.address || '-'} + + {licensesData.length > 0 && ( + + + + )} { }); const updateMutation = useMutation({ - mutationFn: (values: UpdateServerDTO) => equipmentApi.updateServer(id!, values), + mutationFn: (values: UpdateServerPayload) => equipmentApi.updateServer(id!, values), onSuccess: () => { message.success('Данные сервера обновлены'); queryClient.invalidateQueries({ queryKey: ['server', id] }); @@ -46,12 +46,13 @@ const ServerDetails: React.FC = () => { const server = serverRes.data; const handleEdit = () => { + // Маппинг для формы (PascalCase -> snake_case payload) form.setFieldsValue({ - device_name: server.device_name, - ip: server.ip, - anydesk: server.anydesk, - teamviewer: server.teamviewer, - description: server.description, + device_name: server.DeviceName, + ip: server.IP, + anydesk: server.Anydesk, + teamviewer: server.Teamviewer, + description: server.Description, }); setIsEditModalOpen(true); }; @@ -65,12 +66,12 @@ const ServerDetails: React.FC = () => {
{getEntityIcon('Server')}
- {server.device_name || server.server_name || 'Server'} - {server.uuid} + {server.DeviceName || server.ServerName || 'Server'} + {server.ID}
- - {server.operational_status?.toUpperCase()} + + {server.Status?.toUpperCase()} @@ -93,23 +94,23 @@ const ServerDetails: React.FC = () => { - {server.ip || '-'} + {server.IP || '-'} - + - {server.unique_id || '-'} - {server.crm_id || '-'} - {server.description || '-'} + {server.UniqueID || '-'} + {server.CRMid || '-'} + {server.Description || '-'} {/* Software Info */} - {server.server_version || '-'} - {server.server_edition || '-'} - {formatDate(server.last_polled_at)} + {server.ServerVersion || '-'} + {server.ServerEdition || '-'} + {formatDate(server.LastPolledAt)} @@ -119,17 +120,17 @@ const ServerDetails: React.FC = () => { - {server.anydesk ? {server.anydesk} : -} + {server.Anydesk ? {server.Anydesk} : -} - {server.teamviewer ? {server.teamviewer} : -} + {server.Teamviewer ? {server.Teamviewer} : -} - - {server.litemanager ? {server.litemanager} : -} + + {server.RDP ? {server.RDP} : -} - {server.partners_link && ( + {server.CabinetLink && ( - diff --git a/src/pages/equipment/WorkstationDetails.tsx b/src/pages/equipment/WorkstationDetails.tsx index fa617b9..49fdbd3 100644 --- a/src/pages/equipment/WorkstationDetails.tsx +++ b/src/pages/equipment/WorkstationDetails.tsx @@ -5,7 +5,7 @@ import { Card, Descriptions, Button, Space, Typography, Spin, Badge, Modal, Form import { ArrowLeftOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { equipmentApi } from '@/api/equipment'; import { getEntityIcon, getStatusColor } from '@/utils/mappers'; -import { UpdateWorkstationDTO } from '@/types/api'; +import { UpdateWorkstationPayload } from '@/types/api'; const { Title, Text, Paragraph } = Typography; @@ -23,7 +23,7 @@ const WorkstationDetails: React.FC = () => { }); const updateMutation = useMutation({ - mutationFn: (values: UpdateWorkstationDTO) => equipmentApi.updateWorkstation(id!, values), + mutationFn: (values: UpdateWorkstationPayload) => equipmentApi.updateWorkstation(id!, values), onSuccess: () => { message.success('Данные обновлены'); queryClient.invalidateQueries({ queryKey: ['workstation', id] }); @@ -39,10 +39,10 @@ const WorkstationDetails: React.FC = () => { const handleEdit = () => { form.setFieldsValue({ - device_name: ws.device_name, - anydesk: ws.anydesk, - teamviewer: ws.teamviewer, - description: ws.description, + device_name: ws.DeviceName, + anydesk: ws.Anydesk, + teamviewer: ws.Teamviewer, + description: ws.Description, }); setIsEditModalOpen(true); }; @@ -55,11 +55,11 @@ const WorkstationDetails: React.FC = () => {
{getEntityIcon('Workstation')}
- {ws.device_name || 'Workstation'} - {ws.uuid} + {ws.DeviceName || 'Workstation'} + {ws.ID}
- + @@ -71,16 +71,16 @@ const WorkstationDetails: React.FC = () => { - {ws.description || '-'} + {ws.Description || '-'} - {ws.anydesk ? {ws.anydesk} : '-'} + {ws.Anydesk ? {ws.Anydesk} : '-'} - {ws.teamviewer ? {ws.teamviewer} : '-'} + {ws.Teamviewer ? {ws.Teamviewer} : '-'} - {ws.litemanager ? {ws.litemanager} : '-'} + {ws.Litemanager ? {ws.Litemanager} : '-'} diff --git a/src/types/api.ts b/src/types/api.ts index 5e4c3ca..094e949 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -206,4 +206,111 @@ export interface UpdateWorkstationDTO { export interface UpdateFiscalDTO { description?: string; // ККТ поля обычно read-only, т.к. приходят из железа, но description можно править +} + +// --- Detail DTOs (Strictly matching Backend JSON) --- + +export interface LicensesDict { + [key: string]: { + name: string; + dateFrom: string; + dateUntil: string; + }; +} + +export interface FiscalDetailDTO { + ID: string; + ModelKKT?: string; + RNKKT?: string; + LegalName?: string; + INN?: string; + FRSerialNumber?: string; + FNNumber?: string; + + // Внимание: смешанный регистр в JSON + kkt_reg_date?: string; + fn_expire_date?: string; + + FRFirmware?: string; + FRDownloader?: string; + DriverVersion?: string; + + HealthStatus?: 'ok' | 'attention_required' | 'locked'; + + // Внимание: lowercase в JSON + address?: string; + Description?: string; + + Licenses?: LicensesDict; +} + +export interface WorkstationDetailDTO { + ID: string; + DeviceName?: string; + Teamviewer?: string; + Anydesk?: string; + Litemanager?: string; + Description?: string; + HealthStatus?: 'ok' | 'attention_required' | 'locked'; +} + +export interface ServerDetailDTO { + ID: string; + UniqueID?: string; + IP?: string; + DeviceName?: string; + ServerName?: string; + ServerVersion?: string; + ServerEdition?: string; + + // PascalCase + LastPolledAt?: string; + Status?: 'active' | 'offline' | 'unknown'; // Operational Status + HealthStatus?: 'ok' | 'attention_required' | 'locked'; + + CabinetLink?: string; + CRMid?: string; + + RDP?: string; + Teamviewer?: string; + Anydesk?: string; + Litemanager?: string; + Description?: string; +} + +// ... (предыдущие TaskDTO и прочее остаются) +export interface ApiResponse { + status: 'success' | 'error'; + data: T; + meta?: PaginationMeta; + error?: { + error: string; + }; +} +// Необходимо убедиться, что PaginationMeta и другие типы на месте +export interface PaginationMeta { + total: number; + limit: number; + offset: number; + has_next: boolean; + has_prev: boolean; +} + +export interface UpdateServerPayload { + device_name?: string; + ip?: string; + anydesk?: string; + teamviewer?: string; + description?: string; +} + +export interface UpdateWorkstationPayload { + device_name?: string; + anydesk?: string; + teamviewer?: string; + description?: string; +} + +export interface UpdateFiscalPayload { + description?: string; } \ No newline at end of file