Skip to content

Commit e88ed3d

Browse files
committed
feat: Added table sorting and contract number + date columns
1 parent e4c3bd7 commit e88ed3d

File tree

3 files changed

+152
-7
lines changed

3 files changed

+152
-7
lines changed

admission-web/src/components/pages/admin/edit-entrant/components/ContractForm.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { isUniquePriorities } from '@/lib/utils/isUnique';
5252
import { useCommonToast } from '@/components/ui/toast/use-common-toast';
5353
import { Specialty } from '$/utils/src/enums/SpecialtyEnum';
5454
import { PaymentType } from '$/utils/src/enums/PaymentTypeEnum';
55+
import { useQueryClient } from '@tanstack/react-query';
5556

5657
interface ContractFormProps {
5758
data: DocumentsApiBody;
@@ -69,6 +70,7 @@ export const ContractForm: FC<ContractFormProps> = ({
6970
entrantLastName,
7071
}) => {
7172
const { toastSuccess, toastError } = useCommonToast();
73+
const queryClient = useQueryClient();
7274

7375
const form = useForm<TAdminDocumentsSchema>({
7476
resolver: zodResolver(AdminDocumentsSchema),
@@ -107,6 +109,9 @@ export const ContractForm: FC<ContractFormProps> = ({
107109
},
108110
data.id
109111
);
112+
queryClient.invalidateQueries({
113+
queryKey: ['all-entrants'],
114+
});
110115
toastSuccess(`Зміни збережено!`);
111116
} catch {
112117
toastError('Щось пішло не так!');

admission-web/src/components/pages/admin/entrants/components/EntrantsColumns.tsx

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,20 @@ export const EntrantsColumns: ColumnDef<AdminUser>[] = [
1818
},
1919
},
2020
{
21-
accessorKey: 'contracts',
21+
accessorKey: 'degree',
2222
header: 'Освітній ступінь',
23+
sortingFn: (rowA, rowB) => {
24+
const getEntrantDegree = ({ contracts }: AdminUser) => {
25+
if (contracts.length === 0) return '-';
26+
return educationalDegreeLabels[
27+
contracts[0].degree as EducationalDegree
28+
];
29+
};
30+
31+
return getEntrantDegree(rowA.original).localeCompare(
32+
getEntrantDegree(rowB.original)
33+
);
34+
},
2335
cell: ({ row }) => {
2436
return row.original.contracts.length
2537
? educationalDegreeLabels[
@@ -31,6 +43,16 @@ export const EntrantsColumns: ColumnDef<AdminUser>[] = [
3143
{
3244
accessorKey: 'fundingSource',
3345
header: 'Форма фінансування',
46+
sortingFn: (rowA, rowB) => {
47+
const getEntrantFundingSource = ({ contracts }: AdminUser) => {
48+
if (contracts.length === 0) return '-';
49+
return FundingSourceLabels[contracts[0].fundingSource as FundingSource];
50+
};
51+
52+
return getEntrantFundingSource(rowA.original).localeCompare(
53+
getEntrantFundingSource(rowB.original)
54+
);
55+
},
3456
cell: ({ row }) => {
3557
return row.original.contracts.length > 0
3658
? FundingSourceLabels[
@@ -42,6 +64,24 @@ export const EntrantsColumns: ColumnDef<AdminUser>[] = [
4264
{
4365
accessorKey: 'expectedSpecialities',
4466
header: 'Спеціальність',
67+
sortingFn: (rowA, rowB) => {
68+
const getEntrantSpecialities = ({
69+
contracts,
70+
expectedSpecialities,
71+
}: AdminUser) => {
72+
if (contracts.length === 0) return '-';
73+
return (
74+
contracts.find((contract) => contract.state === 'APPROVED')
75+
?.specialty ||
76+
expectedSpecialities ||
77+
contracts[0].specialty
78+
);
79+
};
80+
81+
return getEntrantSpecialities(rowA.original).localeCompare(
82+
getEntrantSpecialities(rowB.original)
83+
);
84+
},
4585
cell: ({ row }) => {
4686
const { contracts, expectedSpecialities } = row.original;
4787
if (contracts.length > 0) {
@@ -61,8 +101,21 @@ export const EntrantsColumns: ColumnDef<AdminUser>[] = [
61101
},
62102
},
63103
{
64-
accessorKey: 'contracts',
104+
accessorKey: 'status',
65105
header: 'Статус',
106+
sortingFn: (rowA, rowB) => {
107+
const getEntrantStatus = ({ role, contracts }: AdminUser) => {
108+
if (role === 'ADMIN') return 'Відсутній';
109+
if (contracts.length === 0) return 'Не подано';
110+
if (contracts.some((contract) => contract.state === 'APPROVED'))
111+
return 'Зареєстровано';
112+
return 'Подано';
113+
};
114+
115+
return getEntrantStatus(rowA.original).localeCompare(
116+
getEntrantStatus(rowB.original)
117+
);
118+
},
66119
cell: ({ row }) => {
67120
const { contracts, role } = row.original;
68121

@@ -93,4 +146,58 @@ export const EntrantsColumns: ColumnDef<AdminUser>[] = [
93146
);
94147
},
95148
},
149+
{
150+
accessorKey: 'contractNumber',
151+
header: 'Номер',
152+
sortingFn: (rowA, rowB) => {
153+
const contractADate =
154+
rowA.original.contracts.find(
155+
(contract) => contract.state === 'APPROVED'
156+
)?.number || '-';
157+
const contractBDate =
158+
rowB.original.contracts.find(
159+
(contract) => contract.state === 'APPROVED'
160+
)?.number || '-';
161+
162+
return contractADate.localeCompare(contractBDate);
163+
},
164+
cell: ({ row }) => {
165+
const { contracts } = row.original;
166+
167+
const approvedContract = contracts.find(
168+
(contract) => contract.state === 'APPROVED'
169+
);
170+
171+
return approvedContract?.number || '-';
172+
},
173+
},
174+
{
175+
accessorKey: 'contractDate',
176+
header: 'Дата',
177+
sortingFn: (rowA, rowB) => {
178+
const contractADate =
179+
rowA.original.contracts.find(
180+
(contract) => contract.state === 'APPROVED'
181+
)?.date || '-';
182+
const contractBDate =
183+
rowB.original.contracts.find(
184+
(contract) => contract.state === 'APPROVED'
185+
)?.date || '-';
186+
187+
return contractADate
188+
.split('.')
189+
.reverse()
190+
.join('.')
191+
.localeCompare(contractBDate.split('.').reverse().join('.'));
192+
},
193+
cell: ({ row }) => {
194+
const { contracts } = row.original;
195+
196+
const approvedContract = contracts.find(
197+
(contract) => contract.state === 'APPROVED'
198+
);
199+
200+
return approvedContract?.date || '-';
201+
},
202+
},
96203
];

admission-web/src/components/pages/admin/entrants/components/EntrantsDataTable.tsx

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
getCoreRowModel,
88
getFilteredRowModel,
99
getPaginationRowModel,
10+
getSortedRowModel,
11+
SortingState,
1012
useReactTable,
1113
} from '@tanstack/react-table';
1214

@@ -22,13 +24,14 @@ import React, { useState } from 'react';
2224
import { Input } from '@/components/ui/input';
2325
import { AdminEntrantTablePagination } from '@/components/pages/admin/entrants/components/AdminEntrantTablePagination';
2426
import { Button } from '@/components/ui/button';
25-
import { Trash2Icon } from 'lucide-react';
27+
import { ChevronDown, ChevronUp, Trash2Icon } from 'lucide-react';
2628
import AdminEntrantsApi from '@/app/api/admin-entrants/admin-entrants-api';
2729
import { useCommonToast } from '@/components/ui/toast/use-common-toast';
2830
import AdminAlertDialog from '../../common/components/AdminAlertDialog';
2931
import { AdminUser } from '@/app/api/admin-entrants/admin-entrants-api.types';
3032
import Link from 'next/link';
3133
import { RefetchOptions, QueryObserverResult } from '@tanstack/react-query';
34+
import { cn } from '@/lib/utils/cn';
3235

3336
interface DataTableProps {
3437
columns: ColumnDef<AdminUser>[];
@@ -44,15 +47,20 @@ export function AdminEntrantDataTable({
4447
refetch,
4548
}: DataTableProps) {
4649
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
50+
const [sorting, setSorting] = useState<SortingState>([]);
51+
4752
const table = useReactTable({
4853
data,
4954
columns,
5055
getCoreRowModel: getCoreRowModel(),
5156
getPaginationRowModel: getPaginationRowModel(),
5257
onColumnFiltersChange: setColumnFilters,
5358
getFilteredRowModel: getFilteredRowModel(),
59+
onSortingChange: setSorting,
60+
getSortedRowModel: getSortedRowModel(),
5461
state: {
5562
columnFilters,
63+
sorting,
5664
},
5765
});
5866
const { toastSuccess, toastError } = useCommonToast();
@@ -89,13 +97,38 @@ export function AdminEntrantDataTable({
8997
<TableRow key={headerGroup.id}>
9098
{headerGroup.headers.map((header) => {
9199
return (
92-
<TableHead key={header.id}>
93-
{header.isPlaceholder
94-
? null
95-
: flexRender(
100+
<TableHead
101+
key={header.id}
102+
onClick={header.column.getToggleSortingHandler()}
103+
className={cn(
104+
header.column.getCanSort() &&
105+
'cursor-pointer select-none'
106+
)}
107+
>
108+
{header.isPlaceholder ? null : (
109+
<div className='flex items-center gap-2'>
110+
{flexRender(
96111
header.column.columnDef.header,
97112
header.getContext()
98113
)}
114+
{{
115+
asc: (
116+
<ChevronUp
117+
className='min-w-5'
118+
height={20}
119+
width={20}
120+
/>
121+
),
122+
desc: (
123+
<ChevronDown
124+
className='min-w-5'
125+
height={20}
126+
width={20}
127+
/>
128+
),
129+
}[header.column.getIsSorted() as string] ?? null}
130+
</div>
131+
)}
99132
</TableHead>
100133
);
101134
})}

0 commit comments

Comments
 (0)