feat: make all pages and components fully responsive for mobile devices
This commit is contained in:
@@ -14,7 +14,6 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import { Sidebar } from "@/components/Sidebar";
|
import { Sidebar } from "@/components/Sidebar";
|
||||||
|
|
||||||
type Organization = {
|
type Organization = {
|
||||||
@@ -57,29 +56,29 @@ export default function DashboardClient({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white flex">
|
<div className="min-h-screen bg-white flex flex-col lg:flex-row">
|
||||||
<Sidebar user={user} organization={organization} />
|
<Sidebar user={user} organization={organization} />
|
||||||
|
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<main className="flex-1 overflow-y-auto">
|
<main className="flex-1 overflow-y-auto pt-16 lg:pt-0">
|
||||||
{/* Top Banner / Hero - Integrated Background */}
|
{/* Top Banner / Hero - Integrated Background */}
|
||||||
<div className="relative border-b border-slate-100 bg-slate-50/40 p-8 lg:p-10">
|
<div className="relative border-b border-slate-100 bg-slate-50/40 p-4 sm:p-6 lg:p-8 xl:p-10">
|
||||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<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>
|
<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>
|
</div>
|
||||||
<h2 className="text-2xl font-black text-slate-900 tracking-tight mb-1">Dashboard</h2>
|
<h2 className="text-xl sm:text-2xl font-black text-slate-900 tracking-tight mb-1">Dashboard</h2>
|
||||||
<p className="text-sm text-slate-500 font-medium">
|
<p className="text-sm text-slate-500 font-medium">
|
||||||
Bem-vindo, Administrador <span className="text-slate-900">{user.name?.split(' ')[0] || "Administrador"}</span>.
|
Bem-vindo, <span className="text-slate-900">{user.name?.split(' ')[0] || "Administrador"}</span>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
style={{ backgroundColor: primaryColor }}
|
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"
|
className="w-full sm:w-auto h-11 sm:h-10 px-4 sm:px-6 rounded-xl sm:rounded-lg font-bold text-xs shadow-none hover:opacity-90 active:scale-95 transition-all text-white"
|
||||||
>
|
>
|
||||||
<Link href="/dashboard/documentos?upload=true">
|
<Link href="/dashboard/documentos?upload=true">
|
||||||
<Plus size={18} className="mr-2 stroke-[3]" />
|
<Plus size={18} className="mr-2 stroke-[3]" />
|
||||||
@@ -91,87 +90,87 @@ export default function DashboardClient({
|
|||||||
|
|
||||||
{/* Faded accent circle */}
|
{/* Faded accent circle */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 right-0 w-[300px] h-[300px] rounded-full blur-[80px] opacity-[0.03] pointer-events-none"
|
className="absolute top-0 right-0 w-[200px] sm:w-[300px] h-[200px] sm:h-[300px] rounded-full blur-[60px] sm:blur-[80px] opacity-[0.03] pointer-events-none"
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-8 lg:p-10 w-full">
|
<div className="p-4 sm:p-6 lg:p-8 xl:p-10 w-full">
|
||||||
{/* Stats Grid - Large and minimalist */}
|
{/* Stats Grid - Responsive */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mb-10">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4 lg:gap-5 mb-6 lg:mb-10">
|
||||||
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }}>
|
<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="group relative p-4 lg:p-5 rounded-2xl lg: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 gap-4 sm:flex-col sm:items-start sm:gap-3.5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between w-full sm:w-auto">
|
||||||
<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">
|
<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]" />
|
<FileText size={20} className="stroke-[2]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-1 rounded-full bg-blue-50 text-blue-600">
|
<div className="hidden sm:block p-1 rounded-full bg-blue-50 text-blue-600">
|
||||||
<ArrowUpRight size={14} />
|
<ArrowUpRight size={14} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 sm:flex-none">
|
||||||
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Total de Documentos</p>
|
<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>
|
<h3 className="text-xl lg:text-2xl font-black text-slate-900">{stats.docCount}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.1 }}>
|
<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="group relative p-4 lg:p-5 rounded-2xl lg: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 gap-4 sm:flex-col sm:items-start sm:gap-3.5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between w-full sm:w-auto">
|
||||||
<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">
|
<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]" />
|
<Eye size={20} className="stroke-[2]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-1 rounded-full bg-green-50 text-green-600">
|
<div className="hidden sm:block p-1 rounded-full bg-green-50 text-green-600">
|
||||||
<ArrowUpRight size={14} />
|
<ArrowUpRight size={14} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 sm:flex-none">
|
||||||
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Visualizações Totais</p>
|
<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>
|
<h3 className="text-xl lg:text-2xl font-black text-slate-900">{stats.viewCount.toLocaleString()}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div initial={{ opacity: 0, y: 15 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.2 }}>
|
<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="group relative p-4 lg:p-5 rounded-2xl lg: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 gap-4 sm:flex-col sm:items-start sm:gap-3.5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between w-full sm:w-auto">
|
||||||
<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">
|
<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]" />
|
<TrendingUp size={20} className="stroke-[2]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-1 rounded-full bg-purple-50 text-purple-600">
|
<div className="hidden sm:block p-1 rounded-full bg-purple-50 text-purple-600">
|
||||||
<ArrowUpRight size={14} />
|
<ArrowUpRight size={14} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1 sm:flex-none">
|
||||||
<p className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Downloads Totais</p>
|
<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>
|
<h3 className="text-xl lg:text-2xl font-black text-slate-900">{stats.downloadCount.toLocaleString()}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-4 lg:gap-8">
|
||||||
{/* Recent Documents Table - More integrated */}
|
{/* 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">
|
<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">
|
<div className="flex items-center justify-between mb-3 lg:mb-4 px-1">
|
||||||
<h3 className="text-lg font-black text-slate-900 tracking-tight">Atividade Recente</h3>
|
<h3 className="text-base lg: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">
|
<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" />
|
Ver Todos <ArrowRight size={12} className="group-hover:translate-x-0.5 transition-transform" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-[24px] border-2 border-slate-100 overflow-hidden">
|
<div className="bg-white rounded-2xl lg:rounded-[24px] border-2 border-slate-100 overflow-hidden">
|
||||||
{stats.recentDocs.length === 0 ? (
|
{stats.recentDocs.length === 0 ? (
|
||||||
<div className="p-14 text-center">
|
<div className="p-10 lg:p-14 text-center">
|
||||||
<div className="w-14 h-14 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-3">
|
<div className="w-12 h-12 lg:w-14 lg:h-14 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||||
<FileText size={24} className="text-slate-300" />
|
<FileText size={20} className="text-slate-300" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-slate-500 font-medium mb-4 text-sm">Nenhum documento encontrado.</p>
|
<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">
|
<Button asChild variant="outline" className="rounded-lg border-2 font-bold px-6 h-9 text-[10px] uppercase tracking-widest">
|
||||||
@@ -184,25 +183,26 @@ export default function DashboardClient({
|
|||||||
<Link
|
<Link
|
||||||
key={doc.id}
|
key={doc.id}
|
||||||
href={`/dashboard/documentos/${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"
|
className="flex items-center gap-3 lg:gap-4 py-3 lg:py-3.5 px-4 lg: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">
|
<div className="w-9 h-9 lg:w-10 lg: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" />
|
<FileText size={16} className="text-slate-500 group-hover:text-slate-900 transition-colors" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<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>
|
<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">
|
<div className="flex items-center gap-2 lg:gap-3 text-[9px] font-bold text-slate-400 uppercase tracking-widest">
|
||||||
<span>{doc.folder?.name || "Sem categoria"}</span>
|
<span className="truncate max-w-[80px] sm:max-w-none">{doc.folder?.name || "Sem categoria"}</span>
|
||||||
<span className="w-1 h-1 rounded-full bg-slate-200" />
|
<span className="w-1 h-1 rounded-full bg-slate-200 shrink-0" />
|
||||||
<span>{formatDate(doc.createdAt)}</span>
|
<span>{formatDate(doc.createdAt)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right shrink-0">
|
<div className="hidden sm:flex text-right shrink-0">
|
||||||
<div className="flex items-center gap-2 text-slate-400 group-hover:text-slate-900 transition-colors">
|
<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>
|
<span className="text-[10px] font-bold uppercase tracking-widest">Detalhes</span>
|
||||||
<ChevronRight size={14} className="stroke-[3]" />
|
<ChevronRight size={14} className="stroke-[3]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ChevronRight size={18} className="sm:hidden text-slate-300 shrink-0" />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -211,29 +211,29 @@ export default function DashboardClient({
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Summary Column */}
|
{/* 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">
|
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.4 }} className="lg:col-span-4 space-y-4 lg:space-y-6">
|
||||||
<div className="bg-slate-900 rounded-[24px] p-6 text-white relative overflow-hidden group">
|
<div className="bg-slate-900 rounded-2xl lg:rounded-[24px] p-5 lg:p-6 text-white relative overflow-hidden group">
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<div className="w-10 h-10 bg-white/10 rounded-lg flex items-center justify-center mb-4">
|
<div className="w-9 h-9 lg:w-10 lg:h-10 bg-white/10 rounded-lg flex items-center justify-center mb-3 lg:mb-4">
|
||||||
<Calendar size={20} className="text-white" />
|
<Calendar size={18} className="text-white" />
|
||||||
</div>
|
</div>
|
||||||
<h4 className="text-xl font-black tracking-tight mb-1">Status do Portal</h4>
|
<h4 className="text-lg lg: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">
|
<p className="text-white/60 text-xs font-medium leading-relaxed mb-4 lg:mb-6">
|
||||||
O portal está online e sincronizado com os últimos envios de documentos.
|
O portal está online e sincronizado com os últimos envios de documentos.
|
||||||
</p>
|
</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">
|
<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
|
Ver Log de Atividades
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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 className="absolute -right-4 -bottom-4 w-24 lg:w-32 h-24 lg:h-32 bg-white/5 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-700" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white border-2 border-slate-100 rounded-[24px] p-6">
|
<div className="bg-white border-2 border-slate-100 rounded-2xl lg:rounded-[24px] p-5 lg:p-6">
|
||||||
<h4 className="text-base font-black text-slate-900 tracking-tight mb-4">Informações Rápidas</h4>
|
<h4 className="text-sm lg:text-base font-black text-slate-900 tracking-tight mb-3 lg:mb-4">Informações Rápidas</h4>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 lg:space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<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-[10px] font-bold text-slate-400 uppercase tracking-widest">Organização</span>
|
||||||
<span className="text-xs font-black text-slate-900">{organization.name}</span>
|
<span className="text-xs font-black text-slate-900 truncate max-w-[120px]">{organization.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-px bg-slate-100" />
|
<div className="h-px bg-slate-100" />
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -254,25 +254,3 @@ export default function DashboardClient({
|
|||||||
</div>
|
</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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -523,11 +523,11 @@ export default function DocumentsClient({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#f9fafb] flex">
|
<div className="min-h-screen bg-[#f9fafb] flex flex-col lg:flex-row">
|
||||||
<Sidebar user={user} organization={organization} />
|
<Sidebar user={user} organization={organization} />
|
||||||
|
|
||||||
<main
|
<main
|
||||||
className="flex-1 overflow-y-auto flex flex-col relative"
|
className="flex-1 overflow-y-auto flex flex-col relative pt-16 lg:pt-0"
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
@@ -575,20 +575,20 @@ export default function DocumentsClient({
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* Header Section - Integrated */}
|
{/* Header Section - Integrated */}
|
||||||
<div className="relative bg-white p-8 lg:p-10">
|
<div className="relative bg-white p-4 sm:p-6 lg:p-8 xl:p-10">
|
||||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between lg:gap-6">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
{/* Breadcrumbs */}
|
{/* Breadcrumbs */}
|
||||||
<nav className="flex items-center gap-1.5 mb-3 flex-wrap">
|
<nav className="flex items-center gap-1.5 mb-3 flex-wrap overflow-x-auto">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/documentos"
|
href="/dashboard/documentos"
|
||||||
className="p-1.5 rounded-lg hover:bg-slate-100 border border-transparent hover:border-slate-200 transition-all text-slate-400 hover:text-slate-900"
|
className="p-1.5 rounded-lg hover:bg-slate-100 border border-transparent hover:border-slate-200 transition-all text-slate-400 hover:text-slate-900 shrink-0"
|
||||||
>
|
>
|
||||||
<Home size={14} />
|
<Home size={14} />
|
||||||
</Link>
|
</Link>
|
||||||
{breadcrumb.length === 0 ? (
|
{breadcrumb.length === 0 ? (
|
||||||
<>
|
<>
|
||||||
<ChevronRight size={10} className="text-slate-300" />
|
<ChevronRight size={10} className="text-slate-300 shrink-0" />
|
||||||
<span className="px-2 py-0.5 bg-slate-100 border border-slate-200 rounded-md text-[9px] font-bold uppercase tracking-widest text-slate-600">
|
<span className="px-2 py-0.5 bg-slate-100 border border-slate-200 rounded-md text-[9px] font-bold uppercase tracking-widest text-slate-600">
|
||||||
Raiz do Portal
|
Raiz do Portal
|
||||||
</span>
|
</span>
|
||||||
@@ -596,15 +596,15 @@ export default function DocumentsClient({
|
|||||||
) : (
|
) : (
|
||||||
breadcrumb.map((folder, index) => (
|
breadcrumb.map((folder, index) => (
|
||||||
<React.Fragment key={folder.id}>
|
<React.Fragment key={folder.id}>
|
||||||
<ChevronRight size={10} className="text-slate-300" />
|
<ChevronRight size={10} className="text-slate-300 shrink-0" />
|
||||||
{index === breadcrumb.length - 1 ? (
|
{index === breadcrumb.length - 1 ? (
|
||||||
<span className="px-2 py-0.5 bg-slate-900 text-white rounded-md text-[9px] font-bold uppercase tracking-widest">
|
<span className="px-2 py-0.5 bg-slate-900 text-white rounded-md text-[9px] font-bold uppercase tracking-widest truncate max-w-[120px] sm:max-w-none">
|
||||||
{folder.name}
|
{folder.name}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<Link
|
<Link
|
||||||
href={`/dashboard/documentos?folder=${folder.id}`}
|
href={`/dashboard/documentos?folder=${folder.id}`}
|
||||||
className="px-2 py-0.5 bg-slate-100 hover:bg-slate-200 border border-slate-200 rounded-md text-[9px] font-bold uppercase tracking-widest text-slate-600 hover:text-slate-900 transition-all"
|
className="px-2 py-0.5 bg-slate-100 hover:bg-slate-200 border border-slate-200 rounded-md text-[9px] font-bold uppercase tracking-widest text-slate-600 hover:text-slate-900 transition-all truncate max-w-[80px] sm:max-w-none"
|
||||||
>
|
>
|
||||||
{folder.name}
|
{folder.name}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -615,33 +615,33 @@ export default function DocumentsClient({
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Título com imagem da pasta se existir */}
|
{/* Título com imagem da pasta se existir */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 sm:gap-4">
|
||||||
{currentFolder?.imageUrl ? (
|
{currentFolder?.imageUrl ? (
|
||||||
<div className="w-12 h-12 rounded-xl overflow-hidden shrink-0 border border-slate-200">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl overflow-hidden shrink-0 border border-slate-200">
|
||||||
<img src={currentFolder.imageUrl} alt={currentFolder.name} className="w-full h-full object-cover" />
|
<img src={currentFolder.imageUrl} alt={currentFolder.name} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : currentFolder ? (
|
) : currentFolder ? (
|
||||||
<div className="w-12 h-12 rounded-xl flex items-center justify-center shrink-0" style={{ backgroundColor: currentFolder.color + '20', color: currentFolder.color }}>
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl flex items-center justify-center shrink-0" style={{ backgroundColor: currentFolder.color + '20', color: currentFolder.color }}>
|
||||||
<FolderOpen size={24} fill="currentColor" fillOpacity={0.3} strokeWidth={2.5} />
|
<FolderOpen size={20} fill="currentColor" fillOpacity={0.3} strokeWidth={2.5} />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h2 className="text-2xl font-black text-slate-900 tracking-tight mb-1">
|
<h2 className="text-xl sm:text-2xl font-black text-slate-900 tracking-tight mb-1 truncate">
|
||||||
{currentFolder ? currentFolder.name : "Documentos"}
|
{currentFolder ? currentFolder.name : "Documentos"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-slate-500 font-medium">
|
<p className="text-xs sm:text-sm text-slate-500 font-medium hidden sm:block">
|
||||||
{currentFolder ? "Conteúdo desta pasta" : "Gestão de arquivos e transparência governamental."}
|
{currentFolder ? "Conteúdo desta pasta" : "Gestão de arquivos e transparência."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
<div className="flex items-center gap-2 shrink-0 flex-wrap">
|
||||||
{/* Botão Editar Pasta */}
|
{/* Botão Editar Pasta */}
|
||||||
{currentFolder && (
|
{currentFolder && (
|
||||||
<button
|
<button
|
||||||
onClick={handleOpenEditFolder}
|
onClick={handleOpenEditFolder}
|
||||||
className="h-9 w-9 rounded-lg bg-slate-100 text-slate-600 border-2 border-slate-200 hover:bg-slate-200 transition-all flex items-center justify-center"
|
className="h-10 w-10 sm:h-9 sm:w-9 rounded-lg bg-slate-100 text-slate-600 border-2 border-slate-200 hover:bg-slate-200 transition-all flex items-center justify-center"
|
||||||
title="Editar pasta"
|
title="Editar pasta"
|
||||||
>
|
>
|
||||||
<Pencil size={14} />
|
<Pencil size={14} />
|
||||||
@@ -651,22 +651,22 @@ export default function DocumentsClient({
|
|||||||
{currentFolder && (
|
{currentFolder && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleShare({ ...currentFolder, isFolder: true })}
|
onClick={() => handleShare({ ...currentFolder, isFolder: true })}
|
||||||
className={`h-9 px-4 rounded-lg font-bold text-[10px] uppercase tracking-widest transition-all flex items-center gap-2 ${currentFolder.isPublished
|
className={`h-10 sm:h-9 px-3 sm:px-4 rounded-lg font-bold text-[10px] uppercase tracking-widest transition-all flex items-center gap-2 ${currentFolder.isPublished
|
||||||
? "bg-green-100 text-green-700 border-2 border-green-200 hover:bg-green-200"
|
? "bg-green-100 text-green-700 border-2 border-green-200 hover:bg-green-200"
|
||||||
: "bg-slate-100 text-slate-600 border-2 border-slate-200 hover:bg-slate-200"
|
: "bg-slate-100 text-slate-600 border-2 border-slate-200 hover:bg-slate-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Share2 size={14} />
|
<Share2 size={14} />
|
||||||
{currentFolder.isPublished ? "Pública" : "Privada"}
|
<span className="hidden sm:inline">{currentFolder.isPublished ? "Pública" : "Privada"}</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => setShowNewFolder(true)}
|
onClick={() => setShowNewFolder(true)}
|
||||||
className="h-9 px-4 rounded-lg border-2 font-bold text-[10px] uppercase tracking-widest text-slate-700 hover:bg-white hover:border-slate-300 transition-all shadow-none"
|
className="h-10 sm:h-9 px-3 sm:px-4 rounded-lg border-2 font-bold text-[10px] uppercase tracking-widest text-slate-700 hover:bg-white hover:border-slate-300 transition-all shadow-none"
|
||||||
>
|
>
|
||||||
<FolderPlus size={16} className="mr-2 stroke-[2.5]" />
|
<FolderPlus size={16} className="sm:mr-2 stroke-[2.5]" />
|
||||||
Nova Pasta
|
<span className="hidden sm:inline">Nova Pasta</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
@@ -674,28 +674,28 @@ export default function DocumentsClient({
|
|||||||
setUpFolderId(currentFolderId || "__none__");
|
setUpFolderId(currentFolderId || "__none__");
|
||||||
setShowUploadDialog(true);
|
setShowUploadDialog(true);
|
||||||
}}
|
}}
|
||||||
className="h-9 px-4 rounded-lg font-bold text-[10px] uppercase tracking-widest shadow-none hover:opacity-90 active:scale-95 transition-all text-white"
|
className="h-10 sm:h-9 px-3 sm:px-4 rounded-lg font-bold text-[10px] uppercase tracking-widest shadow-none hover:opacity-90 active:scale-95 transition-all text-white"
|
||||||
>
|
>
|
||||||
<Plus size={16} className="mr-2 stroke-[3]" />
|
<Plus size={16} className="sm:mr-2 stroke-[3]" />
|
||||||
Fazer Upload
|
<span className="hidden sm:inline">Fazer Upload</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Toolbar: Search & Filters */}
|
{/* Toolbar: Search & Filters */}
|
||||||
<div className="sticky top-0 z-10 bg-[#f9fafb] px-8 pt-6 pb-4">
|
<div className="sticky top-16 lg:top-0 z-10 bg-[#f9fafb] px-4 sm:px-6 lg:px-8 pt-4 lg:pt-6 pb-3 lg:pb-4">
|
||||||
<div className="flex items-center justify-between gap-4">
|
<div className="flex items-center justify-between gap-2 sm:gap-4">
|
||||||
{/* Search - Compact */}
|
{/* Search - Compact */}
|
||||||
<div className="relative w-full max-w-sm group">
|
<div className="relative flex-1 max-w-sm group">
|
||||||
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 sm:pl-4 flex items-center pointer-events-none">
|
||||||
<Search className="text-slate-400 group-focus-within:text-red-500 transition-colors" size={18} />
|
<Search className="text-slate-400 group-focus-within:text-red-500 transition-colors" size={16} />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Buscar pastas ou arquivos..."
|
placeholder="Buscar..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => { setSearchTerm(e.target.value); setCurrentPage(1); }}
|
onChange={(e) => { setSearchTerm(e.target.value); setCurrentPage(1); }}
|
||||||
className="h-12 pl-12 pr-4 bg-white border-slate-200 rounded-xl text-sm font-medium focus:ring-0 focus:border-slate-300 transition-all"
|
className="h-10 sm:h-12 pl-9 sm:pl-12 pr-4 bg-white border-slate-200 rounded-xl text-sm font-medium focus:ring-0 focus:border-slate-300 transition-all"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -703,18 +703,18 @@ export default function DocumentsClient({
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => setShowFilterPanel(true)}
|
onClick={() => setShowFilterPanel(true)}
|
||||||
className={`h-12 px-5 rounded-xl border-2 font-bold text-xs uppercase tracking-wider transition-all ${selectedYear !== 'all' ? 'bg-blue-50 border-blue-200 text-blue-600' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
className={`h-10 sm:h-12 px-3 sm:px-5 rounded-xl border-2 font-bold text-xs uppercase tracking-wider transition-all ${selectedYear !== 'all' ? 'bg-blue-50 border-blue-200 text-blue-600' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
||||||
>
|
>
|
||||||
<Filter size={16} className="mr-2" />
|
<Filter size={16} className="sm:mr-2" />
|
||||||
Filtros
|
<span className="hidden sm:inline">Filtros</span>
|
||||||
{selectedYear !== 'all' && (
|
{selectedYear !== 'all' && (
|
||||||
<span className="ml-2 w-5 h-5 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[9px] font-black">1</span>
|
<span className="ml-1 sm:ml-2 w-5 h-5 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[9px] font-black">1</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-8 lg:p-10 w-full">
|
<div className="p-4 sm:p-6 lg:p-8 xl:p-10 w-full">
|
||||||
{/* Unified Explorer using StandardTable */}
|
{/* Unified Explorer using StandardTable */}
|
||||||
<StandardTable
|
<StandardTable
|
||||||
hideSearch={true}
|
hideSearch={true}
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ export default function SetupClient() {
|
|||||||
const prevStep = () => setStep((s) => Math.max(s - 1, 1));
|
const prevStep = () => setStep((s) => Math.max(s - 1, 1));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#f8fafc] flex items-center justify-center p-6 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-50 via-slate-50 to-white font-sans">
|
<div className="min-h-screen bg-[#f8fafc] flex items-center justify-center p-4 sm:p-6 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-50 via-slate-50 to-white font-sans">
|
||||||
<div className="max-w-4xl w-full">
|
<div className="max-w-4xl w-full">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="text-center mb-10">
|
<div className="text-center mb-6 sm:mb-10">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: -20 }}
|
initial={{ opacity: 0, y: -20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
@@ -111,7 +111,7 @@ export default function SetupClient() {
|
|||||||
initial={{ opacity: 0, y: -10 }}
|
initial={{ opacity: 0, y: -10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.1 }}
|
transition={{ delay: 0.1 }}
|
||||||
className="text-4xl font-bold text-slate-900 tracking-tight mb-2"
|
className="text-2xl sm:text-3xl lg:text-4xl font-bold text-slate-900 tracking-tight mb-2"
|
||||||
>
|
>
|
||||||
Configuração Master
|
Configuração Master
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
@@ -119,28 +119,28 @@ export default function SetupClient() {
|
|||||||
initial={{ opacity: 0, y: -10 }}
|
initial={{ opacity: 0, y: -10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.2 }}
|
transition={{ delay: 0.2 }}
|
||||||
className="text-slate-500"
|
className="text-slate-500 text-sm sm:text-base px-2"
|
||||||
>
|
>
|
||||||
Este portal está cru. Vamos realizar o setup inicial da sua organização.
|
Setup inicial da sua organização.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<div className="mb-12 relative">
|
<div className="mb-8 sm:mb-12 relative px-4">
|
||||||
<div className="flex justify-between items-center max-w-2xl mx-auto relative z-10">
|
<div className="flex justify-between items-center max-w-xs sm:max-w-2xl mx-auto relative z-10">
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className={`w-10 h-10 rounded-full flex items-center justify-center transition-all duration-500 ${step >= i ? "bg-blue-600 text-white shadow-lg shadow-blue-200" : "bg-white text-slate-400 border border-slate-200"
|
className={`w-8 h-8 sm:w-10 sm:h-10 rounded-full flex items-center justify-center transition-all duration-500 text-sm sm:text-base ${step >= i ? "bg-blue-600 text-white shadow-lg shadow-blue-200" : "bg-white text-slate-400 border border-slate-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{step > i ? <CheckCircle2 size={24} /> : <span>{i}</span>}
|
{step > i ? <CheckCircle2 size={20} /> : <span>{i}</span>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-1/2 left-0 w-full h-0.5 bg-slate-200 -translate-y-1/2 max-w-2xl mx-auto right-0" />
|
<div className="absolute top-1/2 left-4 right-4 h-0.5 bg-slate-200 -translate-y-1/2 max-w-xs sm:max-w-2xl mx-auto" />
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute top-1/2 left-0 h-0.5 bg-blue-600 -translate-y-1/2 max-w-2xl mx-auto right-0 origin-left"
|
className="absolute top-1/2 left-4 right-4 h-0.5 bg-blue-600 -translate-y-1/2 max-w-xs sm:max-w-2xl mx-auto origin-left"
|
||||||
initial={{ scaleX: 0 }}
|
initial={{ scaleX: 0 }}
|
||||||
animate={{ scaleX: (step - 1) / (totalSteps - 1) }}
|
animate={{ scaleX: (step - 1) / (totalSteps - 1) }}
|
||||||
transition={{ duration: 0.5 }}
|
transition={{ duration: 0.5 }}
|
||||||
@@ -149,7 +149,7 @@ export default function SetupClient() {
|
|||||||
|
|
||||||
{/* Form Card */}
|
{/* Form Card */}
|
||||||
<Card className="border-0 shadow-xl bg-white/80 backdrop-blur-md">
|
<Card className="border-0 shadow-xl bg-white/80 backdrop-blur-md">
|
||||||
<CardContent className="p-8 md:p-12 min-h-[500px] flex flex-col justify-between">
|
<CardContent className="p-4 sm:p-8 md:p-12 min-h-[450px] sm:min-h-[500px] flex flex-col justify-between">
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -159,13 +159,14 @@ export default function SetupClient() {
|
|||||||
exit={{ opacity: 0, x: -20 }}
|
exit={{ opacity: 0, x: -20 }}
|
||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4 mb-8">
|
<div className="flex items-start sm:items-center gap-3 sm:gap-4 mb-6 sm:mb-8">
|
||||||
<div className="p-3 bg-blue-50 text-blue-600 rounded-2xl">
|
<div className="p-2 sm:p-3 bg-blue-50 text-blue-600 rounded-xl sm:rounded-2xl shrink-0">
|
||||||
<Building2 size={32} />
|
<Building2 size={24} className="sm:hidden" />
|
||||||
|
<Building2 size={32} className="hidden sm:block" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-semibold text-slate-800">Identidade da Organização</h2>
|
<h2 className="text-lg sm:text-2xl font-semibold text-slate-800">Identidade da Organização</h2>
|
||||||
<p className="text-slate-500">Defina o nome oficial e a logo que aparecerá no portal.</p>
|
<p className="text-slate-500 text-sm hidden sm:block">Defina o nome oficial e a logo que aparecerá no portal.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -234,13 +235,14 @@ export default function SetupClient() {
|
|||||||
exit={{ opacity: 0, x: -20 }}
|
exit={{ opacity: 0, x: -20 }}
|
||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4 mb-8">
|
<div className="flex items-start sm:items-center gap-3 sm:gap-4 mb-6 sm:mb-8">
|
||||||
<div className="p-3 bg-indigo-50 text-indigo-600 rounded-2xl">
|
<div className="p-2 sm:p-3 bg-indigo-50 text-indigo-600 rounded-xl sm:rounded-2xl shrink-0">
|
||||||
<Palette size={32} />
|
<Palette size={24} className="sm:hidden" />
|
||||||
|
<Palette size={32} className="hidden sm:block" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-semibold text-slate-800">Personalização Visual</h2>
|
<h2 className="text-lg sm:text-2xl font-semibold text-slate-800">Personalização Visual</h2>
|
||||||
<p className="text-slate-500">Adapte o portal às cores da sua marca.</p>
|
<p className="text-slate-500 text-sm hidden sm:block">Adapte o portal às cores da sua marca.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -294,13 +296,14 @@ export default function SetupClient() {
|
|||||||
exit={{ opacity: 0, x: -20 }}
|
exit={{ opacity: 0, x: -20 }}
|
||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4 mb-8">
|
<div className="flex items-start sm:items-center gap-3 sm:gap-4 mb-6 sm:mb-8">
|
||||||
<div className="p-3 bg-emerald-50 text-emerald-600 rounded-2xl">
|
<div className="p-2 sm:p-3 bg-emerald-50 text-emerald-600 rounded-xl sm:rounded-2xl shrink-0">
|
||||||
<User size={32} />
|
<User size={24} className="sm:hidden" />
|
||||||
|
<User size={32} className="hidden sm:block" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-semibold text-slate-800">Administrador Master</h2>
|
<h2 className="text-lg sm:text-2xl font-semibold text-slate-800">Administrador Master</h2>
|
||||||
<p className="text-slate-500">Crie a conta que terá controle total sobre o portal.</p>
|
<p className="text-slate-500 text-sm hidden sm:block">Crie a conta que terá controle total sobre o portal.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -365,30 +368,32 @@ export default function SetupClient() {
|
|||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<div className="flex justify-between items-center mt-12 pt-8 border-t border-slate-100">
|
<div className="flex justify-between items-center mt-8 sm:mt-12 pt-6 sm:pt-8 border-t border-slate-100">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={prevStep}
|
onClick={prevStep}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
className={step === 1 ? "invisible" : ""}
|
className={`h-10 sm:h-11 ${step === 1 ? "invisible" : ""}`}
|
||||||
>
|
>
|
||||||
<ArrowLeft size={18} className="mr-2" />
|
<ArrowLeft size={18} className="mr-1 sm:mr-2" />
|
||||||
Voltar
|
<span className="hidden sm:inline">Voltar</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={nextStep}
|
onClick={nextStep}
|
||||||
disabled={isSubmitting || isUploading}
|
disabled={isSubmitting || isUploading}
|
||||||
className="h-12 px-6"
|
className="h-10 sm:h-12 px-4 sm:px-6"
|
||||||
>
|
>
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 size={18} className="animate-spin mr-2" />
|
<Loader2 size={18} className="animate-spin mr-2" />
|
||||||
Finalizando...
|
<span className="hidden sm:inline">Finalizando...</span>
|
||||||
|
<span className="sm:hidden">...</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{step === totalSteps ? "Finalizar Instalação" : "Próximo Passo"}
|
<span className="hidden sm:inline">{step === totalSteps ? "Finalizar Instalação" : "Próximo Passo"}</span>
|
||||||
<ArrowRight size={18} className="ml-2" />
|
<span className="sm:hidden">{step === totalSteps ? "Finalizar" : "Próximo"}</span>
|
||||||
|
<ArrowRight size={18} className="ml-1 sm:ml-2" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -100,49 +100,50 @@ export default function FolderViewClient({
|
|||||||
<div className="min-h-screen bg-[#f9fafb] selection:bg-red-100 selection:text-red-900 flex flex-col">
|
<div className="min-h-screen bg-[#f9fafb] selection:bg-red-100 selection:text-red-900 flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
||||||
<div className="max-w-7xl mx-auto px-8 h-20 flex items-center justify-between">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 sm:gap-4 min-w-0">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<img src={organization.logoUrl} alt={organization.name} className="h-10 object-contain" />
|
<img src={organization.logoUrl} alt={organization.name} className="h-8 sm:h-10 object-contain shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-10 h-10 rounded-xl bg-slate-100 flex items-center justify-center font-bold text-slate-800">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg sm:rounded-xl bg-slate-100 flex items-center justify-center font-bold text-slate-800 shrink-0 text-sm">
|
||||||
{organization.name[0]}
|
{organization.name[0]}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h1 className="text-sm font-black text-slate-900 uppercase tracking-tight">{organization.name}</h1>
|
<h1 className="text-xs sm:text-sm font-black text-slate-900 uppercase tracking-tight truncate">{organization.name}</h1>
|
||||||
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Portal da Transparência</p>
|
<p className="text-[9px] sm:text-[10px] font-bold text-slate-400 uppercase tracking-widest hidden sm:block">Portal da Transparência</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="outline" className="border-green-200 bg-green-50 text-green-600 font-bold text-[10px] uppercase py-1 px-3">
|
<Badge variant="outline" className="border-green-200 bg-green-50 text-green-600 font-bold text-[9px] sm:text-[10px] uppercase py-1 px-2 sm:px-3 shrink-0">
|
||||||
Ambiente Seguro
|
<span className="hidden sm:inline">Ambiente </span>Seguro
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="w-full flex-1">
|
<main className="w-full flex-1">
|
||||||
<div className="w-full max-w-7xl mx-auto px-8 py-12">
|
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8 lg:py-12">
|
||||||
{/* Folder Info / Project Header */}
|
{/* Folder Info / Project Header */}
|
||||||
<div className="mb-8">
|
<div className="mb-6 sm:mb-8">
|
||||||
<div className="flex items-center gap-5 mb-6">
|
<div className="flex items-start sm:items-center gap-3 sm:gap-5 mb-4 sm:mb-6">
|
||||||
{displayFolder.imageUrl ? (
|
{displayFolder.imageUrl ? (
|
||||||
<div className="w-20 h-20 rounded-2xl overflow-hidden shrink-0 border border-slate-100">
|
<div className="w-14 h-14 sm:w-20 sm:h-20 rounded-xl sm:rounded-2xl overflow-hidden shrink-0 border border-slate-100">
|
||||||
<img src={displayFolder.imageUrl} alt={displayFolder.name} className="w-full h-full object-cover" />
|
<img src={displayFolder.imageUrl} alt={displayFolder.name} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="w-16 h-16 rounded-2xl flex items-center justify-center shrink-0"
|
className="w-12 h-12 sm:w-16 sm:h-16 rounded-xl sm:rounded-2xl flex items-center justify-center shrink-0"
|
||||||
style={{ backgroundColor: `${displayFolder.color}15`, color: displayFolder.color }}
|
style={{ backgroundColor: `${displayFolder.color}15`, color: displayFolder.color }}
|
||||||
>
|
>
|
||||||
<FolderOpen size={32} fill="currentColor" fillOpacity={0.2} strokeWidth={2} />
|
<FolderOpen size={24} className="sm:hidden" fill="currentColor" fillOpacity={0.2} strokeWidth={2} />
|
||||||
|
<FolderOpen size={32} className="hidden sm:block" fill="currentColor" fillOpacity={0.2} strokeWidth={2} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h2 className="text-3xl font-black text-slate-900 uppercase tracking-tighter leading-tight mb-1">
|
<h2 className="text-xl sm:text-2xl lg:text-3xl font-black text-slate-900 uppercase tracking-tighter leading-tight mb-1 truncate">
|
||||||
{displayFolder.name}
|
{displayFolder.name}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm font-medium text-slate-500">
|
<p className="text-xs sm:text-sm font-medium text-slate-500 line-clamp-2">
|
||||||
{displayFolder.description || "Pasta pública contendo documentos oficiais e informativos."}
|
{displayFolder.description || "Pasta pública contendo documentos oficiais."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -168,24 +169,24 @@ export default function FolderViewClient({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search & Stats */}
|
{/* Search & Stats */}
|
||||||
<div className="flex flex-col md:flex-row justify-between items-center gap-6 mb-8 mt-4">
|
<div className="flex flex-col sm:flex-row justify-between items-stretch sm:items-center gap-3 sm:gap-6 mb-6 sm:mb-8 mt-4">
|
||||||
<div className="relative w-full md:w-96 group">
|
<div className="relative flex-1 max-w-full sm:max-w-sm group">
|
||||||
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 sm:pl-4 flex items-center pointer-events-none">
|
||||||
<Search className="text-slate-400 group-focus-within:text-red-500 transition-colors" size={18} />
|
<Search className="text-slate-400 group-focus-within:text-red-500 transition-colors" size={16} />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Buscar documentos nesta pasta..."
|
placeholder="Buscar documentos..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="h-12 pl-12 pr-4 bg-white border-slate-200 rounded-xl text-sm font-medium focus:ring-0 focus:border-slate-300 transition-all shadow-none"
|
className="h-10 sm:h-12 pl-10 sm:pl-12 pr-4 bg-white border-slate-200 rounded-xl text-sm font-medium focus:ring-0 focus:border-slate-300 transition-all shadow-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3 bg-white px-5 py-2.5 rounded-xl border border-slate-100">
|
<div className="flex items-center gap-3 bg-white px-4 sm:px-5 py-2 sm:py-2.5 rounded-xl border border-slate-100 self-start">
|
||||||
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Itens</span>
|
<span className="text-[9px] sm:text-[10px] font-bold text-slate-400 uppercase tracking-widest">Itens</span>
|
||||||
<span className="text-sm font-black text-slate-900">{filteredChildren.length + filteredDocs.length}</span>
|
<span className="text-sm font-black text-slate-900">{filteredChildren.length + filteredDocs.length}</span>
|
||||||
<div className="w-[1px] h-5 bg-slate-100 mx-1" />
|
<div className="w-[1px] h-5 bg-slate-100 mx-1" />
|
||||||
<ShieldCheck className="text-green-500" size={18} />
|
<ShieldCheck className="text-green-500" size={16} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -255,12 +256,12 @@ export default function FolderViewClient({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 relative z-10">
|
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 relative z-10">
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-10 border-slate-200 hover:bg-slate-50 text-slate-600 font-bold text-[10px] uppercase rounded-lg px-4 shadow-none"
|
className="h-9 sm:h-10 border-slate-200 hover:bg-slate-50 text-slate-600 font-bold text-[10px] uppercase rounded-lg px-3 sm:px-4 shadow-none justify-center"
|
||||||
>
|
>
|
||||||
<Link href={`/documento/${doc.id}`} target="_blank">
|
<Link href={`/documento/${doc.id}`} target="_blank">
|
||||||
<ExternalLink size={14} className="mr-2" />
|
<ExternalLink size={14} className="mr-2" />
|
||||||
@@ -272,7 +273,7 @@ export default function FolderViewClient({
|
|||||||
asChild
|
asChild
|
||||||
variant="default"
|
variant="default"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-10 bg-slate-900 border-none hover:bg-red-600 text-white font-bold text-[10px] uppercase rounded-lg px-4 transition-all shadow-none"
|
className="h-9 sm:h-10 bg-slate-900 border-none hover:bg-red-600 text-white font-bold text-[10px] uppercase rounded-lg px-3 sm:px-4 transition-all shadow-none justify-center"
|
||||||
>
|
>
|
||||||
<a href={`/api/view/${doc.id}`} download={doc.fileName}>
|
<a href={`/api/view/${doc.id}`} download={doc.fileName}>
|
||||||
<Download size={14} className="mr-2" />
|
<Download size={14} className="mr-2" />
|
||||||
@@ -301,19 +302,19 @@ export default function FolderViewClient({
|
|||||||
|
|
||||||
{/* Footer - Apenas logo da organização */}
|
{/* Footer - Apenas logo da organização */}
|
||||||
<footer className="bg-white border-t border-slate-100 mt-auto">
|
<footer className="bg-white border-t border-slate-100 mt-auto">
|
||||||
<div className="max-w-7xl mx-auto px-8 h-20 flex items-center justify-center">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-center">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<img
|
<img
|
||||||
src={organization.logoUrl}
|
src={organization.logoUrl}
|
||||||
alt={organization.name}
|
alt={organization.name}
|
||||||
className="h-10 object-contain opacity-60 grayscale hover:opacity-100 hover:grayscale-0 transition-all cursor-pointer"
|
className="h-8 sm:h-10 object-contain opacity-60 grayscale hover:opacity-100 hover:grayscale-0 transition-all cursor-pointer"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-3 text-slate-400">
|
<div className="flex items-center gap-2 sm:gap-3 text-slate-400">
|
||||||
<div className="w-10 h-10 rounded-xl bg-slate-100 flex items-center justify-center font-bold text-slate-500">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg sm:rounded-xl bg-slate-100 flex items-center justify-center font-bold text-slate-500 text-sm">
|
||||||
{organization.name[0]}
|
{organization.name[0]}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-bold uppercase tracking-widest">{organization.name}</span>
|
<span className="text-xs sm:text-sm font-bold uppercase tracking-widest truncate max-w-[150px] sm:max-w-none">{organization.name}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Lock, Mail, ArrowRight, ShieldCheck, CreditCard, FileText } from "lucide-react";
|
import { Lock, Mail, ArrowRight, ShieldCheck, CreditCard, FileText, Loader2 } from "lucide-react";
|
||||||
import { login } from "@/app/actions/auth";
|
import { login } from "@/app/actions/auth";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
|
|
||||||
type Organization = {
|
type Organization = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -42,8 +41,8 @@ export default function LoginClient({ organization }: { organization: Organizati
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen grid grid-cols-1 lg:grid-cols-2 bg-white selection:bg-slate-900 selection:text-white">
|
<div className="min-h-screen flex flex-col lg:grid lg:grid-cols-2 bg-white selection:bg-slate-900 selection:text-white">
|
||||||
{/* Left Side: Illustration & Info */}
|
{/* Left Side: Illustration & Info - Hidden on Mobile */}
|
||||||
<div
|
<div
|
||||||
className="hidden lg:flex flex-col justify-between p-16 text-white relative overflow-hidden"
|
className="hidden lg:flex flex-col justify-between p-16 text-white relative overflow-hidden"
|
||||||
style={{ background: `linear-gradient(135deg, ${primaryColor} 0%, ${primaryColor}dd 100%)` }}
|
style={{ background: `linear-gradient(135deg, ${primaryColor} 0%, ${primaryColor}dd 100%)` }}
|
||||||
@@ -103,20 +102,43 @@ export default function LoginClient({ organization }: { organization: Organizati
|
|||||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] bg-black/10 rounded-full blur-[100px] -ml-40 -mb-40 opacity-20 pointer-events-none" />
|
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] bg-black/10 rounded-full blur-[100px] -ml-40 -mb-40 opacity-20 pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Side: Login Form */}
|
{/* Right Side: Login Form - Full screen on mobile */}
|
||||||
<div className="flex flex-col items-center justify-center p-10 lg:p-16 relative overflow-hidden bg-white">
|
<div className="flex-1 flex flex-col items-center justify-center p-6 sm:p-10 lg:p-16 relative overflow-hidden bg-white min-h-screen lg:min-h-0">
|
||||||
|
{/* Mobile Header */}
|
||||||
|
<div className="lg:hidden w-full max-w-sm mb-8">
|
||||||
|
<div className="flex items-center justify-center gap-3 mb-6">
|
||||||
|
{organization.logoUrl ? (
|
||||||
|
<div
|
||||||
|
className="w-12 h-12 rounded-xl flex items-center justify-center p-2 border"
|
||||||
|
style={{ borderColor: `${primaryColor}30`, backgroundColor: `${primaryColor}10` }}
|
||||||
|
>
|
||||||
|
<img src={organization.logoUrl} alt="Logo" className="max-w-full max-h-full object-contain" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="w-12 h-12 rounded-xl flex items-center justify-center text-white font-bold"
|
||||||
|
style={{ backgroundColor: primaryColor }}
|
||||||
|
>
|
||||||
|
{organization.name.charAt(0)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h1 className="text-xl font-black text-slate-900 text-center tracking-tight">{organization.name}</h1>
|
||||||
|
<p className="text-xs text-slate-400 text-center font-medium mt-1">Portal de Transparência</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<main className="w-full max-w-sm relative z-10">
|
<main className="w-full max-w-sm relative z-10">
|
||||||
<div className="mb-10 text-center lg:text-left">
|
<div className="mb-8 lg:mb-10 text-center lg:text-left">
|
||||||
<div className="inline-block px-2.5 py-0.5 bg-slate-50 border border-slate-100 rounded-full text-[9px] font-black uppercase tracking-[0.15em] text-slate-400 mb-4">
|
<div className="inline-block px-2.5 py-0.5 bg-slate-50 border border-slate-100 rounded-full text-[9px] font-black uppercase tracking-[0.15em] text-slate-400 mb-3 lg:mb-4">
|
||||||
Admin Access
|
Admin Access
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl font-black text-slate-900 tracking-tighter mb-2 leading-none italic">Acesso Restrito.</h2>
|
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-black text-slate-900 tracking-tighter mb-2 leading-none italic">Acesso Restrito.</h2>
|
||||||
<p className="text-base text-slate-500 font-medium">Insira suas credenciais corporativas.</p>
|
<p className="text-sm lg:text-base text-slate-500 font-medium">Insira suas credenciais corporativas.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form className="space-y-6" onSubmit={handleSubmit}>
|
<form className="space-y-5 lg:space-y-6" onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<Alert variant="destructive" className="rounded-xl border-2 border-red-100 bg-red-50 text-red-600 font-bold p-5">
|
<Alert variant="destructive" className="rounded-xl border-2 border-red-100 bg-red-50 text-red-600 font-bold p-4 lg:p-5">
|
||||||
<AlertDescription className="text-sm">{error}</AlertDescription>
|
<AlertDescription className="text-sm">{error}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
@@ -131,7 +153,7 @@ export default function LoginClient({ organization }: { organization: Organizati
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
placeholder="admin@exemplo.com"
|
placeholder="admin@exemplo.com"
|
||||||
className="pl-12 h-11 bg-slate-50 border-none rounded-xl text-sm font-bold focus:bg-white focus:ring-4 focus:ring-slate-50 transition-all shadow-none"
|
className="pl-12 h-12 lg:h-11 bg-slate-50 border-none rounded-xl text-sm font-bold focus:bg-white focus:ring-4 focus:ring-slate-50 transition-all shadow-none"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,7 +169,7 @@ export default function LoginClient({ organization }: { organization: Organizati
|
|||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
className="pl-12 h-11 bg-slate-50 border-none rounded-xl text-sm font-bold focus:bg-white focus:ring-4 focus:ring-slate-50 transition-all shadow-none"
|
className="pl-12 h-12 lg:h-11 bg-slate-50 border-none rounded-xl text-sm font-bold focus:bg-white focus:ring-4 focus:ring-slate-50 transition-all shadow-none"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -156,15 +178,24 @@ export default function LoginClient({ organization }: { organization: Organizati
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="w-full h-11 rounded-xl text-xs font-black uppercase tracking-widest italic shadow-none transition-all active:scale-95 flex items-center justify-center gap-3 group text-white"
|
className="w-full h-12 lg:h-11 rounded-xl text-xs font-black uppercase tracking-widest italic shadow-none transition-all active:scale-95 flex items-center justify-center gap-3 group text-white"
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
>
|
>
|
||||||
{isLoading ? "Validando..." : "Entrar no Painel"}
|
{isLoading ? (
|
||||||
{!isLoading && <ArrowRight size={20} className="stroke-[3] group-hover:translate-x-1.5 transition-transform" />}
|
<>
|
||||||
|
<Loader2 size={18} className="animate-spin" />
|
||||||
|
Validando...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Entrar no Painel
|
||||||
|
<ArrowRight size={20} className="stroke-[3] group-hover:translate-x-1.5 transition-transform" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p className="mt-10 text-center text-slate-400 text-[10px] font-bold uppercase tracking-widest">
|
<p className="mt-8 lg:mt-10 text-center text-slate-400 text-[10px] font-bold uppercase tracking-widest">
|
||||||
Segurança protegida por criptografia.
|
Segurança protegida por criptografia.
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
FileText,
|
FileText,
|
||||||
Users,
|
Users,
|
||||||
Settings,
|
Settings,
|
||||||
LogOut,
|
LogOut,
|
||||||
|
Menu,
|
||||||
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
@@ -36,6 +38,7 @@ export function Sidebar({
|
|||||||
}) {
|
}) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const primaryColor = organization.primaryColor || "#2563eb";
|
const primaryColor = organization.primaryColor || "#2563eb";
|
||||||
|
const [mobileOpen, setMobileOpen] = useState(false);
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
||||||
@@ -44,12 +47,12 @@ export function Sidebar({
|
|||||||
{ href: "/dashboard/configuracoes", label: "Configurações", icon: Settings },
|
{ href: "/dashboard/configuracoes", label: "Configurações", icon: Settings },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
const SidebarContent = () => (
|
||||||
<aside className="w-72 bg-slate-50/50 border-r border-slate-200/60 flex flex-col h-screen sticky top-0 shrink-0 select-none">
|
<>
|
||||||
<div className="p-8">
|
<div className="p-6 lg:p-8">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 lg:gap-4">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<div className="w-12 h-12 rounded-2xl bg-white border border-slate-200 p-2 flex items-center justify-center">
|
<div className="w-10 h-10 lg:w-12 lg:h-12 rounded-xl lg:rounded-2xl bg-white border border-slate-200 p-1.5 lg:p-2 flex items-center justify-center shrink-0">
|
||||||
<img
|
<img
|
||||||
src={organization.logoUrl}
|
src={organization.logoUrl}
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
@@ -58,58 +61,67 @@ export function Sidebar({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-2xl flex items-center justify-center text-white font-bold text-xl shrink-0 border border-white/20"
|
className="w-10 h-10 lg:w-12 lg:h-12 rounded-xl lg:rounded-2xl flex items-center justify-center text-white font-bold text-lg lg:text-xl shrink-0 border border-white/20"
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
>
|
>
|
||||||
{organization.name.charAt(0)}
|
{organization.name.charAt(0)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="min-w-0">
|
<div className="min-w-0 flex-1">
|
||||||
<h1 className="text-base font-black text-slate-800 tracking-tight truncate leading-tight">
|
<h1 className="text-sm lg:text-base font-black text-slate-800 tracking-tight truncate leading-tight">
|
||||||
{organization.name}
|
{organization.name}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mt-0.5">Gestão Fiscal</p>
|
<p className="text-[9px] lg:text-[10px] font-bold text-slate-400 uppercase tracking-widest mt-0.5">Gestão Fiscal</p>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Close button for mobile */}
|
||||||
|
<button
|
||||||
|
className="lg:hidden p-2 -mr-2 text-slate-500 hover:text-slate-900"
|
||||||
|
onClick={() => setMobileOpen(false)}
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="flex-1 px-4 space-y-1.5 overflow-y-auto pt-2">
|
<nav className="flex-1 px-3 lg:px-4 space-y-1 lg:space-y-1.5 overflow-y-auto pt-2">
|
||||||
{menuItems.map((item) => {
|
{menuItems.map((item) => {
|
||||||
const isActive = pathname === item.href || (item.href !== "/dashboard" && pathname.startsWith(item.href));
|
const isActive = pathname === item.href || (item.href !== "/dashboard" && pathname.startsWith(item.href));
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={item.href}
|
key={item.href}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
className={`flex items-center gap-3.5 px-5 py-3.5 rounded-2xl transition-all duration-300 group ${isActive
|
onClick={() => setMobileOpen(false)}
|
||||||
? "font-bold"
|
className={`flex items-center gap-3 lg:gap-3.5 px-4 lg:px-5 py-3 lg:py-3.5 rounded-xl lg:rounded-2xl transition-all duration-300 group ${isActive
|
||||||
: "text-slate-500 hover:text-slate-900 hover:bg-white"
|
? "font-bold"
|
||||||
|
: "text-slate-500 hover:text-slate-900 hover:bg-white"
|
||||||
}`}
|
}`}
|
||||||
style={isActive ? { backgroundColor: `${primaryColor}10`, color: primaryColor } : {}}
|
style={isActive ? { backgroundColor: `${primaryColor}10`, color: primaryColor } : {}}
|
||||||
>
|
>
|
||||||
<item.icon size={20} className={`transition-transform duration-300 ${isActive ? "scale-110" : "group-hover:scale-110 opacity-70 group-hover:opacity-100"}`} />
|
<item.icon size={18} className={`transition-transform duration-300 ${isActive ? "scale-110" : "group-hover:scale-110 opacity-70 group-hover:opacity-100"}`} />
|
||||||
<span className="text-[13px] tracking-tight">{item.label}</span>
|
<span className="text-[13px] tracking-tight">{item.label}</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="p-6 mt-auto">
|
<div className="p-4 lg:p-6 mt-auto">
|
||||||
<div className="flex flex-col gap-4 p-5 rounded-[24px] bg-white border border-slate-200/80">
|
<div className="flex flex-col gap-3 lg:gap-4 p-4 lg:p-5 rounded-2xl lg:rounded-[24px] bg-white border border-slate-200/80">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/perfil"
|
href="/dashboard/perfil"
|
||||||
className="flex items-center gap-3.5 group"
|
onClick={() => setMobileOpen(false)}
|
||||||
|
className="flex items-center gap-3 lg:gap-3.5 group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-10 h-10 rounded-full flex items-center justify-center text-white text-sm font-black shrink-0 border-2 border-slate-100"
|
className="w-9 h-9 lg:w-10 lg:h-10 rounded-full flex items-center justify-center text-white text-xs lg:text-sm font-black shrink-0 border-2 border-slate-100"
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
>
|
>
|
||||||
{user.name?.charAt(0) || user.email.charAt(0).toUpperCase()}
|
{user.name?.charAt(0) || user.email.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-[13px] font-black text-slate-900 truncate leading-tight group-hover:underline decoration-2 underline-offset-2">
|
<p className="text-[12px] lg:text-[13px] font-black text-slate-900 truncate leading-tight group-hover:underline decoration-2 underline-offset-2">
|
||||||
{user.name || "Usuário"}
|
{user.name || "Usuário"}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-[11px] font-medium text-slate-400 truncate mt-0.5">{user.email}</p>
|
<p className="text-[10px] lg:text-[11px] font-medium text-slate-400 truncate mt-0.5">{user.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@@ -119,15 +131,69 @@ export function Sidebar({
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="w-full justify-start gap-3 h-11 px-4 rounded-xl text-slate-500 hover:text-red-600 hover:bg-red-50 text-[13px] font-bold transition-all duration-300"
|
className="w-full justify-start gap-3 h-10 lg:h-11 px-3 lg:px-4 rounded-xl text-slate-500 hover:text-red-600 hover:bg-red-50 text-[12px] lg:text-[13px] font-bold transition-all duration-300"
|
||||||
title="Sair do sistema"
|
title="Sair do sistema"
|
||||||
>
|
>
|
||||||
<LogOut size={18} />
|
<LogOut size={16} />
|
||||||
Encerrar Sessão
|
Encerrar Sessão
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Mobile Header */}
|
||||||
|
<div className="lg:hidden fixed top-0 left-0 right-0 h-16 bg-white border-b border-slate-200 z-40 flex items-center justify-between px-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{organization.logoUrl ? (
|
||||||
|
<div className="w-9 h-9 rounded-xl bg-white border border-slate-200 p-1.5 flex items-center justify-center">
|
||||||
|
<img
|
||||||
|
src={organization.logoUrl}
|
||||||
|
alt="Logo"
|
||||||
|
className="max-w-full max-h-full object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="w-9 h-9 rounded-xl flex items-center justify-center text-white font-bold text-sm"
|
||||||
|
style={{ backgroundColor: primaryColor }}
|
||||||
|
>
|
||||||
|
{organization.name.charAt(0)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className="text-sm font-bold text-slate-800 truncate max-w-[180px]">{organization.name}</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setMobileOpen(true)}
|
||||||
|
className="p-2 text-slate-600 hover:text-slate-900 hover:bg-slate-100 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<Menu size={22} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Overlay */}
|
||||||
|
{mobileOpen && (
|
||||||
|
<div
|
||||||
|
className="lg:hidden fixed inset-0 bg-black/50 z-40 backdrop-blur-sm"
|
||||||
|
onClick={() => setMobileOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Mobile Sidebar Drawer */}
|
||||||
|
<aside
|
||||||
|
className={`lg:hidden fixed top-0 left-0 bottom-0 w-[280px] bg-slate-50/95 backdrop-blur-lg z-50 flex flex-col transform transition-transform duration-300 ease-out ${mobileOpen ? 'translate-x-0' : '-translate-x-full'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<SidebarContent />
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* Desktop Sidebar */}
|
||||||
|
<aside className="hidden lg:flex w-72 bg-slate-50/50 border-r border-slate-200/60 flex-col h-screen sticky top-0 shrink-0 select-none">
|
||||||
|
<SidebarContent />
|
||||||
|
</aside>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user