add multiselect for files tanstak-table

This commit is contained in:
smanylov
2026-03-30 12:48:31 +07:00
parent 534e18bbd4
commit 154e9cc5c1
5 changed files with 175 additions and 30 deletions
@@ -0,0 +1,48 @@
'use client'
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export function SelectedFilesAction({ filesId, callBack }: { filesId: Set<string>, callBack: (value: Set<string>) => void }) {
const t = useTranslations('Global');
if (filesId.size === 0) {
return null;
}
function downloadHandler() {
if (filesId.size) {
const filesArray = Array.from(filesId);
const filesString = filesArray.join(', ');
toast.success(`${t('download')}: ${filesString}`)
}
}
function clearHandler() {
callBack(new Set());
}
return (
<div
className="selected-files-wrapper"
>
<button
className="btn btn-primary"
onClick={() => {
downloadHandler();
}}
>
{t('download')}
</button>
<button
className="btn btn-primary"
onClick={() => {
clearHandler();
}}
>
{t('cancel')}
</button>
</div>
)
}
@@ -12,7 +12,7 @@ import {
ColumnFiltersState,
PaginationState
} from '@tanstack/react-table';
import { IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload, IconInfo } from '@/app/ui/icons/icons';
import { IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload, IconInfo, IconEye, IconCheck } from '@/app/ui/icons/icons';
import { useTranslations, useLocale } from 'next-intl';
import DropDownList from '@/app/components/DropDownList';
import { useQuery, useQueryClient } from '@tanstack/react-query';
@@ -29,6 +29,8 @@ import { formatDate, formatDateTime } from '@/app/lib/formatDate';
import { useViewport } from '@/app/hooks/useViewport';
import { downloadFile } from '@/app/actions/fileEntity';
import { useDebouncedCallback } from 'use-debounce';
import Link from 'next/link';
import { SelectedFilesAction } from '@/app/components/tanstak-table/SelectedFilesActions';
export type FileItem = {
id: string;
@@ -80,9 +82,10 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
pageIndex: 0,
pageSize: 10,
});
const [query, setQuery] = useState<string>('');
/* const [query, setQuery] = useState<string>(''); */
const [searchInputValue, setSearchInputValue] = useState<string>('');
const [searchQuery, setSearchQuery] = useState<string>('');
const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set());
const debouncedSetSearchQuery = useDebouncedCallback(
(value: string) => {
@@ -260,9 +263,49 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
return nameWithoutExtension.substring(0, maxNameLength) + '...' /* + extension */;
}
function selectHandler(fileId: string) {
setSelectedFiles((prev) => {
const newSet = new Set(prev)
if (newSet.has(fileId)) {
newSet.delete(fileId)
} else {
newSet.add(fileId)
}
return newSet;
});
}
// Определение колонок
const columns = useMemo<ColumnDef<FileItem>[]>(
() => [
{
accessorKey: 'selectd',
header: ({ column }) => (
<div className="column">
<span>
</span>
</div>
),
cell: ({ row }) => {
return (
<div className="text-center table-item table-item-id">
<button
className={`table-item-checkbox ${selectedFiles.has(row.original.id) ? 'selected' : ''}`}
onClick={() => {
selectHandler(row.original.id);
}}
>
{selectedFiles.has(row.original.id) && (
<IconCheck />
)}
</button>
</div>
)
},
},
{
accessorKey: 'id',
header: ({ column }) => (
@@ -478,6 +521,13 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
cell: ({ row }) => (
<div className="actions">
<div className="actions-group">
<Link
href={`/pages/file/${row.original.id}`}
className="bg-violet-500 hover:bg-violet-600"
title={t('view')}
>
<IconEye />
</Link>
</div>
<div className="actions-group">
{row.original.protectStatus === 'PROTECTED' && (
@@ -507,7 +557,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
enableColumnFilter: false,
},
],
[viewport.device]
[viewport.device, selectedFiles]
);
const handleView = async (file: FileItem) => {
@@ -613,7 +663,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
}
return result;
}, [query, typeFilter, dateFilter]);
}, [/* query, */ typeFilter, dateFilter]);
useEffect(() => {
const currentPageRows = table.getRowModel().rows;
@@ -904,6 +954,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
</button>
</div>
</div>
<SelectedFilesAction filesId={selectedFiles} callBack={setSelectedFiles} />
</div >
);
}
+64 -26
View File
@@ -321,6 +321,7 @@
border-radius: 20px;
padding: 30px;
box-shadow: 0 4px 20px v.$shadow-1;
margin-bottom: 2rem;
h3 {
margin-bottom: 20px;
@@ -635,7 +636,7 @@
.table-filtres-text-filter {
background-color: #fff;
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
border-radius: 12px;
justify-content: space-between;
align-items: center;
@@ -694,7 +695,7 @@
padding-right: 0.5rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
border-radius: 10px;
box-shadow: 0 1px 2px #0000000d;
color: v.$p-color;
@@ -721,7 +722,7 @@
gap: 0.25rem;
button {
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
padding: 0.25rem 0.75rem;
border-radius: 10px;
color: v.$p-color;
@@ -947,6 +948,22 @@
}
}
}
.table-item-checkbox {
width: 16px;
height: 16px;
border-radius: 4px;
border: 2px solid v.$border-color-1;
cursor: pointer;
transition: all 0.3s ease-in;
display: flex;
justify-content: center;
align-items: center;
&:hover {
border: 2px solid v.$border-color-1-hover;
}
}
}
}
@@ -1117,7 +1134,7 @@
transition: all 0.3s ease;
position: relative;
overflow: hidden;
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
&::before {
content: '';
@@ -1194,7 +1211,7 @@
display: flex;
flex: 1;
padding: 10px;
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
border-radius: 12px;
font-size: 14px;
background: #f8f9fa;
@@ -1267,7 +1284,7 @@
.level-card {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
border-radius: 16px;
padding: 18px;
text-align: center;
@@ -1704,7 +1721,7 @@
padding: 16px;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 14px;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
transition: all 0.3s ease;
&:hover {
@@ -1984,7 +2001,7 @@
.progress-bar-container {
width: 100%;
height: 12px;
background: #e2e8f0;
background: v.$border-color-1;
border-radius: 6px;
overflow: hidden;
position: relative;
@@ -2082,7 +2099,7 @@
.country-bar {
width: 100%;
height: 5px;
background: #e2e8f0;
background: v.$border-color-1;
border-radius: 2.5px;
overflow: hidden;
}
@@ -2201,7 +2218,7 @@
border-radius: 16px;
overflow: hidden;
margin-bottom: 16px;
background: linear-gradient(135deg, #f1f5f9, #e2e8f0);
background: linear-gradient(135deg, #f1f5f9, v.$border-color-1);
display: flex;
align-items: center;
justify-content: center;
@@ -2257,7 +2274,7 @@
align-items: center;
justify-content: space-between;
padding-bottom: 12px;
border-bottom: 2px solid #e2e8f0;
border-bottom: 2px solid v.$border-color-1;
}
.violation-filename {
@@ -2555,7 +2572,7 @@
.case-card {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
border-radius: 16px;
padding: 24px;
transition: all 0.3s ease;
@@ -2759,7 +2776,7 @@
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
overflow: auto;
.violations-table {
@@ -2919,7 +2936,7 @@
width: 32px;
height: 32px;
border-radius: 8px;
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
background: white;
font-size: 1rem;
cursor: pointer;
@@ -3587,7 +3604,7 @@
.file-info-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
border-radius: 12px;
padding: 0;
overflow: hidden;
@@ -3602,7 +3619,7 @@
background: linear-gradient(0deg, #6366f1 50%, #8b5cf6 130%);
color: white;
padding: 16px 20px;
border-bottom: 1px solid #e2e8f0;
border-bottom: 1px solid v.$border-color-1;
h4 {
margin: 0;
@@ -4018,7 +4035,7 @@
margin-top: 10px;
.pagination-button {
border: 2px solid #e2e8f0;
border: 2px solid v.$border-color-1;
padding: 0.25rem 0.75rem;
border-radius: 10px;
color: v.$p-color;
@@ -4256,7 +4273,7 @@
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e2e8f0;
border-bottom: 1px solid v.$border-color-1;
}
.info-row {
@@ -4292,7 +4309,7 @@
background: #f8fafc;
padding: 12px;
border-radius: 8px;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
margin-top: 10px;
word-break: break-all;
@@ -4309,7 +4326,7 @@
padding: 15px;
border-radius: 12px;
margin-top: 15px;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
text-align: left;
.notes-title {
@@ -4345,7 +4362,7 @@
padding: 15px;
border-radius: 12px;
text-align: center;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
}
.stat-mini-value {
@@ -4391,7 +4408,7 @@
color: #64748b;
&:hover {
background: #e2e8f0;
background: v.$border-color-1;
}
}
@@ -4409,7 +4426,7 @@
.source-card {
/* background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 12px;
border: 1px solid #e2e8f0; */
border: 1px solid v.$border-color-1; */
background: #fff;
border: 1px solid #f3f4f6;
@@ -4596,7 +4613,7 @@
}
&-title {
border-bottom: 1px solid #e2e8f0;
border-bottom: 1px solid v.$border-color-1;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
@@ -4646,7 +4663,7 @@
}
&-actions {
border-left: 1px solid #e2e8f0;
border-left: 1px solid v.$border-color-1;
padding-left: 15px;
}
@@ -4730,7 +4747,7 @@
width: 100%;
min-height: 80px;
padding: 12px;
border: 1px solid #e2e8f0;
border: 1px solid v.$border-color-1;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
@@ -5009,3 +5026,24 @@
}
}
}
.selected-files-wrapper {
position: fixed;
bottom: 40px;
left: calc(50% + (var(--side-bar-width) / 2));
transform: translateX(-50%);
display: flex;
gap: 10px;
background: v.$white;
border-radius: 20px;
padding: 20px 25px;
box-shadow: 0 4px 20px v.$shadow-1;
border: 1px solid v.$b-color-1;
.btn-primary {
background: #2b7fff;
padding: 10px 16px;
}
}
+2
View File
@@ -15,6 +15,8 @@ $red: #dc2626;
$green: #00a63e;
$p-color-disabled: #6365f18e;
$s-color-disabled: #8a5cf663;
$border-color-1: #e2e8f0;
$border-color-1-hover: #b1b7be;
$color-image: #f08c00;
$color-video: #2f9e44;
+6
View File
@@ -201,3 +201,9 @@ export function IconGraph() {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon"><path fill="currentColor" d="m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z" /></svg>
)
}
export function IconCheck() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon"><path fill="currentColor" d="m9.55 15.15l8.475-8.475q.3-.3.7-.3t.7.3t.3.713t-.3.712l-9.175 9.2q-.3.3-.7.3t-.7-.3L4.55 13q-.3-.3-.288-.712t.313-.713t.713-.3t.712.3z" /></svg>
)
}