Merge pull request #20 from felipegcoutinho/refactor/ui-improvements-mobile

refactor: melhorias de UI e responsividade mobile
This commit is contained in:
Felipe Coutinho
2026-02-28 10:42:33 -03:00
committed by GitHub
26 changed files with 422 additions and 425 deletions

View File

@@ -1,16 +1,20 @@
import { DashboardGridSkeleton } from "@/components/skeletons"; import { DashboardGridSkeleton } from "@/components/skeletons";
import { Skeleton } from "@/components/ui/skeleton";
/** /**
* Loading state para a página do dashboard * Loading state para a página do dashboard
* Usa skeleton fiel ao layout final para evitar layout shift * Estrutura: Welcome Banner → Month Picker → Section Cards → Widget Grid
*/ */
export default function DashboardLoading() { export default function DashboardLoading() {
return ( return (
<main className="flex flex-col gap-6 px-6"> <main className="flex flex-col gap-4">
{/* Month Picker placeholder */} {/* Welcome Banner skeleton */}
<div className="h-[60px] animate-pulse rounded-2xl bg-foreground/10" /> <Skeleton className="h-[104px] w-full rounded-xl bg-foreground/10" />
{/* Dashboard content skeleton */} {/* Month Picker skeleton */}
<Skeleton className="h-[56px] w-full rounded-xl bg-foreground/10" />
{/* Dashboard content skeleton (Section Cards + Widget Grid) */}
<DashboardGridSkeleton /> <DashboardGridSkeleton />
</main> </main>
); );

View File

@@ -59,7 +59,8 @@ export default async function DashboardLayout({
preLancamentosCount={preLancamentosCount} preLancamentosCount={preLancamentosCount}
notificationsSnapshot={notificationsSnapshot} notificationsSnapshot={notificationsSnapshot}
/> />
<div className="flex flex-1 flex-col pt-14"> <div className="relative flex flex-1 flex-col pt-16">
<div className="pointer-events-none absolute inset-0 bg-linear-to-b from-primary/5 via-transparent to-transparent" />
<div className="@container/main flex flex-1 flex-col gap-2"> <div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-5 md:gap-6 w-full max-w-8xl mx-auto px-4"> <div className="flex flex-col gap-4 py-5 md:gap-6 w-full max-w-8xl mx-auto px-4">
{children} {children}

View File

@@ -63,7 +63,7 @@ export default async function TopEstabelecimentosPage({
<HighlightsCards summary={data.summary} /> <HighlightsCards summary={data.summary} />
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div> <div>
<EstablishmentsList establishments={data.establishments} /> <EstablishmentsList establishments={data.establishments} />
</div> </div>

View File

@@ -154,8 +154,8 @@ export default async function Page() {
return ( return (
<div className="flex min-h-screen flex-col"> <div className="flex min-h-screen flex-col">
{/* Navigation */} {/* Navigation */}
<header className="sticky top-0 z-50 bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60"> <header className="sticky top-0 z-50 bg-card backdrop-blur-lg supports-backdrop-filter:bg-card/50">
<div className="max-w-8xl mx-auto px-4 flex h-14 items-center justify-between"> <div className="max-w-8xl mx-auto px-4 flex h-16 items-center justify-between">
<Logo variant="compact" /> <Logo variant="compact" />
{/* Center Navigation Links */} {/* Center Navigation Links */}

View File

@@ -105,7 +105,7 @@
/* Base surfaces - warm dark with consistent hue family */ /* Base surfaces - warm dark with consistent hue family */
--background: oklch(18.5% 0.002 70); --background: oklch(18.5% 0.002 70);
--foreground: oklch(92% 0.015 80); --foreground: oklch(92% 0.015 80);
--card: oklch(24% 0.003 70); --card: oklch(22.717% 0.00244 67.467);
--card-foreground: oklch(92% 0.015 80); --card-foreground: oklch(92% 0.015 80);
--popover: oklch(24% 0.003 70); --popover: oklch(24% 0.003 70);
--popover-foreground: oklch(92% 0.015 80); --popover-foreground: oklch(92% 0.015 80);

View File

@@ -197,13 +197,13 @@ export function NotesPage({ notes, archivedNotes }: NotesPageProps) {
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
<div className="flex justify-start"> <div className="flex">
<NoteDialog <NoteDialog
mode="create" mode="create"
open={createOpen} open={createOpen}
onOpenChange={handleCreateOpenChange} onOpenChange={handleCreateOpenChange}
trigger={ trigger={
<Button> <Button className="w-full sm:w-auto">
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Nova anotação Nova anotação
</Button> </Button>

View File

@@ -152,13 +152,13 @@ export function CardsPage({
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
<div className="flex justify-start"> <div className="flex">
<CardDialog <CardDialog
mode="create" mode="create"
accounts={accounts} accounts={accounts}
logoOptions={logoOptions} logoOptions={logoOptions}
trigger={ trigger={
<Button> <Button className="w-full sm:w-auto">
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Novo cartão Novo cartão
</Button> </Button>

View File

@@ -95,12 +95,12 @@ export function CategoriesPage({ categories }: CategoriesPageProps) {
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
<div className="flex justify-start"> <div className="flex">
<CategoryDialog <CategoryDialog
mode="create" mode="create"
defaultType={activeType} defaultType={activeType}
trigger={ trigger={
<Button> <Button className="w-full sm:w-auto">
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Nova categoria Nova categoria
</Button> </Button>

View File

@@ -175,12 +175,12 @@ export function AccountsPage({
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
<div className="flex justify-start"> <div className="flex">
<AccountDialog <AccountDialog
mode="create" mode="create"
logoOptions={logoOptions} logoOptions={logoOptions}
trigger={ trigger={
<Button> <Button className="w-full sm:w-auto">
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Nova conta Nova conta
</Button> </Button>

View File

@@ -75,9 +75,8 @@ export function InstallmentGroupCard({
/> />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="flex items-start justify-between"> <div className="flex flex-col gap-1">
<div className="min-w-0 flex-1"> <div className="flex gap-1 items-center flex-wrap">
<div className="flex gap-1 items-center">
{group.cartaoLogo && ( {group.cartaoLogo && (
<img <img
src={`/logos/${group.cartaoLogo}`} src={`/logos/${group.cartaoLogo}`}
@@ -85,14 +84,13 @@ export function InstallmentGroupCard({
className="h-6 w-auto object-contain rounded" className="h-6 w-auto object-contain rounded"
/> />
)} )}
<span className="font-medium">{group.name}</span>| <span className="font-medium truncate">{group.name}</span>
<span className="text-xs text-muted-foreground"> <span className="text-xs text-muted-foreground">
{group.cartaoName} | {group.cartaoName}
</span> </span>
</div> </div>
</div>
<div className="shrink-0 flex items-center gap-3"> <div className="flex items-center gap-3 flex-wrap">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<span className="text-xs text-muted-foreground">Total:</span> <span className="text-xs text-muted-foreground">Total:</span>
<MoneyValues <MoneyValues
@@ -114,11 +112,11 @@ export function InstallmentGroupCard({
{/* Progress bar */} {/* Progress bar */}
<div className="mt-3"> <div className="mt-3">
<div className="mb-2 flex items-center px-1 justify-between text-xs text-muted-foreground"> <div className="mb-2 flex flex-wrap items-center px-1 justify-between gap-x-2 gap-y-0.5 text-xs text-muted-foreground">
<span> <span>
{group.paidInstallments} de {group.totalInstallments} pagas {group.paidInstallments} de {group.totalInstallments} pagas
</span> </span>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 flex-wrap">
<span> <span>
{unpaidCount} {unpaidCount === 1 ? "pendente" : "pendentes"} {unpaidCount} {unpaidCount === 1 ? "pendente" : "pendentes"}
</span> </span>
@@ -159,7 +157,7 @@ export function InstallmentGroupCard({
{/* Lista de parcelas expandida */} {/* Lista de parcelas expandida */}
{isExpanded && ( {isExpanded && (
<div className="px-8 mt-2 flex flex-col gap-2"> <div className="px-2 sm:px-8 mt-2 flex flex-col gap-2">
{group.pendingInstallments.map((installment) => { {group.pendingInstallments.map((installment) => {
const isSelected = selectedInstallments.has(installment.id); const isSelected = selectedInstallments.has(installment.id);
const isPaid = installment.isSettled; const isPaid = installment.isSettled;

View File

@@ -38,23 +38,26 @@ function CategorySection({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium text-foreground">{title}</span> <span className="text-sm font-medium text-foreground">{title}</span>
<MoneyValues amount={total} /> <MoneyValues
amount={total}
className="text-sm font-medium tabular-nums"
/>
</div> </div>
{/* Barra de progresso */} {/* Barra de progresso */}
<Progress value={confirmedPercentage} className="h-2" /> <Progress value={confirmedPercentage} className="h-2" />
{/* Status de confirmados e pendentes */} {/* Status de confirmados e pendentes */}
<div className="flex items-center justify-between gap-4 text-sm"> <div className="flex flex-col gap-1 text-sm sm:flex-row sm:items-center sm:justify-between sm:gap-4">
<div className="flex items-center gap-1.5 "> <div className="flex items-center gap-1.5">
<RiCheckboxCircleLine className="size-3 text-success" /> <RiCheckboxCircleLine className="size-3 shrink-0 text-success" />
<MoneyValues amount={confirmed} /> <MoneyValues amount={confirmed} className="tabular-nums" />
<span className="text-xs text-muted-foreground">confirmados</span> <span className="text-xs text-muted-foreground">confirmados</span>
</div> </div>
<div className="flex items-center gap-1.5 "> <div className="flex items-center gap-1.5">
<RiHourglass2Line className="size-3 text-warning" /> <RiHourglass2Line className="size-3 shrink-0 text-warning" />
<MoneyValues amount={pending} /> <MoneyValues amount={pending} className="tabular-nums" />
<span className="text-xs text-muted-foreground">pendentes</span> <span className="text-xs text-muted-foreground">pendentes</span>
</div> </div>
</div> </div>

View File

@@ -29,7 +29,7 @@ export function BasicFieldsSection({
</div> </div>
<div className="flex w-full flex-col gap-2 md:flex-row"> <div className="flex w-full flex-col gap-2 md:flex-row">
<div className="w-1/2 space-y-1"> <div className="w-full md:w-1/2 space-y-1">
<Label htmlFor="purchaseDate">Data</Label> <Label htmlFor="purchaseDate">Data</Label>
<DatePicker <DatePicker
id="purchaseDate" id="purchaseDate"
@@ -40,7 +40,7 @@ export function BasicFieldsSection({
/> />
</div> </div>
<div className="w-1/2 space-y-1"> <div className="w-full md:w-1/2 space-y-1">
<Label htmlFor="amount">Valor</Label> <Label htmlFor="amount">Valor</Label>
<div className="relative"> <div className="relative">
<CurrencyInput <CurrencyInput

View File

@@ -261,16 +261,26 @@ export function LancamentosFilters({
}; };
return ( return (
<div className={cn("flex flex-wrap items-center gap-2", className)}> <div
className={cn(
"flex flex-col gap-2 md:flex-row md:flex-wrap md:items-center",
className,
)}
>
<Input <Input
value={searchValue} value={searchValue}
onChange={(event) => setSearchValue(event.target.value)} onChange={(event) => setSearchValue(event.target.value)}
placeholder="Buscar" placeholder="Buscar"
aria-label="Buscar lançamentos" aria-label="Buscar lançamentos"
className="w-[250px] text-sm border-dashed" className="w-full md:w-[250px] text-sm border-dashed"
/> />
<div className="flex w-full gap-2 md:w-auto">
{exportButton && (
<div className="flex-1 md:flex-none [&>*]:w-full [&>*]:md:w-auto">
{exportButton} {exportButton}
</div>
)}
{!hideAdvancedFilters && ( {!hideAdvancedFilters && (
<Drawer <Drawer
@@ -281,7 +291,7 @@ export function LancamentosFilters({
<DrawerTrigger asChild> <DrawerTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="text-sm border-dashed relative" className="flex-1 md:flex-none text-sm border-dashed relative"
aria-label="Abrir filtros" aria-label="Abrir filtros"
> >
<RiFilter3Line className="size-4" /> <RiFilter3Line className="size-4" />
@@ -543,5 +553,6 @@ export function LancamentosFilters({
</Drawer> </Drawer>
)} )}
</div> </div>
</div>
); );
} }

View File

@@ -3,7 +3,6 @@ import {
RiAddCircleFill, RiAddCircleFill,
RiAddCircleLine, RiAddCircleLine,
RiArrowLeftRightLine, RiArrowLeftRightLine,
RiArrowRightSLine,
RiChat1Line, RiChat1Line,
RiCheckLine, RiCheckLine,
RiDeleteBin5Line, RiDeleteBin5Line,
@@ -864,25 +863,21 @@ export function LancamentosTable({
{showTopControls ? ( {showTopControls ? (
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between"> <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
{onCreate || onMassAdd ? ( {onCreate || onMassAdd ? (
<div className="relative -mx-6 px-6 md:mx-0 md:px-0"> <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row">
<div className="overflow-x-auto overflow-y-hidden scroll-smooth md:overflow-visible [-webkit-overflow-scrolling:touch] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
<div className="flex w-max shrink-0 gap-2 py-1 md:w-full md:py-0">
{onCreate ? ( {onCreate ? (
<> <>
<Button <Button
onClick={() => onCreate("Receita")} onClick={() => onCreate("Receita")}
variant="outline" className="w-full sm:w-auto"
className="h-8 shrink-0 px-3 text-xs sm:w-auto md:h-9 md:px-4 md:text-sm"
> >
<RiAddCircleLine className="size-4 text-success" /> <RiAddCircleLine className="size-4" />
Nova Receita Nova Receita
</Button> </Button>
<Button <Button
onClick={() => onCreate("Despesa")} onClick={() => onCreate("Despesa")}
variant="outline" className="w-full sm:w-auto"
className="h-8 shrink-0 px-3 text-xs sm:w-auto md:h-9 md:px-4 md:text-sm"
> >
<RiAddCircleLine className="size-4 text-destructive" /> <RiAddCircleLine className="size-4" />
Nova Despesa Nova Despesa
</Button> </Button>
</> </>
@@ -894,7 +889,7 @@ export function LancamentosTable({
onClick={onMassAdd} onClick={onMassAdd}
variant="outline" variant="outline"
size="icon" size="icon"
className="size-8 shrink-0 md:size-9" className="hidden size-9 sm:inline-flex"
> >
<RiAddCircleFill className="size-4" /> <RiAddCircleFill className="size-4" />
<span className="sr-only"> <span className="sr-only">
@@ -908,14 +903,6 @@ export function LancamentosTable({
</Tooltip> </Tooltip>
) : null} ) : null}
</div> </div>
</div>
<div
className="pointer-events-none absolute right-0 top-0 hidden h-9 w-10 items-center justify-end bg-gradient-to-l from-background to-transparent py-1 md:hidden"
aria-hidden
>
<RiArrowRightSLine className="size-5 shrink-0 text-muted-foreground" />
</div>
</div>
) : ( ) : (
<span className={showFilters ? "hidden sm:block" : ""} /> <span className={showFilters ? "hidden sm:block" : ""} />
)} )}

View File

@@ -79,7 +79,7 @@ export default function MonthNavigation() {
}; };
return ( return (
<Card className="w-full flex-row bg-card text-card-foreground p-4 sticky top-14 z-10"> <Card className="flex w-full flex-row bg-card text-card-foreground p-4 sticky top-16 z-10">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<NavigationButton <NavigationButton
direction="left" direction="left"

View File

@@ -10,13 +10,13 @@ interface ReturnButtonProps {
export default function ReturnButton({ disabled, onClick }: ReturnButtonProps) { export default function ReturnButton({ disabled, onClick }: ReturnButtonProps) {
return ( return (
<Button <Button
className="w-32 h-6 rounded-sm lowercase" className="w-max h-6 lowercase"
size="sm" size="sm"
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}
aria-label="Retornar para o mês atual" aria-label="Retornar para o mês atual"
> >
Ir para Mês Atual Mês Atual
</Button> </Button>
); );
} }

View File

@@ -26,7 +26,7 @@ export function AppNavbar({
notificationsSnapshot, notificationsSnapshot,
}: AppNavbarProps) { }: AppNavbarProps) {
return ( return (
<header className="fixed top-0 left-0 right-0 z-50 h-15 shrink-0 flex items-center bg-card backdrop-blur-lg supports-backdrop-filter:bg-card/60"> <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">
<div className="w-full max-w-8xl mx-auto px-4 flex items-center gap-4 h-full"> <div className="w-full max-w-8xl mx-auto px-4 flex items-center gap-4 h-full">
{/* Logo */} {/* Logo */}
<Link href="/dashboard" className="shrink-0 mr-1"> <Link href="/dashboard" className="shrink-0 mr-1">

View File

@@ -18,6 +18,7 @@ export type NavItem = {
icon: React.ReactNode; icon: React.ReactNode;
badge?: number; badge?: number;
preservePeriod?: boolean; preservePeriod?: boolean;
hideOnMobile?: boolean;
}; };
export type NavSection = { export type NavSection = {
@@ -44,6 +45,7 @@ export const NAV_SECTIONS: NavSection[] = [
href: "/calendario", href: "/calendario",
label: "calendário", label: "calendário",
icon: <RiCalendarEventLine className="size-4" />, icon: <RiCalendarEventLine className="size-4" />,
hideOnMobile: true,
}, },
], ],
}, },

View File

@@ -31,7 +31,7 @@ export function NavMenu() {
return ( return (
<> <>
{/* Desktop */} {/* Desktop */}
<nav className="hidden md:flex items-center flex-1"> <nav className="hidden md:flex items-center justify-center flex-1">
<NavigationMenu viewport={false}> <NavigationMenu viewport={false}>
<NavigationMenuList className="gap-0"> <NavigationMenuList className="gap-0">
<NavigationMenuItem> <NavigationMenuItem>
@@ -63,13 +63,13 @@ export function NavMenu() {
</NavigationMenu> </NavigationMenu>
</nav> </nav>
{/* Mobile */} {/* Mobile - order-[-1] places hamburger before logo visually */}
<Sheet open={sheetOpen} onOpenChange={setSheetOpen}> <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
<SheetTrigger asChild> <SheetTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="md:hidden text-foreground hover:bg-foreground/10 hover:text-foreground" className="-order-1 md:hidden text-foreground hover:bg-foreground/10 hover:text-foreground"
> >
<RiMenuLine className="size-5" /> <RiMenuLine className="size-5" />
<span className="sr-only">Abrir menu</span> <span className="sr-only">Abrir menu</span>
@@ -86,13 +86,18 @@ export function NavMenu() {
onClick={close} onClick={close}
preservePeriod preservePeriod
> >
Dashboard dashboard
</MobileLink> </MobileLink>
{NAV_SECTIONS.map((section) => ( {NAV_SECTIONS.map((section) => {
const mobileItems = section.items.filter(
(item) => !item.hideOnMobile,
);
if (mobileItems.length === 0) return null;
return (
<div key={section.label}> <div key={section.label}>
<MobileSectionLabel label={section.label} /> <MobileSectionLabel label={section.label} />
{section.items.map((item) => ( {mobileItems.map((item) => (
<MobileLink <MobileLink
key={item.href} key={item.href}
href={item.href} href={item.href}
@@ -105,7 +110,8 @@ export function NavMenu() {
</MobileLink> </MobileLink>
))} ))}
</div> </div>
))} );
})}
<MobileSectionLabel label="Ferramentas" /> <MobileSectionLabel label="Ferramentas" />
<MobileTools onClose={close} /> <MobileTools onClose={close} />

View File

@@ -1,11 +1,6 @@
"use client"; "use client";
import { import { RiAddCircleLine, RiFileCopyLine, RiFundsLine } from "@remixicon/react";
RiAddCircleLine,
RiArrowRightSLine,
RiFileCopyLine,
RiFundsLine,
} from "@remixicon/react";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { import {
@@ -110,10 +105,7 @@ export function BudgetsPage({
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
{/* No mobile: rolagem horizontal + seta + botões menores */} <div className="flex flex-col gap-2 sm:flex-row sm:gap-3">
<div className="relative -mx-6 px-6 md:mx-0 md:px-0">
<div className="overflow-x-auto overflow-y-hidden scroll-smooth md:overflow-visible [-webkit-overflow-scrolling:touch] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
<div className="flex w-max shrink-0 justify-start gap-3 py-1 md:w-full md:gap-4 md:py-0">
<BudgetDialog <BudgetDialog
mode="create" mode="create"
categories={categories} categories={categories}
@@ -121,7 +113,7 @@ export function BudgetsPage({
trigger={ trigger={
<Button <Button
disabled={categories.length === 0} disabled={categories.length === 0}
className="h-8 shrink-0 px-3 text-xs md:h-9 md:px-4 md:text-sm" className="w-full sm:w-auto"
> >
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Novo orçamento Novo orçamento
@@ -132,20 +124,12 @@ export function BudgetsPage({
variant="outline" variant="outline"
disabled={categories.length === 0} disabled={categories.length === 0}
onClick={() => setDuplicateOpen(true)} onClick={() => setDuplicateOpen(true)}
className="h-8 shrink-0 px-3 text-xs md:h-9 md:px-4 md:text-sm" className="w-full sm:w-auto"
> >
<RiFileCopyLine className="size-4" /> <RiFileCopyLine className="size-4" />
Copiar orçamentos do último mês Copiar orçamentos do último mês
</Button> </Button>
</div> </div>
</div>
<div
className="pointer-events-none absolute right-0 top-0 hidden h-9 w-10 items-center justify-end bg-gradient-to-l from-background to-transparent py-1 md:hidden"
aria-hidden
>
<RiArrowRightSLine className="size-5 shrink-0 text-muted-foreground" />
</div>
</div>
{hasBudgets ? ( {hasBudgets ? (
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3"> <div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">

View File

@@ -131,7 +131,7 @@ export function PagadoresPage({
mode="create" mode="create"
avatarOptions={avatarOptions} avatarOptions={avatarOptions}
trigger={ trigger={
<Button> <Button className="w-full sm:w-auto">
<RiAddCircleLine className="size-4" /> <RiAddCircleLine className="size-4" />
Novo pagador Novo pagador
</Button> </Button>
@@ -139,14 +139,14 @@ export function PagadoresPage({
/> />
<form <form
onSubmit={handleJoinByCode} onSubmit={handleJoinByCode}
className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row" className="flex w-full flex-row items-center justify-center gap-2 sm:w-auto"
> >
<Input <Input
placeholder="Código de Compartilhamento" placeholder="Código de Compartilhamento"
value={shareCodeInput} value={shareCodeInput}
onChange={(event) => setShareCodeInput(event.target.value)} onChange={(event) => setShareCodeInput(event.target.value)}
disabled={joinPending} disabled={joinPending}
className="w-56 border-dashed" className="w-full sm:w-56 border-dashed"
/> />
<Button type="submit" disabled={joinPending}> <Button type="submit" disabled={joinPending}>
{joinPending ? "Adicionando..." : "Adicionar por código"} {joinPending ? "Adicionando..." : "Adicionar por código"}

View File

@@ -162,8 +162,8 @@ export function CategoryReportFilters({
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex flex-wrap items-center justify-between gap-2"> <div className="flex flex-wrap items-center justify-center gap-2 md:justify-between">
<div className="flex flex-wrap items-center gap-2"> <div className="flex w-full flex-wrap items-center justify-center gap-2 md:w-auto md:justify-start">
{/* Category Multi-Select */} {/* Category Multi-Select */}
<Popover open={open} onOpenChange={setOpen} modal> <Popover open={open} onOpenChange={setOpen} modal>
<PopoverTrigger asChild> <PopoverTrigger asChild>
@@ -172,7 +172,7 @@ export function CategoryReportFilters({
role="combobox" role="combobox"
aria-expanded={open} aria-expanded={open}
aria-label="Selecionar categorias para filtrar" aria-label="Selecionar categorias para filtrar"
className="w-[180px] justify-between text-sm border-dashed border-input" className="w-full md:w-[180px] justify-between text-sm border-dashed border-input"
disabled={isLoading} disabled={isLoading}
> >
<span className="truncate">{selectedText}</span> <span className="truncate">{selectedText}</span>
@@ -257,7 +257,7 @@ export function CategoryReportFilters({
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="w-[150px] justify-start text-sm border-dashed" className="w-[calc(50%-0.25rem)] md:w-[150px] justify-start text-sm border-dashed"
disabled={isLoading} disabled={isLoading}
> >
<RiCalendarLine className="mr-2 h-4 w-4" /> <RiCalendarLine className="mr-2 h-4 w-4" />
@@ -277,7 +277,7 @@ export function CategoryReportFilters({
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="w-[150px] justify-start text-sm border-dashed" className="w-[calc(50%-0.25rem)] md:w-[150px] justify-start text-sm border-dashed"
disabled={isLoading} disabled={isLoading}
> >
<RiCalendarLine className="mr-2 h-4 w-4" /> <RiCalendarLine className="mr-2 h-4 w-4" />
@@ -299,13 +299,14 @@ export function CategoryReportFilters({
size="sm" size="sm"
onClick={handleReset} onClick={handleReset}
disabled={isLoading} disabled={isLoading}
className="w-full text-center md:w-auto md:text-left"
> >
Limpar Limpar
</Button> </Button>
</div> </div>
{/* Export Button */} {/* Export Button */}
{exportButton} <div className="w-full md:w-auto">{exportButton}</div>
</div> </div>
{/* Validation Message */} {/* Validation Message */}

View File

@@ -11,9 +11,9 @@ export function DashboardGridSkeleton() {
{/* Section Cards no topo */} {/* Section Cards no topo */}
<SectionCardsSkeleton /> <SectionCardsSkeleton />
{/* Grid de widgets */} {/* Grid de widgets - mesmos breakpoints do dashboard real */}
<div className="grid grid-cols-1 gap-4 @3xl/main:grid-cols-2 @7xl/main:grid-cols-3"> <div className="grid grid-cols-1 gap-3 @4xl/main:grid-cols-2 @6xl/main:grid-cols-3">
{Array.from({ length: 12 }).map((_, i) => ( {Array.from({ length: 9 }).map((_, i) => (
<WidgetSkeleton key={i} /> <WidgetSkeleton key={i} />
))} ))}
</div> </div>

View File

@@ -7,7 +7,7 @@ import { Skeleton } from "@/components/ui/skeleton";
*/ */
export function WidgetSkeleton() { export function WidgetSkeleton() {
return ( return (
<Card className="md:h-custom-height-1 relative h-auto md:overflow-hidden"> <Card className="relative h-auto md:h-custom-height-1 md:overflow-hidden">
<CardHeader className="border-b [.border-b]:pb-2"> <CardHeader className="border-b [.border-b]:pb-2">
<div className="flex w-full items-start justify-between"> <div className="flex w-full items-start justify-between">
<div className="space-y-2"> <div className="space-y-2">

View File

@@ -56,7 +56,7 @@ function DialogContent({
<DialogPrimitive.Content <DialogPrimitive.Content
data-slot="dialog-content" data-slot="dialog-content"
className={cn( className={cn(
"bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-10 shadow-lg sm:max-w-xl", "bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] max-h-[90vh] overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-4 shadow-lg sm:p-10 sm:max-w-xl",
className, className,
)} )}
{...props} {...props}

View File

@@ -111,8 +111,8 @@ export default function WidgetCard({
)} )}
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="max-h-[85vh] w-full max-w-3xl overflow-hidden p-0"> <DialogContent className="max-h-[85vh] w-full max-w-[calc(100%-2rem)] sm:max-w-3xl overflow-hidden p-6">
<DialogHeader className="px-6 pt-4"> <DialogHeader className="text-left">
<DialogTitle className="flex items-center gap-2"> <DialogTitle className="flex items-center gap-2">
{icon} {icon}
<span>{title}</span> <span>{title}</span>
@@ -121,7 +121,7 @@ export default function WidgetCard({
<p className="text-muted-foreground text-sm">{subtitle}</p> <p className="text-muted-foreground text-sm">{subtitle}</p>
) : null} ) : null}
</DialogHeader> </DialogHeader>
<div className="scrollbar-hide max-h-[calc(85vh-6rem)] overflow-y-auto px-6 pb-6"> <div className="scrollbar-hide max-h-[calc(85vh-6rem)] overflow-y-auto pb-6">
{children} {children}
</div> </div>
</DialogContent> </DialogContent>