refactor: move financial sub-menus to sidebar and simplify dashboard header

This commit is contained in:
Erik Silva
2026-02-04 19:43:21 -03:00
parent 4e6926f7a6
commit aaff3638bc
3 changed files with 116 additions and 103 deletions

View File

@@ -2,7 +2,12 @@ import { getActiveGroup } from '@/lib/auth'
import { getFinancialEvents, getTransactions } from '@/actions/finance' import { getFinancialEvents, getTransactions } from '@/actions/finance'
import { FinancialDashboard } from '@/components/FinancialDashboard' import { FinancialDashboard } from '@/components/FinancialDashboard'
export default async function FinancialPage() { interface FinancialPageProps {
searchParams: Promise<{ tab?: string }>
}
export default async function FinancialPage({ searchParams }: FinancialPageProps) {
const { tab } = await searchParams
const group = await getActiveGroup() const group = await getActiveGroup()
if (!group) return null if (!group) return null
@@ -24,6 +29,7 @@ export default async function FinancialPage() {
transactions={transactions} transactions={transactions}
players={group.players} players={group.players}
group={group} group={group}
initialTab={(tab as any) || 'EVENTS'}
/> />
</div> </div>
) )

View File

@@ -19,9 +19,10 @@ interface FinancialPageProps {
transactions: any[] transactions: any[]
players: any[] players: any[]
group: any group: any
initialTab: 'EVENTS' | 'TRANSACTIONS' | 'REPORTS'
} }
export function FinancialDashboard({ events, transactions, players, group }: FinancialPageProps) { export function FinancialDashboard({ events, transactions, players, group, initialTab }: FinancialPageProps) {
const router = useRouter() const router = useRouter()
const [mounted, setMounted] = React.useState(false) const [mounted, setMounted] = React.useState(false)
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false) const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)
@@ -47,7 +48,12 @@ export function FinancialDashboard({ events, transactions, players, group }: Fin
const [privacyPendingId, setPrivacyPendingId] = useState<string | null>(null) const [privacyPendingId, setPrivacyPendingId] = useState<string | null>(null)
// Filter & View State // Filter & View State
const [mainTab, setMainTab] = useState<'EVENTS' | 'TRANSACTIONS' | 'REPORTS'>('EVENTS') const [mainTab, setMainTab] = useState<'EVENTS' | 'TRANSACTIONS' | 'REPORTS'>(initialTab)
// Sync state when prop changes (from sidebar clicks)
React.useEffect(() => {
setMainTab(initialTab)
}, [initialTab])
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [activeTab, setActiveTab] = useState<'ALL' | 'MONTHLY_FEE' | 'EXTRA_EVENT' | 'INCOME' | 'EXPENSE'>('ALL') const [activeTab, setActiveTab] = useState<'ALL' | 'MONTHLY_FEE' | 'EXTRA_EVENT' | 'INCOME' | 'EXPENSE'>('ALL')
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
@@ -328,56 +334,25 @@ export function FinancialDashboard({ events, transactions, players, group }: Fin
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6"> <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6">
<div className="flex bg-surface-raised border border-border p-1 rounded-2xl w-fit overflow-hidden scrollbar-none max-w-full overflow-x-auto"> <div className="flex-1 w-full lg:w-auto">
<button {selectedIds.size > 0 ? (
onClick={() => { setMainTab('EVENTS'); setActiveTab('ALL'); setSelectedIds(new Set()); }}
className={clsx(
"px-6 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all shrink-0",
mainTab === 'EVENTS' ? "bg-primary text-background shadow-lg shadow-primary/20" : "text-muted hover:text-white"
)}
>
<History className="w-4 h-4 inline-block mr-2" /> Eventos
</button>
<button
onClick={() => { setMainTab('TRANSACTIONS'); setActiveTab('ALL'); setSelectedIds(new Set()); }}
className={clsx(
"px-6 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all shrink-0",
mainTab === 'TRANSACTIONS' ? "bg-primary text-background shadow-lg shadow-primary/20" : "text-muted hover:text-white"
)}
>
<Receipt className="w-4 h-4 inline-block mr-2" /> Caixa
</button>
<button
onClick={() => { setMainTab('REPORTS'); setActiveTab('ALL'); setSelectedIds(new Set()); }}
className={clsx(
"px-6 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all shrink-0",
mainTab === 'REPORTS' ? "bg-primary text-background shadow-lg shadow-primary/20" : "text-muted hover:text-white"
)}
>
<Sparkles className="w-4 h-4 inline-block mr-2" /> Relatórios
</button>
</div>
<div className="flex flex-col sm:flex-row items-center gap-4">
{selectedIds.size > 0 && (
<div className="flex items-center gap-3 animate-in fade-in slide-in-from-top-1"> <div className="flex items-center gap-3 animate-in fade-in slide-in-from-top-1">
<div className="hidden sm:flex items-center px-3 py-1 bg-primary/10 border border-primary/20 rounded-full"> <div className="flex items-center px-3 py-1 bg-primary/10 border border-primary/20 rounded-full">
<span className="text-[10px] font-black uppercase text-primary"> <span className="text-[10px] font-black uppercase text-primary">
{selectedIds.size} {selectedIds.size === 1 ? 'SELECIONADO' : 'SELECIONADOS'} {selectedIds.size} {selectedIds.size === 1 ? 'SELECIONADO' : 'SELECIONADOS'}
</span> </span>
</div> </div>
<button <button
onClick={handleDeleteSelected} onClick={handleDeleteSelected}
className="ui-button bg-red-500/10 text-red-500 hover:bg-red-500 hover:text-white border-red-500/20 h-10 w-full sm:w-auto shadow-lg shadow-red-500/10" className="ui-button bg-red-500/10 text-red-500 hover:bg-red-500 hover:text-white border-red-500/20 h-10 shadow-lg shadow-red-500/10 px-4"
> >
<Trash2 className="w-4 h-4 mr-2" /> Excluir <Trash2 className="w-4 h-4 mr-2" /> Excluir
</button> </button>
</div> </div>
)} ) : (
{selectedIds.size === 0 && ( <div className="flex flex-col sm:flex-row items-center gap-4 w-full">
<>
{mainTab !== 'REPORTS' && ( {mainTab !== 'REPORTS' && (
<div className="relative group w-full sm:w-64"> <div className="relative group w-full sm:flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted group-focus-within:text-primary transition-colors" /> <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted group-focus-within:text-primary transition-colors" />
<input <input
value={searchQuery} value={searchQuery}
@@ -388,56 +363,58 @@ export function FinancialDashboard({ events, transactions, players, group }: Fin
</div> </div>
)} )}
<div className="flex p-1 bg-surface-raised border border-border rounded-lg w-full sm:w-auto"> <div className="flex items-center gap-4 w-full sm:w-auto">
<button <div className="flex p-1 bg-surface-raised border border-border rounded-lg">
onClick={() => setViewMode('grid')} <button
className={clsx("p-2 rounded-md transition-all", viewMode === 'grid' ? "bg-white/10 text-primary shadow-sm" : "text-muted")} onClick={() => setViewMode('grid')}
> className={clsx("p-2 rounded-md transition-all", viewMode === 'grid' ? "bg-white/10 text-primary shadow-sm" : "text-muted")}
<LayoutGrid className="w-4 h-4" /> >
</button> <LayoutGrid className="w-4 h-4" />
<button </button>
onClick={() => setViewMode('list')} <button
className={clsx("p-2 rounded-md transition-all", viewMode === 'list' ? "bg-white/10 text-primary shadow-sm" : "text-muted")} onClick={() => setViewMode('list')}
> className={clsx("p-2 rounded-md transition-all", viewMode === 'list' ? "bg-white/10 text-primary shadow-sm" : "text-muted")}
<List className="w-4 h-4" /> >
</button> <List className="w-4 h-4" />
</div> </button>
</div>
<div className="flex items-center gap-2 w-full sm:w-auto"> <div className="flex items-center gap-2">
<button <button
onClick={() => setIsFilterModalOpen(true)} onClick={() => setIsFilterModalOpen(true)}
className={clsx( className={clsx(
"p-2.5 bg-surface-raised border rounded-xl transition-all shadow-lg", "p-2.5 bg-surface-raised border rounded-xl transition-all shadow-lg",
(startDate || endDate || activeTab !== 'ALL' || filterCategory !== 'ALL') (startDate || endDate || activeTab !== 'ALL' || filterCategory !== 'ALL')
? "border-primary text-primary bg-primary/10" ? "border-primary text-primary bg-primary/10"
: "border-border text-muted hover:text-primary hover:border-primary/30" : "border-border text-muted hover:text-primary hover:border-primary/30"
)}
title="Filtros"
>
<Filter className="w-5 h-5" />
</button>
{mainTab === 'EVENTS' && (
<button
onClick={() => setIsSettingsModalOpen(true)}
className="p-2.5 bg-surface-raised border border-border rounded-xl text-muted hover:text-primary hover:border-primary/30 transition-all shadow-lg shadow-black/20"
title="Configurações de Privacidade"
>
<Settings className="w-5 h-5" />
</button>
)} )}
title="Filtros"
>
<Filter className="w-5 h-5" />
</button>
{mainTab === 'EVENTS' && ( {mainTab !== 'REPORTS' && (
<button <button
onClick={() => setIsSettingsModalOpen(true)} onClick={() => mainTab === 'EVENTS' ? setIsCreateModalOpen(true) : setIsCreateTransactionModalOpen(true)}
className="p-2.5 bg-surface-raised border border-border rounded-xl text-muted hover:text-primary hover:border-primary/30 transition-all shadow-lg shadow-black/20" className="ui-button shadow-lg shadow-primary/20 h-10 px-6 font-black whitespace-nowrap"
title="Configurações de Privacidade" >
> <Plus className="w-4 h-4 mr-2" />
<Settings className="w-5 h-5" /> {mainTab === 'EVENTS' ? 'Novo Evento' : 'Registrar'}
</button> </button>
)} )}
</div>
{mainTab !== 'REPORTS' && (
<button
onClick={() => mainTab === 'EVENTS' ? setIsCreateModalOpen(true) : setIsCreateTransactionModalOpen(true)}
className="ui-button flex-1 sm:flex-initial shadow-lg shadow-primary/20 h-10 px-6 font-black"
>
<Plus className="w-4 h-4 mr-2" />
{mainTab === 'EVENTS' ? 'Novo Evento' : 'Registrar'}
</button>
)}
</div> </div>
</> </div>
)} )}
</div> </div>
</div> </div>

View File

@@ -11,7 +11,10 @@ import {
Search, Search,
Banknote, Banknote,
LogOut, LogOut,
Eye Eye,
History,
Receipt,
Sparkles
} from 'lucide-react' } from 'lucide-react'
import { clsx } from 'clsx' import { clsx } from 'clsx'
@@ -23,7 +26,16 @@ export function Sidebar({ group }: { group: any }) {
{ name: 'Início', href: '/dashboard', icon: Home }, { name: 'Início', href: '/dashboard', icon: Home },
{ name: 'Partidas', href: '/dashboard/matches', icon: Calendar }, { name: 'Partidas', href: '/dashboard/matches', icon: Calendar },
{ name: 'Jogadores', href: '/dashboard/players', icon: Users }, { name: 'Jogadores', href: '/dashboard/players', icon: Users },
{ name: 'Financeiro', href: '/dashboard/financial', icon: Banknote }, {
name: 'Financeiro',
href: '/dashboard/financial',
icon: Banknote,
subItems: [
{ name: 'Eventos', href: '/dashboard/financial?tab=EVENTS', icon: History },
{ name: 'Caixa', href: '/dashboard/financial?tab=TRANSACTIONS', icon: Receipt },
{ name: 'Relatórios', href: '/dashboard/financial?tab=REPORTS', icon: Sparkles },
]
},
{ name: 'Agenda Pública', href: '/agenda', icon: Eye }, { name: 'Agenda Pública', href: '/agenda', icon: Eye },
{ name: 'Configurações', href: '/dashboard/settings', icon: Settings }, { name: 'Configurações', href: '/dashboard/settings', icon: Settings },
] ]
@@ -58,21 +70,40 @@ export function Sidebar({ group }: { group: any }) {
<nav className="flex-1 px-3 py-2 space-y-1"> <nav className="flex-1 px-3 py-2 space-y-1">
<p className="text-[10px] font-bold text-muted uppercase tracking-widest px-3 mb-2 mt-4">Navegação</p> <p className="text-[10px] font-bold text-muted uppercase tracking-widest px-3 mb-2 mt-4">Navegação</p>
{navItems.map((item) => { {navItems.map((item) => {
const isActive = pathname === item.href const isActive = pathname === item.href || (item.subItems && pathname.startsWith(item.href))
const hasSubItems = item.subItems && item.subItems.length > 0
return ( return (
<Link <div key={item.href} className="space-y-1">
key={item.href} <Link
href={item.href} href={item.href}
className={clsx( className={clsx(
"flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-all", "flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-all",
isActive isActive
? "bg-primary/5 text-primary border border-primary/20" ? "bg-primary/5 text-primary border border-primary/20"
: "text-muted hover:text-foreground hover:bg-surface-raised" : "text-muted hover:text-foreground hover:bg-surface-raised"
)}
>
<item.icon className={clsx("w-4 h-4", isActive ? "text-primary" : "text-muted")} />
<span>{item.name}</span>
</Link>
{hasSubItems && (isActive || pathname.startsWith(item.href)) && (
<div className="ml-9 space-y-1 border-l border-border pl-2">
{item.subItems!.map((sub) => {
return (
<Link
key={sub.href}
href={sub.href}
className="flex items-center gap-2 px-2 py-1.5 rounded-md text-[11px] font-bold uppercase tracking-wider text-muted hover:text-primary transition-colors"
>
<span>{sub.name}</span>
</Link>
)
})}
</div>
)} )}
> </div>
<item.icon className={clsx("w-4 h-4", isActive ? "text-primary" : "text-muted")} />
<span>{item.name}</span>
</Link>
) )
})} })}
</nav> </nav>
@@ -106,4 +137,3 @@ export function Sidebar({ group }: { group: any }) {
</aside> </aside>
) )
} }