Files
no-copy-frontend/src/app/ui/violations/tables/violations-my-claims-table.tsx
T

343 lines
9.2 KiB
TypeScript

'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>
);
}