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

@@ -9,7 +9,7 @@ const buttonVariants = cva(
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
default: "bg-primary hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 border-transparent border drop-shadow-xs py-6 rounded-md hover:border-primary/50 transition-all ease-in-out duration-300",
"bg-card text-card-foreground flex flex-col gap-6 border py-6 rounded-md hover:border-primary/50 transition-all ease-in-out duration-300",
className,
)}
{...props}

View File

@@ -1,6 +1,7 @@
"use client";
import { RiCalendarLine } from "@remixicon/react";
import { ptBR } from "date-fns/locale";
import * as React from "react";
import { Button } from "@/components/ui/button";
@@ -11,6 +12,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { parseLocalDateString, toLocalDateString } from "@/lib/utils/date";
import { cn } from "@/lib/utils/ui";
function formatDate(date: Date | undefined, compact = false): string {
@@ -43,13 +45,7 @@ function isValidDate(date: Date | undefined): boolean {
}
function dateToYYYYMMDD(date: Date | undefined): string {
if (!date || !isValidDate(date)) {
return "";
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
return isValidDate(date) ? (toLocalDateString(date) ?? "") : "";
}
function parseYYYYMMDD(dateString: string): Date | undefined {
@@ -62,8 +58,7 @@ function parseYYYYMMDD(dateString: string): Date | undefined {
// which in Brazil (UTC-3) becomes 2025-11-26 03:00 local time!
const ymdMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (ymdMatch) {
const [, year, month, day] = ymdMatch;
const date = new Date(Number(year), Number(month) - 1, Number(day));
const date = parseLocalDateString(dateString);
return isValidDate(date) ? date : undefined;
}
@@ -179,29 +174,7 @@ export function DatePicker({
onSelect={handleCalendarSelect}
fromYear={2020}
toYear={new Date().getFullYear() + 10}
locale={{
localize: {
day: (n) => ["D", "S", "T", "Q", "Q", "S", "S"][n],
month: (n) =>
[
"Jan",
"Fev",
"Mar",
"Abr",
"Mai",
"Jun",
"Jul",
"Ago",
"Set",
"Out",
"Nov",
"Dez",
][n],
},
formatLong: {
date: () => "dd/MM/yyyy",
},
}}
locale={ptBR}
/>
</PopoverContent>
</Popover>

View File

@@ -74,7 +74,7 @@ function NavigationMenuTrigger({
>
{children}{" "}
<RiArrowDropDownLine
className="relative top-px ml-1 size-4 text-primary transition duration-300 group-data-[state=open]:rotate-180"
className="relative top-px size-5 opacity-70 transition duration-300 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
@@ -90,7 +90,7 @@ function NavigationMenuContent({
data-slot="navigation-menu-content"
className={cn(
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow-none group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
className,
)}
{...props}

View File

@@ -70,7 +70,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
<th
data-slot="table-head"
className={cn(
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
@@ -83,7 +83,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
<td
data-slot="table-cell"
className={cn(
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}

View File

@@ -1,26 +1 @@
import * as React from "react";
const MOBILE_BREAKPOINT = 768;
const MOBILE_MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;
export function useIsMobile() {
const subscribe = React.useCallback((onStoreChange: () => void) => {
if (typeof window === "undefined") {
return () => {};
}
const mediaQueryList = window.matchMedia(MOBILE_MEDIA_QUERY);
mediaQueryList.addEventListener("change", onStoreChange);
return () => mediaQueryList.removeEventListener("change", onStoreChange);
}, []);
const getSnapshot = React.useCallback(() => {
if (typeof window === "undefined") {
return false;
}
return window.matchMedia(MOBILE_MEDIA_QUERY).matches;
}, []);
return React.useSyncExternalStore(subscribe, getSnapshot, () => false);
}
export { useIsMobile, useMobile } from "@/lib/hooks/use-mobile";