import { useEffect, useRef, useState, type ReactNode, } from 'react' import * as Dialog from '@radix-ui/react-dialog' import { useMutation, useQuery } from '@apollo/client/react' import SimpleBar from 'simplebar-react' import { Check, CircleAlert, History, Wrench, X } from 'lucide-react' import { GET_ACTIVE_PHONE_MAINTENANCE_CASE_QUERY, MARK_PHONE_HEALTHY_MUTATION, RETURN_PHONE_FROM_MAINTENANCE_MUTATION, SEND_PHONE_TO_MAINTENANCE_MUTATION, SET_PHONE_MAINTENANCE_REQUIRED_MUTATION, } from '../../../../entities/device/api/device.graphql' import { deviceMalfunctionLabels, isRedundantMalfunctionComment, } from '../../../../entities/device/lib/maintenance' import type { DeviceMalfunction, GetActivePhoneMaintenanceCaseData, GetActivePhoneMaintenanceCaseVariables, MarkPhoneHealthyData, MarkPhoneHealthyVariables, ReturnPhoneFromMaintenanceData, ReturnPhoneFromMaintenanceVariables, SendPhoneToMaintenanceData, SendPhoneToMaintenanceVariables, SetPhoneMaintenanceRequiredData, SetPhoneMaintenanceRequiredVariables, } from '../../../../entities/device/model/types' import { MotionDialogContent } from '../../../../shared/ui/MotionDialog/MotionDialog' import { Notification, type NotificationVariant, } from '../../../../widgets/Notification/Notification' import type { Device } from '../../types' import { conditionText, getStatusClass } from '../../types' import { DeviceMaintenanceMetrics } from '../DeviceMaintenanceMetrics/DeviceMaintenanceMetrics' import './DeviceMaintenanceModal.scss' export type DeviceMaintenanceMode = 'view' | 'manage' type DeviceMaintenanceModalProps = { open: boolean mode: DeviceMaintenanceMode device: Device onOpenChange: (open: boolean) => void onOpenHistory: () => void onUpdated: () => Promise | unknown } type NotificationState = { variant: NotificationVariant title: string description?: string } const malfunctionOptions: Array<{ value: DeviceMalfunction description: string }> = [ { value: 'LowBatteryCapacity', description: 'Остаточная ёмкость аккумулятора ниже допустимой.', }, { value: 'CriticalHits', description: 'Количество зафиксированных ударов достигло лимита.', }, { value: 'CriticalChargeCycles', description: 'Количество циклов зарядки достигло лимита.', }, { value: 'DoesNotTurnOn', description: 'Устройство не загружается или не реагирует на включение.', }, { value: 'Other', description: 'Неисправность не относится к перечисленным категориям.', }, ] function getErrorMessage(error: unknown) { return error instanceof Error ? error.message : 'Не удалось выполнить действие.' } function ActionButton({ children, className = '', disabled, onClick, }: { children: ReactNode className?: string disabled?: boolean onClick: () => void }) { return ( ) } export function DeviceMaintenanceModal({ open, mode, device, onOpenChange, onOpenHistory, onUpdated, }: DeviceMaintenanceModalProps) { const [selectedMalfunctions, setSelectedMalfunctions] = useState([]) const [comment, setComment] = useState('') const [notification, setNotification] = useState(null) const [activeAction, setActiveAction] = useState(null) const initializedDraftKey = useRef('') const { data: activeCaseData, loading: isActiveCaseLoading, error: activeCaseError, refetch: refetchActiveCase, } = useQuery< GetActivePhoneMaintenanceCaseData, GetActivePhoneMaintenanceCaseVariables >(GET_ACTIVE_PHONE_MAINTENANCE_CASE_QUERY, { variables: { phoneId: String(device.id) }, skip: !open, fetchPolicy: 'cache-and-network', }) const [setMaintenanceRequired] = useMutation< SetPhoneMaintenanceRequiredData, SetPhoneMaintenanceRequiredVariables >(SET_PHONE_MAINTENANCE_REQUIRED_MUTATION) const [markPhoneHealthy] = useMutation< MarkPhoneHealthyData, MarkPhoneHealthyVariables >(MARK_PHONE_HEALTHY_MUTATION) const [sendPhoneToMaintenance] = useMutation< SendPhoneToMaintenanceData, SendPhoneToMaintenanceVariables >(SEND_PHONE_TO_MAINTENANCE_MUTATION) const [returnPhoneFromMaintenance] = useMutation< ReturnPhoneFromMaintenanceData, ReturnPhoneFromMaintenanceVariables >(RETURN_PHONE_FROM_MAINTENANCE_MUTATION) const isManageMode = mode === 'manage' const isBusy = activeAction !== null const isOtherSelected = selectedMalfunctions.includes('Other') const isDraftValid = selectedMalfunctions.length > 0 && (!isOtherSelected || comment.trim().length > 0) const visibleComment = isRedundantMalfunctionComment( device.malfunctions, device.malfunctionComment, ) ? '' : device.malfunctionComment?.trim() ?? '' const activeMaintenanceCase = activeCaseData?.getActivePhoneMaintenanceCase ?? null useEffect(() => { if (!open) { initializedDraftKey.current = '' return } const draftKey = `${device.id}:${device.technicalStatus}` if (initializedDraftKey.current === draftKey) return initializedDraftKey.current = draftKey setSelectedMalfunctions(device.malfunctions) setComment(device.malfunctionComment ?? '') }, [ device.id, device.malfunctionComment, device.malfunctions, device.technicalStatus, open, ]) function toggleMalfunction(value: DeviceMalfunction) { setSelectedMalfunctions((current) => current.includes(value) ? current.filter((item) => item !== value) : [...current, value], ) } async function runAction( action: string, title: string, callback: () => Promise, ) { setActiveAction(action) try { await callback() await Promise.all([ Promise.resolve(onUpdated()), refetchActiveCase(), ]) setNotification({ variant: 'success', title }) } catch (actionError) { setNotification({ variant: 'error', title: 'Не удалось изменить состояние устройства', description: getErrorMessage(actionError), }) } finally { setActiveAction(null) } } function saveMaintenanceRequired() { if (!isDraftValid) return void runAction('save', 'Неисправность сохранена', () => setMaintenanceRequired({ variables: { id: String(device.id), malfunctions: selectedMalfunctions, comment: comment.trim() || null, }, }), ) } function setHealthy() { void runAction('healthy', 'Устройство признано исправным', () => markPhoneHealthy({ variables: { id: String(device.id) } }), ) } function sendToMaintenance() { if (!isDraftValid) return void runAction('service', 'Устройство отправлено на обслуживание', () => sendPhoneToMaintenance({ variables: { id: String(device.id), malfunctions: selectedMalfunctions, comment: comment.trim() || null, }, }), ) } function returnToService() { void runAction('return', 'Устройство возвращено в эксплуатацию', () => returnPhoneFromMaintenance({ variables: { id: String(device.id) } }), ) } return ( <>
Техническое состояние {device.factoryNumber} · ID {device.id}
{device.condition === 'ok' ? : }
Текущее состояние {conditionText[device.condition]}
{visibleComment && (
Комментарий

{visibleComment}

)}
{device.technicalStatus !== 'Healthy' && (

{device.technicalStatus === 'InService' ? 'Показатели при отправке' : 'Показатели устройства'}

{device.technicalStatus === 'InService' ? 'Снимок устройства при передаче на обслуживание.' : 'Текущие показатели для открытого случая.'}

{activeMaintenanceCase ? ( ) : (

{isActiveCaseLoading ? 'Загружаем показатели...' : activeCaseError ? 'Не удалось загрузить показатели.' : 'Показатели не зафиксированы.'}

)}
)} {!isManageMode && device.malfunctions.length > 0 && (

Неисправности

Неисправности, закреплённые за текущим состоянием.

{device.malfunctions.map((malfunction) => ( {deviceMalfunctionLabels[malfunction]} ))}
)} {isManageMode && device.technicalStatus !== 'InService' && (

Неисправности

Укажите одну или несколько обнаруженных неисправностей.

{malfunctionOptions.map((option) => { const isSelected = selectedMalfunctions.includes(option.value) return ( ) })}