From d52dfb7ab839df910ce57dbc88b08a8966259f32 Mon Sep 17 00:00:00 2001 From: smanylov Date: Fri, 26 Dec 2025 14:03:57 +0700 Subject: [PATCH] add life data for tanstak table --- src/app/[locale]/pages/layout.tsx | 9 +- src/app/actions/auth.ts | 6 +- src/app/actions/file-entity.ts | 0 src/app/actions/fileEntity.ts | 86 +++++++++++++ src/app/actions/fileUpload.ts | 5 +- src/app/components/tanstakTable.tsx | 190 ++++++++++++++++++++++------ src/app/lib/prefetch-queries.ts | 16 +++ src/app/ui/icons/icons.tsx | 8 +- 8 files changed, 262 insertions(+), 58 deletions(-) delete mode 100644 src/app/actions/file-entity.ts create mode 100644 src/app/actions/fileEntity.ts create mode 100644 src/app/lib/prefetch-queries.ts diff --git a/src/app/[locale]/pages/layout.tsx b/src/app/[locale]/pages/layout.tsx index c5cb136..f7a5d82 100644 --- a/src/app/[locale]/pages/layout.tsx +++ b/src/app/[locale]/pages/layout.tsx @@ -3,17 +3,12 @@ import styles from '@/app/styles/page.module.scss' import HeaderPanel from '@/app/ui/header/headerPanel'; import { getQueryClient } from '@/app/providers/getQueryClient'; import { HydrationBoundary, dehydrate } from '@tanstack/react-query'; -import { getUserData } from '@/app/actions/action'; +import { prefetchLayoutQueries } from '@/app/lib/prefetch-queries'; export default async function Layout({ children }: { children: React.ReactNode }) { const queryClient = getQueryClient(); - await Promise.all([ - queryClient.prefetchQuery({ - queryKey: ['userData'], - queryFn: () => getUserData() - }) - ]) + await prefetchLayoutQueries(queryClient); return (
diff --git a/src/app/actions/auth.ts b/src/app/actions/auth.ts index d65a6b9..8088167 100644 --- a/src/app/actions/auth.ts +++ b/src/app/actions/auth.ts @@ -224,7 +224,6 @@ export async function registration( if (parsed.message_desc === 'Operation successful') { await createSession(parsed.message_body.token, email); } else { - console.log('error'); throw parsed; } } else { @@ -234,8 +233,6 @@ export async function registration( if (error) { const typedError = error as any; const errors: Record = {}; - console.log(typedError); - console.log(typedError.message_body); switch (typedError.message_code) { case 1: @@ -273,8 +270,7 @@ export async function registration( error: errors }; } - console.log('finale error'); - console.log(error); + throw error; } diff --git a/src/app/actions/file-entity.ts b/src/app/actions/file-entity.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/actions/fileEntity.ts b/src/app/actions/fileEntity.ts new file mode 100644 index 0000000..565e196 --- /dev/null +++ b/src/app/actions/fileEntity.ts @@ -0,0 +1,86 @@ +'use server' + +import { getSessionData } from '@/app/actions/session'; +import { localDevelopmentUrl } from '@/app/actions/definitions'; +const API_BASE_URL = process.env.NODE_ENV === 'development' + ? localDevelopmentUrl + : 'http://app:8080'; + + +export async function getUserFilesData() { + const token = await getSessionData('token'); + console.log('fetch'); + + try { + const response = await fetch(`${API_BASE_URL}/api/v1/data`, { + method: 'POST', + body: JSON.stringify({ + version: 1, + msg_id: 20005, + message_body: { + action: 'user_files', + page: 1, + page_size: 20, + token: token + } + }), + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + } + }); + + if (response.ok) { + let parsed = await response.json(); + if (parsed.message_code === 0) { + return parsed.message_body; + } else { + throw parsed; + } + } else { + throw (`${response.status}`); + } + + } catch (error) { + return error + } +} + +export async function removeUserFile(fileId : string) { + const token = await getSessionData('token'); + console.log('remove'); + + try { + const response = await fetch(`${API_BASE_URL}/api/v1/data`, { + method: 'POST', + body: JSON.stringify({ + version: 1, + msg_id: 20005, + message_body: { + action: 'delete_file', + file_id: fileId, + token: token + } + }), + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + } + }); + + if (response.ok) { + let parsed = await response.json(); + + if (parsed.message_code === 0) { + return parsed.message_body.file_id; + } else { + throw parsed; + } + } else { + throw (`${response.status}`); + } + + } catch (error) { + return error + } +} \ No newline at end of file diff --git a/src/app/actions/fileUpload.ts b/src/app/actions/fileUpload.ts index 44cd1db..43d7124 100644 --- a/src/app/actions/fileUpload.ts +++ b/src/app/actions/fileUpload.ts @@ -1,7 +1,7 @@ 'use server' import { localDevelopmentUrl } from '@/app/actions/definitions'; -import { getSessionData, createSession } from '@/app/actions/session'; +import { getSessionData } from '@/app/actions/session'; const API_BASE_URL = process.env.NODE_ENV === 'development' ? localDevelopmentUrl @@ -93,7 +93,6 @@ export async function cancelUpload(udloadId: string) { }; if (parsed.message_desc === 'Upload cancelled successfully') { - console.log('cancelUpload done'); return parsed.message_body; } else { throw parsed; @@ -102,7 +101,6 @@ export async function cancelUpload(udloadId: string) { throw (`${response.status}`); } } catch (error) { - console.log('cancelUpload error'); return error; } } @@ -190,7 +188,6 @@ export async function getAllowedFilesExtensions(type: string) { if (parsed.message_desc === 'Operation successful') { return parsed.message_body; } else { - console.log('error cancel getAllowedFilesExtensions'); throw parsed; } } else { diff --git a/src/app/components/tanstakTable.tsx b/src/app/components/tanstakTable.tsx index ad89cd0..56404e9 100644 --- a/src/app/components/tanstakTable.tsx +++ b/src/app/components/tanstakTable.tsx @@ -11,19 +11,21 @@ import { SortingState, ColumnFiltersState, } from '@tanstack/react-table'; -import {IconImageFile, IconVideoFile, IconAudioFile, IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileOpen, IconShieldExclamation} from '@/app/ui/icons/icons'; +import { IconImageFile, IconVideoFile, IconAudioFile, IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileOpen, IconShieldExclamation, IconDelete } from '@/app/ui/icons/icons'; import { useTranslations } from 'next-intl'; import DropDownList from '@/app/components/dropDownList'; +import { useQuery } from '@tanstack/react-query'; +import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity'; // Типы данных type FileType = 'image' | 'video' | 'audio'; type FileItem = { - id: number; + id: string; fileName: string; fileType: FileType; - violations: number; - checks: number; - lastCheck: number; // timestamp в миллисекундах + violations?: number | undefined; + checks?: number | undefined; + lastCheck?: number; // timestamp в миллисекундах }; // Иконки для типов файлов @@ -52,29 +54,52 @@ const formatDate = (timestamp: number) => { const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); + return `${day}.${month}.${year}`; }; -// Компонент таблицы +const formatDateTime = (timestamp: number) => { + const date = new Date(timestamp); + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + + return `${hours}:${minutes}`; +}; + export default function TanstakFilesTable() { - // Данные с timestamp вместо Date объектов - const [data] = useState([ - { id: 1, fileName: 'image1.jpg', fileType: 'image', violations: 5, checks: 12, lastCheck: 1705267200000 }, // 15.01.2024 - { id: 2, fileName: 'prespresentationpresentationprpresentationpresentationesentationentation.mp4', fileType: 'video', violations: 2, checks: 8, lastCheck: 1705180800000 }, // 14.01.2024 - { id: 3, fileName: 'song.mp3', fileType: 'audio', violations: 0, checks: 5, lastCheck: 1705094400000 }, // 13.01.2024 - { id: 4, fileName: 'photo1.jpg', fileType: 'image', violations: 3, checks: 10, lastCheck: 1705008000000 }, // 12.01.2024 - { id: 5, fileName: 'movie.mp4', fileType: 'video', violations: 1, checks: 7, lastCheck: 1704921600000 }, // 11.01.2024 - { id: 6, fileName: 'podcast.mp3', fileType: 'audio', violations: 4, checks: 9, lastCheck: 1704835200000 }, // 10.01.2024 - { id: 7, fileName: 'screenshot.png', fileType: 'image', violations: 0, checks: 3, lastCheck: 1704748800000 }, // 09.01.2024 - { id: 8, fileName: 'tutorial.mp4', fileType: 'video', violations: 2, checks: 6, lastCheck: 1704662400000 }, // 08.01.2024 - { id: 9, fileName: 'music.mp3', fileType: 'audio', violations: 1, checks: 4, lastCheck: 1704576000000 }, // 07.01.2024 - { id: 10, fileName: 'image2.jpg', fileType: 'image', violations: 5, checks: 11, lastCheck: 1704489600000 }, // 06.01.2024 - { id: 11, fileName: 'film.mov', fileType: 'video', violations: 0, checks: 2, lastCheck: 1704403200000 }, // 05.01.2024 - { id: 12, fileName: 'sound.wav', fileType: 'audio', violations: 3, checks: 8, lastCheck: 1704316800000 }, // 04.01.2024 - { id: 13, fileName: 'picture.png', fileType: 'image', violations: 2, checks: 7, lastCheck: 1704230400000 }, // 03.01.2024 - { id: 14, fileName: 'video2.mp4', fileType: 'video', violations: 1, checks: 5, lastCheck: 1704144000000 }, // 02.01.2024 - { id: 15, fileName: 'audio2.mp3', fileType: 'audio', violations: 0, checks: 3, lastCheck: 1704057600000 }, // 01.01.2024 - ]); + const { + data: userFilesData, + isLoading, + isError, + error + } = useQuery({ + queryKey: ['userFilesData'], + queryFn: () => { + return getUserFilesData(); + } + }); + + const [tableData] = useState( + userFilesData.files.map((item: any) => { + //когда с бека будет приходить время в милисекундах можно будет удалить + const [datePart, timePart] = item.updatedAt.split(' '); + const [day, month, year] = datePart.split('-').map(Number); + const [hours, minutes, seconds] = timePart.split(':').map(Number); + const newDate = new Date(year, month - 1, day, hours, minutes, seconds).getTime(); + + return { + id: item.id, + fileName: item.originalFileName, + fileType: item.mimeType.toLocaleLowerCase(), + violations: item.violations, + checks: item.checks, + lastCheck: newDate + } + }) + ) + + if (typeof window !== 'undefined') { + } // Состояния const [sorting, setSorting] = useState([]); @@ -124,6 +149,41 @@ export default function TanstakFilesTable() { ), enableColumnFilter: false, }, + { + accessorKey: 'status', + header: ({ column }) => ( +
+ + status + + +
+ ), + cell: ({ row }) => { + return ( +
+ status +
+ ) + }, + }, { accessorKey: 'violations', header: ({ column }) => ( @@ -151,11 +211,20 @@ export default function TanstakFilesTable() {
), - cell: ({ row }) => ( -
0 ? 'text-red-600' : 'text-green-600'}`}> - {row.original.violations} -
- ), + cell: ({ row }) => { + let classCollor = () => { + let result = '' + if (row.original.violations !== undefined) { + result = row.original.violations > 0 ? 'text-red-600' : 'text-green-600' + } + return result; + } + return ( +
+ {row.original.violations !== undefined ? row.original.violations : '-'} +
+ ) + }, }, { accessorKey: 'checks', @@ -186,7 +255,7 @@ export default function TanstakFilesTable() { ), cell: ({ row }) => (
- {row.original.checks} + {row.original.checks !== undefined ? row.original.checks : '-'}
), }, @@ -217,11 +286,21 @@ export default function TanstakFilesTable() { ), - cell: ({ row }) => ( -
- {formatDate(row.original.lastCheck)} -
- ), + cell: ({ row }) => { + return ( +
+ {row.original.lastCheck ? ( + <> + {formatDate(row.original.lastCheck)} +
+ {formatDateTime(row.original.lastCheck)} + + ) : ( +
-
+ )} +
+ ) + }, enableColumnFilter: false, }, { @@ -253,6 +332,12 @@ export default function TanstakFilesTable() { > + ), enableSorting: false, @@ -275,9 +360,14 @@ export default function TanstakFilesTable() { console.log(`Щиток: ${file.fileName}`); }; + const handleRemove = async (file: FileItem) => { + const response = await removeUserFile(file.id); + console.log(`Removed: ${response}`); + }; + // Фильтрация по типу файла и дате const filteredData = useMemo(() => { - let result = data; + let result = tableData; // Фильтр по типу файла if (typeFilter !== 'all') { @@ -294,25 +384,43 @@ export default function TanstakFilesTable() { switch (dateFilter) { case 'today': const todayStart = new Date().setHours(0, 0, 0, 0); - result = result.filter(item => item.lastCheck >= todayStart); + result = result.filter(item => { + if (item.lastCheck) { + return item.lastCheck >= todayStart + } else { + return 0 + } + }); break; case 'week': const weekAgo = now - sevenDays; - result = result.filter(item => item.lastCheck >= weekAgo); + result = result.filter(item => { + if (item.lastCheck) { + return item.lastCheck >= weekAgo; + } + }); break; case 'month': const monthAgo = now - thirtyDays; - result = result.filter(item => item.lastCheck >= monthAgo); + result = result.filter(item => { + if (item.lastCheck) { + return item.lastCheck >= monthAgo; + } + }); break; case 'older': const monthAgo2 = now - thirtyDays; - result = result.filter(item => item.lastCheck < monthAgo2); + result = result.filter(item => { + if (item.lastCheck) { + return item.lastCheck < monthAgo2; + } + }); break; } } return result; - }, [data, typeFilter, dateFilter]); + }, [tableData, typeFilter, dateFilter]); // Создание таблицы const table = useReactTable({ diff --git a/src/app/lib/prefetch-queries.ts b/src/app/lib/prefetch-queries.ts new file mode 100644 index 0000000..68f4227 --- /dev/null +++ b/src/app/lib/prefetch-queries.ts @@ -0,0 +1,16 @@ +import { QueryClient } from '@tanstack/react-query'; +import { getUserData } from '@/app/actions/action'; +import {getUserFilesData} from '@/app/actions/fileEntity'; + +export async function prefetchLayoutQueries(queryClient: QueryClient) { + await Promise.all([ + queryClient.prefetchQuery({ + queryKey: ['userData'], + queryFn: () => getUserData() + }), + queryClient.prefetchQuery({ + queryKey: ['userFilesData'], + queryFn: () => getUserFilesData() + }), + ]); +} \ No newline at end of file diff --git a/src/app/ui/icons/icons.tsx b/src/app/ui/icons/icons.tsx index 8ac182a..ea6031d 100644 --- a/src/app/ui/icons/icons.tsx +++ b/src/app/ui/icons/icons.tsx @@ -117,6 +117,12 @@ export function IconFileOpen() { export function IconShieldExclamation() { return ( - + + ) +} + +export function IconDelete() { + return ( + ) } \ No newline at end of file