diff --git a/src/app/actions/violationActions.ts b/src/app/actions/violationActions.ts index 58ff158..55dc08d 100644 --- a/src/app/actions/violationActions.ts +++ b/src/app/actions/violationActions.ts @@ -261,8 +261,9 @@ export async function fetchViolationStatistic() { interface complainBody { previousText: string, - errorMessage: string | null, - success: boolean + violationID: string | null, + success: boolean, + errorMessage?: string | null } export async function createComplaint( @@ -297,7 +298,7 @@ export async function createComplaint( if (parsed?.message_code === 0) { return { - errorMessage: violationId, + violationID: violationId, previousText: text, success: true }; @@ -307,11 +308,24 @@ export async function createComplaint( } else { throw (`${response.status}`); } - } catch (error) { + } catch (error: unknown) { + if (error && typeof error === 'object' && 'message_code' in error) { + const err = error as { message_code: number }; + if (err.message_code === 2) { + return { + violationID: violationId, + previousText: text, + success: false, + errorMessage: 'the-complaint-has-not-been-registered' + } + } + } + return { - errorMessage: violationId, + violationID: violationId, previousText: text, - success: false + success: false, + errorMessage: 'error-save' } } } @@ -359,7 +373,6 @@ export type MatchStatus = 'NEW' | 'SHOWED' | 'LEGAL_IN_WORK' | 'COMPLAINT_IN_WOR export async function updateMatchStatus(id: number, status: MatchStatus) { const token = await getSessionData('token'); - console.log('updateMatchStatus'); try { const response = await fetch(`${API_BASE_URL}/api/v1/data`, { diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index c7f8060..ccc9b3f 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -4526,6 +4526,10 @@ transition: all 0.3s ease; font-size: 14px; max-width: 300px; + + &:disabled { + opacity: 0.5; + } } .btn-case { diff --git a/src/app/ui/file-page/violation-table/case-complaint.tsx b/src/app/ui/file-page/violation-table/case-complaint.tsx index a13f3ed..6a96356 100644 --- a/src/app/ui/file-page/violation-table/case-complaint.tsx +++ b/src/app/ui/file-page/violation-table/case-complaint.tsx @@ -19,39 +19,57 @@ interface complaintInfo { } export default function CaseComplaint({ selectedViolation }: { selectedViolation: ViolationFileDetail }) { - const [showCreateCase, setShowCreateCase] = useState(false); const t = useTranslations('Global'); - const [state, formAction, isPending] = useActionState(createComplaint, undefined); const queryClient = useQueryClient(); + const [showCreateCase, setShowCreateCase] = useState(false); + const [localViolation, setLocalViolation] = useState(selectedViolation); + const [state, formAction, isPending] = useActionState(createComplaint, undefined); + + useEffect(() => { + setLocalViolation(selectedViolation); + }, [selectedViolation]); + const { data: complainInfo, isLoading: isLoadingComplain } = useQuery({ - queryKey: ['complainInfo', selectedViolation.id], - queryFn: () => fetchComplainInfo(selectedViolation.id), + queryKey: ['complainInfo', localViolation.id], + queryFn: () => fetchComplainInfo(localViolation.id), select: (data) => { if (data) { return data } }, - enabled: selectedViolation.status !== 'NEW' && - selectedViolation.status !== 'CREATED' && - selectedViolation.status !== 'SHOWED', + enabled: localViolation.status !== 'NEW' && + localViolation.status !== 'CREATED' && + localViolation.status !== 'SHOWED', }); useEffect(() => { if (state?.success) { - toast.success('Жалоба зарегистрирована'); + toast.success(t('the-complaint-has-been-registered')); + + setLocalViolation(prev => ({ + ...prev, + status: 'COMPLAINT_IN_WORK' + })); + queryClient.invalidateQueries({ queryKey: ['fileViolations'] }); - queryClient.invalidateQueries({ queryKey: ['complainInfo', selectedViolation.id] }); + queryClient.invalidateQueries({ queryKey: ['complainInfo', localViolation.id] }); + + setShowCreateCase(false); } else if (state && !state.success) { - toast.warning('Жалоба не зарегистрирована'); + if (state.errorMessage) { + toast.warning(t(state.errorMessage)); + } else { + toast.warning(t('the-complaint-has-not-been-registered')); + } } - }, [state, queryClient, selectedViolation.id]); + }, [state, queryClient, localViolation.id]); function toggleForm() { setShowCreateCase(!showCreateCase); } - if (selectedViolation.status === 'NEW' || selectedViolation.status === 'CREATED' || selectedViolation.status === 'SHOWED') { + if (localViolation.status === 'NEW' || localViolation.status === 'CREATED' || localViolation.status === 'SHOWED') { return (
{!showCreateCase ? ( @@ -60,7 +78,7 @@ export default function CaseComplaint({ selectedViolation }: { selectedViolation className="btn-action btn-case" onClick={toggleForm} > - Подача жалобы + {t('filing-complaint')} ) : (
@@ -74,18 +92,18 @@ export default function CaseComplaint({ selectedViolation }: { selectedViolation
- +
@@ -97,7 +115,7 @@ export default function CaseComplaint({ selectedViolation }: { selectedViolation if (isLoadingComplain) { return (
-
Загрузка...
+
{t('loading')}...
); } @@ -105,31 +123,30 @@ export default function CaseComplaint({ selectedViolation }: { selectedViolation return (
{complainInfo?.body.content ? ( -
{complainInfo.body.content.map((item: complaintInfo) => { return (
-

- Информация о жалобе +

+ {t('complaint-Information')}

ID: {item.id}

-

Статус: {item.status}

-

Текст жалобы: {item.complaint_text}

-

ID нарушения: {item.violation_id}

-

Email: {item.email}

-

Дата создания: {item.created_at}

-

Дата обновления: {item.updated_at}

+

{t('status')}: {item.status}

+

{t('text-of-the-complaint')}: {item.complaint_text}

+

{t('violation-id')}: {item.violation_id}

+

{t('email')}: {item.email}

+

{t('date-of-creation')}: {item.created_at}

+

{t('date-of-update')}: {item.updated_at}

); })}
) : ( -
Нет информации о жалобе
+
+ {t('no-information-about-the-complaint')} +
)}
); diff --git a/src/app/ui/file-page/violation-table/file-page-violation-info.tsx b/src/app/ui/file-page/violation-table/file-page-violation-info.tsx index f5281e6..068d4e5 100644 --- a/src/app/ui/file-page/violation-table/file-page-violation-info.tsx +++ b/src/app/ui/file-page/violation-table/file-page-violation-info.tsx @@ -8,7 +8,7 @@ import CaseComplaint from '@/app/ui/file-page/violation-table/case-complaint'; import CaseViolation from '@/app/ui/file-page/violation-table/case-violation'; import { updateMatchStatus } from '@/app/actions/violationActions'; import { useQueryClient } from '@tanstack/react-query'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { MatchStatus } from '@/app/actions/violationActions'; import { toast } from 'sonner'; @@ -16,18 +16,44 @@ export default function FilePageViolationInfo({ selectedViolation }: { selectedV const t = useTranslations('Global'); const tStatus = useTranslations('Match-status'); const queryClient = useQueryClient(); + const [localViolation, setLocalViolation] = useState(null); + const [isUpdating, setIsUpdating] = useState(false); + + useEffect(() => { + setLocalViolation(selectedViolation); + }, [selectedViolation]); + const updateStatusHandler = useCallback(async (id: number, status: MatchStatus) => { - const response = await updateMatchStatus(id, status); - if (response) { - if (status === 'AUTHORIZED_USE') { - toast.success('Статус обновлен'); + setIsUpdating(true); + + // Оптимистичное обновление UI + setLocalViolation(prev => prev ? { ...prev, status } : null); + + try { + const response = await updateMatchStatus(id, status); + if (response) { + if (status === 'AUTHORIZED_USE') { + toast.success('Статус обновлен'); + } + // Инвалидируем кэш, чтобы данные синхронизировались с сервером + queryClient.invalidateQueries({ queryKey: ['fileViolations'] }); + return true; + } else { + // Если ошибка - откатываем изменение + setLocalViolation(selectedViolation); + toast.error('Ошибка при обновлении статуса'); + return false; } - queryClient.invalidateQueries({ queryKey: ['fileViolations'] }); - return true; + } catch (error) { + // При ошибке откатываем + setLocalViolation(selectedViolation); + toast.error('Ошибка при обновлении статуса'); + return false; + } finally { + setIsUpdating(false); } - return false; - }, [queryClient]); + }, [queryClient, selectedViolation]); useEffect(() => { const updateStatus = async () => { @@ -94,7 +120,7 @@ export default function FilePageViolationInfo({ selectedViolation }: { selectedV {t('date')} {selectedViolation?.created_date ? formatDate(selectedViolation?.created_date) : '#'} {/* COMPLAINT_IN_WORK COMPLAINT_AND_LEGAL_IN_WORK*/} - {selectedViolation?.status === "COMPLAINT_AND_LEGAL_IN_WORK" ? ( + {localViolation?.status === "COMPLAINT_AND_LEGAL_IN_WORK" ? ( <> {tStatus('LEGAL_IN_WORK')} @@ -104,9 +130,11 @@ export default function FilePageViolationInfo({ selectedViolation }: { selectedV ) : ( - - {tStatus(selectedViolation?.status)} - + localViolation?.status && ( + + {tStatus(localViolation?.status)} + + ) )}
{ - updateStatusHandler(selectedViolation?.id, 'AUTHORIZED_USE'); + if (selectedViolation?.id) { + updateStatusHandler(selectedViolation.id, 'AUTHORIZED_USE'); + } }} + disabled={isUpdating} > {t('mark-as-solved')} @@ -127,8 +158,8 @@ export default function FilePageViolationInfo({ selectedViolation }: { selectedV
- - + +
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 3e0ee93..111160c 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -369,7 +369,18 @@ "search-info-description-text": "Our system analyzes the uploaded file and compares it with your protected files, using computer vision and digital fingerprinting algorithms.", "documents-few": "Documents", "change": "Change", - "mark-as-solved": "Mark as solved" + "mark-as-solved": "Mark as solved", + "loading": "Loading", + "filing-complaint": "Filing a complaint", + "submit-violation": "Submit a violation", + "complaint-Information": "Complaint Information", + "date-of-creation": "Date of creation", + "date-of-update": "Date of update", + "text-of-the-complaint": "Text of the complaint", + "violation-id": "Violation ID", + "no-information-about-the-complaint": "No information about the complaint", + "the-complaint-has-not-been-registered": "The complaint has not been registered", + "the-complaint-has-been-registered": "The complaint has been registered" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 8a7c276..aa2a94d 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -369,7 +369,18 @@ "search-info-description-text": "Наша система анализирует загруженный файл и сравнивает его с вашими защищенными файлами, используя алгоритмы компьютерного зрения и цифровых отпечатков.", "documents-few": "Документов", "change": "Изменить", - "mark-as-solved": "Отметить решенным" + "mark-as-solved": "Отметить решенным", + "loading": "Загрузка", + "filing-complaint": "Подача жалобы", + "submit-violation": "Отправить нарушение", + "complaint-Information": "Информация о жалобе", + "date-of-creation": "Дата создания", + "date-of-update": "Дата обновления", + "text-of-the-complaint": "Текст жалобы", + "violation-id": "ID нарушения", + "no-information-about-the-complaint": "Нет информации о жалобе", + "the-complaint-has-not-been-registered": "Жалоба не зарегистрирована", + "the-complaint-has-been-registered": "Жалоба зарегистрирована" }, "Login-register-form": { "and": "и",