mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
refactor(core): centraliza hooks, providers e base compartilhada
This commit is contained in:
@@ -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 */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />;
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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(" ");
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user