feat: setup docker and push project to remote
This commit is contained in:
278
src/app/dashboard/DashboardClient.tsx
Normal file
278
src/app/dashboard/DashboardClient.tsx
Normal file
@@ -0,0 +1,278 @@
|
||||
"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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user