add two tanstack tables for claims and complaints
This commit is contained in:
@@ -7,6 +7,8 @@ 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';
|
import ViolationsLastCombinedCases from '@/app/ui/violations/violations-last-combined-cases';
|
||||||
|
import { ViolationsMyComplaintsTable } from '@/app/ui/violations/tables/violations-my-complaints-table';
|
||||||
|
import { ViolationsMyClaimsTable } from '@/app/ui/violations/tables/violations-my-claims-table';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
@@ -25,7 +27,10 @@ export default function Page() {
|
|||||||
<AnalyticsCardDistribution />
|
<AnalyticsCardDistribution />
|
||||||
<AnalyticsCardGeography />
|
<AnalyticsCardGeography />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="analytics-grid">
|
||||||
|
<ViolationsMyClaimsTable />
|
||||||
|
<ViolationsMyComplaintsTable />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { fetchLastCase, ComplaintsCaseQueryParams } from '@/app/actions/violationActions';
|
import { fetchLastCase, ComplaintsCaseQueryParams } from '@/app/actions/violationActions';
|
||||||
|
|
||||||
interface Case {
|
export interface Case {
|
||||||
amount: number;
|
amount: number;
|
||||||
content: null | string;
|
content: null | string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
@@ -21,15 +21,15 @@ interface Case {
|
|||||||
}
|
}
|
||||||
export interface useLastCase {
|
export interface useLastCase {
|
||||||
content: Case[],
|
content: Case[],
|
||||||
page: 1,
|
page: number,
|
||||||
size: 5,
|
size: number,
|
||||||
totalElements: 10,
|
totalElements: number,
|
||||||
totalPages: 2,
|
totalPages: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLastCases = (params: ComplaintsCaseQueryParams) => {
|
export const useLastCases = (params: ComplaintsCaseQueryParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['lastCases'],
|
queryKey: ['lastCases', params.page, params.size, params.sort_by, params.sort_direction],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return fetchLastCase(params)
|
return fetchLastCase(params)
|
||||||
},
|
},
|
||||||
@@ -39,6 +39,7 @@ export const useLastCases = (params: ComplaintsCaseQueryParams) => {
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
placeholderData: (previousData) => previousData,
|
||||||
retry: false
|
retry: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { fetchLastComplaint, ComplaintsCaseQueryParams } from '@/app/actions/violationActions';
|
import { fetchLastComplaint, ComplaintsCaseQueryParams } from '@/app/actions/violationActions';
|
||||||
|
|
||||||
interface Complaint {
|
export interface Complaint {
|
||||||
complaint_text: string;
|
complaint_text: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -14,15 +14,15 @@ interface Complaint {
|
|||||||
}
|
}
|
||||||
export interface useLastComplaint {
|
export interface useLastComplaint {
|
||||||
content: Complaint[],
|
content: Complaint[],
|
||||||
page: 1,
|
page: number,
|
||||||
size: 5,
|
size: number,
|
||||||
totalElements: 10,
|
totalElements: number,
|
||||||
totalPages: 2,
|
totalPages: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLastComplaints = (params: ComplaintsCaseQueryParams) => {
|
export const useLastComplaints = (params: ComplaintsCaseQueryParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['lastComplaints'],
|
queryKey: ['lastComplaints', params.page, params.size, params.sort_by, params.sort_direction],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return fetchLastComplaint(params)
|
return fetchLastComplaint(params)
|
||||||
},
|
},
|
||||||
@@ -32,6 +32,7 @@ export const useLastComplaints = (params: ComplaintsCaseQueryParams) => {
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
placeholderData: (previousData) => previousData,
|
||||||
retry: false
|
retry: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useMemo, useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
useReactTable,
|
||||||
|
getCoreRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
ColumnDef,
|
||||||
|
SortingState,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
import { IconArrowUp, IconArrowDown, IconFilter, IconArrowRight, IconArrowLeft, IconDoubleArrowLeft, IconDoubleArrowRight, IconEye } from '@/app/ui/icons/icons';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { useLastCases, Case } from '@/app/hooks/react-query/useLastCases';
|
||||||
|
import { formatDate, formatDateTime } from '@/app/lib/formatDate';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function ViolationsMyClaimsTable() {
|
||||||
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
const [pagination, setPagination] = useState({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 5,
|
||||||
|
});
|
||||||
|
const t = useTranslations("Global");
|
||||||
|
|
||||||
|
const getSortParams: {
|
||||||
|
sort_by: string;
|
||||||
|
sort_direction: 'asc' | 'desc'
|
||||||
|
} = useMemo(() => {
|
||||||
|
if (sorting.length === 0) {
|
||||||
|
return { sort_by: 'createdAt', sort_direction: 'desc' as const };
|
||||||
|
}
|
||||||
|
|
||||||
|
const sort = sorting[0];
|
||||||
|
const sortByMap: Record<string, string> = {
|
||||||
|
'caseName': 'name',
|
||||||
|
'createdAt': 'created_at',
|
||||||
|
'updatedAt': 'updated_at',
|
||||||
|
'status': 'type'
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sort_by: sortByMap[sort.id] || sort.id,
|
||||||
|
sort_direction: sort.desc ? 'desc' : 'asc' as const
|
||||||
|
};
|
||||||
|
}, [sorting]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: apiResponse,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
} = useLastCases({
|
||||||
|
page: pagination.pageIndex + 1,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
sort_by: getSortParams.sort_by,
|
||||||
|
sort_direction: getSortParams.sort_direction ? getSortParams.sort_direction : 'desc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableData = apiResponse?.content || [];
|
||||||
|
const totalItems = apiResponse?.totalElements || 0;
|
||||||
|
const totalPages = apiResponse?.totalPages || 0;
|
||||||
|
|
||||||
|
const columns = useMemo<ColumnDef<Case>[]>(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
accessorKey: 'caseName',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column start">
|
||||||
|
<span>{t('case-name')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="table-item" title={row.original.name}>
|
||||||
|
<span className="font-medium">
|
||||||
|
{row.original.name.length > 50
|
||||||
|
? `${row.original.name.substring(0, 50)}...`
|
||||||
|
: row.original.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'createdAt',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('date-of-creation')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-center table-item">
|
||||||
|
{formatDate(new Date(row.original.created_at).getTime())}
|
||||||
|
<br />
|
||||||
|
{formatDateTime(new Date(row.original.created_at).getTime())}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'updatedAt',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('date-of-update')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-center table-item">
|
||||||
|
{formatDate(new Date(row.original.updated_at).getTime())}
|
||||||
|
<br />
|
||||||
|
{formatDateTime(new Date(row.original.updated_at).getTime())}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('status')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const status = row.original.type;
|
||||||
|
const statusColor = status === 'ACTIVE' ? 'text-green-600' : 'text-gray-500';
|
||||||
|
return (
|
||||||
|
<div className={`text-center table-item font-semibold ${statusColor}`}>
|
||||||
|
{t(status.toLowerCase())}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: () => <div className="column">{t('actions')}</div>,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="actions">
|
||||||
|
<div className="actions-group">
|
||||||
|
<Link
|
||||||
|
href={`/pages/file/${row.original.file_id}?violationId=${row.original.violation_id}&caseType=claim`}
|
||||||
|
className="bg-violet-500 hover:bg-violet-600"
|
||||||
|
title={t('view')}
|
||||||
|
>
|
||||||
|
<IconEye />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: tableData,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
pageCount: totalPages,
|
||||||
|
manualPagination: true,
|
||||||
|
manualSorting: true,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
onSortingChange: (updater) => {
|
||||||
|
const newSorting = typeof updater === 'function' ? updater(sorting) : updater;
|
||||||
|
setSorting(newSorting);
|
||||||
|
setPagination(prev => ({ ...prev, pageIndex: 0 }));
|
||||||
|
},
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tanstak-table-wrapper block-wrapper">
|
||||||
|
<h3 className="tanstak-table-title">{t('claims')}</h3>
|
||||||
|
|
||||||
|
<div className="tanstak-table-block">
|
||||||
|
<table className="tanstak-table">
|
||||||
|
<thead className="tanstak-table-head">
|
||||||
|
{table.getHeaderGroups().map(headerGroup => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map(header => (
|
||||||
|
<th key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: typeof header.column.columnDef.header === 'function'
|
||||||
|
? header.column.columnDef.header(header.getContext())
|
||||||
|
: header.column.columnDef.header as string}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className={`tanstak-table-body ${isFetching ? 'loading' : ''}`}>
|
||||||
|
{table.getRowModel().rows.length > 0 ? (
|
||||||
|
table.getRowModel().rows.map(row => (
|
||||||
|
<tr key={row.id}>
|
||||||
|
{row.getVisibleCells().map(cell => (
|
||||||
|
<td key={cell.id}>
|
||||||
|
{typeof cell.column.columnDef.cell === 'function'
|
||||||
|
? cell.column.columnDef.cell(cell.getContext())
|
||||||
|
: cell.getValue() as string}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={columns.length} className="text-center">
|
||||||
|
{t('no-data')}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<div className="tanstak-table-pagination">
|
||||||
|
<div className="pagination-info">
|
||||||
|
<span className="pagination-info-pages">
|
||||||
|
{t('page')}{' '}
|
||||||
|
<strong>
|
||||||
|
{pagination.pageIndex + 1} {t('out-of')} {totalPages || 1}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
<span className="pagination-info-files">
|
||||||
|
| {t('shown')} {tableData.length} {t('out-of')} {totalItems}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pagination-controls">
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.firstPage()}
|
||||||
|
disabled={!table.getCanPreviousPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconDoubleArrowLeft />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconArrowLeft />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="pagination-controls-pages">
|
||||||
|
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||||
|
let pageIndex;
|
||||||
|
if (totalPages <= 5) {
|
||||||
|
pageIndex = i;
|
||||||
|
} else if (pagination.pageIndex <= 2) {
|
||||||
|
pageIndex = i;
|
||||||
|
} else if (pagination.pageIndex >= totalPages - 3) {
|
||||||
|
pageIndex = totalPages - 5 + i;
|
||||||
|
} else {
|
||||||
|
pageIndex = pagination.pageIndex - 2 + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageIndex >= 0 && pageIndex < totalPages) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={pageIndex}
|
||||||
|
className={pagination.pageIndex === pageIndex ? 'current' : 'other'}
|
||||||
|
onClick={() => table.setPageIndex(pageIndex)}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{pageIndex + 1}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconArrowRight />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.lastPage()}
|
||||||
|
disabled={!table.getCanNextPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconDoubleArrowRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useLastComplaints, Complaint } from '@/app/hooks/react-query/useLastComplaints';
|
||||||
|
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
useReactTable,
|
||||||
|
getCoreRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
ColumnDef,
|
||||||
|
SortingState,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
import { IconArrowUp, IconArrowDown, IconFilter, IconArrowRight, IconArrowLeft, IconDoubleArrowLeft, IconDoubleArrowRight, IconEye } from '@/app/ui/icons/icons';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { formatDate, formatDateTime } from '@/app/lib/formatDate';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function ViolationsMyComplaintsTable() {
|
||||||
|
|
||||||
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
const [pagination, setPagination] = useState({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 5,
|
||||||
|
});
|
||||||
|
const t = useTranslations("Global");
|
||||||
|
|
||||||
|
const getSortParams: {
|
||||||
|
sort_by: string;
|
||||||
|
sort_direction: 'asc' | 'desc'
|
||||||
|
} = useMemo(() => {
|
||||||
|
if (sorting.length === 0) {
|
||||||
|
return { sort_by: 'createdAt', sort_direction: 'desc' as const };
|
||||||
|
}
|
||||||
|
|
||||||
|
const sort = sorting[0];
|
||||||
|
const sortByMap: Record<string, string> = {
|
||||||
|
'caseName': 'name',
|
||||||
|
'createdAt': 'created_at',
|
||||||
|
'updatedAt': 'updated_at',
|
||||||
|
'status': 'type'
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sort_by: sortByMap[sort.id] || sort.id,
|
||||||
|
sort_direction: sort.desc ? 'desc' : 'asc' as const
|
||||||
|
};
|
||||||
|
}, [sorting]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: apiResponse,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
} = useLastComplaints({
|
||||||
|
page: pagination.pageIndex + 1,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
sort_by: getSortParams.sort_by,
|
||||||
|
sort_direction: getSortParams.sort_direction ? getSortParams.sort_direction : 'desc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableData = apiResponse?.content || [];
|
||||||
|
const totalItems = apiResponse?.totalElements || 0;
|
||||||
|
const totalPages = apiResponse?.totalPages || 0;
|
||||||
|
|
||||||
|
const columns = useMemo<ColumnDef<Complaint>[]>(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
accessorKey: 'caseName',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column start">
|
||||||
|
<span>{t('complaint-name')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="table-item" title={`COM-${row.original.id}`}>
|
||||||
|
<span className="font-medium">
|
||||||
|
{`COM-${row.original.id}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'createdAt',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('date-of-creation')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-center table-item">
|
||||||
|
{formatDate(new Date(row.original.created_at).getTime())}
|
||||||
|
<br />
|
||||||
|
{formatDateTime(new Date(row.original.created_at).getTime())}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'updatedAt',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('date-of-update')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-center table-item">
|
||||||
|
{formatDate(new Date(row.original.updated_at).getTime())}
|
||||||
|
<br />
|
||||||
|
{formatDateTime(new Date(row.original.updated_at).getTime())}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div className="column">
|
||||||
|
<span>{t('status')}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
className="sort-button hidden"
|
||||||
|
>
|
||||||
|
{column.getIsSorted() === 'asc' ? (
|
||||||
|
<IconArrowUp />
|
||||||
|
) : column.getIsSorted() === 'desc' ? (
|
||||||
|
<IconArrowDown />
|
||||||
|
) : (
|
||||||
|
<IconFilter />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const status = row.original.status;
|
||||||
|
const statusColor = status === 'ACTIVE' ? 'text-green-600' : 'text-gray-500';
|
||||||
|
return (
|
||||||
|
<div className={`text-center table-item font-semibold ${statusColor}`}>
|
||||||
|
{/* {t(status.toLowerCase())} */}
|
||||||
|
{status}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: () => <div className="column">{t('actions')}</div>,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="actions">
|
||||||
|
<div className="actions-group">
|
||||||
|
<Link
|
||||||
|
href={`/pages/file/${row.original.file_id}?violationId=${row.original.violation_id}&caseType=complaint`}
|
||||||
|
className="bg-violet-500 hover:bg-violet-600"
|
||||||
|
title={t('view')}
|
||||||
|
>
|
||||||
|
<IconEye />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: tableData,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
pageCount: totalPages,
|
||||||
|
manualPagination: true,
|
||||||
|
manualSorting: true,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
onSortingChange: (updater) => {
|
||||||
|
const newSorting = typeof updater === 'function' ? updater(sorting) : updater;
|
||||||
|
setSorting(newSorting);
|
||||||
|
setPagination(prev => ({ ...prev, pageIndex: 0 }));
|
||||||
|
},
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tanstak-table-wrapper block-wrapper">
|
||||||
|
<h3 className="tanstak-table-title">{t('complaints')}</h3>
|
||||||
|
|
||||||
|
<div className="tanstak-table-block">
|
||||||
|
<table className="tanstak-table">
|
||||||
|
<thead className="tanstak-table-head">
|
||||||
|
{table.getHeaderGroups().map(headerGroup => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map(header => (
|
||||||
|
<th key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: typeof header.column.columnDef.header === 'function'
|
||||||
|
? header.column.columnDef.header(header.getContext())
|
||||||
|
: header.column.columnDef.header as string}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className={`tanstak-table-body ${isFetching ? 'loading' : ''}`}>
|
||||||
|
{table.getRowModel().rows.length > 0 ? (
|
||||||
|
table.getRowModel().rows.map(row => (
|
||||||
|
<tr key={row.id}>
|
||||||
|
{row.getVisibleCells().map(cell => (
|
||||||
|
<td key={cell.id}>
|
||||||
|
{typeof cell.column.columnDef.cell === 'function'
|
||||||
|
? cell.column.columnDef.cell(cell.getContext())
|
||||||
|
: cell.getValue() as string}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={columns.length} className="text-center">
|
||||||
|
{t('no-data')}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<div className="tanstak-table-pagination">
|
||||||
|
<div className="pagination-info">
|
||||||
|
<span className="pagination-info-pages">
|
||||||
|
{t('page')}{' '}
|
||||||
|
<strong>
|
||||||
|
{pagination.pageIndex + 1} {t('out-of')} {totalPages || 1}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
<span className="pagination-info-files">
|
||||||
|
| {t('shown')} {tableData.length} {t('out-of')} {totalItems}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pagination-controls">
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.firstPage()}
|
||||||
|
disabled={!table.getCanPreviousPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconDoubleArrowLeft />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconArrowLeft />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="pagination-controls-pages">
|
||||||
|
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||||
|
let pageIndex;
|
||||||
|
if (totalPages <= 5) {
|
||||||
|
pageIndex = i;
|
||||||
|
} else if (pagination.pageIndex <= 2) {
|
||||||
|
pageIndex = i;
|
||||||
|
} else if (pagination.pageIndex >= totalPages - 3) {
|
||||||
|
pageIndex = totalPages - 5 + i;
|
||||||
|
} else {
|
||||||
|
pageIndex = pagination.pageIndex - 2 + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageIndex >= 0 && pageIndex < totalPages) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={pageIndex}
|
||||||
|
className={pagination.pageIndex === pageIndex ? 'current' : 'other'}
|
||||||
|
onClick={() => table.setPageIndex(pageIndex)}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{pageIndex + 1}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconArrowRight />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="arrow"
|
||||||
|
onClick={() => table.lastPage()}
|
||||||
|
disabled={!table.getCanNextPage() || isLoading}
|
||||||
|
>
|
||||||
|
<IconDoubleArrowRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -431,6 +431,7 @@
|
|||||||
"file-a-complaint": "File a complaint",
|
"file-a-complaint": "File a complaint",
|
||||||
"you-can": "You can",
|
"you-can": "You can",
|
||||||
"complaint": "Complaint",
|
"complaint": "Complaint",
|
||||||
|
"complaint-name": "Complaint name",
|
||||||
"recent-complaints": "Recent complaints",
|
"recent-complaints": "Recent complaints",
|
||||||
"recent-claims": "Recent claims",
|
"recent-claims": "Recent claims",
|
||||||
"no-complaints": "No complaints",
|
"no-complaints": "No complaints",
|
||||||
|
|||||||
@@ -431,6 +431,7 @@
|
|||||||
"file-a-complaint": "Подать жалобу",
|
"file-a-complaint": "Подать жалобу",
|
||||||
"you-can": "Вы можете",
|
"you-can": "Вы можете",
|
||||||
"complaint": "Жалоба",
|
"complaint": "Жалоба",
|
||||||
|
"complaint-name": "Название жалобы",
|
||||||
"recent-complaints": "Недавние жалобы",
|
"recent-complaints": "Недавние жалобы",
|
||||||
"recent-claims": "Недавние претензии",
|
"recent-claims": "Недавние претензии",
|
||||||
"no-complaints": "Жалоб нет",
|
"no-complaints": "Жалоб нет",
|
||||||
|
|||||||
Reference in New Issue
Block a user