-
+
-
+
@@ -33,7 +44,6 @@ export default function Page() {
-
)
}
\ No newline at end of file
diff --git a/src/app/actions/violationActions.ts b/src/app/actions/violationActions.ts
index e1780e8..9337843 100644
--- a/src/app/actions/violationActions.ts
+++ b/src/app/actions/violationActions.ts
@@ -80,4 +80,108 @@ export async function startGlobalMonitoring(monitoringType: 'monitoring' | 'all_
} catch (error) {
return null
}
+}
+
+export async function getViolationFilesArray() {
+ const token = await getSessionData('token');
+
+ try {
+
+ const response = await fetch(`${API_BASE_URL}/api/v1/global-search/violation-summary`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ }
+ });
+
+ if (response.ok) {
+ let parsed = await response.json();
+ if (parsed.length) {
+ return parsed;
+ } else {
+ throw parsed;
+ }
+ } else {
+ throw (`${response.status}`);
+ }
+
+ } catch (error) {
+ return []
+ }
+}
+
+/* статистика нурешений */
+export async function fetchViolationStats() {
+ const token = await getSessionData('token');
+
+ try {
+ const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
+ method: 'POST',
+ body: JSON.stringify({
+ version: 1,
+ msg_id: 30010,
+ message_body: {
+ token: token
+ }
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ }
+ });
+
+ if (response.ok) {
+ let parsed = await response.json();
+ if (parsed) {
+ return parsed;
+ } else {
+ throw null;
+ }
+ } else {
+ throw (`${response.status}`);
+ }
+ } catch (error) {
+ return null
+ }
+}
+
+export async function getFileViolations(fileId: string, page: number) {
+ console.log('getFileViolations');
+ console.log(page);
+
+ try {
+ const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
+ method: 'POST',
+ body: JSON.stringify({
+ version: 1,
+ msg_id: 30009,
+ message_body: {
+ file_id: fileId,
+ page: page,
+ size: 5,
+ sort_direction: "desc"
+ }
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ }
+ });
+
+ if (response.ok) {
+ let parsed = await response.json();
+ if (parsed.message_body) {
+ return parsed.message_body;
+ } else {
+ throw null;
+ }
+ } else {
+ throw (`${response.status}`);
+ }
+
+ } catch (error) {
+ return null
+ }
}
\ No newline at end of file
diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss
index eeb2c4f..41df816 100644
--- a/src/app/styles/pages-styles.scss
+++ b/src/app/styles/pages-styles.scss
@@ -789,15 +789,15 @@
}
td {
- padding: 15px 0;
+ padding: 15px 16px;
white-space: nowrap;
&:first-child {
- padding-left: 15px;
+ /* padding-left: 15px; */
}
&:last-child {
- padding-right: 15px;
+ /* padding-right: 15px; */
}
&:has(.table-item-id) {
@@ -862,7 +862,8 @@
gap: 5px;
}
- button {
+ button,
+ a {
padding: 4px 12px;
font-size: 14px;
line-height: 20px;
@@ -4400,6 +4401,11 @@
margin-bottom: 10px;
}
+ .source-image {
+ max-width: 120px;
+ width: 100%;
+ }
+
.source-url {
color: #6366f1;
font-weight: 600;
diff --git a/src/app/ui/violations/file-page/violation-page-title.tsx b/src/app/ui/violations/file-page/violation-page-title.tsx
index 0c1bcf5..2894d9d 100644
--- a/src/app/ui/violations/file-page/violation-page-title.tsx
+++ b/src/app/ui/violations/file-page/violation-page-title.tsx
@@ -1,3 +1,5 @@
+'use client'
+
import { useTranslations } from 'next-intl';
import { useParams } from 'next/navigation'
import { IconEyeDashed } from '@/app/ui/icons/icons';
diff --git a/src/app/ui/violations/file-page/violation-page-violations-list.tsx b/src/app/ui/violations/file-page/violation-page-violations-list.tsx
index 4d6eb61..68ee6ed 100644
--- a/src/app/ui/violations/file-page/violation-page-violations-list.tsx
+++ b/src/app/ui/violations/file-page/violation-page-violations-list.tsx
@@ -1,52 +1,149 @@
-export default function ViolationPageViolationsList() {
+// app/ui/violations/file-page/violation-page-violations-list.tsx
+'use client'
+
+import { useTranslations } from 'next-intl';
+import { useRouter, usePathname } from 'next/navigation';
+import { useEffect } from 'react';
+
+interface ViolationFileDetail {
+ id: number;
+ url: string;
+ page_url: string;
+ page_title: string;
+ host: string;
+ status: string;
+ created_date: string;
+ file_id: string;
+}
+
+interface ViolationFile {
+ violations: ViolationFileDetail[],
+ total_elements: number,
+ total_pages: number,
+ current_page: number,
+ page_size: number,
+ has_next: boolean,
+ has_previous: boolean
+}
+
+export default function ViolationPageViolationsList({
+ fileViolations,
+ currentPage,
+ fileId
+}: {
+ fileViolations: ViolationFile,
+ currentPage: number,
+ fileId: string
+}) {
+ const t = useTranslations('Global');
+ const router = useRouter();
+ const pathname = usePathname();
+
+ if (!fileViolations?.violations) {
+ return null;
+ }
+
+ useEffect(() => {
+ console.log(fileViolations);
+ }, [fileViolations])
+
+ const handlePageChange = (page: number) => {
+ router.push(`${pathname}?page=${page}`);
+ };
+
+ const getPageNumbers = () => {
+ const pages = [];
+ const maxVisible = 5;
+ const total = fileViolations.total_pages;
+
+ let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
+ let end = Math.min(total, start + maxVisible - 1);
+
+ if (end - start + 1 < maxVisible) {
+ start = Math.max(1, end - maxVisible + 1);
+ }
+
+ for (let i = start; i <= end; i++) {
+ pages.push(i);
+ }
+
+ return pages;
+ };
+
return (
-
+
Все нарушения этого файла
- Всего: 0 | Страница 0 из 0
+ Всего: {fileViolations.total_elements} | Страница {fileViolations.current_page} из {fileViolations.total_pages}
- {/* это нужно будет сделать отдельным компонентом */}
-
-
-
-
- 0
- Неавторизованное использование
- 0%
-
-
-
+
+ {fileViolations.violations.map(item => {
+ return (
+
+
+

+
+
+
+ {item.host}
+
+
+
+ );
+ })}
-
+ {fileViolations.total_pages > 1 && (
+
+ {fileViolations.has_previous && (
+
+ )}
+
+ {getPageNumbers().map(pageNum => (
+
+ ))}
+
+ {fileViolations.has_next && (
+
+ )}
+
+ )}
- )
+ );
}
\ No newline at end of file
diff --git a/src/app/ui/violations/violations-check-all-section.tsx b/src/app/ui/violations/violations-check-all-section.tsx
index 99db6ad..cc98c27 100644
--- a/src/app/ui/violations/violations-check-all-section.tsx
+++ b/src/app/ui/violations/violations-check-all-section.tsx
@@ -3,7 +3,7 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useTranslations } from 'next-intl';
import { getViolationSearchStatus, startGlobalMonitoring } from '@/app/actions/violationActions';
-import { useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import ModalWindow from '@/app/components/ModalWindow';
import { toast } from 'sonner';
@@ -40,9 +40,6 @@ export default function ViolationsCheckAllSection() {
return getViolationSearchStatus();
},
select: (data: violationSearchStatus) => {
- console.log('violationSearchStatus');
- console.log(data);
-
if (data) {
return data;
} else {
@@ -62,6 +59,18 @@ export default function ViolationsCheckAllSection() {
const [isProcessing, setIsProcessing] = useState
(false);
const [openWindow, setOpenWindow] = useState(false);
const t = useTranslations('Global');
+ const previousStatusRef = useRef('');
+
+ useEffect(() => {
+ const currentStatus = violationSearchStatus?.status;
+ const previousStatus = previousStatusRef.current;
+
+ if (currentStatus === 'task-not-found' && previousStatus === 'PROCESSING') {
+ queryClient.invalidateQueries({ queryKey: ['violationData'] });
+ toast.success(t('violation-data-updated'));
+ }
+ previousStatusRef.current = currentStatus;
+ }, [violationSearchStatus?.status, queryClient]);
async function openModalWindow() {
await queryClient.invalidateQueries({ queryKey: ['violationSearchStatus'] });
@@ -135,9 +144,9 @@ export default function ViolationsCheckAllSection() {
Поиск нарушений во всех ваших изображениях. Проверка только точных совпадений (95%+).
- {violationSearchStatus?.status && (
+ {(violationSearchStatus?.status !== 'task-not-found') && (
- {violationSearchStatus?.status}
+ {t('content-verification-status')}: {violationSearchStatus?.status}
)}
diff --git a/src/app/ui/violations/violations-table.tsx b/src/app/ui/violations/violations-table.tsx
index da2b555..1787225 100644
--- a/src/app/ui/violations/violations-table.tsx
+++ b/src/app/ui/violations/violations-table.tsx
@@ -3,11 +3,27 @@
import { useMemo, useState } from 'react';
import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, ColumnDef, SortingState, ColumnFiltersState } from '@tanstack/react-table';
import { useTranslations } from 'next-intl';
-import { IconArrowUp, IconArrowDown, IconFilter, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft } from '@/app/ui/icons/icons';
+import { IconArrowUp, IconArrowDown, IconFilter, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconEye } from '@/app/ui/icons/icons';
import DropDownList from '@/app/components/DropDownList';
import { useQuery } from '@tanstack/react-query';
-import { getUserFilesData } from '@/app/actions/fileEntity';
+import { getViolationFilesArray } from '@/app/actions/violationActions';
+import { useViewport } from '@/app/hooks/useViewport';
+import { formatDate, formatDateTime } from '@/app/lib/formatDate';
+import Link from 'next/link';
+interface ViolationFile {
+ createdAt: string;
+ fileId: string;
+ fileName: string;
+ fileSize: number;
+ latestViolationDate: string;
+ mimeType: string;
+ status: string;
+ supportId: number;
+ violationCount: number;
+}
+
+interface ViolationDataResponse extends Array
{ }
export default function ViolationsTable() {
const {
@@ -15,42 +31,65 @@ export default function ViolationsTable() {
isLoading,
isError,
error,
- } = useQuery({
- queryKey: ['violationData', 1, 10000],
- queryFn: () => getUserFilesData(1, 10000),
+ } = useQuery({
+ queryKey: ['violationData'],
+ queryFn: () => getViolationFilesArray(),
select: (data) => {
- if (!data?.files) return [];
-
- return data.files.map((item: any) => {
- /* const newDate = new Date(item.updatedAt).getTime(); */
-
- return {
- id: item.id,
- };
- });
+ return data;
},
- refetchInterval: 30000
+ /* refetchInterval: 30000 */
});
const t = useTranslations('Global');
// Состояния
const [sorting, setSorting] = useState([]);
const [columnFilters, setColumnFilters] = useState([]);
- const [dateFilter, setDateFilter] = useState('all');
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
});
- const columns = useMemo[]>(
+ const viewport = useViewport();
+ const getMaxLengthByWidth = (): number => {
+ if (viewport.device === 'MOBILE') return 26;
+ if (viewport.device === 'TABLET') return 32;
+ if (viewport.device === 'DESKTOP') return 84;
+ return 100;
+ };
+
+ const cutFileName = (fileName: string) => {
+ const MAX_FILE_NAME_LENGTH = getMaxLengthByWidth();
+ const lastDotIndex = fileName.lastIndexOf('.');
+
+ if (lastDotIndex <= 0) {
+ return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.substring(0, MAX_FILE_NAME_LENGTH - 3) + '...' : fileName;
+ }
+
+ const nameWithoutExtension = fileName.substring(0, lastDotIndex);
+ const extension = fileName.substring(lastDotIndex);
+
+ if (fileName.length <= MAX_FILE_NAME_LENGTH) {
+ return fileName;
+ }
+
+ const maxNameLength = MAX_FILE_NAME_LENGTH - extension.length - 3;
+
+ if (maxNameLength <= 0) {
+ return '...' /* + extension */;
+ }
+
+ return nameWithoutExtension.substring(0, maxNameLength) + '...' /* + extension */;
+ }
+
+ const columns = useMemo[]>(
() => [
{
- accessorKey: 'file',
+ accessorKey: 'supportId',
header: ({ column }) => (
- Файл
+ ID