add new marking-content layout
This commit is contained in:
@@ -4,6 +4,8 @@ import PageTitle from '@/app/ui/page-title';
|
|||||||
import UploadSectionFile from '@/app/components/upload-section-file';
|
import UploadSectionFile from '@/app/components/upload-section-file';
|
||||||
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
||||||
import { StackedBarChart } from '@/app/components/StackedBarChart';
|
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 = [
|
const data = [
|
||||||
{
|
{
|
||||||
@@ -44,9 +46,13 @@ export default async function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageTitle title="audio-protection" />
|
<PageTitleColorFrame
|
||||||
|
title="audio-protection"
|
||||||
|
description="multi-layered-protection-for-your-audios"
|
||||||
|
/>
|
||||||
|
<ProtectionStatistic fileType={FILE_TYPE} />
|
||||||
<div className="split-blocks">
|
<div className="split-blocks">
|
||||||
<ProtectionSummary fileType={FILE_TYPE}/>
|
<ProtectionSummary fileType={FILE_TYPE} />
|
||||||
<div className="protection-overview">
|
<div className="protection-overview">
|
||||||
<StackedBarChart data={data} />
|
<StackedBarChart data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import FilesTable from '@/app/ui/dashboard/files-table';
|
|||||||
import PageTitle from '@/app/ui/page-title';
|
import PageTitle from '@/app/ui/page-title';
|
||||||
import UploadSectionFile from '@/app/components/upload-section-file';
|
import UploadSectionFile from '@/app/components/upload-section-file';
|
||||||
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
||||||
import {StackedBarChart} from '@/app/components/StackedBarChart';
|
import { StackedBarChart } from '@/app/components/StackedBarChart';
|
||||||
import {TestComponent} from '@/app/components/test-component';
|
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 = [
|
const data = [
|
||||||
{
|
{
|
||||||
@@ -40,10 +42,14 @@ export default async function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageTitle title="image-protection" />
|
<PageTitleColorFrame
|
||||||
<TestComponent/>
|
title="image-protection"
|
||||||
|
description="multi-layered-protection-for-your-images"
|
||||||
|
/>
|
||||||
|
<TestComponent />
|
||||||
|
<ProtectionStatistic fileType={FILE_TYPE}/>
|
||||||
<div className="split-blocks">
|
<div className="split-blocks">
|
||||||
<ProtectionSummary fileType={FILE_TYPE}/>
|
<ProtectionSummary fileType={FILE_TYPE} />
|
||||||
<div className="protection-overview">
|
<div className="protection-overview">
|
||||||
<StackedBarChart data={data} />
|
<StackedBarChart data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import PageTitle from '@/app/ui/page-title';
|
|||||||
import UploadSectionFile from '@/app/components/upload-section-file';
|
import UploadSectionFile from '@/app/components/upload-section-file';
|
||||||
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
import { getAllowedFilesExtensions } from '@/app/actions/fileUpload';
|
||||||
import { StackedBarChart } from '@/app/components/StackedBarChart';
|
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 = [
|
const data = [
|
||||||
{
|
{
|
||||||
@@ -44,9 +46,13 @@ export default async function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageTitle title="video-protection" />
|
<PageTitleColorFrame
|
||||||
|
title="video-protection"
|
||||||
|
description="multi-layered-protection-for-your-videos"
|
||||||
|
/>
|
||||||
|
<ProtectionStatistic fileType={FILE_TYPE} />
|
||||||
<div className="split-blocks">
|
<div className="split-blocks">
|
||||||
<ProtectionSummary fileType={FILE_TYPE}/>
|
<ProtectionSummary fileType={FILE_TYPE} />
|
||||||
<div className="protection-overview">
|
<div className="protection-overview">
|
||||||
<StackedBarChart data={data} />
|
<StackedBarChart data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity';
|
import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity';
|
||||||
import ModalWindow from '@/app/components/modalWindow';
|
import ModalWindow from '@/app/components/modalWindow';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { pluralize } from '@/app/lib/pluralize';
|
||||||
|
|
||||||
type FileType = 'image' | 'video' | 'audio';
|
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 (
|
return (
|
||||||
<div className="tanstak-table-wrapper">
|
<div className="tanstak-table-wrapper">
|
||||||
<ModalWindow children={openWindowChildren} state={openWindow} callBack={setOpenWindow}></ModalWindow>
|
<ModalWindow children={openWindowChildren} state={openWindow} callBack={setOpenWindow}></ModalWindow>
|
||||||
@@ -755,7 +761,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
|
|||||||
</strong>
|
</strong>
|
||||||
</span>
|
</span>
|
||||||
<span className="pagination-info-files">
|
<span className="pagination-info-files">
|
||||||
| {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)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
export const pluralize = (number: number, one: string, few: string, many: string) => {
|
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') {
|
if (currentLang === 'ru') {
|
||||||
const n = Math.abs(number) % 100;
|
const n = Math.abs(number) % 100;
|
||||||
|
|||||||
@@ -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 {
|
.main-containter-wrapper {
|
||||||
margin-left: var(--side-bar-width);
|
margin-left: var(--side-bar-width);
|
||||||
;
|
;
|
||||||
@@ -400,3 +424,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yaPreloadingSuggestBlockContainer {
|
||||||
|
height: 52px !important;
|
||||||
|
}
|
||||||
@@ -1825,6 +1825,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yaPreloadingSuggestBlockContainer {
|
.protection-statistic {
|
||||||
height: 52px !important;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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 (
|
||||||
|
<div className="protection-statistic">
|
||||||
|
<div className="protection-statistic-stat-card">
|
||||||
|
<div className="protection-statistic-stat-number">0</div>
|
||||||
|
<div className="protection-statistic-stat-label">Видео защищено</div>
|
||||||
|
</div>
|
||||||
|
<div className="protection-statistic-stat-card">
|
||||||
|
<div className="protection-statistic-stat-number">0</div>
|
||||||
|
<div className="protection-statistic-stat-label">Видео проверялось</div>
|
||||||
|
</div>
|
||||||
|
<div className="protection-statistic-stat-card">
|
||||||
|
<div className="protection-statistic-stat-number">
|
||||||
|
{filesInfo?.totalSize ? convertBytes(filesInfo?.totalSize) : 0}
|
||||||
|
</div>
|
||||||
|
<div className="protection-statistic-stat-label">Занимают видео</div>
|
||||||
|
</div>
|
||||||
|
<div className="protection-statistic-stat-card">
|
||||||
|
<div className="protection-statistic-stat-number">0</div>
|
||||||
|
<div className="protection-statistic-stat-label">Найдено нарушений</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
|
export default function PageTitleColorFrame({ title, description }: { title: string, description: string }) {
|
||||||
|
const t = useTranslations('Global');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page-title-color-frame">
|
||||||
|
<h1>
|
||||||
|
{t(title)}
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
{t(description)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -166,7 +166,10 @@
|
|||||||
"your-image": "Your images",
|
"your-image": "Your images",
|
||||||
"your-videos": "Your videos",
|
"your-videos": "Your videos",
|
||||||
"your-audios": "Your audios",
|
"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": {
|
"Login-register-form": {
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|||||||
@@ -166,7 +166,10 @@
|
|||||||
"your-image": "Ваши изображения",
|
"your-image": "Ваши изображения",
|
||||||
"your-videos": "Ваш видео",
|
"your-videos": "Ваш видео",
|
||||||
"your-audios": "Ваш аудио",
|
"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": {
|
"Login-register-form": {
|
||||||
"and": "и",
|
"and": "и",
|
||||||
|
|||||||
Reference in New Issue
Block a user