add my content layout

This commit is contained in:
smanylov
2026-01-15 12:27:39 +07:00
parent 719a3e3582
commit 7213e40b96
13 changed files with 257 additions and 30 deletions
+6 -2
View File
@@ -1,14 +1,18 @@
import MyContentInfoBlock from '@/app/ui/my-content/my-content-info-block'; import MyContentInfoBlock from '@/app/ui/my-content/my-content-info-block';
import FilesTable from '@/app/ui/dashboard/files-table'; import FilesTable from '@/app/ui/dashboard/files-table';
import MyContentPieChart from '@/app/ui/my-content/my-content-pie-chart'; import MyContentPieChart from '@/app/ui/my-content/my-content-pie-chart';
import MyContentPageTitleColorFrame from '@/app/ui/my-content/new/my-content-page-title';
import MyContentStatsOverview from '@/app/ui/my-content/new/my-content-stats-overview';
export default function Page() { export default function Page() {
return ( return (
<> <>
<div className="split-blocks"> <MyContentPageTitleColorFrame title="my-content" description="my-content-description" />
<MyContentStatsOverview />
{/* <div className="split-blocks">
<MyContentInfoBlock /> <MyContentInfoBlock />
<MyContentPieChart /> <MyContentPieChart />
</div> </div> */}
<FilesTable fileType={'all'} /> <FilesTable fileType={'all'} />
</> </>
) )
+4 -3
View File
@@ -1,6 +1,6 @@
'use client' 'use client'
import { useTranslations } from 'next-intl'; import { useTranslations, useLocale } from 'next-intl';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { BarChart, Bar, Cell, XAxis, Tooltip } from 'recharts'; import { BarChart, Bar, Cell, XAxis, Tooltip } from 'recharts';
import { pluralize } from '@/app/lib/pluralize'; import { pluralize } from '@/app/lib/pluralize';
@@ -24,6 +24,7 @@ interface BarShapeProps {
} }
export const StackedBarChart = ({ data }: Files) => { export const StackedBarChart = ({ data }: Files) => {
const locale = useLocale();
const t = useTranslations('Global'); const t = useTranslations('Global');
const CHECKS_COLOR = '#6366f1'; const CHECKS_COLOR = '#6366f1';
@@ -154,12 +155,12 @@ export const StackedBarChart = ({ data }: Files) => {
const pluralizeCheks = (number: number) => { const pluralizeCheks = (number: number) => {
const translate = [t('check'), t('checks-few'), t('checks')]; const translate = [t('check'), t('checks-few'), t('checks')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const pluralizeViolations = (number: number) => { const pluralizeViolations = (number: number) => {
const translate = [t('violation'), t('violations-few'), t('violations')]; const translate = [t('violation'), t('violations-few'), t('violations')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
return ( return (
+3 -2
View File
@@ -12,7 +12,7 @@ import {
ColumnFiltersState, ColumnFiltersState,
} from '@tanstack/react-table'; } from '@tanstack/react-table';
import { IconImageFile, IconVideoFile, IconAudioFile, IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload, IconShieldExclamation, IconDelete } from '@/app/ui/icons/icons'; import { IconImageFile, IconVideoFile, IconAudioFile, IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload, IconShieldExclamation, IconDelete } from '@/app/ui/icons/icons';
import { useTranslations } from 'next-intl'; import { useTranslations, useLocale } from 'next-intl';
import DropDownList from '@/app/components/dropDownList'; import DropDownList from '@/app/components/dropDownList';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity'; import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity';
@@ -156,6 +156,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
const [isFileLoading, setIsFileLoading] = useState(false); const [isFileLoading, setIsFileLoading] = useState(false);
const t = useTranslations("Global"); const t = useTranslations("Global");
const locale = useLocale();
// Определение колонок // Определение колонок
const columns = useMemo<ColumnDef<FileItem>[]>( const columns = useMemo<ColumnDef<FileItem>[]>(
@@ -606,7 +607,7 @@ export default function TanstakFilesTable({ fileType }: { fileType: string }) {
const pluralizeFiles = (number: number) => { const pluralizeFiles = (number: number) => {
const translate = [t('file'), t('files-few'), t('files')]; const translate = [t('file'), t('files-few'), t('files')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
return ( return (
+1 -9
View File
@@ -1,14 +1,6 @@
'use client' 'use client'
export const pluralize = (number: number, one: string, few: string, many: string) => { export const pluralize = (number: number, one: string, few: string, many: string, currentLang: string) => {
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;
const n1 = n % 10; const n1 = n % 10;
+35 -2
View File
@@ -38,9 +38,9 @@
border-radius: 16px; border-radius: 16px;
margin-bottom: 2rem; margin-bottom: 2rem;
box-shadow: 0 10px 40px rgba(102, 126, 234, 0.2); box-shadow: 0 10px 40px rgba(102, 126, 234, 0.2);
color: v.$white;
h1 { h1 {
color: v.$white;
font-size: 2rem; font-size: 2rem;
font-weight: 700; font-weight: 700;
margin: 0 0 0.5rem 0; margin: 0 0 0.5rem 0;
@@ -50,10 +50,43 @@
} }
p { p {
color: rgba(255, 255, 255, 0.9);
font-size: 1rem; font-size: 1rem;
margin: 0; margin: 0;
} }
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 40px;
.header-main {
flex: 1;
}
.header-stats {
display: flex;
gap: 28px;
align-items: center;
}
.header-stat-item {
text-align: center;
}
.header-stat-value {
font-size: 28px;
font-weight: 700;
margin-bottom: 4px;
}
.header-stat-label {
font-size: 13px;
opacity: 0.85;
text-transform: uppercase;
letter-spacing: 0.5px;
}
}
} }
.main-containter-wrapper { .main-containter-wrapper {
+81
View File
@@ -1856,4 +1856,85 @@
color: #6b7280; color: #6b7280;
font-weight: 500; font-weight: 500;
} }
}
.my-content-stats-overview {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
@media (max-width: 1400px) {
grid-template-columns: repeat(2, 1fr);
}
.stat-card.files {
--card-color-1: #ff6b8a;
--card-color-2: #ff8da1;
}
.stat-card.protected {
--card-color-1: #8b5cf6;
--card-color-2: #a78bfa;
}
.stat-card.checks {
--card-color-1: #10b981;
--card-color-2: #34d399;
}
.stat-card.storage {
--card-color-1: #f59e0b;
--card-color-2: #fbbf24;
}
.stat-card {
background: linear-gradient(135deg, var(--card-color-1), var(--card-color-2));
border-radius: 20px;
padding: 32px 28px;
color: white;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
transition: all 0.4s ease;
cursor: pointer;
position: relative;
overflow: hidden;
&:hover {
transform: translateY(-8px);
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
}
}
.stat-icon {
font-size: 36px;
}
.stat-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
position: relative;
z-index: 1;
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: white;
margin-bottom: 6px;
position: relative;
z-index: 1;
}
.stat-label {
font-size: 13px;
color: white;
opacity: 0.9;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
position: relative;
z-index: 1;
}
} }
+5 -4
View File
@@ -2,7 +2,7 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import { PieChartComponent } from '@/app/components/PieChartComponent'; import { PieChartComponent } from '@/app/components/PieChartComponent';
import { useTranslations } from 'next-intl'; import {useTranslations, useLocale} from 'next-intl';
import Link from 'next/link'; import Link from 'next/link';
import { useClickOutside } from '@/app/hooks/useClickOutside'; import { useClickOutside } from '@/app/hooks/useClickOutside';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
@@ -30,6 +30,7 @@ export default function ProtectionOverview() {
{ name: 'videos', value: 0, color: '#2f9e44' }, { name: 'videos', value: 0, color: '#2f9e44' },
{ name: 'audios', value: 0, color: '#1971c2' }, { name: 'audios', value: 0, color: '#1971c2' },
]); ]);
const locale = useLocale();
const t = useTranslations('Global'); const t = useTranslations('Global');
const [openDropDownList, setOpenDropDownList] = useState(false); const [openDropDownList, setOpenDropDownList] = useState(false);
const dropDownList = useRef(null); const dropDownList = useRef(null);
@@ -106,17 +107,17 @@ export default function ProtectionOverview() {
const pluralizeFiles = (number: number) => { const pluralizeFiles = (number: number) => {
const translate = [t('file'), t('files-few'), t('files')]; const translate = [t('file'), t('files-few'), t('files')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const pluralizeCheks = (number: number) => { const pluralizeCheks = (number: number) => {
const translate = [t('check'), t('checks-few'), t('checks')]; const translate = [t('check'), t('checks-few'), t('checks')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const pluralizeViolations = (number: number) => { const pluralizeViolations = (number: number) => {
const translate = [t('violation'), t('violations-few'), t('violations')]; const translate = [t('violation'), t('violations-few'), t('violations')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const CHECKS = 6; const CHECKS = 6;
@@ -1,6 +1,6 @@
'use client' 'use client'
import { useTranslations } from 'next-intl'; import {useTranslations, useLocale} from 'next-intl';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { getUserFilesInfo } from '@/app/actions/action'; import { getUserFilesInfo } from '@/app/actions/action';
import { convertBytes } from '@/app/lib/convertBytes'; import { convertBytes } from '@/app/lib/convertBytes';
@@ -72,19 +72,21 @@ export default function ProtectionSummary({ fileType }: { fileType: string }) {
} }
}); });
const locale = useLocale();
const pluralizeFilesName = (number: number) => { const pluralizeFilesName = (number: number) => {
const translate = [t(`${fileType}`), t(`${fileType}s-few`), t(`${fileType}s`)]; const translate = [t(`${fileType}`), t(`${fileType}s-few`), t(`${fileType}s`)];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const pluralizeCheks = (number: number) => { const pluralizeCheks = (number: number) => {
const translate = [t('check'), t('checks-few'), t('checks')]; const translate = [t('check'), t('checks-few'), t('checks')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
const pluralizeViolations = (number: number) => { const pluralizeViolations = (number: number) => {
const translate = [t('violation'), t('violations-few'), t('violations')]; const translate = [t('violation'), t('violations-few'), t('violations')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
return ( return (
@@ -3,11 +3,12 @@
import { IconDocument, IconImageFile, IconVideoFile, IconAudioFile } from '@/app/ui/icons/icons'; import { IconDocument, IconImageFile, IconVideoFile, IconAudioFile } from '@/app/ui/icons/icons';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { getUserFilesInfo } from '@/app/actions/action'; import { getUserFilesInfo } from '@/app/actions/action';
import { useTranslations } from 'next-intl'; import {useTranslations, useLocale} from 'next-intl';
import { convertBytes } from '@/app/lib/convertBytes'; import { convertBytes } from '@/app/lib/convertBytes';
import { pluralize } from '@/app/lib/pluralize'; import { pluralize } from '@/app/lib/pluralize';
export default function MyContentInfoBlock() { export default function MyContentInfoBlock() {
const locale = useLocale();
const t = useTranslations('Global'); const t = useTranslations('Global');
const { const {
@@ -70,7 +71,7 @@ export default function MyContentInfoBlock() {
const pluralizeFiles = (number: number) => { const pluralizeFiles = (number: number) => {
const translate = [t('file'), t('files-few'), t('files')]; const translate = [t('file'), t('files-few'), t('files')];
return pluralize(number, translate[0], translate[1], translate[2]); return pluralize(number, translate[0], translate[1], translate[2], locale);
}; };
return ( return (
@@ -0,0 +1,50 @@
'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';
export default function MyContentPageTitleColorFrame({ title, description }: { title: string, description: string }) {
const t = useTranslations('Global');
const {
data: filesInfo,
isLoading,
isError,
error,
} = useQuery({
queryKey: ['userFilesInfo'],
queryFn: getUserFilesInfo,
});
return (
<div className="page-title-color-frame">
<div className="header-content">
<div className="header-main">
<h1>
{t(title)}
</h1>
<p>
{t(description)}
</p>
</div>
<div className="header-stats">
<div className="header-stat-item">
<div className="header-stat-value">{filesInfo?.all_files_quantity}</div>
<div className="header-stat-label">Файлов</div>
</div>
<div className="header-stat-divider"></div>
<div className="header-stat-item">
<div className="header-stat-value">0</div>
<div className="header-stat-label">Защищено</div>
</div>
<div className="header-stat-divider"></div>
<div className="header-stat-item">
<div className="header-stat-value">{filesInfo.all_files_size ? convertBytes(filesInfo.all_files_size) : 0}</div>
<div className="header-stat-label">Хранилище</div>
</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,59 @@
'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';
export default function MyContentStatsOverview() {
const t = useTranslations('Global');
const {
data: filesInfo,
isLoading,
isError,
error,
} = useQuery({
queryKey: ['userFilesInfo'],
queryFn: getUserFilesInfo,
});
return (
<div className="my-content-stats-overview">
<div className="stat-card files" data-icon="📄">
<div className="stat-header">
<div className="stat-icon">📄</div>
<div className="stat-trend">+0%</div>
</div>
<div className="stat-value">{filesInfo?.all_files_quantity}</div>
<div className="stat-label">Всего файлов</div>
</div>
<div className="stat-card protected" data-icon="🛡">
<div className="stat-header">
<div className="stat-icon">🛡</div>
<div className="stat-trend">+0%</div>
</div>
<div className="stat-value">0</div>
<div className="stat-label">Защищено файлов</div>
</div>
<div className="stat-card checks" data-icon="🔍">
<div className="stat-header">
<div className="stat-icon">🔍</div>
<div className="stat-trend">+0%</div>
</div>
<div className="stat-value">0</div>
<div className="stat-label">Проверок защиты</div>
</div>
<div className="stat-card storage" data-icon="💾">
<div className="stat-header">
<div className="stat-icon">💾</div>
<div className="stat-trend">+0%</div>
</div>
<div className="stat-value">{filesInfo.all_files_size ? convertBytes(filesInfo.all_files_size) : 0}</div>
<div className="stat-label">Хранилище</div>
</div>
</div>
)
}
+2 -1
View File
@@ -169,7 +169,8 @@
"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-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-audios": "Multi-layered audio content protection with invisible watermarks",
"multi-layered-protection-for-your-videos": "Multi-layered video content protection with invisible watermarks" "multi-layered-protection-for-your-videos": "Multi-layered video content protection with invisible watermarks",
"my-content-description": "Managing protected files and copyright control"
}, },
"Login-register-form": { "Login-register-form": {
"and": "and", "and": "and",
+2 -1
View File
@@ -169,7 +169,8 @@
"your-documents": "Ваши документы", "your-documents": "Ваши документы",
"multi-layered-protection-for-your-images": "Многослойная защита изображений с невидимыми водяными знаками", "multi-layered-protection-for-your-images": "Многослойная защита изображений с невидимыми водяными знаками",
"multi-layered-protection-for-your-audios": "Многослойная защита аудиоконтента с невидимыми водяными знаками", "multi-layered-protection-for-your-audios": "Многослойная защита аудиоконтента с невидимыми водяными знаками",
"multi-layered-protection-for-your-videos": "Многослойная защита видеоконтента с невидимыми водяными знаками" "multi-layered-protection-for-your-videos": "Многослойная защита видеоконтента с невидимыми водяными знаками",
"my-content-description": "Управление защищенными файлами и контролем авторских прав"
}, },
"Login-register-form": { "Login-register-form": {
"and": "и", "and": "и",