From 154e9cc5c1dab8025601d4c086b7b07f726ef7e6 Mon Sep 17 00:00:00 2001 From: smanylov Date: Mon, 30 Mar 2026 12:48:31 +0700 Subject: [PATCH] add multiselect for files tanstak-table --- .../tanstak-table/SelectedFilesActions.tsx | 48 ++++++++++ .../components/tanstak-table/TanstakTable.tsx | 59 +++++++++++- src/app/styles/pages-styles.scss | 90 +++++++++++++------ src/app/styles/variable.scss | 2 + src/app/ui/icons/icons.tsx | 6 ++ 5 files changed, 175 insertions(+), 30 deletions(-) create mode 100644 src/app/components/tanstak-table/SelectedFilesActions.tsx diff --git a/src/app/components/tanstak-table/SelectedFilesActions.tsx b/src/app/components/tanstak-table/SelectedFilesActions.tsx new file mode 100644 index 0000000..a3dc209 --- /dev/null +++ b/src/app/components/tanstak-table/SelectedFilesActions.tsx @@ -0,0 +1,48 @@ +'use client' + +import { useTranslations } from 'next-intl'; +import { toast } from 'sonner'; + +export function SelectedFilesAction({ filesId, callBack }: { filesId: Set, callBack: (value: Set) => 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 ( +
+ + + +
+ ) +} \ No newline at end of file diff --git a/src/app/components/tanstak-table/TanstakTable.tsx b/src/app/components/tanstak-table/TanstakTable.tsx index c97b318..924262f 100644 --- a/src/app/components/tanstak-table/TanstakTable.tsx +++ b/src/app/components/tanstak-table/TanstakTable.tsx @@ -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(''); + /* const [query, setQuery] = useState(''); */ const [searchInputValue, setSearchInputValue] = useState(''); const [searchQuery, setSearchQuery] = useState(''); + const [selectedFiles, setSelectedFiles] = useState>(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[]>( () => [ + { + accessorKey: 'selectd', + header: ({ column }) => ( +
+ + + +
+ ), + cell: ({ row }) => { + return ( +
+ +
+ ) + }, + }, { accessorKey: 'id', header: ({ column }) => ( @@ -478,6 +521,13 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) { cell: ({ row }) => (
+ + +
{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 }) {
+ ); } \ No newline at end of file diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index 456f71b..c88d634 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -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; @@ -5008,4 +5025,25 @@ font-size: 12px; } } +} + +.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; + } } \ No newline at end of file diff --git a/src/app/styles/variable.scss b/src/app/styles/variable.scss index a5b35d0..c4150ce 100644 --- a/src/app/styles/variable.scss +++ b/src/app/styles/variable.scss @@ -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; diff --git a/src/app/ui/icons/icons.tsx b/src/app/ui/icons/icons.tsx index fd8dca0..733ef00 100644 --- a/src/app/ui/icons/icons.tsx +++ b/src/app/ui/icons/icons.tsx @@ -200,4 +200,10 @@ export function IconGraph() { return ( ) +} + +export function IconCheck() { + return ( + + ) } \ No newline at end of file