diff --git a/src/shared/components/navigation/navbar/nav-dropdown.tsx b/src/shared/components/navigation/navbar/nav-dropdown.tsx index 609eefa..a80a79b 100644 --- a/src/shared/components/navigation/navbar/nav-dropdown.tsx +++ b/src/shared/components/navigation/navbar/nav-dropdown.tsx @@ -25,10 +25,10 @@ export function NavDropdown({ items }: NavDropdownProps) { href={item.href} preservePeriod={item.preservePeriod} className={cn( - "flex items-center gap-3 rounded-sm px-2 py-2 text-sm transition-colors", + "flex items-center gap-3 rounded-sm px-2 py-3 text-sm transition-colors", isActive - ? "bg-accent text-foreground" - : "text-foreground hover:bg-accent", + ? "border-primary bg-accent text-foreground" + : "border-transparent text-foreground hover:bg-accent", )} > {children} diff --git a/src/shared/components/navigation/navbar/nav-tools.tsx b/src/shared/components/navigation/navbar/nav-tools.tsx index 5f1f6c0..8ba14b8 100644 --- a/src/shared/components/navigation/navbar/nav-tools.tsx +++ b/src/shared/components/navigation/navbar/nav-tools.tsx @@ -5,7 +5,7 @@ import { usePrivacyMode } from "@/shared/components/providers/privacy-provider"; import { Badge } from "@/shared/components/ui/badge"; const itemClass = - "flex w-full items-center gap-2.5 rounded-sm px-2 py-2 text-sm text-foreground hover:bg-accent transition-colors cursor-pointer"; + "flex w-full items-center gap-2.5 rounded-sm px-2 py-3 text-sm text-foreground hover:bg-accent transition-colors cursor-pointer"; type NavToolsDropdownProps = { onOpenCalculator: () => void; @@ -22,7 +22,7 @@ export function NavToolsDropdown({ onOpenCalculator }: NavToolsDropdownProps) { - Calculadora + Calculadora Faça cálculos rápidos @@ -39,7 +39,7 @@ export function NavToolsDropdown({ onOpenCalculator }: NavToolsDropdownProps) { )} - Privacidade + Privacidade Oculta valores na tela diff --git a/src/shared/components/navigation/navbar/navbar-shell.tsx b/src/shared/components/navigation/navbar/navbar-shell.tsx index 89a8392..23cf96d 100644 --- a/src/shared/components/navigation/navbar/navbar-shell.tsx +++ b/src/shared/components/navigation/navbar/navbar-shell.tsx @@ -16,15 +16,21 @@ export function NavbarShell({ return ( {logoHref ? ( - + ) : ( - + )} {children} diff --git a/src/shared/components/navigation/navbar/notification-bell/notification-bell-trigger.tsx b/src/shared/components/navigation/navbar/notification-bell/notification-bell-trigger.tsx index b1f892b..83b2216 100644 --- a/src/shared/components/navigation/navbar/notification-bell/notification-bell-trigger.tsx +++ b/src/shared/components/navigation/navbar/notification-bell/notification-bell-trigger.tsx @@ -34,9 +34,11 @@ export function NotificationBellTrigger({ className={cn( buttonVariants({ variant: "ghost", size: "icon-sm" }), "group relative shadow-none transition-all duration-200", - "hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-2 focus-visible:ring-black/20", - "data-[state=open]:bg-black/10 data-[state=open]:text-black", - hasAnySourceItems ? "text-black" : "text-black/75", + "hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-2 focus-visible:ring-black/20 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white dark:focus-visible:ring-white/20", + "data-[state=open]:bg-black/10 data-[state=open]:text-black dark:data-[state=open]:bg-white/10 dark:data-[state=open]:text-white", + hasAnySourceItems + ? "text-black dark:text-white" + : "text-black/75 dark:text-white/75", )} > { - user: AppUser; - pagadorAvatarUrl: string | null; - pagadores: PagadorLike[]; - preLancamentosCount?: number; -} - -export function AppSidebar({ - user, - pagadorAvatarUrl, - pagadores, - preLancamentosCount = 0, - ...props -}: AppSidebarProps) { - if (!user) { - throw new Error("AppSidebar requires a user but received undefined."); - } - - const navigation = React.useMemo( - () => createSidebarNavData({ pagadores, preLancamentosCount }), - [pagadores, preLancamentosCount], - ); - - return ( - - - - - - - - - - - - - - - - - - - - - ); -} - -function LogoContent() { - const { state } = useSidebar(); - const isCollapsed = state === "collapsed"; - - return ; -} diff --git a/src/shared/components/navigation/sidebar/nav-link.tsx b/src/shared/components/navigation/sidebar/nav-link.tsx deleted file mode 100644 index 58e36ba..0000000 --- a/src/shared/components/navigation/sidebar/nav-link.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import { - type RemixiconComponentType, - RiArrowLeftRightLine, - RiAtLine, - RiBankCard2Line, - RiBankLine, - RiCalendarEventLine, - RiDashboardLine, - RiFileChartLine, - RiFundsLine, - RiGroupLine, - RiPriceTag3Line, - RiSecurePaymentLine, - RiSettings2Line, - RiSparklingLine, - RiTodoLine, -} from "@remixicon/react"; - -export type SidebarSubItem = { - title: string; - url: string; - avatarUrl?: string | null; - isShared?: boolean; - key?: string; - icon?: RemixiconComponentType; - badge?: number; -}; - -export type SidebarItem = { - title: string; - url: string; - icon: RemixiconComponentType; - isActive?: boolean; - items?: SidebarSubItem[]; -}; - -export type SidebarSection = { - title: string; - items: SidebarItem[]; -}; - -export type SidebarNavData = { - navMain: SidebarSection[]; - navSecondary: { - title: string; - url: string; - icon: RemixiconComponentType; - }[]; -}; - -export interface PagadorLike { - id: string; - name: string | null; - avatarUrl: string | null; - canEdit?: boolean; -} - -export interface SidebarNavOptions { - pagadores: PagadorLike[]; - preLancamentosCount?: number; -} - -export function createSidebarNavData( - options: SidebarNavOptions, -): SidebarNavData { - const { pagadores, preLancamentosCount = 0 } = options; - const pagadorItems = pagadores - .map((pagador) => ({ - title: pagador.name?.trim().length - ? pagador.name.trim() - : "Pagador sem nome", - url: `/payers/${pagador.id}`, - key: pagador.canEdit ? pagador.id : `${pagador.id}-shared`, - isShared: !pagador.canEdit, - avatarUrl: pagador.avatarUrl, - })) - .sort((a, b) => - a.title.localeCompare(b.title, "pt-BR", { sensitivity: "base" }), - ); - - const pagadorItemsWithHistory: SidebarSubItem[] = pagadorItems; - - return { - navMain: [ - { - title: "Gestão Financeira", - items: [ - { - title: "Dashboard", - url: "/dashboard", - icon: RiDashboardLine, - }, - { - title: "Lançamentos", - url: "/transactions", - icon: RiArrowLeftRightLine, - items: [ - { - title: "Pré-Lançamentos", - url: "/inbox", - key: "pre-lancamentos", - icon: RiAtLine, - badge: - preLancamentosCount > 0 ? preLancamentosCount : undefined, - }, - ], - }, - { - title: "Calendário", - url: "/calendar", - icon: RiCalendarEventLine, - }, - { - title: "Cartões", - url: "/cards", - icon: RiBankCard2Line, - }, - { - title: "Contas", - url: "/accounts", - icon: RiBankLine, - }, - { - title: "Orçamentos", - url: "/budgets", - icon: RiFundsLine, - }, - ], - }, - { - title: "Organização", - items: [ - { - title: "Pagadores", - url: "/payers", - icon: RiGroupLine, - items: pagadorItemsWithHistory, - }, - { - title: "Categorias", - url: "/categories", - icon: RiPriceTag3Line, - }, - { - title: "Anotações", - url: "/notes", - icon: RiTodoLine, - }, - ], - }, - { - title: "Análise", - items: [ - { - title: "Insights", - url: "/insights", - icon: RiSparklingLine, - }, - { - title: "Tendências", - url: "/reports/category-trends", - icon: RiFileChartLine, - }, - { - title: "Uso de Cartões", - url: "/reports/card-usage", - icon: RiBankCard2Line, - }, - { - title: "Análise de Parcelas", - url: "/reports/installment-analysis", - icon: RiSecurePaymentLine, - }, - ], - }, - ], - navSecondary: [ - { - title: "Ajustes", - url: "/settings", - icon: RiSettings2Line, - }, - ], - }; -} diff --git a/src/shared/components/navigation/sidebar/nav-main.tsx b/src/shared/components/navigation/sidebar/nav-main.tsx deleted file mode 100644 index d850d4f..0000000 --- a/src/shared/components/navigation/sidebar/nav-main.tsx +++ /dev/null @@ -1,212 +0,0 @@ -"use client"; - -import { - type RemixiconComponentType, - RiArrowRightSLine, - RiUserSharedLine, -} from "@remixicon/react"; -import Link from "next/link"; -import { usePathname, useSearchParams } from "next/navigation"; -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/shared/components/ui/avatar"; -import { Badge } from "@/shared/components/ui/badge"; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from "@/shared/components/ui/collapsible"; -import { - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuAction, - SidebarMenuButton, - SidebarMenuItem, - SidebarMenuSub, - SidebarMenuSubButton, - SidebarMenuSubItem, -} from "@/shared/components/ui/sidebar"; -import { getAvatarSrc } from "@/shared/lib/payers/utils"; - -type NavItem = { - title: string; - url: string; - icon: RemixiconComponentType; - isActive?: boolean; - items?: { - title: string; - url: string; - avatarUrl?: string | null; - isShared?: boolean; - key?: string; - icon?: RemixiconComponentType; - badge?: number; - }[]; -}; - -type NavSection = { - title: string; - items: NavItem[]; -}; - -const MONTH_PERIOD_PARAM = "periodo"; - -const PERIOD_AWARE_PATHS = new Set(["/dashboard", "/transactions", "/budgets"]); - -export function NavMain({ sections }: { sections: NavSection[] }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const periodParam = searchParams.get(MONTH_PERIOD_PARAM); - - const normalizedPathname = - pathname.endsWith("/") && pathname !== "/" - ? pathname.slice(0, -1) - : pathname; - - const isLinkActive = (url: string) => { - const normalizedUrl = - url.endsWith("/") && url !== "/" ? url.slice(0, -1) : url; - - // Verifica se é exatamente igual ou se o pathname começa com a URL - return ( - normalizedPathname === normalizedUrl || - normalizedPathname.startsWith(`${normalizedUrl}/`) - ); - }; - - const buildHrefWithPeriod = (url: string) => { - if (!periodParam) { - return url; - } - - const [rawPathname, existingSearch = ""] = url.split("?"); - const normalizedRawPathname = - rawPathname.endsWith("/") && rawPathname !== "/" - ? rawPathname.slice(0, -1) - : rawPathname; - - if (!PERIOD_AWARE_PATHS.has(normalizedRawPathname)) { - return url; - } - - const params = new URLSearchParams(existingSearch); - params.set(MONTH_PERIOD_PARAM, periodParam); - - const queryString = params.toString(); - return queryString - ? `${normalizedRawPathname}?${queryString}` - : normalizedRawPathname; - }; - - const activeLinkClasses = - "data-[active=true]:bg-sidebar-accent data-[active=true]:text-dark! hover:text-primary!"; - - return ( - <> - {sections.map((section, index) => ( - - - {section.title} - - - - {section.items.map((item) => { - const itemIsActive = isLinkActive(item.url); - return ( - - - - - - {item.title} - - - {item.items?.length ? ( - <> - - - - Toggle - - - - - {item.items?.map((subItem) => { - const subItemIsActive = isLinkActive( - subItem.url, - ); - const avatarSrc = getAvatarSrc( - subItem.avatarUrl, - ); - const initial = - subItem.title.charAt(0).toUpperCase() || "?"; - return ( - - - - {subItem.icon ? ( - - ) : subItem.avatarUrl !== undefined ? ( - - - - {initial} - - - ) : null} - {subItem.title} - {subItem.badge ? ( - - {subItem.badge} - - ) : null} - {subItem.isShared ? ( - - ) : null} - - - - ); - })} - - - > - ) : null} - - - ); - })} - - - - ))} - > - ); -} diff --git a/src/shared/components/navigation/sidebar/nav-secondary.tsx b/src/shared/components/navigation/sidebar/nav-secondary.tsx deleted file mode 100644 index d9a814f..0000000 --- a/src/shared/components/navigation/sidebar/nav-secondary.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import type { RemixiconComponentType } from "@remixicon/react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import type * as React from "react"; -import { - SidebarGroup, - SidebarGroupContent, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, -} from "@/shared/components/ui/sidebar"; - -export function NavSecondary({ - items, - ...props -}: { - items: { - title: string; - url: string; - icon: RemixiconComponentType; - }[]; -} & React.ComponentPropsWithoutRef) { - const pathname = usePathname(); - - return ( - - - - {items.map((item) => { - const normalizedPathname = - pathname.endsWith("/") && pathname !== "/" - ? pathname.slice(0, -1) - : pathname; - const normalizedUrl = - item.url.endsWith("/") && item.url !== "/" - ? item.url.slice(0, -1) - : item.url; - const itemIsActive = - normalizedPathname === normalizedUrl || - normalizedPathname.startsWith(`${normalizedUrl}/`); - return ( - - - - - {item.title} - - - - ); - })} - - - - ); -} diff --git a/src/shared/components/navigation/sidebar/nav-user.tsx b/src/shared/components/navigation/sidebar/nav-user.tsx deleted file mode 100644 index a8afe09..0000000 --- a/src/shared/components/navigation/sidebar/nav-user.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client"; - -import Image from "next/image"; -import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, -} from "@/shared/components/ui/sidebar"; -import { getAvatarSrc } from "@/shared/lib/payers/utils"; - -type NavUserProps = { - user: { - id: string; - name: string; - email: string; - image: string | null; - }; - pagadorAvatarUrl: string | null; -}; - -export function NavUser({ user, pagadorAvatarUrl }: NavUserProps) { - const avatarSrc = pagadorAvatarUrl - ? getAvatarSrc(pagadorAvatarUrl) - : user.image || getAvatarSrc(null); - const isDataUrl = avatarSrc.startsWith("data:"); - - return ( - - - - - - - - {user.name} - - {user.email} - - - - - - ); -} diff --git a/src/shared/components/providers/index.ts b/src/shared/components/providers/index.ts deleted file mode 100644 index a043c89..0000000 --- a/src/shared/components/providers/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { PrivacyProvider } from "./privacy-provider"; -export { QueryProvider } from "./query-provider"; -export { ThemeProvider } from "./theme-provider"; diff --git a/src/shared/components/ui/sidebar.tsx b/src/shared/components/ui/sidebar.tsx deleted file mode 100644 index 46d2a98..0000000 --- a/src/shared/components/ui/sidebar.tsx +++ /dev/null @@ -1,726 +0,0 @@ -"use client"; - -import { Slot } from "@radix-ui/react-slot"; -import { RiLayoutLeft2Line } from "@remixicon/react"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; -import { Button } from "@/shared/components/ui/button"; -import { Input } from "@/shared/components/ui/input"; -import { Separator } from "@/shared/components/ui/separator"; -import { - Sheet, - SheetContent, - SheetDescription, - SheetHeader, - SheetTitle, -} from "@/shared/components/ui/sheet"; -import { Skeleton } from "@/shared/components/ui/skeleton"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/shared/components/ui/tooltip"; -import { cn } from "@/shared/utils/ui"; -import { useIsMobile } from "./use-mobile"; - -const SIDEBAR_COOKIE_NAME = "sidebar_state"; -const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; -const SIDEBAR_WIDTH = "16rem"; -const SIDEBAR_WIDTH_MOBILE = "18rem"; -const SIDEBAR_WIDTH_ICON = "3rem"; -const SIDEBAR_KEYBOARD_SHORTCUT = "b"; - -type SidebarContextProps = { - state: "expanded" | "collapsed"; - open: boolean; - setOpen: (open: boolean) => void; - openMobile: boolean; - setOpenMobile: (open: boolean) => void; - isMobile: boolean; - toggleSidebar: () => void; -}; - -const SidebarContext = React.createContext(null); - -function useSidebar() { - const context = React.useContext(SidebarContext); - if (!context) { - throw new Error("useSidebar must be used within a SidebarProvider."); - } - - return context; -} - -function SidebarProvider({ - defaultOpen = true, - open: openProp, - onOpenChange: setOpenProp, - className, - style, - children, - ...props -}: React.ComponentProps<"div"> & { - defaultOpen?: boolean; - open?: boolean; - onOpenChange?: (open: boolean) => void; -}) { - const isMobile = useIsMobile(); - const [openMobile, setOpenMobile] = React.useState(false); - - // This is the internal state of the sidebar. - // We use openProp and setOpenProp for control from outside the component. - const [_open, _setOpen] = React.useState(defaultOpen); - const open = openProp ?? _open; - const setOpen = React.useCallback( - (value: boolean | ((value: boolean) => boolean)) => { - const openState = typeof value === "function" ? value(open) : value; - if (setOpenProp) { - setOpenProp(openState); - } else { - _setOpen(openState); - } - - // This sets the cookie to keep the sidebar state. - document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; - }, - [setOpenProp, open], - ); - - // Helper to toggle the sidebar. - const toggleSidebar = React.useCallback(() => { - return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open); - }, [isMobile, setOpen]); - - // Adds a keyboard shortcut to toggle the sidebar. - React.useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ( - event.key === SIDEBAR_KEYBOARD_SHORTCUT && - (event.metaKey || event.ctrlKey) - ) { - event.preventDefault(); - toggleSidebar(); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [toggleSidebar]); - - // We add a state so that we can do data-state="expanded" or "collapsed". - // This makes it easier to style the sidebar with Tailwind classes. - const state = open ? "expanded" : "collapsed"; - - const contextValue = React.useMemo( - () => ({ - state, - open, - setOpen, - isMobile, - openMobile, - setOpenMobile, - toggleSidebar, - }), - [state, open, setOpen, isMobile, openMobile, toggleSidebar], - ); - - return ( - - - - {children} - - - - ); -} - -function Sidebar({ - side = "left", - variant = "sidebar", - collapsible = "offcanvas", - className, - children, - ...props -}: React.ComponentProps<"div"> & { - side?: "left" | "right"; - variant?: "sidebar" | "floating" | "inset"; - collapsible?: "offcanvas" | "icon" | "none"; -}) { - const { state, openMobile, setOpenMobile } = useSidebar(); - - if (collapsible === "none") { - return ( - - {children} - - ); - } - - return ( - <> - - - - Sidebar - Displays the mobile sidebar. - - {children} - - - - - {/* This is what handles the sidebar gap on desktop */} - - - - {children} - - - - > - ); -} - -function SidebarTrigger({ - className, - onClick, - ...props -}: React.ComponentProps) { - const { toggleSidebar } = useSidebar(); - - return ( - { - onClick?.(event); - toggleSidebar(); - }} - {...props} - > - - Toggle Sidebar - - ); -} - -function SidebarRail({ className, ...props }: React.ComponentProps<"button">) { - const { toggleSidebar } = useSidebar(); - - return ( - - ); -} - -function SidebarInset({ className, ...props }: React.ComponentProps<"main">) { - return ( - - ); -} - -function SidebarInput({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarSeparator({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function SidebarContent({ className, ...props }: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarGroupLabel({ - className, - asChild = false, - ...props -}: React.ComponentProps<"div"> & { asChild?: boolean }) { - const Comp = asChild ? Slot : "div"; - - return ( - svg]:size-4 [&>svg]:shrink-0", - "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", - className, - )} - {...props} - /> - ); -} - -function SidebarGroupAction({ - className, - asChild = false, - ...props -}: React.ComponentProps<"button"> & { asChild?: boolean }) { - const Comp = asChild ? Slot : "button"; - - return ( - svg]:size-4 [&>svg]:shrink-0", - // Increases the hit area of the button on mobile. - "after:absolute after:-inset-2 md:after:hidden", - "group-data-[collapsible=icon]:hidden", - className, - )} - {...props} - /> - ); -} - -function SidebarGroupContent({ - className, - ...props -}: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) { - return ( - - ); -} - -function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) { - return ( - - ); -} - -const sidebarMenuButtonVariants = cva( - "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", - { - variants: { - variant: { - default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", - outline: - "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", - }, - size: { - default: "h-8 text-sm", - sm: "h-7 text-xs", - lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - -function SidebarMenuButton({ - asChild = false, - isActive = false, - variant = "default", - size = "default", - tooltip, - className, - ...props -}: React.ComponentProps<"button"> & { - asChild?: boolean; - isActive?: boolean; - tooltip?: string | React.ComponentProps; -} & VariantProps) { - const Comp = asChild ? Slot : "button"; - const { isMobile, state } = useSidebar(); - - const button = ( - - ); - - if (!tooltip) { - return button; - } - - if (typeof tooltip === "string") { - tooltip = { - children: tooltip, - }; - } - - return ( - - {button} - - - ); -} - -function SidebarMenuAction({ - className, - asChild = false, - showOnHover = false, - ...props -}: React.ComponentProps<"button"> & { - asChild?: boolean; - showOnHover?: boolean; -}) { - const Comp = asChild ? Slot : "button"; - - return ( - svg]:size-4 [&>svg]:shrink-0", - // Increases the hit area of the button on mobile. - "after:absolute after:-inset-2 md:after:hidden", - "peer-data-[size=sm]/menu-button:top-1", - "peer-data-[size=default]/menu-button:top-1.5", - "peer-data-[size=lg]/menu-button:top-2.5", - "group-data-[collapsible=icon]:hidden", - showOnHover && - "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0", - className, - )} - {...props} - /> - ); -} - -function SidebarMenuBadge({ - className, - ...props -}: React.ComponentProps<"div">) { - return ( - - ); -} - -function SidebarMenuSkeleton({ - className, - showIcon = false, - ...props -}: React.ComponentProps<"div"> & { - showIcon?: boolean; -}) { - // Random width between 50 to 90%. - const width = React.useMemo(() => { - return `${Math.floor(Math.random() * 40) + 50}%`; - }, []); - - return ( - - {showIcon && ( - - )} - - - ); -} - -function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) { - return ( - - ); -} - -function SidebarMenuSubItem({ - className, - ...props -}: React.ComponentProps<"li">) { - return ( - - ); -} - -function SidebarMenuSubButton({ - asChild = false, - size = "md", - isActive = false, - className, - ...props -}: React.ComponentProps<"a"> & { - asChild?: boolean; - size?: "sm" | "md"; - isActive?: boolean; -}) { - const Comp = asChild ? Slot : "a"; - - return ( - svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", - "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", - size === "sm" && "text-xs", - size === "md" && "text-sm", - "group-data-[collapsible=icon]:hidden", - className, - )} - {...props} - /> - ); -} - -export { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarGroup, - SidebarGroupAction, - SidebarGroupContent, - SidebarGroupLabel, - SidebarHeader, - SidebarInput, - SidebarInset, - SidebarMenu, - SidebarMenuAction, - SidebarMenuBadge, - SidebarMenuButton, - SidebarMenuItem, - SidebarMenuSkeleton, - SidebarMenuSub, - SidebarMenuSubButton, - SidebarMenuSubItem, - SidebarProvider, - SidebarRail, - SidebarSeparator, - SidebarTrigger, - useSidebar, -}; diff --git a/src/shared/components/ui/use-mobile.ts b/src/shared/components/ui/use-mobile.ts deleted file mode 100644 index 5772abc..0000000 --- a/src/shared/components/ui/use-mobile.ts +++ /dev/null @@ -1 +0,0 @@ -export { useIsMobile, useMobile } from "@/shared/hooks/use-mobile"; diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts deleted file mode 100644 index 9f427ff..0000000 --- a/src/shared/hooks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { useControlledState } from "./use-controlled-state"; -export { useFormState } from "./use-form-state"; -export { useIsMobile, useMobile } from "./use-mobile"; diff --git a/src/shared/hooks/use-mobile.ts b/src/shared/hooks/use-mobile.ts deleted file mode 100644 index 55e0e9b..0000000 --- a/src/shared/hooks/use-mobile.ts +++ /dev/null @@ -1,28 +0,0 @@ -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 const useMobile = useIsMobile; diff --git a/src/shared/lib/schemas/index.ts b/src/shared/lib/schemas/index.ts deleted file mode 100644 index bdfe04c..0000000 --- a/src/shared/lib/schemas/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./common"; -export * from "./inbox"; -export * from "./insights"; diff --git a/src/shared/lib/types/index.ts b/src/shared/lib/types/index.ts deleted file mode 100644 index da3ff06..0000000 --- a/src/shared/lib/types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./actions"; -export * from "./calendar"; -export * from "./notifications"; -export * from "./reports";