combine complaint and cases

This commit is contained in:
smanylov
2026-04-21 18:55:27 +07:00
parent 0ad1ce98ad
commit f8cf3618f7
5 changed files with 290 additions and 37 deletions
+4 -2
View File
@@ -6,6 +6,7 @@ import ViolationsStatistic from '@/app/ui/violations/violations-statistic';
import AnalyticsCardGeography from '@/app/ui/reports/analytics-card-geography'; import AnalyticsCardGeography from '@/app/ui/reports/analytics-card-geography';
import AnalyticsCardDistribution from '@/app/ui/reports/analytics-card-distribution'; import AnalyticsCardDistribution from '@/app/ui/reports/analytics-card-distribution';
import ViolationsMyClaims from '@/app/ui/violations/violations-my-claims'; import ViolationsMyClaims from '@/app/ui/violations/violations-my-claims';
import ViolationsLastCombinedCases from '@/app/ui/violations/violations-last-combined-cases';
export default function Page() { export default function Page() {
return ( return (
@@ -17,8 +18,9 @@ export default function Page() {
<ViolationsStatistic /> <ViolationsStatistic />
<ViolationsCheckAllSection /> <ViolationsCheckAllSection />
<ViolationsTable /> <ViolationsTable />
<ViolationsMyComplaint /> <ViolationsLastCombinedCases />
<ViolationsMyClaims /> {/* <ViolationsMyComplaint />
<ViolationsMyClaims /> */}
<div className="analytics-grid"> <div className="analytics-grid">
<AnalyticsCardDistribution /> <AnalyticsCardDistribution />
<AnalyticsCardGeography /> <AnalyticsCardGeography />
+35 -29
View File
@@ -2598,30 +2598,9 @@
} }
.my-cases-title { .my-cases-title {
font-size: 1.5rem; font-size: 18px;
font-weight: 800; font-weight: 600;
color: #1e293b; color: v.$text-p;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background-clip: text;
-webkit-text-fill-color: transparent;
}
.btn-outline-small {
background: transparent;
color: #667eea;
border: 2px solid #667eea;
padding: 10px 20px;
border-radius: 10px;
font-weight: 700;
font-size: 0.9rem;
text-decoration: none;
transition: all 0.3s ease;
&:hover {
background: #667eea;
color: white;
transform: translateY(-2px);
}
} }
.case-card { .case-card {
@@ -2650,7 +2629,7 @@
left: 0; left: 0;
width: 4px; width: 4px;
height: 100%; height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: #667eea;
opacity: 0; opacity: 0;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
border-radius: 16px 0 0 16px; border-radius: 16px 0 0 16px;
@@ -2728,20 +2707,47 @@
letter-spacing: 0.3px; letter-spacing: 0.3px;
white-space: nowrap; white-space: nowrap;
} }
a {
color: v.$status-showed;
&:hover {
color: #667eea;
}
}
} }
.cases-carousel-track { .cases-carousel-track {
position: relative; position: relative;
/* .swiper-wrapper {
align-items: stretch;
} */
.swiper-slide { .swiper-slide {
height: auto; height: auto;
display: flex; display: flex;
} }
} }
.my-cases-tabs {
display: flex;
gap: 10px;
}
.tab-button {
cursor: pointer;
padding: 4px 10px;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.3px;
white-space: nowrap;
background: v.$b-color-1;
&.active {
background: #dbeafe;
color: #1e40af;
}
}
} }
.violation-check-all-section { .violation-check-all-section {
@@ -0,0 +1,239 @@
'use client';
import { useLastComplaints } from '@/app/hooks/react-query/useLastComplaints';
import { useLastCases } from '@/app/hooks/react-query/useLastCases';
import { formatDate, formatDateTime } from '@/app/lib/formatDate';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination } from 'swiper/modules';
import { IconEye } from '@/app/ui/icons/icons';
import Link from 'next/link';
type FilterState = {
complaints: boolean;
claims: boolean;
};
export default function ViolationsLastCombinedCases() {
const t = useTranslations('Global');
const [filters, setFilters] = useState<FilterState>({
complaints: true,
claims: true,
});
const { data: dataComplaint } = useLastComplaints({
page: 1,
size: 5,
sort_by: 'createdAt',
sort_direction: 'desc'
});
const { data: dataCases } = useLastCases({
page: 1,
size: 5,
sort_by: 'createdAt',
sort_direction: 'desc'
});
useEffect(() => {
console.log(dataCases);
console.log(dataComplaint);
}, [dataComplaint, dataCases])
const getCombinedItems = () => {
const complaints = (dataComplaint?.content || []).map(item => ({
...item,
type: 'complaint' as const,
displayId: `COM-${item.id}`,
title: item.complaint_text,
status: item.status,
createdAt: item.created_at,
updatedAt: item.updated_at,
lawyer: null,
}));
const claims = (dataCases?.content || []).map(item => ({
...item,
type: 'claim' as const,
displayId: item.name,
title: item.description,
status: item.type,
createdAt: item.createdAt,
updatedAt: item.updatedAt,
lawyer: item.lawyer,
}));
let combined = [...complaints, ...claims];
combined.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
return combined;
};
const getFilteredItems = () => {
const allItems = getCombinedItems();
return allItems.filter(item => {
if (item.type === 'complaint') return filters.complaints;
if (item.type === 'claim') return filters.claims;
return false;
});
};
const toggleFilter = (filterType: 'complaints' | 'claims') => {
if (filters.claims && !filters.complaints && filterType === 'claims') {
return
}
if (!filters.claims && filters.complaints && filterType === 'complaints') {
return
}
setFilters(prev => ({
...prev,
[filterType]: !prev[filterType]
}));
};
const filteredItems = getFilteredItems();
const MAX_VISIBLE_ITEMS = 5;
const getSlidesPerView = () => {
if (!filteredItems || filteredItems.length === 0) return 1;
return Math.min(MAX_VISIBLE_ITEMS, filteredItems.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: filteredItems.length > 3,
};
const isLoading = (!dataComplaint || !dataCases);
if (isLoading) {
return (
<div className="violations-my-cases-section">
<div className="my-cases-header">
<h3 className="my-cases-title">
{t('recent-cases')}
</h3>
<div className="my-cases-tabs">
<button className="tab-button tab-button-complaints">
{t('complaints')}
</button>
<button className="tab-button tab-button-claims">
{t('claims')}
</button>
</div>
</div>
<div className="cases-carousel-track">
<div className="loading-animation">
<div className="global-spinner"></div>
</div>
</div>
</div>
);
}
const isAllSelected = filters.complaints && filters.claims;
const isNothingSelected = !filters.complaints && !filters.claims;
return (
<div className="violations-my-cases-section">
<div className="my-cases-header">
<h3 className="my-cases-title">
{t('recent-cases')}
</h3>
<div className="my-cases-tabs">
<button
className={`tab-button tab-button-complaints ${filters.complaints ? 'active' : ''}`}
onClick={() => toggleFilter('complaints')}
>
{t('complaints')}
</button>
<button
className={`tab-button tab-button-claims ${filters.claims ? 'active' : ''}`}
onClick={() => toggleFilter('claims')}
>
{t('claims')}
</button>
</div>
</div>
<div className="cases-carousel-track">
{isNothingSelected ? (
<div className="empty-state">
{t('select-at-least-one-filter')}
</div>
) : filteredItems.length === 0 ? (
<div className="empty-state">
{t('no-items-found')}
</div>
) : (
<Swiper {...swiperSettings}>
{filteredItems.map((item) => (
<SwiperSlide key={`${item.type}-${item.id}`}>
<div className="case-card">
<div className="case-card-header">
<div className="case-number">
{item.displayId}
</div>
<span className="case-status-badge status-submitted">
{item.status}
</span>
</div>
<div className="case-card-meta">
<div>
<strong>{t('date-of-creation')}:</strong> {item.createdAt ? `${formatDate(item.createdAt)} ${formatDateTime(item.createdAt)}` : '---'}
</div>
<div>
<strong>{t('date-of-update')}:</strong> {item.updatedAt ? `${formatDate(item.updatedAt)} ${formatDateTime(item.updatedAt)}` : '---'}
</div>
{item.type === 'claim' && (
<div>
<strong>{t('lawyer')}: </strong>
<span style={{ color: '#f59e0b' }}>{item.lawyer ? item.lawyer : t('not-assigned')}</span>
</div>
)}
</div>
<Link
href={`/pages/file/${item.displayId}`}
className="mt-auto"
title={t('view')}
onClick={(e) => {
e.preventDefault();
}}
>
<IconEye />
</Link>
</div>
</SwiperSlide>
))}
</Swiper>
)}
</div>
</div>
);
}
+6 -4
View File
@@ -441,7 +441,11 @@
"week": "week", "week": "week",
"month": "month", "month": "month",
"every": "every", "every": "every",
"country": "Country" "country": "Country",
"claims": "Claims",
"complaints": "Complaints",
"recent-cases": "Recent cases",
"select-at-least-one-filter": "Select at least one filter"
}, },
"Login-register-form": { "Login-register-form": {
"and": "and", "and": "and",
@@ -582,7 +586,5 @@
"user-verification": "User verification", "user-verification": "User verification",
"Status": "Status" "Status": "Status"
}, },
"Countryes": { "Countryes": {}
}
} }
+5 -1
View File
@@ -441,7 +441,11 @@
"week_accusative": "неделю", "week_accusative": "неделю",
"month": "месяц", "month": "месяц",
"every": "каждый", "every": "каждый",
"country": "Страна" "country": "Страна",
"claims": "Претензии",
"complaints": "Жалобы",
"recent-cases": "Недавние дела",
"select-at-least-one-filter": "Выберите хотя бы один фильтр"
}, },
"Login-register-form": { "Login-register-form": {
"and": "и", "and": "и",