refactor: move financial sub-menus to sidebar and simplify dashboard header
This commit is contained in:
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,7 +363,8 @@ 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">
|
||||||
|
<div className="flex p-1 bg-surface-raised border border-border rounded-lg">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode('grid')}
|
onClick={() => setViewMode('grid')}
|
||||||
className={clsx("p-2 rounded-md transition-all", viewMode === 'grid' ? "bg-white/10 text-primary shadow-sm" : "text-muted")}
|
className={clsx("p-2 rounded-md transition-all", viewMode === 'grid' ? "bg-white/10 text-primary shadow-sm" : "text-muted")}
|
||||||
@@ -403,7 +379,7 @@ export function FinancialDashboard({ events, transactions, players, group }: Fin
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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(
|
||||||
@@ -430,14 +406,15 @@ export function FinancialDashboard({ events, transactions, players, group }: Fin
|
|||||||
{mainTab !== 'REPORTS' && (
|
{mainTab !== 'REPORTS' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => mainTab === 'EVENTS' ? setIsCreateModalOpen(true) : setIsCreateTransactionModalOpen(true)}
|
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"
|
className="ui-button shadow-lg shadow-primary/20 h-10 px-6 font-black whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4 mr-2" />
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
{mainTab === 'EVENTS' ? 'Novo Evento' : 'Registrar'}
|
{mainTab === 'EVENTS' ? 'Novo Evento' : 'Registrar'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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,10 +70,12 @@ 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 (
|
||||||
|
<div key={item.href} className="space-y-1">
|
||||||
<Link
|
<Link
|
||||||
key={item.href}
|
|
||||||
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",
|
||||||
@@ -73,6 +87,23 @@ export function Sidebar({ group }: { group: any }) {
|
|||||||
<item.icon className={clsx("w-4 h-4", isActive ? "text-primary" : "text-muted")} />
|
<item.icon className={clsx("w-4 h-4", isActive ? "text-primary" : "text-muted")} />
|
||||||
<span>{item.name}</span>
|
<span>{item.name}</span>
|
||||||
</Link>
|
</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>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
@@ -106,4 +137,3 @@ export function Sidebar({ group }: { group: any }) {
|
|||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user