279 lines
17 KiB
TypeScript
279 lines
17 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { motion } from "framer-motion";
|
|
import {
|
|
FileText,
|
|
Plus,
|
|
TrendingUp,
|
|
Eye,
|
|
Calendar,
|
|
ChevronRight,
|
|
ArrowUpRight,
|
|
ArrowRight,
|
|
} from "lucide-react";
|
|
import Link from "next/link";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Sidebar } from "@/components/Sidebar";
|
|
|
|
type Organization = {
|
|
id: string;
|
|
name: string;
|
|
logoUrl: string | null;
|
|
primaryColor: string;
|
|
};
|
|
|
|
type UserType = {
|
|
id: string;
|
|
name: string | null;
|
|
email: string;
|
|
role: string;
|
|
};
|
|
|
|
type DashboardStats = {
|
|
docCount: number;
|
|
viewCount: number;
|
|
downloadCount: number;
|
|
recentDocs: any[];
|
|
};
|
|
|
|
export default function DashboardClient({
|
|
user,
|
|
organization,
|
|
stats,
|
|
}: {
|
|
user: UserType;
|
|
organization: Organization;
|
|
stats: DashboardStats;
|
|
}) {
|
|
const primaryColor = organization.primaryColor || "#2563eb";
|
|
|
|
const formatDate = (date: Date) => {
|
|
return new Date(date).toLocaleDateString("pt-BR", {
|
|
day: "2-digit",
|
|
month: "short",
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white flex">
|
|
<Sidebar user={user} organization={organization} />
|
|
|
|
{/* Main Content Area */}
|
|
<main className="flex-1 overflow-y-auto">
|
|
{/* Top Banner / Hero - Integrated Background */}
|
|
<div className="relative border-b border-slate-100 bg-slate-50/40 p-8 lg:p-10">
|
|
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
|
<div>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="px-2 py-0.5 bg-white border border-slate-200 rounded-full text-[9px] font-bold uppercase tracking-[0.1em] text-slate-500">Overview Panel</span>
|
|
</div>
|
|
<h2 className="text-2xl font-black text-slate-900 tracking-tight mb-1">Dashboard</h2>
|
|
<p className="text-sm text-slate-500 font-medium">
|
|
Bem-vindo, Administrador <span className="text-slate-900">{user.name?.split(' ')[0] || "Administrador"}</span>.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<Button
|
|
asChild
|
|
style={{ backgroundColor: primaryColor }}
|
|
className="h-10 px-6 rounded-lg font-bold text-xs shadow-none hover:opacity-90 active:scale-95 transition-all text-white"
|
|
>
|
|
<Link href="/dashboard/documentos?upload=true">
|
|
<Plus size={18} className="mr-2 stroke-[3]" />
|
|
Novo Documento
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Faded accent circle */}
|
|
<div
|
|
className="absolute top-0 right-0 w-[300px] h-[300px] rounded-full blur-[80px] opacity-[0.03] pointer-events-none"
|
|
style={{ backgroundColor: primaryColor }}
|
|
/>
|
|
</div>
|
|
|
|
<div className="p-8 lg:p-10 w-full">
|
|
{/* Stats Grid - Large and minimalist */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mb-10">
|
|
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }}>
|
|
<div className="group relative p-5 rounded-[20px] bg-slate-50 hover:bg-white border-2 border-transparent hover:border-slate-100 transition-all duration-500">
|
|
<div className="flex flex-col gap-3.5">
|
|
<div className="flex items-center justify-between">
|
|
<div className="w-10 h-10 rounded-lg bg-white flex items-center justify-center text-slate-800 border border-slate-100 group-hover:border-blue-100 transition-colors">
|
|
<FileText size={20} className="stroke-[2]" />
|
|
</div>
|
|
<div className="p-1 rounded-full bg-blue-50 text-blue-600">
|
|
<ArrowUpRight size={14} />
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Total de Documentos</p>
|
|
<h3 className="text-2xl font-black text-slate-900">{stats.docCount}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.1 }}>
|
|
<div className="group relative p-5 rounded-[20px] bg-slate-50 hover:bg-white border-2 border-transparent hover:border-slate-100 transition-all duration-500">
|
|
<div className="flex flex-col gap-3.5">
|
|
<div className="flex items-center justify-between">
|
|
<div className="w-10 h-10 rounded-lg bg-white flex items-center justify-center text-slate-800 border border-slate-100 group-hover:border-green-100 transition-colors">
|
|
<Eye size={20} className="stroke-[2]" />
|
|
</div>
|
|
<div className="p-1 rounded-full bg-green-50 text-green-600">
|
|
<ArrowUpRight size={14} />
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Visualizações Totais</p>
|
|
<h3 className="text-2xl font-black text-slate-900">{stats.viewCount.toLocaleString()}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.2 }}>
|
|
<div className="group relative p-5 rounded-[20px] bg-slate-50 hover:bg-white border-2 border-transparent hover:border-slate-100 transition-all duration-500">
|
|
<div className="flex flex-col gap-3.5">
|
|
<div className="flex items-center justify-between">
|
|
<div className="w-10 h-10 rounded-lg bg-white flex items-center justify-center text-slate-800 border border-slate-100 group-hover:border-purple-100 transition-colors">
|
|
<TrendingUp size={20} className="stroke-[2]" />
|
|
</div>
|
|
<div className="p-1 rounded-full bg-purple-50 text-purple-600">
|
|
<ArrowUpRight size={14} />
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Downloads Totais</p>
|
|
<h3 className="text-2xl font-black text-slate-900">{stats.downloadCount.toLocaleString()}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
|
{/* Recent Documents Table - More integrated */}
|
|
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.3 }} className="lg:col-span-8">
|
|
<div className="flex items-center justify-between mb-4 px-1">
|
|
<h3 className="text-lg font-black text-slate-900 tracking-tight">Atividade Recente</h3>
|
|
<Link href="/dashboard/documentos" className="group flex items-center gap-2 text-[9px] font-bold uppercase tracking-widest text-slate-400 hover:text-slate-900 transition-colors">
|
|
Ver Todos <ArrowRight size={12} className="group-hover:translate-x-0.5 transition-transform" />
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-[24px] border-2 border-slate-100 overflow-hidden">
|
|
{stats.recentDocs.length === 0 ? (
|
|
<div className="p-14 text-center">
|
|
<div className="w-14 h-14 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-3">
|
|
<FileText size={24} className="text-slate-300" />
|
|
</div>
|
|
<p className="text-slate-500 font-medium mb-4 text-sm">Nenhum documento encontrado.</p>
|
|
<Button asChild variant="outline" className="rounded-lg border-2 font-bold px-6 h-9 text-[10px] uppercase tracking-widest">
|
|
<Link href="/dashboard/documentos/novo">Começar Agora</Link>
|
|
</Button>
|
|
</div>
|
|
) : (
|
|
<div className="divide-y-2 divide-slate-50">
|
|
{stats.recentDocs.map((doc) => (
|
|
<Link
|
|
key={doc.id}
|
|
href={`/dashboard/documentos/${doc.id}`}
|
|
className="flex items-center gap-4 py-3.5 px-6 hover:bg-slate-50/50 transition-all group"
|
|
>
|
|
<div className="w-10 h-10 rounded-lg bg-slate-50 group-hover:bg-white border-2 border-transparent group-hover:border-slate-100 flex items-center justify-center transition-all shrink-0">
|
|
<FileText size={18} className="text-slate-500 group-hover:text-slate-900 transition-colors" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<h4 className="font-black text-slate-900 group-hover:text-blue-600 transition-colors truncate mb-0.5 text-sm">{doc.title}</h4>
|
|
<div className="flex items-center gap-3 text-[9px] font-bold text-slate-400 uppercase tracking-widest">
|
|
<span>{doc.folder?.name || "Sem categoria"}</span>
|
|
<span className="w-1 h-1 rounded-full bg-slate-200" />
|
|
<span>{formatDate(doc.createdAt)}</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-right shrink-0">
|
|
<div className="flex items-center gap-2 text-slate-400 group-hover:text-slate-900 transition-colors">
|
|
<span className="text-[10px] font-bold uppercase tracking-widest">Detalhes</span>
|
|
<ChevronRight size={14} className="stroke-[3]" />
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Summary Column */}
|
|
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.4 }} className="lg:col-span-4 space-y-6">
|
|
<div className="bg-slate-900 rounded-[24px] p-6 text-white relative overflow-hidden group">
|
|
<div className="relative z-10">
|
|
<div className="w-10 h-10 bg-white/10 rounded-lg flex items-center justify-center mb-4">
|
|
<Calendar size={20} className="text-white" />
|
|
</div>
|
|
<h4 className="text-xl font-black tracking-tight mb-1">Status do Portal</h4>
|
|
<p className="text-white/60 text-xs font-medium leading-relaxed mb-6">
|
|
O portal está online e sincronizado com os últimos envios de documentos.
|
|
</p>
|
|
<Button variant="ghost" className="w-full bg-white/10 hover:bg-white/20 text-white font-bold text-[10px] uppercase tracking-widest h-10 rounded-lg border-none">
|
|
Ver Log de Atividades
|
|
</Button>
|
|
</div>
|
|
<div className="absolute -right-4 -bottom-4 w-32 h-32 bg-white/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
|
|
</div>
|
|
|
|
<div className="bg-white border-2 border-slate-100 rounded-[24px] p-6">
|
|
<h4 className="text-base font-black text-slate-900 tracking-tight mb-4">Informações Rápidas</h4>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Organização</span>
|
|
<span className="text-xs font-black text-slate-900">{organization.name}</span>
|
|
</div>
|
|
<div className="h-px bg-slate-100" />
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Seu Perfil</span>
|
|
<span className="text-xs font-black text-slate-900 uppercase tracking-tighter">{user.role}</span>
|
|
</div>
|
|
<div className="h-px bg-slate-100" />
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Data</span>
|
|
<span className="text-xs font-black text-slate-900">{new Date().toLocaleDateString('pt-BR')}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Users(props: any) {
|
|
return (
|
|
<svg
|
|
{...props}
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="24"
|
|
height="24"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
|
<circle cx="9" cy="7" r="4" />
|
|
<path d="M22 21v-2a4 4 0 0 0-3-3.87" />
|
|
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
</svg>
|
|
)
|
|
}
|