change dash-board layout

This commit is contained in:
smanylov
2026-01-13 15:29:34 +07:00
parent 2e928fb91a
commit afb6bf1760
14 changed files with 1484 additions and 14 deletions
+72 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "no-copy-frontend", "name": "no-copy-frontend",
"version": "0.15.0", "version": "0.17.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "no-copy-frontend", "name": "no-copy-frontend",
"version": "0.15.0", "version": "0.17.0",
"dependencies": { "dependencies": {
"@react-input/mask": "^2.0.4", "@react-input/mask": "^2.0.4",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
@@ -21,8 +21,10 @@
"next-intl": "^4.5.8", "next-intl": "^4.5.8",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"react-slick": "^0.31.0",
"recharts": "^3.5.1", "recharts": "^3.5.1",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"validator": "^13.15.26", "validator": "^13.15.26",
"zod": "^4.1.13", "zod": "^4.1.13",
@@ -3484,6 +3486,12 @@
"url": "https://paulmillr.com/funding/" "url": "https://paulmillr.com/funding/"
} }
}, },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/client-only": { "node_modules/client-only": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -5581,6 +5589,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/json2mq": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
"license": "MIT",
"dependencies": {
"string-convert": "^0.2.0"
}
},
"node_modules/json5": { "node_modules/json5": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -5919,6 +5936,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -6676,6 +6699,22 @@
} }
} }
}, },
"node_modules/react-slick": {
"version": "0.31.0",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.31.0.tgz",
"integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==",
"license": "MIT",
"dependencies": {
"classnames": "^2.2.5",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -6791,6 +6830,12 @@
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.11", "version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -7205,6 +7250,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/string-convert": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
"license": "MIT"
},
"node_modules/string.prototype.includes": { "node_modules/string.prototype.includes": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -7390,6 +7441,25 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/swiper": {
"version": "12.0.3",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-12.0.3.tgz",
"integrity": "sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
+2
View File
@@ -22,8 +22,10 @@
"next-intl": "^4.5.8", "next-intl": "^4.5.8",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"react-slick": "^0.31.0",
"recharts": "^3.5.1", "recharts": "^3.5.1",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"validator": "^13.15.26", "validator": "^13.15.26",
"zod": "^4.1.13", "zod": "^4.1.13",
+28 -10
View File
@@ -5,6 +5,14 @@ import StatsGrid from '@/app/ui/dashboard/stats-grid';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import FilesTable from '@/app/ui/dashboard/files-table'; import FilesTable from '@/app/ui/dashboard/files-table';
import PageTitle from '@/app/ui/page-title'; import PageTitle from '@/app/ui/page-title';
import DashboardUserStats from '@/app/ui/dashboard/new/dashboard-user-stats';
import DashboardUserFiles from '@/app/ui/dashboard/new/dashboard-user-files';
import DashboardPlatformsList from '@/app/ui/dashboard/new/dashboard-platforms-list';
import DashboardFilesInfo from '@/app/ui/dashboard/new/dashboard-files-info';
import DahboardLimitsSection from '@/app/ui/dashboard/new/dashborad-limits-section';
import DahboardGeographySection from '@/app/ui/dashboard/new/dashboard-geography-section';
import DashboardLastCheck from '@/app/ui/dashboard/new/dashboard-last-check';
import ViolationsCarousel from '@/app/ui/dashboard/new/violations-carousel';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Dashboard', title: 'Dashboard',
@@ -14,16 +22,26 @@ export default function Page() {
const t = useTranslations('Global'); const t = useTranslations('Global');
return ( return (
<> <Suspense fallback={<>...</>}>
<Suspense fallback={<>...</>}> <PageTitle title="statistics">
<PageTitle title="statistics"> </PageTitle>
</PageTitle> <DashboardUserStats />
<div className="split-blocks"> <div className="dashboard-main-grid">
<ProtectionOverview /> <div className="left-column">
<StatsGrid /> <DashboardUserFiles />
<DashboardPlatformsList />
</div> </div>
<FilesTable fileType={'all'} /> <div className="right-column">
</Suspense> <ViolationsCarousel />
</> </div>
</div>
<DashboardFilesInfo />
<div className="grid grid-cols-2 mb-[30px] gap-[20px]">
<DahboardLimitsSection />
<DahboardGeographySection />
</div>
<FilesTable fileType={'all'} />
<DashboardLastCheck />
</Suspense>
); );
} }
@@ -0,0 +1,29 @@
import { Metadata } from 'next';
import { Suspense } from 'react';
import ProtectionOverview from '@/app/ui/dashboard/protection-overview';
import StatsGrid from '@/app/ui/dashboard/stats-grid';
import { useTranslations } from 'next-intl';
import FilesTable from '@/app/ui/dashboard/files-table';
import PageTitle from '@/app/ui/page-title';
export const metadata: Metadata = {
title: 'Dashboard',
};
export default function Page() {
const t = useTranslations('Global');
return (
<>
<Suspense fallback={<>...</>}>
<PageTitle title="statistics">
</PageTitle>
<div className="split-blocks">
<ProtectionOverview />
<StatsGrid />
</div>
<FilesTable fileType={'all'} />
</Suspense>
</>
);
}
-2
View File
@@ -299,8 +299,6 @@ export async function tokenLifeExtension() {
if (response.ok) { if (response.ok) {
const parsed = await response.json(); const parsed = await response.json();
console.log('tokenLifeExtension');
console.log(parsed.message_code);
await updateSession(); await updateSession();
} }
} catch (error) { } catch (error) {
+717
View File
@@ -1107,3 +1107,720 @@
} }
} }
} }
.dashboard-user-stats {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
margin-bottom: 40px;
.stat-card.matches {
--card-color-1: #3b82f6;
--card-color-2: #60a5fa;
}
.stat-card.projects {
--card-color-1: #6366f1;
--card-color-2: #8b5cf6;
}
.stat-card.violations {
--card-color-1: #ef4444;
--card-color-2: #f87171;
}
.stat-card.storage {
--card-color-1: #8b5cf6;
--card-color-2: #a78bfa;
}
.stat-card.legal {
--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: 30px 24px;
color: white;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.stat-type {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.2px;
opacity: 0.9;
margin-bottom: 16px;
}
.stat-value {
font-size: 42px;
font-weight: 800;
line-height: 1;
margin-bottom: 12px;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.stat-label {
font-size: 14px;
font-weight: 500;
opacity: 0.95;
}
.stat-icon {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 48px;
opacity: 0.2;
filter: blur(1px);
}
}
.dashboard-main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
.left-column {
display: flex;
flex-direction: column;
gap: 30px;
}
.right-column {
display: flex;
flex-direction: column;
}
.content-section {
background: white;
border-radius: 20px;
padding: 32px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.section-title {
font-size: 22px;
font-weight: 700;
color: #1e293b;
}
.view-all-link {
color: #6366f1;
text-decoration: none;
font-size: 14px;
font-weight: 600;
}
}
}
.user-files-list {
display: flex;
flex-direction: column;
gap: 16px;
.file-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 14px;
border: 1px solid #e2e8f0;
transition: all 0.3s ease;
}
.file-icon {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
&.video {
background: linear-gradient(135deg, #f3e8ff, #e9d5ff);
}
}
.file-info {
flex: 1;
min-width: 0;
}
.file-name {
font-weight: 600;
color: #1e293b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.file-meta {
font-size: 13px;
color: #64748b;
}
.file-badge {
padding: 4px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 700;
white-space: nowrap;
&.protected {
background: #d1fae5;
color: #065f46;
}
}
}
.dashboard-platforms-list {
display: flex;
flex-direction: column;
gap: 12px;
.platform-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
border-radius: 12px;
transition: all 0.3s ease;
}
.platform-name {
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
color: #1e293b;
}
.platform-count {
font-weight: 700;
color: #6366f1;
font-size: 16px;
}
}
.dashboard-files-info {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 40px;
.type-card.images {
--card-color-1: #ff6b8a;
--card-color-2: #ff8da1;
}
.type-card.videos {
--card-color-1: #8b5cf6;
--card-color-2: #a78bfa;
}
.type-card.audio {
--card-color-1: #10b981;
--card-color-2: #34d399;
}
.type-card.scripts {
--card-color-1: #f59e0b;
--card-color-2: #fbbf24;
}
.type-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;
.stat-type {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.2px;
opacity: 0.9;
margin-bottom: 16px;
position: relative;
z-index: 1;
}
.stat-value {
font-size: 48px;
font-weight: 800;
margin-bottom: 12px;
position: relative;
z-index: 1;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.stat-label {
font-size: 14px;
opacity: 0.95;
margin-bottom: 16px;
position: relative;
z-index: 1;
}
.type-card-stats {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
}
.type-card-stat-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
}
.type-card-stat-label {
opacity: 0.9;
font-weight: 500;
}
.type-card-stat-value {
font-weight: 700;
font-size: 14px;
}
}
}
.dashboard-limits-section {
background: white;
border-radius: 20px;
padding: 32px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
height: 100%;
.limits-grid {
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 20px;
flex: 1;
}
.limit-item {
display: flex;
flex-direction: column;
gap: 12px;
}
.limit-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.limit-label {
font-size: 14px;
font-weight: 600;
color: #64748b;
display: flex;
align-items: center;
gap: 8px;
}
.limit-value {
font-size: 18px;
font-weight: 700;
color: #1e293b;
}
.progress-bar-container {
width: 100%;
height: 12px;
background: #e2e8f0;
border-radius: 6px;
overflow: hidden;
position: relative;
}
.progress-bar {
height: 100%;
border-radius: 6px;
transition: width 0.5s ease, background 0.3s ease;
position: relative;
overflow: hidden;
&.low {
background: linear-gradient(90deg, #10b981, #34d399);
}
&.medium {
background: linear-gradient(90deg, #f59e0b, #fbbf24);
}
}
.progress-bar-value {
text-align: right;
font-size: 12px;
font-weight: 600;
color: #64748b;
margin-top: 4px;
}
}
.dahboard-geography-section {
background: white;
border-radius: 20px;
padding: 32px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
height: 100%;
.geography-grid {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 20px;
flex: 1;
}
.country-item {
padding: 10px 14px;
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
border-radius: 12px;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: 50px 1fr 70px;
align-items: center;
gap: 12px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
}
}
.country-flag {
font-size: 28px;
text-align: center;
}
.country-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.country-name {
font-size: 14px;
font-weight: 600;
color: #1e293b;
}
.country-bar {
width: 100%;
height: 5px;
background: #e2e8f0;
border-radius: 2.5px;
overflow: hidden;
}
.country-bar-fill {
height: 100%;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
border-radius: 2.5px;
transition: width 0.5s ease;
}
.country-stats {
text-align: right;
}
.country-violations {
font-size: 20px;
font-weight: 800;
color: #6366f1;
line-height: 1;
}
.country-percent {
font-size: 10px;
color: #64748b;
font-weight: 600;
margin-top: 2px;
}
}
.dashboard-last-check {
background: white;
border-radius: 20px;
padding: 20px 28px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
.last-check-title {
font-size: 14px;
font-weight: 600;
color: #64748b;
margin-bottom: 6px;
}
.last-check-date {
font-size: 20px;
font-weight: 700;
color: #1e293b;
}
.last-check-info {
flex: 1;
}
.last-check-actions {
display: flex;
gap: 12px;
}
.btn-report {
padding: 10px 20px;
background: white;
color: #6366f1;
border: 2px solid #6366f1;
border-radius: 12px;
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: #6366f1;
color: white;
}
}
.btn-new-search {
padding: 10px 20px;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
border: none;
border-radius: 12px;
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(99, 102, 241, 0.4);
}
}
}
.violations-carousel {
display: flex;
flex-direction: column;
.violation-slide {
.violation-image-container {
width: 100%;
height: 200px;
border-radius: 16px;
overflow: hidden;
margin-bottom: 16px;
background: linear-gradient(135deg, #f1f5f9, #e2e8f0);
display: flex;
align-items: center;
justify-content: center;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.violation-image {
width: 300px;
height: 100%;
object-fit: contain;
background: white;
}
.violation-badge {
position: absolute;
top: 12px;
right: 12px;
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
padding: 6px 14px;
border-radius: 8px;
font-weight: 700;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
}
.violation-status-badge {
position: absolute;
top: 12px;
left: 12px;
background: linear-gradient(135deg, #10b981, #059669);
color: white;
padding: 6px 14px;
border-radius: 8px;
font-weight: 700;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.violation-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-height: 0;
.violation-title-row {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 12px;
border-bottom: 2px solid #e2e8f0;
}
.violation-filename {
font-size: 16px;
font-weight: 700;
color: #1e293b;
display: flex;
align-items: center;
gap: 8px;
}
.violation-type-badge {
padding: 4px 10px;
border-radius: 6px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
background: #dbeafe;
color: #1e40af;
}
}
.violation-info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.violation-info-item {
padding: 10px 12px;
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
border-radius: 10px;
display: flex;
flex-direction: column;
gap: 4px;
}
.violation-info-value {
font-size: 13px;
font-weight: 700;
color: #1e293b;
display: flex;
align-items: center;
gap: 6px;
}
.violation-info-label {
font-size: 10px;
font-weight: 600;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.5px;
display: flex;
align-items: center;
gap: 4px;
}
.violation-actions-row {
display: flex;
gap: 10px;
margin-top: auto;
flex-shrink: 0;
}
.btn-visit-site {
flex: 1;
padding: 12px 20px;
background: white;
color: #3b82f6;
border: 2px solid #3b82f6;
border-radius: 10px;
font-weight: 600;
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.violation-action {
flex: 1;
padding: 12px 20px;
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
border: none;
border-radius: 10px;
font-weight: 600;
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
}
.swiper-slide {
width: 100% !important;
}
}
@@ -0,0 +1,85 @@
export default function DashboardFilesInfo() {
return (
<div className="dashboard-files-info">
<div className="type-card images" data-icon="🖼️">
<div className="stat-type">ИЗОБРАЖЕНИЯ</div>
<div className="stat-value">235</div>
<div className="stat-label">Ваши изображения</div>
<div className="type-card-stats">
<div className="type-card-stat-row">
<span className="type-card-stat-label">💾 Размер</span>
<span className="type-card-stat-value">340.5 МБ</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label">🛡 Защищено</span>
<span className="type-card-stat-value">233 файлов</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label"> Нарушений</span>
<span className="type-card-stat-value">504</span>
</div>
</div>
</div>
<div className="type-card videos" data-icon="🎬">
<div className="stat-type">ВИДЕО</div>
<div className="stat-value">25</div>
<div className="stat-label">Ваш видео контент</div>
<div className="type-card-stats">
<div className="type-card-stat-row">
<span className="type-card-stat-label">💾 Размер</span>
<span className="type-card-stat-value">196.9 МБ</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label">🛡 Защищено</span>
<span className="type-card-stat-value">23 файлов</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label"> Нарушений</span>
<span className="type-card-stat-value">0</span>
</div>
</div>
</div>
<div className="type-card audio" data-icon="🎵">
<div className="stat-type">АУДИО</div>
<div className="stat-value">43</div>
<div className="stat-label">Ваш аудио контент</div>
<div className="type-card-stats">
<div className="type-card-stat-row">
<span className="type-card-stat-label">💾 Размер</span>
<span className="type-card-stat-value">223.7 МБ</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label">🛡 Защищено</span>
<span className="type-card-stat-value">36 файлов</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label"> Нарушений</span>
<span className="type-card-stat-value">0</span>
</div>
</div>
</div>
<div className="type-card scripts" data-icon="📄">
<div className="stat-type">PDF-ФАЙЛЫ</div>
<div className="stat-value">73</div>
<div className="stat-label">Ваши PDF-файлы</div>
<div className="type-card-stats">
<div className="type-card-stat-row">
<span className="type-card-stat-label">💾 Размер</span>
<span className="type-card-stat-value">28.9 МБ</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label">🛡 Защищено</span>
<span className="type-card-stat-value">70 файлов</span>
</div>
<div className="type-card-stat-row">
<span className="type-card-stat-label"> Нарушений</span>
<span className="type-card-stat-value">0</span>
</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,74 @@
export default function DahboardGeographySection() {
return (
<div className="dahboard-geography-section">
<h3 className="section-title">🌍 География нарушений</h3>
<div className="geography-grid">
<div className="country-item">
<div className="country-flag">🇺🇸</div>
<div className="country-info">
<div className="country-name">США</div>
<div className="country-bar">
<div className="country-bar-fill" style={{width: '44%'}}></div>
</div>
</div>
<div className="country-stats">
<div className="country-violations">231</div>
<div className="country-percent">44%</div>
</div>
</div>
<div className="country-item">
<div className="country-flag">🇷🇺</div>
<div className="country-info">
<div className="country-name">Россия</div>
<div className="country-bar">
<div className="country-bar-fill" style={{width: '44%'}}></div>
</div>
</div>
<div className="country-stats">
<div className="country-violations">227</div>
<div className="country-percent">44%</div>
</div>
</div>
<div className="country-item">
<div className="country-flag">🌐</div>
<div className="country-info">
<div className="country-name">Другие</div>
<div className="country-bar">
<div className="country-bar-fill" style={{width: '11%'}}></div>
</div>
</div>
<div className="country-stats">
<div className="country-violations">57</div>
<div className="country-percent">11%</div>
</div>
</div>
<div className="country-item">
<div className="country-flag">🇬🇧</div>
<div className="country-info">
<div className="country-name">Великобритания</div>
<div className="country-bar">
<div className="country-bar-fill" style={{width: '1%'}}></div>
</div>
</div>
<div className="country-stats">
<div className="country-violations">4</div>
<div className="country-percent">1%</div>
</div>
</div>
<div className="country-item">
<div className="country-flag">🇩🇪</div>
<div className="country-info">
<div className="country-name">Германия</div>
<div className="country-bar">
<div className="country-bar-fill" style={{width: '0%'}}></div>
</div>
</div>
<div className="country-stats">
<div className="country-violations">1</div>
<div className="country-percent">0%</div>
</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,19 @@
export default function DashboardLastCheck() {
return (
<div className="dashboard-last-check">
<div className="last-check-info">
<div className="last-check-title">Последняя проверка контента</div>
<div className="last-check-date">
17.10.2025 в 10:23</div>
</div>
<div className="last-check-actions">
<button className="btn-report">
📄 Отчёт
</button>
<button className="btn-new-search">
🔍 Мониторинг нарушений
</button>
</div>
</div>
)
}
@@ -0,0 +1,47 @@
export default function DashboardPlatformsList() {
return (
<div className="content-section">
<div className="section-header">
<h3 className="section-title">Аналитика распространения</h3>
</div>
<div className="dashboard-platforms-list">
<div className="platform-item">
<div className="platform-name">
<span className="platform-icon">🌐</span>
<span>Другие</span>
</div>
<span className="platform-count">367</span>
</div>
<div className="platform-item">
<div className="platform-name">
<span className="platform-icon">📦</span>
<span>Ozon</span>
</div>
<span className="platform-count">56</span>
</div>
<div className="platform-item">
<div className="platform-name">
<span className="platform-icon">🔴</span>
<span>Yandex</span>
</div>
<span className="platform-count">40</span>
</div>
<div className="platform-item">
<div className="platform-name">
<span className="platform-icon">📌</span>
<span>Pinterest</span>
</div>
<span className="platform-count">33</span>
</div>
<div className="platform-item">
<div className="platform-name">
<span className="platform-icon">🛍</span>
<span>Wildberries</span>
</div>
<span className="platform-count">18</span>
</div>
</div>
</div>
)
}
@@ -0,0 +1,49 @@
export default function DashboardUserFiles() {
return (
<div className="content-section">
<div className="section-header">
<h3 className="section-title">Ваши файлы</h3>
<a href="pages/marking-photo.php" className="view-all-link">Загрузить файл </a>
</div>
<div className="user-files-list">
<div className="file-item">
<div className="file-icon video">
🎬</div>
<div className="file-info">
<div className="file-name" title="Запись экрана 2026-01-09 в 15.45.28.mov">
Запись экрана 2026-01-09 в 15.45.28.mov</div>
<div className="file-meta">
11.3 МБ
11.01.2026</div>
</div>
<div className="file-badge protected"> Защищено</div>
</div>
<div className="file-item">
<div className="file-icon video">
🎬</div>
<div className="file-info">
<div className="file-name" title="SOUNE_миграция.mov">
SOUNE_миграция.mov</div>
<div className="file-meta">
17.1 МБ
11.01.2026</div>
</div>
<div className="file-badge protected"> Защищено</div>
</div>
<div className="file-item">
<div className="file-icon video">
🎬</div>
<div className="file-info">
<div className="file-name" title="SOUNE_миграция.mov">
SOUNE_миграция.mov</div>
<div className="file-meta">
17.1 МБ
11.01.2026</div>
</div>
<div className="file-badge protected"> Защищено</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,41 @@
export default function DashboardUserStats() {
return (
<div className="dashboard-user-stats">
<div className="stat-card projects">
<div className="stat-type">ЗАЩИЩЁННЫЕ ФАЙЛЫ</div>
<div className="stat-value">367</div>
<div className="stat-label">Файлов под защитой</div>
<div className="stat-icon">🛡</div>
</div>
<div className="stat-card matches">
<div className="stat-type">СОВПАДЕНИЯ</div>
<div className="stat-value">520</div>
<div className="stat-label">Найдено совпадений</div>
<div className="stat-icon">🔍</div>
</div>
<div className="stat-card violations">
<div className="stat-type">НАРУШЕНИЯ</div>
<div className="stat-value">499</div>
<div className="stat-label">Требуют внимания</div>
<div className="stat-icon"></div>
</div>
<div className="stat-card storage">
<div className="stat-type">ХРАНИЛИЩЕ</div>
<div className="stat-value">790.2 MB</div>
<div className="stat-label">Использовано места</div>
<div className="stat-icon">💾</div>
</div>
<div className="stat-card legal">
<div className="stat-type">ЖАЛОБЫ</div>
<div className="stat-value">0</div>
<div className="stat-label">Отправлено юристам</div>
<div className="stat-icon"></div>
</div>
</div>
)
}
@@ -0,0 +1,55 @@
export default function DahboardLimitsSection() {
return (
<div className="dashboard-limits-section">
<h3 className="section-title">💰 Использование лимитов</h3>
<div className="limits-grid">
<div className="limit-item">
<div className="limit-header">
<div className="limit-label">
<span>🔍</span>
<span>Проверки</span>
</div>
<div className="limit-value">520 / 10,000</div>
</div>
<div className="progress-bar-container">
<div className="progress-bar low" style={{width: '44%'}}></div>
</div>
<div className="progress-bar-value">
5%
</div>
</div>
<div className="limit-item">
<div className="limit-header">
<div className="limit-label">
<span>💾</span>
<span>Хранилище</span>
</div>
<div className="limit-value">790.2 / 1,024 МБ</div>
</div>
<div className="progress-bar-container">
<div className="progress-bar medium" style={{width: '77%'}}></div>
</div>
<div className="progress-bar-value">
77%
</div>
</div>
<div className="limit-item">
<div className="limit-header">
<div className="limit-label">
<span>📁</span>
<span>Файлы</span>
</div>
<div className="limit-value">386 / 500</div>
</div>
<div className="progress-bar-container">
<div className="progress-bar medium" style={{width: '77%'}}></div>
</div>
<div className="progress-bar-value">
77%
</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,266 @@
'use client'
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination, Autoplay } from 'swiper/modules';
import Image from 'next/image';
import 'swiper/css';
import 'swiper/css/pagination';
import testImage from '@/app/src/image-preview.png'
import { useRef } from 'react';
export default function ViolationsCarousel() {
const paginationRef = useRef(null);
return (
<div className="content-section">
<div className="section-header">
<h3 className="section-title">Нарушения</h3>
<a href="pages/violations.php" className="view-all-link">Смотреть все </a>
</div>
<div className="violations-carousel">
<Swiper
spaceBetween={50}
slidesPerView={1}
modules={[Pagination, Autoplay]}
pagination={{
/* dynamicBullets: true, */
/* el: paginationRef.current, */
clickable: true,
el: '.custon-swiper-pagination'
}}
loop={true}
autoplay={{
delay: 3000,
disableOnInteraction: false,
}}
/* width={300} */
>
<SwiperSlide>
<div className="violation-slide">
<div className="violation-image-container">
<Image src={testImage} alt="Preview" className="violation-image" />
<div className="violation-badge">НАРУШЕНИЕ</div>
<div className="violation-status-badge">ОБРАБОТАНО</div>
</div>
<div className="violation-content">
<div className="violation-title-row">
<div className="violation-filename">
🖼banner111.png</div>
<div className="violation-type-badge photo">
Фото
</div>
</div>
<div className="violation-info-grid">
<div className="violation-info-item">
<div className="violation-info-label">
🌍 Регион
</div>
<div className="violation-info-value">
<span className="region-flag">🇺🇸</span>
<span>США</span>
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📅 Обнаружено</div>
<div className="violation-info-value">
17.10.2025</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">🌐 Домен</div>
<div className="violation-info-value">
m.apkpure.com </div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📊 Категория</div>
<div className="violation-info-value">
Единичное
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📁 Тип</div>
<div className="violation-info-value">
Изображение</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label"> Статус</div>
<div className="violation-info-value">
Обработано</div>
</div>
</div>
<div className="violation-actions-row">
<button className="btn-visit-site">
🔗 Перейти на сайт
</button>
<button className="violation-action">
🚨 Открыть дело
</button>
</div>
</div>
</div>
</SwiperSlide>
<SwiperSlide>
<div className="violation-slide">
<div className="violation-image-container">
<Image src={testImage} alt="Preview" className="violation-image" />
<div className="violation-badge">НАРУШЕНИЕ</div>
<div className="violation-status-badge">ОБРАБОТАНО</div>
</div>
<div className="violation-content">
<div className="violation-title-row">
<div className="violation-filename">
🖼banner111.png</div>
<div className="violation-type-badge photo">
Фото
</div>
</div>
<div className="violation-info-grid">
<div className="violation-info-item">
<div className="violation-info-label">
🌍 Регион
</div>
<div className="violation-info-value">
<span className="region-flag">🇺🇸</span>
<span>США</span>
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📅 Обнаружено</div>
<div className="violation-info-value">
17.10.2025</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">🌐 Домен</div>
<div className="violation-info-value">
m.apkpure.com </div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📊 Категория</div>
<div className="violation-info-value">
Единичное
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📁 Тип</div>
<div className="violation-info-value">
Изображение</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label"> Статус</div>
<div className="violation-info-value">
Обработано</div>
</div>
</div>
<div className="violation-actions-row">
<button className="btn-visit-site">
🔗 Перейти на сайт
</button>
<button className="violation-action">
🚨 Открыть дело
</button>
</div>
</div>
</div>
</SwiperSlide>
<SwiperSlide>
<div className="violation-slide">
<div className="violation-image-container">
<Image src={testImage} alt="Preview" className="violation-image" />
<div className="violation-badge">НАРУШЕНИЕ</div>
<div className="violation-status-badge">ОБРАБОТАНО</div>
</div>
<div className="violation-content">
<div className="violation-title-row">
<div className="violation-filename">
🖼banner111.png</div>
<div className="violation-type-badge photo">
Фото
</div>
</div>
<div className="violation-info-grid">
<div className="violation-info-item">
<div className="violation-info-label">
🌍 Регион
</div>
<div className="violation-info-value">
<span className="region-flag">🇺🇸</span>
<span>США</span>
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📅 Обнаружено</div>
<div className="violation-info-value">
17.10.2025</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">🌐 Домен</div>
<div className="violation-info-value">
m.apkpure.com </div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📊 Категория</div>
<div className="violation-info-value">
Единичное
</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label">📁 Тип</div>
<div className="violation-info-value">
Изображение</div>
</div>
<div className="violation-info-item">
<div className="violation-info-label"> Статус</div>
<div className="violation-info-value">
Обработано</div>
</div>
</div>
<div className="violation-actions-row">
<button className="btn-visit-site">
🔗 Перейти на сайт
</button>
<button className="violation-action">
🚨 Открыть дело
</button>
</div>
</div>
</div>
</SwiperSlide>
</Swiper>
<div
className="flex justify-center mt-6"
>
<div
ref={paginationRef}
className="flex justify-center gap-2 custon-swiper-pagination"
>
</div>
</div>
</div>
</div>
)
}