diff --git a/src/app/[locale]/pages/marking-audio/page.tsx b/src/app/[locale]/pages/marking-audio/page.tsx index 9669682..17774ed 100644 --- a/src/app/[locale]/pages/marking-audio/page.tsx +++ b/src/app/[locale]/pages/marking-audio/page.tsx @@ -4,6 +4,8 @@ import PageTitle from '@/app/ui/page-title'; import UploadSectionFile from '@/app/components/upload-section-file'; import { getAllowedFilesExtensions } from '@/app/actions/fileUpload'; import { StackedBarChart } from '@/app/components/StackedBarChart'; +import PageTitleColorFrame from '@/app/ui/page-title-color-frame'; +import ProtectionStatistic from '@/app/ui/marking-page/new/protection-statistic'; const data = [ { @@ -44,9 +46,13 @@ export default async function Page() { return (
- + +
- +
diff --git a/src/app/[locale]/pages/marking-photo/page.tsx b/src/app/[locale]/pages/marking-photo/page.tsx index aeed655..c3fbce1 100644 --- a/src/app/[locale]/pages/marking-photo/page.tsx +++ b/src/app/[locale]/pages/marking-photo/page.tsx @@ -3,8 +3,10 @@ import FilesTable from '@/app/ui/dashboard/files-table'; import PageTitle from '@/app/ui/page-title'; import UploadSectionFile from '@/app/components/upload-section-file'; import { getAllowedFilesExtensions } from '@/app/actions/fileUpload'; -import {StackedBarChart} from '@/app/components/StackedBarChart'; -import {TestComponent} from '@/app/components/test-component'; +import { StackedBarChart } from '@/app/components/StackedBarChart'; +import { TestComponent } from '@/app/components/test-component'; +import PageTitleColorFrame from '@/app/ui/page-title-color-frame'; +import ProtectionStatistic from '@/app/ui/marking-page/new/protection-statistic'; const data = [ { @@ -40,10 +42,14 @@ export default async function Page() { return (
- - + + +
- +
diff --git a/src/app/[locale]/pages/marking-video/page.tsx b/src/app/[locale]/pages/marking-video/page.tsx index 9f7b121..bb89d72 100644 --- a/src/app/[locale]/pages/marking-video/page.tsx +++ b/src/app/[locale]/pages/marking-video/page.tsx @@ -4,6 +4,8 @@ import PageTitle from '@/app/ui/page-title'; import UploadSectionFile from '@/app/components/upload-section-file'; import { getAllowedFilesExtensions } from '@/app/actions/fileUpload'; import { StackedBarChart } from '@/app/components/StackedBarChart'; +import PageTitleColorFrame from '@/app/ui/page-title-color-frame'; +import ProtectionStatistic from '@/app/ui/marking-page/new/protection-statistic'; const data = [ { @@ -44,9 +46,13 @@ export default async function Page() { return (
- + +
- +
diff --git a/src/app/components/tanstakTable.tsx b/src/app/components/tanstakTable.tsx index e240be3..9abc0c7 100644 --- a/src/app/components/tanstakTable.tsx +++ b/src/app/components/tanstakTable.tsx @@ -18,6 +18,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity'; import ModalWindow from '@/app/components/modalWindow'; import { toast } from 'sonner'; +import { pluralize } from '@/app/lib/pluralize'; type FileType = 'image' | 'video' | 'audio'; @@ -603,6 +604,11 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) { }, }); + const pluralizeFiles = (number: number) => { + const translate = [t('file'), t('files-few'), t('files')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + return (
@@ -755,7 +761,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) { - | {t('shown')} {table.getRowModel().rows.length} {t('out-of')} {filteredData.length} {t('files')} + | {t('shown')} {table.getRowModel().rows.length} {t('out-of')} {filteredData.length} {pluralizeFiles(filteredData.length || 0)}
diff --git a/src/app/lib/pluralize.ts b/src/app/lib/pluralize.ts index 9f10a51..4efe7fd 100644 --- a/src/app/lib/pluralize.ts +++ b/src/app/lib/pluralize.ts @@ -1,5 +1,13 @@ +'use client' + export const pluralize = (number: number, one: string, few: string, many: string) => { - const currentLang = document.documentElement.getAttribute('lang') || 'ru'; + const getCurrentLang = () => { + if (typeof window !== 'undefined') { + return document?.documentElement?.getAttribute('lang') || 'ru'; + } + return 'ru'; // значение по умолчанию для сервера + } + const currentLang = getCurrentLang(); if (currentLang === 'ru') { const n = Math.abs(number) % 100; diff --git a/src/app/styles/global-styles.scss b/src/app/styles/global-styles.scss index 9235c26..8e8c23a 100644 --- a/src/app/styles/global-styles.scss +++ b/src/app/styles/global-styles.scss @@ -32,6 +32,30 @@ } } +.page-title-color-frame { + background: linear-gradient(135deg, v.$p-color 0%, v.$s-color 100%); + padding: 2rem; + border-radius: 16px; + margin-bottom: 2rem; + box-shadow: 0 10px 40px rgba(102, 126, 234, 0.2); + + h1 { + color: v.$white; + font-size: 2rem; + font-weight: 700; + margin: 0 0 0.5rem 0; + display: flex; + align-items: center; + gap: 0.75rem; + } + + p { + color: rgba(255, 255, 255, 0.9); + font-size: 1rem; + margin: 0; + } +} + .main-containter-wrapper { margin-left: var(--side-bar-width); ; @@ -399,4 +423,8 @@ } } } +} + +.yaPreloadingSuggestBlockContainer { + height: 52px !important; } \ No newline at end of file diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index b32f128..aefb378 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -1825,6 +1825,35 @@ } } -.yaPreloadingSuggestBlockContainer { - height: 52px !important; +.protection-statistic { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; + + &-stat-card { + background: white; + padding: 1.5rem; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07); + border: 2px solid #f3f4f6; + text-align: center; + transition: all 0.3s; + } + + &-stat-number { + font-size: 2rem; + font-weight: 700; + /* background: linear-gradient(135deg, #667eea, #764ba2); + background-clip: text; + -webkit-text-fill-color: transparent; */ + color: v.$p-color; + margin-bottom: 0.5rem; + } + + &-stat-label { + font-size: 0.9rem; + color: #6b7280; + font-weight: 500; + } } \ No newline at end of file diff --git a/src/app/ui/marking-page/new/protection-statistic.tsx b/src/app/ui/marking-page/new/protection-statistic.tsx new file mode 100644 index 0000000..dab0d3d --- /dev/null +++ b/src/app/ui/marking-page/new/protection-statistic.tsx @@ -0,0 +1,91 @@ +'use client' + +import { useTranslations } from 'next-intl'; +import { useQuery } from '@tanstack/react-query'; +import { getUserFilesInfo } from '@/app/actions/action'; +import { convertBytes } from '@/app/lib/convertBytes'; + +type FilesInfo = { + totalSize: number; + totalCount: number; +}; + +export default function ProtectionStatistic({ fileType }: { fileType: string }) { + const t = useTranslations('Global'); + + const getFileTypeFields = (fileType: string): { totalSize: string, totalCount: string } => { + switch (fileType) { + case 'image': + return { + totalSize: 'images_size', + totalCount: 'images_quantity' + }; + case 'video': + return { + totalSize: 'videos_size', + totalCount: 'videos_quantity' + }; + case 'audio': + return { + totalSize: 'audios_size', + totalCount: 'audios_quantity' + }; + default: + return { + totalSize: 'all_files_size', + totalCount: 'all_files_quantity' + }; + } + }; + + const { + data: filesInfo, + isLoading, + isError, + error, + } = useQuery<{ + all_files_size: number, + all_files_quantity: number + }, Error, FilesInfo>({ + queryKey: ['userFilesInfo'], + queryFn: getUserFilesInfo, + select: (data): FilesInfo => { + if (!data) { + return { + totalSize: 0, + totalCount: 0 + } + } + + const fields = getFileTypeFields(fileType); + + return { + totalSize: data[fields.totalSize as keyof typeof data], + totalCount: data[fields.totalCount as keyof typeof data] + } + } + }); + + return ( +
+
+
0
+
Видео защищено
+
+
+
0
+
Видео проверялось
+
+
+
+ {filesInfo?.totalSize ? convertBytes(filesInfo?.totalSize) : 0} +
+
Занимают видео
+
+
+
0
+
Найдено нарушений
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/ui/page-title-color-frame.tsx b/src/app/ui/page-title-color-frame.tsx new file mode 100644 index 0000000..cbf3d4b --- /dev/null +++ b/src/app/ui/page-title-color-frame.tsx @@ -0,0 +1,16 @@ +import { useTranslations } from 'next-intl'; + +export default function PageTitleColorFrame({ title, description }: { title: string, description: string }) { + const t = useTranslations('Global'); + + return ( +
+

+ {t(title)} +

+

+ {t(description)} +

+
+ ) +} \ No newline at end of file diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 304e07e..cdd4560 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -166,7 +166,10 @@ "your-image": "Your images", "your-videos": "Your videos", "your-audios": "Your audios", - "your-documents": "Your documents" + "your-documents": "Your documents", + "multi-layered-protection-for-your-images": "Multi-layered image content protection with invisible watermarks", + "multi-layered-protection-for-your-audios": "Multi-layered audio content protection with invisible watermarks", + "multi-layered-protection-for-your-videos": "Multi-layered video content protection with invisible watermarks" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 4e45131..ffcaa34 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -166,7 +166,10 @@ "your-image": "Ваши изображения", "your-videos": "Ваш видео", "your-audios": "Ваш аудио", - "your-documents": "Ваши документы" + "your-documents": "Ваши документы", + "multi-layered-protection-for-your-images": "Многослойная защита изображений с невидимыми водяными знаками", + "multi-layered-protection-for-your-audios": "Многослойная защита аудиоконтента с невидимыми водяными знаками", + "multi-layered-protection-for-your-videos": "Многослойная защита видеоконтента с невидимыми водяными знаками" }, "Login-register-form": { "and": "и",