From f55f4cedfe57aa88dda4c2a68cfb0c519f79deeb Mon Sep 17 00:00:00 2001 From: smanylov Date: Mon, 20 Apr 2026 16:08:10 +0700 Subject: [PATCH] add complaints\claims blocks --- src/app/[locale]/pages/violations/page.tsx | 6 +- src/app/actions/violationActions.ts | 41 ++-- src/app/hooks/react-query/useLastCases.ts | 29 ++- .../hooks/react-query/useLastComplaints.ts | 22 +- src/app/styles/globals.css | 5 +- src/app/styles/pages-styles.scss | 197 ++++++++++-------- src/app/styles/reports.scss | 2 +- src/app/styles/violation-details.scss | 6 +- .../violation-table/case-complaint.tsx | 2 +- .../file-page-violation-epmty-screen.tsx | 2 +- src/app/ui/violations/violations-my-cases.tsx | 72 ------- .../ui/violations/violations-my-claims.tsx | 133 ++++++++++++ .../ui/violations/violations-my-complaint.tsx | 130 ++++++++++++ src/i18n/messages/en.json | 6 +- src/i18n/messages/ru.json | 10 +- 15 files changed, 468 insertions(+), 195 deletions(-) delete mode 100644 src/app/ui/violations/violations-my-cases.tsx create mode 100644 src/app/ui/violations/violations-my-claims.tsx create mode 100644 src/app/ui/violations/violations-my-complaint.tsx diff --git a/src/app/[locale]/pages/violations/page.tsx b/src/app/[locale]/pages/violations/page.tsx index 81256eb..813e4fc 100644 --- a/src/app/[locale]/pages/violations/page.tsx +++ b/src/app/[locale]/pages/violations/page.tsx @@ -1,10 +1,11 @@ import PageTitleColorFrame from '@/app/ui/page-title-color-frame'; -import ViolationsMyCases from '@/app/ui/violations/violations-my-cases'; +import ViolationsMyComplaint from '@/app/ui/violations/violations-my-complaint'; import ViolationsCheckAllSection from '@/app/ui/violations/violations-check-all-section'; import ViolationsTable from '@/app/ui/violations/violations-table'; import ViolationsStatistic from '@/app/ui/violations/violations-statistic'; import AnalyticsCardGeography from '@/app/ui/reports/analytics-card-geography'; import AnalyticsCardDistribution from '@/app/ui/reports/analytics-card-distribution'; +import ViolationsMyClaims from '@/app/ui/violations/violations-my-claims'; export default function Page() { return ( @@ -16,11 +17,12 @@ export default function Page() { - +
+ ) } \ No newline at end of file diff --git a/src/app/actions/violationActions.ts b/src/app/actions/violationActions.ts index 66347b7..d3d7a62 100644 --- a/src/app/actions/violationActions.ts +++ b/src/app/actions/violationActions.ts @@ -44,7 +44,6 @@ export async function getViolationSearchStatus() { export async function startGlobalMonitoring(monitoringType: 'monitoring' | 'all_files') { const token = await getSessionData('token'); - console.log('startGlobalMonitoring'); try { const response = await fetch(`${API_BASE_URL}/api/v1/global-search/start`, { @@ -67,7 +66,6 @@ export async function startGlobalMonitoring(monitoringType: 'monitoring' | 'all_ totalFiles: number } = await response.json(); - console.log(parsed); if (parsed?.status === 'ACCEPTED') { return parsed } else { @@ -465,7 +463,7 @@ export async function fetchCaseInfo(id: number) { token: token, pageSize: 10, pageNumber: 0, - sortBy:"createdAt", + sortBy: "createdAt", sortDir: "desc", violation_id: id } @@ -614,8 +612,16 @@ export async function createCase( } } -export async function fetchLastCase() { +export interface ComplaintsCaseQueryParams { + page?: number, + size?: number, + sort_by?: string, + sort_direction?: 'desc' | 'asc' +} + +export async function fetchLastCase(params: ComplaintsCaseQueryParams) { const token = await getSessionData('token'); + const { page, size, sort_by, sort_direction } = params; try { const response = await fetch(`${API_BASE_URL}/api/v1/data`, { @@ -625,7 +631,11 @@ export async function fetchLastCase() { msg_id: 30017, message_body: { action: "get_all", - token: token + token: token, + page, + size, + sort_by, + sort_direction } }), headers: { @@ -636,8 +646,9 @@ export async function fetchLastCase() { if (response.ok) { let parsed = await response.json(); - if (parsed) { - return parsed; + + if (parsed.message_code === 0) { + return parsed.message_body; } else { throw null; } @@ -649,18 +660,23 @@ export async function fetchLastCase() { } } -export async function fetchLastComplaint() { +export async function fetchLastComplaint(params: ComplaintsCaseQueryParams) { const token = await getSessionData('token'); + const { page, size, sort_by, sort_direction } = params; try { const response = await fetch(`${API_BASE_URL}/api/v1/data`, { method: 'POST', body: JSON.stringify({ version: 1, - msg_id: 30017, + msg_id: 30013, message_body: { action: "get_all", - token: token + token: token, + page, + size, + sort_by, + sort_direction } }), headers: { @@ -671,8 +687,9 @@ export async function fetchLastComplaint() { if (response.ok) { let parsed = await response.json(); - if (parsed) { - return parsed; + + if (parsed.message_code === 0) { + return parsed.message_body; } else { throw null; } diff --git a/src/app/hooks/react-query/useLastCases.ts b/src/app/hooks/react-query/useLastCases.ts index ce3cc69..9c6364c 100644 --- a/src/app/hooks/react-query/useLastCases.ts +++ b/src/app/hooks/react-query/useLastCases.ts @@ -1,15 +1,36 @@ import { useQuery } from '@tanstack/react-query'; -import { fetchLastCase } from '@/app/actions/violationActions'; +import { fetchLastCase, ComplaintsCaseQueryParams } from '@/app/actions/violationActions'; +interface Case { + amount: number; + content: null | string; + createdAt: string; + description: string; + id: number; + lawyer: null | string; + name: string; + pageNumber: number; + pageSize: number; + priority: "HIGH" | "MEDIUM" | "LOW"; + totalElements: number; + totalPages: number; + type: "ACTIVE" | "INACTIVE" | string; + updatedAt: string; + violationId: number; +} export interface useLastCase { - test: number, + content: Case[], + page: 1, + size: 5, + totalElements: 10, + totalPages: 2, } -export const useLastCases = () => { +export const useLastCases = (params: ComplaintsCaseQueryParams) => { return useQuery({ queryKey: ['lastCases'], queryFn: () => { - return fetchLastCase() + return fetchLastCase(params) }, select: (data: useLastCase | null) => { if (!data) { diff --git a/src/app/hooks/react-query/useLastComplaints.ts b/src/app/hooks/react-query/useLastComplaints.ts index 145deeb..ab6c2cc 100644 --- a/src/app/hooks/react-query/useLastComplaints.ts +++ b/src/app/hooks/react-query/useLastComplaints.ts @@ -1,15 +1,29 @@ import { useQuery } from '@tanstack/react-query'; -import { fetchLastComplaint } from '@/app/actions/violationActions'; +import { fetchLastComplaint, ComplaintsCaseQueryParams } from '@/app/actions/violationActions'; +interface Complaint { + complaint_text: string, + created_at: string, + email: string, + id: number, + not_moderated: boolean, + status: "CREATED" | string, + updated_at: string, + violation_id: number, +} export interface useLastComplaint { - test: number, + content: Complaint[], + page: 1, + size: 5, + totalElements: 10, + totalPages: 2, } -export const useLastComplaints = () => { +export const useLastComplaints = (params: ComplaintsCaseQueryParams) => { return useQuery({ queryKey: ['lastComplaints'], queryFn: () => { - return fetchLastComplaint() + return fetchLastComplaint(params) }, select: (data: useLastComplaint | null) => { if (!data) { diff --git a/src/app/styles/globals.css b/src/app/styles/globals.css index 33e0cfb..cc2f10c 100644 --- a/src/app/styles/globals.css +++ b/src/app/styles/globals.css @@ -52,4 +52,7 @@ path:focus { outline-style: none; } -@import "tailwindcss"; \ No newline at end of file +@import "tailwindcss"; +@import 'swiper/css'; +@import 'swiper/css/navigation'; +@import 'swiper/css/pagination'; \ No newline at end of file diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index 3de19f6..e83c885 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -2624,109 +2624,122 @@ } } - .cases-carousel-track { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 20px; + .case-card { + background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); + border: 2px solid v.$border-color-1; + border-radius: 16px; + padding: 24px; + transition: all 0.3s ease; + cursor: pointer; + position: relative; + overflow: hidden; - @media (max-width: 890px) { - grid-template-columns: auto; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + + &:hover::before { + opacity: 1; } - .case-card { - background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); - border: 2px solid v.$border-color-1; - border-radius: 16px; - padding: 24px; - transition: all 0.3s ease; - cursor: pointer; - position: relative; - overflow: hidden; + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + opacity: 0; + transition: opacity 0.3s ease; + border-radius: 16px 0 0 16px; + } - &:hover::before { - opacity: 1; - } + .case-card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + gap: 12px; + flex-wrap: wrap; - &::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 4px; - height: 100%; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - opacity: 0; - transition: opacity 0.3s ease; - border-radius: 16px 0 0 16px; - } - - .case-card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; - gap: 12px; - - .case-number { - font-weight: 800; - color: #667eea; - font-size: 0.85rem; - } - } - - .case-card-title { - font-size: 0.95rem; - font-weight: 600; - color: #1e293b; - margin-bottom: 16px; - min-height: 50px; - } - - .case-card-meta { - font-size: 0.9rem; - color: #64748b; - line-height: 1.8; - margin-bottom: 16px; - } - - .case-card-actions { - display: flex; - justify-content: flex-end; - } - - .btn-primary-small { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 10px 20px; - border-radius: 10px; - font-weight: 700; + .case-number { + font-weight: 800; + color: #667eea; font-size: 0.85rem; - text-decoration: none; - transition: all 0.3s ease; - display: inline-flex; - align-items: center; - gap: 8px; } + } - .status-assigned { - background: #fef3c7; - color: #d97706; - } + .case-card-content { + font-size: 0.95rem; + font-weight: 600; + color: #1e293b; + margin-bottom: 16px; + min-height: 50px; - .status-submitted { - background: #dbeafe; - color: #1e40af; + span { + color: #64748b; } + } - .case-status-badge { - padding: 4px 10px; - border-radius: 20px; - font-size: 0.7rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.3px; - white-space: nowrap; - } + .case-card-meta { + font-size: 0.9rem; + color: #64748b; + line-height: 1.8; + margin-bottom: 16px; + } + + .case-card-actions { + display: flex; + justify-content: flex-end; + } + + .btn-primary-small { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 10px 20px; + border-radius: 10px; + font-weight: 700; + font-size: 0.85rem; + text-decoration: none; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + gap: 8px; + } + + .status-assigned { + background: #fef3c7; + color: #d97706; + } + + .status-submitted { + background: #dbeafe; + color: #1e40af; + } + + .case-status-badge { + padding: 4px 10px; + border-radius: 20px; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.3px; + white-space: nowrap; + } + } + + .cases-carousel-track { + position: relative; + +/* .swiper-wrapper { + align-items: stretch; + } */ + + .swiper-slide { + height: auto; + display: flex; } } } diff --git a/src/app/styles/reports.scss b/src/app/styles/reports.scss index 2f88c3d..439891a 100644 --- a/src/app/styles/reports.scss +++ b/src/app/styles/reports.scss @@ -72,7 +72,7 @@ .analytics-grid { display: flex; gap: 30px; - margin-bottom: 40px; + margin-bottom: 10px; justify-content: space-between; @media (max-width: 970px) { diff --git a/src/app/styles/violation-details.scss b/src/app/styles/violation-details.scss index bb910ce..cdaf1e3 100644 --- a/src/app/styles/violation-details.scss +++ b/src/app/styles/violation-details.scss @@ -420,12 +420,16 @@ } &-title { - /* border-bottom: 1px solid v.$border-color-1; */ font-size: 18px; justify-content: space-between; align-items: center; margin-bottom: 5px; display: flex; + + &.not-selected-match { + height: 100%; + justify-content: center; + } } &-image-wrapper { 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 5bfaf0c..3717681 100644 --- a/src/app/ui/file-page/violation-table/case-complaint.tsx +++ b/src/app/ui/file-page/violation-table/case-complaint.tsx @@ -266,7 +266,7 @@ export default function CaseComplaint({ selectedViolation, updateStatusHandler } return (
- {complainInfo?.body.content ? ( + {complainInfo?.body?.content ? ( <> {complainInfo.body.content.map((item: complaintInfo) => { return ( diff --git a/src/app/ui/file-page/violation-table/file-page-violation-epmty-screen.tsx b/src/app/ui/file-page/violation-table/file-page-violation-epmty-screen.tsx index 0fc8de9..0c6e583 100644 --- a/src/app/ui/file-page/violation-table/file-page-violation-epmty-screen.tsx +++ b/src/app/ui/file-page/violation-table/file-page-violation-epmty-screen.tsx @@ -10,7 +10,7 @@ export default function FilePageViolationEpmtyScreen() { className="violation-info" >

{t('to-view-a-file-match-click-on-the-card-from-the-list')}

diff --git a/src/app/ui/violations/violations-my-cases.tsx b/src/app/ui/violations/violations-my-cases.tsx deleted file mode 100644 index cc53520..0000000 --- a/src/app/ui/violations/violations-my-cases.tsx +++ /dev/null @@ -1,72 +0,0 @@ -export default function ViolationsMyCases() { - return ( -
-
-
⚖️ Мои дела
- Все дела → -
- -
-
-
-
№ARB-2025-000271
- ПОДАНО
- -
- Нарушение авторских прав: scale_1200 (2).jpeg
- -
-
Создано: 12.10.2025
-
Юрист: Не назначен
-
- - -
-
-
-
№ARB-2025-4944
- НАЗНАЧЕН ЮРИСТ
- -
- Нарушение авторских прав: scale_1200 (2).jpeg
- -
-
Создано: 23.09.2025
-
Юрист: Анна Иванова
-
Ущерб: ₽0
-
- - -
-
-
-
№ARB-2025-0535
- НАЗНАЧЕН ЮРИСТ
- -
- Нарушение авторских прав: scale_1200 (2).jpeg
- -
-
Создано: 22.09.2025
-
Юрист: Анна Иванова
-
Ущерб: ₽100 000
-
- - -
-
-
- ) -} \ No newline at end of file diff --git a/src/app/ui/violations/violations-my-claims.tsx b/src/app/ui/violations/violations-my-claims.tsx new file mode 100644 index 0000000..0e70c9b --- /dev/null +++ b/src/app/ui/violations/violations-my-claims.tsx @@ -0,0 +1,133 @@ +'use client' + +import { useLastCases } from '@/app/hooks/react-query/useLastCases'; +import { useEffect, useMemo, useState } from 'react'; +import { formatDate, formatDateTime } from '@/app/lib/formatDate'; +import { useTranslations } from 'next-intl'; + +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation, Pagination } from 'swiper/modules'; + +export default function ViolationsMyClaims() { + const t = useTranslations('Global'); + const { data: dataCases } = useLastCases({ + page: 1, + size: 5, + sort_by: 'createdAt', + sort_direction: 'desc' + }); + + const MAX_VISIBLE_ITEMS = 5; + + const getSlidesPerView = () => { + if (!dataCases || !dataCases.content) return 1; + return Math.min(MAX_VISIBLE_ITEMS, dataCases.content.length); + }; + + const slidesPerViewCount = getSlidesPerView(); + + const swiperSettings = { + modules: [Navigation, Pagination], + breakpoints: { + 0: { + slidesPerView: Math.min(slidesPerViewCount, 1), + spaceBetween: 20, + }, + 768: { + slidesPerView: Math.min(slidesPerViewCount, 2), + spaceBetween: 30, + }, + 1024: { + slidesPerView: Math.min(slidesPerViewCount, 3), + spaceBetween: 30, + }, + 1792: { + slidesPerView: slidesPerViewCount, + spaceBetween: 30, + } + }, + loop: dataCases ? dataCases.content.length > 3 : false, + /* navigation: true, */ + /* pagination: { clickable: true }, */ + }; + + if (!dataCases) { + return ( +
+
+
+ {t('recent-claims')} +
+
+
+
+
+
+
+
+ ); + } + + return ( +
+
+
+ {t('recent-claims')} +
+
+
+ {dataCases?.content.length === 0 ? ( +
+ {t('no-claims')} +
+ ) : ( + + {dataCases?.content.map((item) => { + return ( + +
+
+
+ {item.name} +
+ + {item.type} + +
+ +
+ {t('text-of-the-case')}: +
+ + {item.description} + +
+ +
+
{t('date-of-creation')}: {item.createdAt ? `${formatDate(item.createdAt)} ${formatDateTime(item.createdAt)}` : '---'}
+
{t('date-of-update')}: {item.updatedAt ? `${formatDate(item.updatedAt)} ${formatDateTime(item.updatedAt)}` : '---'}
+
{t('lawyer')}: + {item.lawyer ? item.lawyer : t('not-assigned')} +
+
+
+
+ ) + })} +
+ )} +
+
+ ) +} \ No newline at end of file diff --git a/src/app/ui/violations/violations-my-complaint.tsx b/src/app/ui/violations/violations-my-complaint.tsx new file mode 100644 index 0000000..cbd5029 --- /dev/null +++ b/src/app/ui/violations/violations-my-complaint.tsx @@ -0,0 +1,130 @@ +'use client'; + +import { useLastComplaints } from '@/app/hooks/react-query/useLastComplaints'; +import { formatDate, formatDateTime } from '@/app/lib/formatDate'; +import { useTranslations } from 'next-intl'; + +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation, Pagination } from 'swiper/modules'; + +export default function ViolationsMyComplaint() { + const t = useTranslations('Global'); + const { data: dataComplaint } = useLastComplaints({ + page: 1, + size: 5, + sort_by: 'createdAt', + sort_direction: 'desc' + }); + + const MAX_VISIBLE_ITEMS = 5; + + const getSlidesPerView = () => { + if (!dataComplaint || !dataComplaint.content) return 1; + return Math.min(MAX_VISIBLE_ITEMS, dataComplaint.content.length); + }; + + const slidesPerViewCount = getSlidesPerView(); + + const swiperSettings = { + modules: [Navigation, Pagination], + breakpoints: { + 0: { + slidesPerView: Math.min(slidesPerViewCount, 1), + spaceBetween: 20, + }, + 768: { + slidesPerView: Math.min(slidesPerViewCount, 2), + spaceBetween: 30, + }, + 1024: { + slidesPerView: Math.min(slidesPerViewCount, 3), + spaceBetween: 30, + }, + 1792: { + slidesPerView: slidesPerViewCount, + spaceBetween: 30, + } + }, + /* navigation: true, */ + /* pagination: { clickable: true }, */ + loop: dataComplaint ? dataComplaint.content.length > 3 : false, + }; + + if (!dataComplaint) { + return ( +
+
+
+ {t('recent-complaints')} +
+
+
+
+
+
+
+
+ ); + } + + return ( +
+
+
+ {t('recent-complaints')} +
+
+
+ {dataComplaint?.content.length === 0 ? ( +
+ {t('no-complaints')} +
+ ) : ( + + {dataComplaint?.content.map((item) => { + return ( + +
+
+
+ COM-{item.id} +
+ + {item.status} + +
+ +
+ {t('text-of-the-complaint')}: +
+ + {item.complaint_text} + +
+ +
+
+ {t('date-of-creation')}: {item.created_at ? `${formatDate(item.created_at)} ${formatDateTime(item.created_at)}` : '---'} +
+
+ {t('date-of-update')}: {item.updated_at ? `${formatDate(item.updated_at)} ${formatDateTime(item.updated_at)}` : '---'} +
+
+
+
+ ); + })} +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 3a0aca1..9d60125 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -429,7 +429,11 @@ "file-a-claim": "File a claim", "file-a-complaint": "File a complaint", "you-can": "You can", - "complaint": "Complaint" + "complaint": "Complaint", + "recent-complaints": "Recent complaints", + "recent-claims": "Recent claims", + "no-complaints": "No complaints", + "no-claims": "No claims" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 1cd4917..d5f29a0 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -407,8 +407,8 @@ "description": "Описание", "priority": "Приоритет", "type": "Тип", - "lawyer": "Адвокат", - "not-assigned": "Не назначено", + "lawyer": "Юрист", + "not-assigned": "Не назначен", "case": "Претензия", "create-case": "Создать претензию", "case-name": "Название претензии", @@ -429,7 +429,11 @@ "file-a-claim": "Подать претензию", "file-a-complaint": "Подать жалобу", "you-can": "Вы можете", - "complaint": "Жалоба" + "complaint": "Жалоба", + "recent-complaints": "Недавние жалобы", + "recent-claims": "Недавние претензии", + "no-complaints": "Жалоб нет", + "no-claims": "Претензии нет" }, "Login-register-form": { "and": "и",