refactor(core): centraliza hooks, providers e base compartilhada

This commit is contained in:
Felipe Coutinho
2026-03-09 17:11:55 +00:00
parent 2de5101058
commit 3e06a1d056
76 changed files with 3271 additions and 709 deletions

View File

@@ -1,12 +1,15 @@
import Link from "next/link";
import { AnimatedThemeToggler } from "@/components/animated-theme-toggler";
import { Logo } from "@/components/logo";
import { NotificationBell } from "@/components/notificacoes/notification-bell";
import { AnimatedThemeToggler } from "@/components/shared/animated-theme-toggler";
import { Logo } from "@/components/shared/logo";
import { RefreshPageButton } from "@/components/shared/refresh-page-button";
import type { DashboardNotificationsSnapshot } from "@/lib/dashboard/notifications";
import { NavMenu } from "./nav-menu";
import { NavbarUser } from "./navbar-user";
const navbarActionClassName =
"border-black/10 bg-transparent text-black/75 shadow-none hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-black/20 data-[state=open]:bg-black/10 data-[state=open]:text-black";
type AppNavbarProps = {
user: {
id: string;
@@ -26,11 +29,11 @@ export function AppNavbar({
notificationsSnapshot,
}: AppNavbarProps) {
return (
<header className="fixed top-0 left-0 right-0 z-50 h-16 shrink-0 flex items-center bg-card backdrop-blur-lg supports-backdrop-filter:bg-card/50">
<header className="fixed top-0 left-0 right-0 z-50 flex h-16 shrink-0 items-center bg-primary font-[aeonik] tracking-tight">
<div className="w-full max-w-8xl mx-auto px-4 flex items-center gap-4 h-full">
{/* Logo */}
<Link href="/dashboard" className="shrink-0 mr-1">
<Logo variant="compact" />
<Logo variant="compact" invertTextOnDark={false} />
</Link>
{/* Navigation */}
@@ -44,8 +47,8 @@ export function AppNavbar({
budgetNotifications={notificationsSnapshot.budgetNotifications}
preLancamentosCount={preLancamentosCount}
/>
<RefreshPageButton />
<AnimatedThemeToggler />
<RefreshPageButton className={navbarActionClassName} />
<AnimatedThemeToggler className={navbarActionClassName} />
</div>
{/* User avatar */}

View File

@@ -23,7 +23,7 @@ export function NavDropdown({ items }: NavDropdownProps) {
{item.badge && item.badge > 0 ? (
<Badge
variant="secondary"
className="text-[10px] px-1.5 py-0 h-4 min-w-4 ml-auto"
className="text-xs px-1.5 py-0 h-4 min-w-4 ml-auto"
>
{item.badge}
</Badge>

View File

@@ -2,7 +2,6 @@
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { useMemo } from "react";
const PERIOD_PARAM = "periodo";
@@ -18,13 +17,14 @@ export function NavLink({
}: NavLinkProps) {
const searchParams = useSearchParams();
const resolvedHref = useMemo(() => {
if (!preservePeriod) return href;
let resolvedHref = href;
if (preservePeriod) {
const periodo = searchParams.get(PERIOD_PARAM);
if (!periodo) return href;
const separator = href.includes("?") ? "&" : "?";
return `${href}${separator}${PERIOD_PARAM}=${encodeURIComponent(periodo)}`;
}, [href, preservePeriod, searchParams]);
if (periodo) {
const separator = href.includes("?") ? "&" : "?";
resolvedHref = `${href}${separator}${PERIOD_PARAM}=${encodeURIComponent(periodo)}`;
}
}
return <Link href={resolvedHref} {...props} />;
}

View File

@@ -35,7 +35,7 @@ export function NavMenu() {
return (
<>
{/* Desktop */}
<nav className="hidden md:flex items-center justify-center flex-1">
<nav className="hidden md:flex items-center justify-center flex-1 ">
<NavigationMenu viewport={false}>
<NavigationMenuList className="gap-0">
<NavigationMenuItem>
@@ -73,14 +73,14 @@ export function NavMenu() {
<Button
variant="ghost"
size="icon"
className="-order-1 md:hidden text-foreground hover:bg-foreground/10 hover:text-foreground"
className="-order-1 border border-black/10 text-black/75 shadow-none md:hidden hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-black/20"
>
<RiMenuLine className="size-5" />
<span className="sr-only">Abrir menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-72 p-0">
<SheetHeader className="p-4 border-b">
<SheetContent side="left" className="w-72 p-0 shadow-none">
<SheetHeader className="border-b border-border/60 p-4">
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="p-3 overflow-y-auto">

View File

@@ -3,11 +3,10 @@ export const linkBase =
"inline-flex h-8 items-center justify-center rounded-full px-3 text-sm font-medium transition-all lowercase";
// Estado inativo: muted, hover suave sem underline
export const linkIdle =
"text-muted-foreground hover:text-foreground hover:bg-accent";
export const linkIdle = "text-black/75 hover:bg-black/10 hover:text-black";
// Estado ativo: pill com cor primária
export const linkActive = "bg-primary/10 text-primary";
export const linkActive = "bg-black/10 text-black";
// Trigger do NavigationMenu — espelha linkBase + linkIdle, remove estilos padrão
export const triggerClass = [
@@ -18,13 +17,15 @@ export const triggerClass = [
"text-sm!",
"font-medium!",
"bg-transparent!",
"text-muted-foreground!",
"hover:text-foreground!",
"hover:bg-accent!",
"focus:text-foreground!",
"focus:bg-accent!",
"data-[state=open]:text-foreground!",
"data-[state=open]:bg-accent!",
"text-black/75!",
"hover:text-black!",
"hover:bg-black/10!",
"focus:text-black!",
"focus:bg-black/10!",
"focus-visible:ring-black/20!",
"data-[state=open]:text-black!",
"data-[state=open]:bg-black/10!",
"shadow-none!",
"[&_svg]:text-current!",
"lowercase!",
].join(" ");

View File

@@ -1,7 +1,7 @@
"use client";
import { RiCalculatorLine, RiEyeLine, RiEyeOffLine } from "@remixicon/react";
import { usePrivacyMode } from "@/components/privacy-provider";
import { usePrivacyMode } from "@/components/providers/privacy-provider";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils/ui";

View File

@@ -9,7 +9,7 @@ import {
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useMemo, useState } from "react";
import { useState } from "react";
import { FeedbackDialogBody } from "@/components/feedback/feedback-dialog";
import { Badge } from "@/components/ui/badge";
import { Dialog, DialogTrigger } from "@/components/ui/dialog";
@@ -44,11 +44,9 @@ export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) {
const [logoutLoading, setLogoutLoading] = useState(false);
const [feedbackOpen, setFeedbackOpen] = useState(false);
const avatarSrc = useMemo(() => {
if (pagadorAvatarUrl) return getAvatarSrc(pagadorAvatarUrl);
if (user.image) return user.image;
return getAvatarSrc(null);
}, [user.image, pagadorAvatarUrl]);
const avatarSrc = pagadorAvatarUrl
? getAvatarSrc(pagadorAvatarUrl)
: user.image || getAvatarSrc(null);
async function handleLogout() {
await authClient.signOut({
@@ -65,7 +63,7 @@ export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="relative flex size-9 items-center justify-center overflow-hidden rounded-full border-background bg-background shadow-lg"
className="relative flex size-9 items-center justify-center overflow-hidden rounded-full shadow-none transition-colors focus-visible:ring-2 focus-visible:ring-black/20 focus-visible:outline-none"
aria-label="Menu do usuário"
>
<Image
@@ -77,7 +75,11 @@ export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) {
/>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-60 p-2" sideOffset={10}>
<DropdownMenuContent
align="end"
className="w-60 border-border/60 p-2 shadow-none"
sideOffset={10}
>
<DropdownMenuLabel className="flex items-center gap-3 px-2 py-2">
<Image
src={avatarSrc}