diff --git a/.vscode/settings.json b/.vscode/settings.json index e37cdf0..656c3cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,11 @@ { + // Backend settings "files.exclude": { "**/__pycache__/**": true }, "mypy-type-checker.args": ["--config-file='backend/mypy.ini'"], - "python.defaultInterpreterPath": "./backend/.venv/bin/python" + "python.defaultInterpreterPath": "./backend/.venv/bin/python", + + // Frontend settings + "prettier.configPath": "./frontend/.prettierrc" } diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..93b2ed6 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,10 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + "proseWrap": "always", + "requireConfig": false, + "useTabs": true, + "trailingComma": "none", + "bracketSpacing": true, + "jsxBracketSameLine": false, + "bracketSameLine": true +} \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 1404f34..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; - -export default function App() { - return ( -
- - -

Welcome to My ShadCN Site

-

This is a sample site using Vite, TypeScript, and ShadCN UI.

- -
-
-
- ); -} diff --git a/frontend/src/components/app-sidebar.tsx b/frontend/src/components/app-sidebar.tsx index 94ad3a0..572bc4b 100644 --- a/frontend/src/components/app-sidebar.tsx +++ b/frontend/src/components/app-sidebar.tsx @@ -1,175 +1,175 @@ -"use client" +"use client"; -import * as React from "react" +import * as React from "react"; import { - AudioWaveform, - BookOpen, - Bot, - Command, - Frame, - GalleryVerticalEnd, - Map, - PieChart, - Settings2, - SquareTerminal, -} from "lucide-react" + AudioWaveform, + BookOpen, + Bot, + Command, + Frame, + GalleryVerticalEnd, + Map, + PieChart, + Settings2, + SquareTerminal +} from "lucide-react"; -import { NavMain } from "@/components/nav-main" -import { NavProjects } from "@/components/nav-projects" -import { NavUser } from "@/components/nav-user" -import { TeamSwitcher } from "@/components/team-switcher" +import { NavMain } from "@/components/nav-main"; +import { NavProjects } from "@/components/nav-projects"; +import { NavUser } from "@/components/nav-user"; +import { TeamSwitcher } from "@/components/team-switcher"; import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarHeader, - SidebarRail, -} from "@/components/ui/sidebar" + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarRail +} from "@/components/ui/sidebar"; // This is sample data. const data = { - user: { - name: "shadcn", - email: "m@example.com", - avatar: "/avatars/shadcn.jpg", - }, - teams: [ - { - name: "Acme Inc", - logo: GalleryVerticalEnd, - plan: "Enterprise", - }, - { - name: "Acme Corp.", - logo: AudioWaveform, - plan: "Startup", - }, - { - name: "Evil Corp.", - logo: Command, - plan: "Free", - }, - ], - navMain: [ - { - title: "Playground", - url: "#", - icon: SquareTerminal, - isActive: true, - items: [ - { - title: "History", - url: "#", - }, - { - title: "Starred", - url: "#", - }, - { - title: "Settings", - url: "#", - }, - ], - }, - { - title: "Models", - url: "#", - icon: Bot, - items: [ - { - title: "Genesis", - url: "#", - }, - { - title: "Explorer", - url: "#", - }, - { - title: "Quantum", - url: "#", - }, - ], - }, - { - title: "Documentation", - url: "#", - icon: BookOpen, - items: [ - { - title: "Introduction", - url: "#", - }, - { - title: "Get Started", - url: "#", - }, - { - title: "Tutorials", - url: "#", - }, - { - title: "Changelog", - url: "#", - }, - ], - }, - { - title: "Settings", - url: "#", - icon: Settings2, - items: [ - { - title: "General", - url: "#", - }, - { - title: "Team", - url: "#", - }, - { - title: "Billing", - url: "#", - }, - { - title: "Limits", - url: "#", - }, - ], - }, - ], - projects: [ - { - name: "Design Engineering", - url: "#", - icon: Frame, - }, - { - name: "Sales & Marketing", - url: "#", - icon: PieChart, - }, - { - name: "Travel", - url: "#", - icon: Map, - }, - ], -} + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg" + }, + teams: [ + { + name: "Acme Inc", + logo: GalleryVerticalEnd, + plan: "Enterprise" + }, + { + name: "Acme Corp.", + logo: AudioWaveform, + plan: "Startup" + }, + { + name: "Evil Corp.", + logo: Command, + plan: "Free" + } + ], + navMain: [ + { + title: "Playground", + url: "#", + icon: SquareTerminal, + isActive: true, + items: [ + { + title: "History", + url: "#" + }, + { + title: "Starred", + url: "#" + }, + { + title: "Settings", + url: "#" + } + ] + }, + { + title: "Models", + url: "#", + icon: Bot, + items: [ + { + title: "Genesis", + url: "#" + }, + { + title: "Explorer", + url: "#" + }, + { + title: "Quantum", + url: "#" + } + ] + }, + { + title: "Documentation", + url: "#", + icon: BookOpen, + items: [ + { + title: "Introduction", + url: "#" + }, + { + title: "Get Started", + url: "#" + }, + { + title: "Tutorials", + url: "#" + }, + { + title: "Changelog", + url: "#" + } + ] + }, + { + title: "Settings", + url: "#", + icon: Settings2, + items: [ + { + title: "General", + url: "#" + }, + { + title: "Team", + url: "#" + }, + { + title: "Billing", + url: "#" + }, + { + title: "Limits", + url: "#" + } + ] + } + ], + projects: [ + { + name: "Design Engineering", + url: "#", + icon: Frame + }, + { + name: "Sales & Marketing", + url: "#", + icon: PieChart + }, + { + name: "Travel", + url: "#", + icon: Map + } + ] +}; export function AppSidebar({ ...props }: React.ComponentProps) { - return ( - - - - - - - - - - - - - - ) + return ( + + + + + + + + + + + + + + ); } diff --git a/frontend/src/components/coming-soon.tsx b/frontend/src/components/coming-soon.tsx index afdfbd5..10afd3d 100644 --- a/frontend/src/components/coming-soon.tsx +++ b/frontend/src/components/coming-soon.tsx @@ -1,16 +1,16 @@ -import { IconPlanet } from '@tabler/icons-react' +import { IconPlanet } from "@tabler/icons-react"; export default function ComingSoon() { - return ( -
-
- -

Coming Soon 👀

-

- This page has not been created yet.
- Stay tuned though! -

-
-
- ) + return ( +
+
+ +

Coming Soon 👀

+

+ This page has not been created yet.
+ Stay tuned though! +

+
+
+ ); } diff --git a/frontend/src/components/command-menu.tsx b/frontend/src/components/command-menu.tsx index 213795f..fb0c476 100644 --- a/frontend/src/components/command-menu.tsx +++ b/frontend/src/components/command-menu.tsx @@ -1,96 +1,94 @@ -import React from 'react' -import { useNavigate } from '@tanstack/react-router' +import React from "react"; +import { useNavigate } from "@tanstack/react-router"; import { - IconArrowRightDashed, - IconDeviceLaptop, - IconMoon, - IconSun, -} from '@tabler/icons-react' -import { useSearch } from '@/context/search-context' -import { useTheme } from '@/context/theme-context' + IconArrowRightDashed, + IconDeviceLaptop, + IconMoon, + IconSun +} from "@tabler/icons-react"; +import { useSearch } from "@/context/search-context"; +import { useTheme } from "@/context/theme-context"; import { - CommandDialog, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - CommandSeparator, -} from '@/components/ui/command' -import { sidebarData } from './layout/data/sidebar-data' -import { ScrollArea } from './ui/scroll-area' + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator +} from "@/components/ui/command"; +import { sidebarData } from "./layout/data/sidebar-data"; +import { ScrollArea } from "./ui/scroll-area"; export function CommandMenu() { - const navigate = useNavigate() - const { setTheme } = useTheme() - const { open, setOpen } = useSearch() + const navigate = useNavigate(); + const { setTheme } = useTheme(); + const { open, setOpen } = useSearch(); - const runCommand = React.useCallback( - (command: () => unknown) => { - setOpen(false) - command() - }, - [setOpen] - ) + const runCommand = React.useCallback( + (command: () => unknown) => { + setOpen(false); + command(); + }, + [setOpen] + ); - return ( - - - - - No results found. - {sidebarData.navGroups.map((group) => ( - - {group.items.map((navItem, i) => { - if (navItem.url) - return ( - { - runCommand(() => navigate({ to: navItem.url })) - }} - > -
- -
- {navItem.title} -
- ) + return ( + + + + + No results found. + {sidebarData.navGroups.map((group) => ( + + {group.items.map((navItem, i) => { + if (navItem.url) + return ( + { + runCommand(() => navigate({ to: navItem.url })); + }}> +
+ +
+ {navItem.title} +
+ ); - return navItem.items?.map((subItem, i) => ( - { - runCommand(() => navigate({ to: subItem.url })) - }} - > -
- -
- {subItem.title} -
- )) - })} -
- ))} - - - runCommand(() => setTheme('light'))}> - Light - - runCommand(() => setTheme('dark'))}> - - Dark - - runCommand(() => setTheme('system'))}> - - System - - -
-
-
- ) + return navItem.items?.map((subItem, i) => ( + { + runCommand(() => navigate({ to: subItem.url })); + }}> +
+ +
+ {subItem.title} +
+ )); + })} +
+ ))} + + + runCommand(() => setTheme("light"))}> + Light + + runCommand(() => setTheme("dark"))}> + + Dark + + runCommand(() => setTheme("system"))}> + + System + + +
+
+
+ ); } diff --git a/frontend/src/components/confirm-dialog.tsx b/frontend/src/components/confirm-dialog.tsx index cd81001..e72fd12 100644 --- a/frontend/src/components/confirm-dialog.tsx +++ b/frontend/src/components/confirm-dialog.tsx @@ -1,67 +1,66 @@ -import { cn } from '@/lib/utils' +import { cn } from "@/lib/utils"; import { - AlertDialog, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog' -import { Button } from '@/components/ui/button' + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle +} from "@/components/ui/alert-dialog"; +import { Button } from "@/components/ui/button"; interface ConfirmDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - title: React.ReactNode - disabled?: boolean - desc: React.JSX.Element | string - cancelBtnText?: string - confirmText?: React.ReactNode - destructive?: boolean - handleConfirm: () => void - isLoading?: boolean - className?: string - children?: React.ReactNode + open: boolean; + onOpenChange: (open: boolean) => void; + title: React.ReactNode; + disabled?: boolean; + desc: React.JSX.Element | string; + cancelBtnText?: string; + confirmText?: React.ReactNode; + destructive?: boolean; + handleConfirm: () => void; + isLoading?: boolean; + className?: string; + children?: React.ReactNode; } export function ConfirmDialog(props: ConfirmDialogProps) { - const { - title, - desc, - children, - className, - confirmText, - cancelBtnText, - destructive, - isLoading, - disabled = false, - handleConfirm, - ...actions - } = props - return ( - - - - {title} - -
{desc}
-
-
- {children} - - - {cancelBtnText ?? 'Cancel'} - - - -
-
- ) + const { + title, + desc, + children, + className, + confirmText, + cancelBtnText, + destructive, + isLoading, + disabled = false, + handleConfirm, + ...actions + } = props; + return ( + + + + {title} + +
{desc}
+
+
+ {children} + + + {cancelBtnText ?? "Cancel"} + + + +
+
+ ); } diff --git a/frontend/src/components/layout/app-sidebar.tsx b/frontend/src/components/layout/app-sidebar.tsx index 7592e91..44b985e 100644 --- a/frontend/src/components/layout/app-sidebar.tsx +++ b/frontend/src/components/layout/app-sidebar.tsx @@ -1,30 +1,30 @@ import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarHeader, - SidebarRail, -} from '@/components/ui/sidebar' -import { NavGroup } from '@/components/layout/nav-group' -import { NavUser } from '@/components/layout/nav-user' -import { TeamSwitcher } from '@/components/layout/team-switcher' -import { sidebarData } from './data/sidebar-data' + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarRail +} from "@/components/ui/sidebar"; +import { NavGroup } from "@/components/layout/nav-group"; +import { NavUser } from "@/components/layout/nav-user"; +import { TeamSwitcher } from "@/components/layout/team-switcher"; +import { sidebarData } from "./data/sidebar-data"; export function AppSidebar({ ...props }: React.ComponentProps) { - return ( - - - - - - {sidebarData.navGroups.map((props) => ( - - ))} - - - - - - - ) + return ( + + + + + + {sidebarData.navGroups.map((props) => ( + + ))} + + + + + + + ); } diff --git a/frontend/src/components/layout/data/sidebar-data.ts b/frontend/src/components/layout/data/sidebar-data.ts index a301ae6..6b6a4b8 100644 --- a/frontend/src/components/layout/data/sidebar-data.ts +++ b/frontend/src/components/layout/data/sidebar-data.ts @@ -1,184 +1,189 @@ import { - IconBarrierBlock, - IconBrowserCheck, - IconBug, - IconChecklist, - IconError404, - IconHelp, - IconLayoutDashboard, - IconLock, - IconLockAccess, - IconMessages, - IconNotification, - IconPackages, - IconPalette, - IconServerOff, - IconSettings, - IconTool, - IconUserCog, - IconUserOff, - IconUsers, -} from '@tabler/icons-react' -import { AudioWaveform, Command, GalleryVerticalEnd } from 'lucide-react' -import { type SidebarData } from '../types' + IconBarrierBlock, + IconBrowserCheck, + IconBug, + IconChecklist, + IconError404, + IconHelp, + IconLayoutDashboard, + IconLock, + IconLockAccess, + IconMessages, + IconNotification, + IconPackages, + IconPalette, + IconServerOff, + IconSettings, + IconTool, + IconUserCog, + IconUserOff, + IconUsers +} from "@tabler/icons-react"; +import { AudioWaveform, Command, GalleryVerticalEnd } from "lucide-react"; +import { type SidebarData } from "../types"; export const sidebarData: SidebarData = { - user: { - name: 'satnaing', - email: 'satnaingdev@gmail.com', - avatar: '/avatars/shadcn.jpg', - }, - teams: [ - { - name: 'Shadcn Admin', - logo: Command, - plan: 'Vite + ShadcnUI', - }, - { - name: 'Acme Inc', - logo: GalleryVerticalEnd, - plan: 'Enterprise', - }, - { - name: 'Acme Corp.', - logo: AudioWaveform, - plan: 'Startup', - }, - ], - navGroups: [ - { - title: 'General', - items: [ - { - title: 'Dashboard', - url: '/', - icon: IconLayoutDashboard, - }, - { - title: 'Tasks', - url: '/tasks', - icon: IconChecklist, - }, - { - title: 'Apps', - url: '/apps', - icon: IconPackages, - }, - { - title: 'Chats', - url: '/chats', - badge: '3', - icon: IconMessages, - }, - { - title: 'Users', - url: '/users', - icon: IconUsers, - }, - ], - }, - { - title: 'Pages', - items: [ - { - title: 'Auth', - icon: IconLockAccess, - items: [ - { - title: 'Sign In', - url: '/sign-in', - }, - { - title: 'Sign In (2 Col)', - url: '/sign-in-2', - }, - { - title: 'Sign Up', - url: '/sign-up', - }, - { - title: 'Forgot Password', - url: '/forgot-password', - }, - { - title: 'OTP', - url: '/otp', - }, - ], - }, - { - title: 'Errors', - icon: IconBug, - items: [ - { - title: 'Unauthorized', - url: '/401', - icon: IconLock, - }, - { - title: 'Forbidden', - url: '/403', - icon: IconUserOff, - }, - { - title: 'Not Found', - url: '/404', - icon: IconError404, - }, - { - title: 'Internal Server Error', - url: '/500', - icon: IconServerOff, - }, - { - title: 'Maintenance Error', - url: '/503', - icon: IconBarrierBlock, - }, - ], - }, - ], - }, - { - title: 'Other', - items: [ - { - title: 'Settings', - icon: IconSettings, - items: [ - { - title: 'Profile', - url: '/settings', - icon: IconUserCog, - }, - { - title: 'Account', - url: '/settings/account', - icon: IconTool, - }, - { - title: 'Appearance', - url: '/settings/appearance', - icon: IconPalette, - }, - { - title: 'Notifications', - url: '/settings/notifications', - icon: IconNotification, - }, - { - title: 'Display', - url: '/settings/display', - icon: IconBrowserCheck, - }, - ], - }, - { - title: 'Help Center', - url: '/help-center', - icon: IconHelp, - }, - ], - }, - ], -} + user: { + name: "satnaing", + email: "satnaingdev@gmail.com", + avatar: "/avatars/shadcn.jpg" + }, + teams: [ + { + name: "Shadcn Admin", + logo: Command, + plan: "Vite + ShadcnUI" + }, + { + name: "Acme Inc", + logo: GalleryVerticalEnd, + plan: "Enterprise" + }, + { + name: "Acme Corp.", + logo: AudioWaveform, + plan: "Startup" + } + ], + navGroups: [ + { + title: "General", + items: [ + { + title: "Dashboard", + url: "/", + icon: IconLayoutDashboard + }, + { + title: "Tasks", + url: "/tasks", + icon: IconChecklist + }, + { + title: "Apps", + url: "/apps", + icon: IconPackages + }, + { + title: "Chats", + url: "/chats", + badge: "3", + icon: IconMessages + }, + { + title: "Users", + url: "/users", + icon: IconUsers + }, + { + title: "NBA", + url: "/nba", + icon: IconChecklist + } + ] + }, + { + title: "Pages", + items: [ + { + title: "Auth", + icon: IconLockAccess, + items: [ + { + title: "Sign In", + url: "/sign-in" + }, + { + title: "Sign In (2 Col)", + url: "/sign-in-2" + }, + { + title: "Sign Up", + url: "/sign-up" + }, + { + title: "Forgot Password", + url: "/forgot-password" + }, + { + title: "OTP", + url: "/otp" + } + ] + }, + { + title: "Errors", + icon: IconBug, + items: [ + { + title: "Unauthorized", + url: "/401", + icon: IconLock + }, + { + title: "Forbidden", + url: "/403", + icon: IconUserOff + }, + { + title: "Not Found", + url: "/404", + icon: IconError404 + }, + { + title: "Internal Server Error", + url: "/500", + icon: IconServerOff + }, + { + title: "Maintenance Error", + url: "/503", + icon: IconBarrierBlock + } + ] + } + ] + }, + { + title: "Other", + items: [ + { + title: "Settings", + icon: IconSettings, + items: [ + { + title: "Profile", + url: "/settings", + icon: IconUserCog + }, + { + title: "Account", + url: "/settings/account", + icon: IconTool + }, + { + title: "Appearance", + url: "/settings/appearance", + icon: IconPalette + }, + { + title: "Notifications", + url: "/settings/notifications", + icon: IconNotification + }, + { + title: "Display", + url: "/settings/display", + icon: IconBrowserCheck + } + ] + }, + { + title: "Help Center", + url: "/help-center", + icon: IconHelp + } + ] + } + ] +}; diff --git a/frontend/src/components/layout/header.tsx b/frontend/src/components/layout/header.tsx index 950ee2f..12a84b2 100644 --- a/frontend/src/components/layout/header.tsx +++ b/frontend/src/components/layout/header.tsx @@ -1,48 +1,47 @@ -import React from 'react' -import { cn } from '@/lib/utils' -import { Separator } from '@/components/ui/separator' -import { SidebarTrigger } from '@/components/ui/sidebar' +import React from "react"; +import { cn } from "@/lib/utils"; +import { Separator } from "@/components/ui/separator"; +import { SidebarTrigger } from "@/components/ui/sidebar"; interface HeaderProps extends React.HTMLAttributes { - fixed?: boolean - ref?: React.Ref + fixed?: boolean; + ref?: React.Ref; } export const Header = ({ - className, - fixed, - children, - ...props + className, + fixed, + children, + ...props }: HeaderProps) => { - const [offset, setOffset] = React.useState(0) + const [offset, setOffset] = React.useState(0); - React.useEffect(() => { - const onScroll = () => { - setOffset(document.body.scrollTop || document.documentElement.scrollTop) - } + React.useEffect(() => { + const onScroll = () => { + setOffset(document.body.scrollTop || document.documentElement.scrollTop); + }; - // Add scroll listener to the body - document.addEventListener('scroll', onScroll, { passive: true }) + // Add scroll listener to the body + document.addEventListener("scroll", onScroll, { passive: true }); - // Clean up the event listener on unmount - return () => document.removeEventListener('scroll', onScroll) - }, []) + // Clean up the event listener on unmount + return () => document.removeEventListener("scroll", onScroll); + }, []); - return ( -
10 && fixed ? 'shadow' : 'shadow-none', - className - )} - {...props} - > - - - {children} -
- ) -} + return ( +
10 && fixed ? "shadow" : "shadow-none", + className + )} + {...props}> + + + {children} +
+ ); +}; -Header.displayName = 'Header' +Header.displayName = "Header"; diff --git a/frontend/src/components/layout/main.tsx b/frontend/src/components/layout/main.tsx index 8d9cdd3..cd7ed34 100644 --- a/frontend/src/components/layout/main.tsx +++ b/frontend/src/components/layout/main.tsx @@ -1,22 +1,22 @@ -import React from 'react' -import { cn } from '@/lib/utils' +import React from "react"; +import { cn } from "@/lib/utils"; interface MainProps extends React.HTMLAttributes { - fixed?: boolean - ref?: React.Ref + fixed?: boolean; + ref?: React.Ref; } export const Main = ({ fixed, ...props }: MainProps) => { - return ( -
- ) -} + return ( +
+ ); +}; -Main.displayName = 'Main' +Main.displayName = "Main"; diff --git a/frontend/src/components/layout/nav-group.tsx b/frontend/src/components/layout/nav-group.tsx index bdaa208..40fd93e 100644 --- a/frontend/src/components/layout/nav-group.tsx +++ b/frontend/src/components/layout/nav-group.tsx @@ -1,180 +1,175 @@ -import { ReactNode } from 'react' -import { Link, useLocation } from '@tanstack/react-router' -import { ChevronRight } from 'lucide-react' +import { ReactNode } from "react"; +import { Link, useLocation } from "@tanstack/react-router"; +import { ChevronRight } from "lucide-react"; import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/components/ui/collapsible' + Collapsible, + CollapsibleContent, + CollapsibleTrigger +} from "@/components/ui/collapsible"; import { - SidebarGroup, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - SidebarMenuSub, - SidebarMenuSubButton, - SidebarMenuSubItem, - useSidebar, -} from '@/components/ui/sidebar' -import { Badge } from '../ui/badge' + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, + useSidebar +} from "@/components/ui/sidebar"; +import { Badge } from "../ui/badge"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '../ui/dropdown-menu' -import { NavCollapsible, NavItem, NavLink, type NavGroup } from './types' + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "../ui/dropdown-menu"; +import { NavCollapsible, NavItem, NavLink, type NavGroup } from "./types"; export function NavGroup({ title, items }: NavGroup) { - const { state } = useSidebar() - const href = useLocation({ select: (location) => location.href }) - return ( - - {title} - - {items.map((item) => { - const key = `${item.title}-${item.url}` + const { state } = useSidebar(); + const href = useLocation({ select: (location) => location.href }); + return ( + + {title} + + {items.map((item) => { + const key = `${item.title}-${item.url}`; - if (!item.items) - return + if (!item.items) + return ; - if (state === 'collapsed') - return ( - - ) + if (state === "collapsed") + return ( + + ); - return - })} - - - ) + return ; + })} + + + ); } const NavBadge = ({ children }: { children: ReactNode }) => ( - {children} -) + {children} +); const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => { - const { setOpenMobile } = useSidebar() - return ( - - - setOpenMobile(false)}> - {item.icon && } - {item.title} - {item.badge && {item.badge}} - - - - ) -} + const { setOpenMobile } = useSidebar(); + return ( + + + setOpenMobile(false)}> + {item.icon && } + {item.title} + {item.badge && {item.badge}} + + + + ); +}; const SidebarMenuCollapsible = ({ - item, - href, + item, + href }: { - item: NavCollapsible - href: string + item: NavCollapsible; + href: string; }) => { - const { setOpenMobile } = useSidebar() - return ( - - - - - {item.icon && } - {item.title} - {item.badge && {item.badge}} - - - - - - {item.items.map((subItem) => ( - - - setOpenMobile(false)}> - {subItem.icon && } - {subItem.title} - {subItem.badge && {subItem.badge}} - - - - ))} - - - - - ) -} + const { setOpenMobile } = useSidebar(); + return ( + + + + + {item.icon && } + {item.title} + {item.badge && {item.badge}} + + + + + + {item.items.map((subItem) => ( + + + setOpenMobile(false)}> + {subItem.icon && } + {subItem.title} + {subItem.badge && {subItem.badge}} + + + + ))} + + + + + ); +}; const SidebarMenuCollapsedDropdown = ({ - item, - href, + item, + href }: { - item: NavCollapsible - href: string + item: NavCollapsible; + href: string; }) => { - return ( - - - - - {item.icon && } - {item.title} - {item.badge && {item.badge}} - - - - - - {item.title} {item.badge ? `(${item.badge})` : ''} - - - {item.items.map((sub) => ( - - - {sub.icon && } - {sub.title} - {sub.badge && ( - {sub.badge} - )} - - - ))} - - - - ) -} + return ( + + + + + {item.icon && } + {item.title} + {item.badge && {item.badge}} + + + + + + {item.title} {item.badge ? `(${item.badge})` : ""} + + + {item.items.map((sub) => ( + + + {sub.icon && } + {sub.title} + {sub.badge && ( + {sub.badge} + )} + + + ))} + + + + ); +}; function checkIsActive(href: string, item: NavItem, mainNav = false) { - return ( - href === item.url || // /endpint?search=param - href.split('?')[0] === item.url || // endpoint - !!item?.items?.filter((i) => i.url === href).length || // if child nav is active - (mainNav && - href.split('/')[1] !== '' && - href.split('/')[1] === item?.url?.split('/')[1]) - ) + return ( + href === item.url || // /endpint?search=param + href.split("?")[0] === item.url || // endpoint + !!item?.items?.filter((i) => i.url === href).length || // if child nav is active + (mainNav && + href.split("/")[1] !== "" && + href.split("/")[1] === item?.url?.split("/")[1]) + ); } diff --git a/frontend/src/components/layout/nav-user.tsx b/frontend/src/components/layout/nav-user.tsx index 6391f33..d340699 100644 --- a/frontend/src/components/layout/nav-user.tsx +++ b/frontend/src/components/layout/nav-user.tsx @@ -1,114 +1,112 @@ -import { Link } from '@tanstack/react-router' +import { Link } from "@tanstack/react-router"; import { - BadgeCheck, - Bell, - ChevronsUpDown, - CreditCard, - LogOut, - Sparkles, -} from 'lucide-react' -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles +} from "lucide-react"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from '@/components/ui/sidebar' + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar"; export function NavUser({ - user, + user }: { - user: { - name: string - email: string - avatar: string - } + user: { + name: string; + email: string; + avatar: string; + }; }) { - const { isMobile } = useSidebar() + const { isMobile } = useSidebar(); - return ( - - - - - - - - SN - -
- {user.name} - {user.email} -
- -
-
- - -
- - - SN - -
- {user.name} - {user.email} -
-
-
- - - - - Upgrade to Pro - - - - - - - - Account - - - - - - Billing - - - - - - Notifications - - - - - - - Log out - -
-
-
-
- ) + return ( + + + + + + + + SN + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + SN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + + Account + + + + + + Billing + + + + + + Notifications + + + + + + + Log out + +
+
+
+
+ ); } diff --git a/frontend/src/components/layout/team-switcher.tsx b/frontend/src/components/layout/team-switcher.tsx index df1e345..1eb944e 100644 --- a/frontend/src/components/layout/team-switcher.tsx +++ b/frontend/src/components/layout/team-switcher.tsx @@ -1,86 +1,83 @@ -import * as React from 'react' -import { ChevronsUpDown, Plus } from 'lucide-react' +import * as React from "react"; +import { ChevronsUpDown, Plus } from "lucide-react"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from '@/components/ui/sidebar' + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar"; export function TeamSwitcher({ - teams, + teams }: { - teams: { - name: string - logo: React.ElementType - plan: string - }[] + teams: { + name: string; + logo: React.ElementType; + plan: string; + }[]; }) { - const { isMobile } = useSidebar() - const [activeTeam, setActiveTeam] = React.useState(teams[0]) + const { isMobile } = useSidebar(); + const [activeTeam, setActiveTeam] = React.useState(teams[0]); - return ( - - - - - -
- -
-
- - {activeTeam.name} - - {activeTeam.plan} -
- -
-
- - - Teams - - {teams.map((team, index) => ( - setActiveTeam(team)} - className='gap-2 p-2' - > -
- -
- {team.name} - ⌘{index + 1} -
- ))} - - -
- -
-
Add team
-
-
-
-
-
- ) + return ( + + + + + +
+ +
+
+ + {activeTeam.name} + + {activeTeam.plan} +
+ +
+
+ + + Teams + + {teams.map((team, index) => ( + setActiveTeam(team)} + className="gap-2 p-2"> +
+ +
+ {team.name} + ⌘{index + 1} +
+ ))} + + +
+ +
+
Add team
+
+
+
+
+
+ ); } diff --git a/frontend/src/components/layout/top-nav.tsx b/frontend/src/components/layout/top-nav.tsx index 29d7316..bda0805 100644 --- a/frontend/src/components/layout/top-nav.tsx +++ b/frontend/src/components/layout/top-nav.tsx @@ -1,67 +1,64 @@ -import { Link } from '@tanstack/react-router' -import { IconMenu } from '@tabler/icons-react' -import { cn } from '@/lib/utils' -import { Button } from '@/components/ui/button' +import { Link } from "@tanstack/react-router"; +import { IconMenu } from "@tabler/icons-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; interface TopNavProps extends React.HTMLAttributes { - links: { - title: string - href: string - isActive: boolean - disabled?: boolean - }[] + links: { + title: string; + href: string; + isActive: boolean; + disabled?: boolean; + }[]; } export function TopNav({ className, links, ...props }: TopNavProps) { - return ( - <> -
- - - - - - {links.map(({ title, href, isActive, disabled }) => ( - - - {title} - - - ))} - - -
+ return ( + <> +
+ + + + + + {links.map(({ title, href, isActive, disabled }) => ( + + + {title} + + + ))} + + +
- - - ) + + + ); } diff --git a/frontend/src/components/layout/types.ts b/frontend/src/components/layout/types.ts index 86e8f6e..235b707 100644 --- a/frontend/src/components/layout/types.ts +++ b/frontend/src/components/layout/types.ts @@ -1,44 +1,44 @@ -import { LinkProps } from '@tanstack/react-router' +import { LinkProps } from "@tanstack/react-router"; interface User { - name: string - email: string - avatar: string + name: string; + email: string; + avatar: string; } interface Team { - name: string - logo: React.ElementType - plan: string + name: string; + logo: React.ElementType; + plan: string; } interface BaseNavItem { - title: string - badge?: string - icon?: React.ElementType + title: string; + badge?: string; + icon?: React.ElementType; } type NavLink = BaseNavItem & { - url: LinkProps['to'] - items?: never -} + url: LinkProps["to"]; + items?: never; +}; type NavCollapsible = BaseNavItem & { - items: (BaseNavItem & { url: LinkProps['to'] })[] - url?: never -} + items: (BaseNavItem & { url: LinkProps["to"] })[]; + url?: never; +}; -type NavItem = NavCollapsible | NavLink +type NavItem = NavCollapsible | NavLink; interface NavGroup { - title: string - items: NavItem[] + title: string; + items: NavItem[]; } interface SidebarData { - user: User - teams: Team[] - navGroups: NavGroup[] + user: User; + teams: Team[]; + navGroups: NavGroup[]; } -export type { SidebarData, NavGroup, NavItem, NavCollapsible, NavLink } +export type { SidebarData, NavGroup, NavItem, NavCollapsible, NavLink }; diff --git a/frontend/src/components/long-text.tsx b/frontend/src/components/long-text.tsx index 5f9fa07..1b675d4 100644 --- a/frontend/src/components/long-text.tsx +++ b/frontend/src/components/long-text.tsx @@ -1,85 +1,85 @@ -import { useEffect, useRef, useState } from 'react' -import { cn } from '@/lib/utils' +import { useEffect, useRef, useState } from "react"; +import { cn } from "@/lib/utils"; import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@/components/ui/popover' + Popover, + PopoverContent, + PopoverTrigger +} from "@/components/ui/popover"; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from '@/components/ui/tooltip' + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@/components/ui/tooltip"; interface Props { - children: React.ReactNode - className?: string - contentClassName?: string + children: React.ReactNode; + className?: string; + contentClassName?: string; } export default function LongText({ - children, - className = '', - contentClassName = '', + children, + className = "", + contentClassName = "" }: Props) { - const ref = useRef(null) - const [isOverflown, setIsOverflown] = useState(false) + const ref = useRef(null); + const [isOverflown, setIsOverflown] = useState(false); - useEffect(() => { - if (checkOverflow(ref.current)) { - setIsOverflown(true) - return - } + useEffect(() => { + if (checkOverflow(ref.current)) { + setIsOverflown(true); + return; + } - setIsOverflown(false) - }, []) + setIsOverflown(false); + }, []); - if (!isOverflown) - return ( -
- {children} -
- ) + if (!isOverflown) + return ( +
+ {children} +
+ ); - return ( - <> -
- - - -
- {children} -
-
- -

{children}

-
-
-
-
-
- - -
- {children} -
-
- -

{children}

-
-
-
- - ) + return ( + <> +
+ + + +
+ {children} +
+
+ +

{children}

+
+
+
+
+
+ + +
+ {children} +
+
+ +

{children}

+
+
+
+ + ); } const checkOverflow = (textContainer: HTMLDivElement | null) => { - if (textContainer) { - return ( - textContainer.offsetHeight < textContainer.scrollHeight || - textContainer.offsetWidth < textContainer.scrollWidth - ) - } - return false -} + if (textContainer) { + return ( + textContainer.offsetHeight < textContainer.scrollHeight || + textContainer.offsetWidth < textContainer.scrollWidth + ); + } + return false; +}; diff --git a/frontend/src/components/nav-main.tsx b/frontend/src/components/nav-main.tsx index 1d71af1..c6c95c4 100644 --- a/frontend/src/components/nav-main.tsx +++ b/frontend/src/components/nav-main.tsx @@ -1,73 +1,72 @@ -"use client" +"use client"; -import { ChevronRight, type LucideIcon } from "lucide-react" +import { ChevronRight, type LucideIcon } from "lucide-react"; import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from "@/components/ui/collapsible" + Collapsible, + CollapsibleContent, + CollapsibleTrigger +} from "@/components/ui/collapsible"; import { - SidebarGroup, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - SidebarMenuSub, - SidebarMenuSubButton, - SidebarMenuSubItem, -} from "@/components/ui/sidebar" + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem +} from "@/components/ui/sidebar"; export function NavMain({ - items, + items }: { - items: { - title: string - url: string - icon?: LucideIcon - isActive?: boolean - items?: { - title: string - url: string - }[] - }[] + items: { + title: string; + url: string; + icon?: LucideIcon; + isActive?: boolean; + items?: { + title: string; + url: string; + }[]; + }[]; }) { - return ( - - Platform - - {items.map((item) => ( - - - - - {item.icon && } - {item.title} - - - - - - {item.items?.map((subItem) => ( - - - - {subItem.title} - - - - ))} - - - - - ))} - - - ) + return ( + + Platform + + {items.map((item) => ( + + + + + {item.icon && } + {item.title} + + + + + + {item.items?.map((subItem) => ( + + + + {subItem.title} + + + + ))} + + + + + ))} + + + ); } diff --git a/frontend/src/components/nav-projects.tsx b/frontend/src/components/nav-projects.tsx index f50b20d..1ce36e9 100644 --- a/frontend/src/components/nav-projects.tsx +++ b/frontend/src/components/nav-projects.tsx @@ -1,89 +1,88 @@ -"use client" +"use client"; import { - Folder, - Forward, - MoreHorizontal, - Trash2, - type LucideIcon, -} from "lucide-react" + Folder, + Forward, + MoreHorizontal, + Trash2, + type LucideIcon +} from "lucide-react"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; import { - SidebarGroup, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuAction, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar"; export function NavProjects({ - projects, + projects }: { - projects: { - name: string - url: string - icon: LucideIcon - }[] + projects: { + name: string; + url: string; + icon: LucideIcon; + }[]; }) { - const { isMobile } = useSidebar() + const { isMobile } = useSidebar(); - return ( - - Projects - - {projects.map((item) => ( - - - - - {item.name} - - - - - - - More - - - - - - View Project - - - - Share Project - - - - - Delete Project - - - - - ))} - - - - More - - - - - ) + return ( + + Projects + + {projects.map((item) => ( + + + + + {item.name} + + + + + + + More + + + + + + View Project + + + + Share Project + + + + + Delete Project + + + + + ))} + + + + More + + + + + ); } diff --git a/frontend/src/components/nav-user.tsx b/frontend/src/components/nav-user.tsx index d12ef78..234d7ec 100644 --- a/frontend/src/components/nav-user.tsx +++ b/frontend/src/components/nav-user.tsx @@ -1,114 +1,108 @@ -"use client" +"use client"; import { - BadgeCheck, - Bell, - ChevronsUpDown, - CreditCard, - LogOut, - Sparkles, -} from "lucide-react" + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles +} from "lucide-react"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/components/ui/avatar" + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar"; export function NavUser({ - user, + user }: { - user: { - name: string - email: string - avatar: string - } + user: { + name: string; + email: string; + avatar: string; + }; }) { - const { isMobile } = useSidebar() + const { isMobile } = useSidebar(); - return ( - - - - - - - - CN - -
- {user.name} - {user.email} -
- -
-
- - -
- - - CN - -
- {user.name} - {user.email} -
-
-
- - - - - Upgrade to Pro - - - - - - - Account - - - - Billing - - - - Notifications - - - - - - Log out - -
-
-
-
- ) + return ( + + + + + + + + CN + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + CN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ); } diff --git a/frontend/src/components/password-input.tsx b/frontend/src/components/password-input.tsx index e5afeb8..93c6fc8 100644 --- a/frontend/src/components/password-input.tsx +++ b/frontend/src/components/password-input.tsx @@ -1,39 +1,38 @@ -import * as React from 'react' -import { IconEye, IconEyeOff } from '@tabler/icons-react' -import { cn } from '@/lib/utils' -import { Button } from './ui/button' +import * as React from "react"; +import { IconEye, IconEyeOff } from "@tabler/icons-react"; +import { cn } from "@/lib/utils"; +import { Button } from "./ui/button"; type PasswordInputProps = Omit< - React.InputHTMLAttributes, - 'type' -> + React.InputHTMLAttributes, + "type" +>; const PasswordInput = React.forwardRef( - ({ className, disabled, ...props }, ref) => { - const [showPassword, setShowPassword] = React.useState(false) - return ( -
- - -
- ) - } -) -PasswordInput.displayName = 'PasswordInput' + ({ className, disabled, ...props }, ref) => { + const [showPassword, setShowPassword] = React.useState(false); + return ( +
+ + +
+ ); + } +); +PasswordInput.displayName = "PasswordInput"; -export { PasswordInput } +export { PasswordInput }; diff --git a/frontend/src/components/pin-input.tsx b/frontend/src/components/pin-input.tsx index 7356d68..d811d20 100644 --- a/frontend/src/components/pin-input.tsx +++ b/frontend/src/components/pin-input.tsx @@ -1,454 +1,454 @@ -import * as React from 'react' -import { cn } from '@/lib/utils' +import * as React from "react"; +import { cn } from "@/lib/utils"; interface PinInputProps { - children: - | React.ReactElement - | React.ReactElement[] - /** - * className for the input container - */ - className?: string - /** - * `aria-label` for the input fields - */ - ariaLabel?: string - /** - * If set, the pin input receives focus on mount, `false` by default - */ - autoFocus?: boolean - /** - * Called when value changes - */ - onChange?: (value: string) => void - /** - * Called when all inputs have valid value - */ - onComplete?: (value: string) => void - /** - * Called when any input doesn't have value - */ - onIncomplete?: (value: string) => void - /** - * `name` attribute for input fields - */ - name?: string - /** - * `form` attribute for hidden input - */ - form?: string - /** - * If set, the input's value will be masked just like password input. This field is `false` by default - */ - mask?: boolean - /** - * If set, the pin input component signals to its fields that they should - * use `autocomplete="one-time-code"`. This field is `false` by default - */ - otp?: boolean - /** - * Uncontrolled pin input default value. - */ - defaultValue?: string - /** - * Controlled pin input value. - */ - value?: string - /** - * The type of value pin input should allow, `alphanumeric` by default - */ - type?: 'numeric' | 'alphanumeric' - /** - * Placeholder for input fields, `â—‹` by default - */ - placeholder?: string - /** - * If set, the user cannot set the value, `false` by default - */ - readOnly?: boolean - /** - * If set, the input fields are disabled, `false` by default - */ - disabled?: boolean - ref?: React.Ref + children: + | React.ReactElement + | React.ReactElement[]; + /** + * className for the input container + */ + className?: string; + /** + * `aria-label` for the input fields + */ + ariaLabel?: string; + /** + * If set, the pin input receives focus on mount, `false` by default + */ + autoFocus?: boolean; + /** + * Called when value changes + */ + onChange?: (value: string) => void; + /** + * Called when all inputs have valid value + */ + onComplete?: (value: string) => void; + /** + * Called when any input doesn't have value + */ + onIncomplete?: (value: string) => void; + /** + * `name` attribute for input fields + */ + name?: string; + /** + * `form` attribute for hidden input + */ + form?: string; + /** + * If set, the input's value will be masked just like password input. This field is `false` by default + */ + mask?: boolean; + /** + * If set, the pin input component signals to its fields that they should + * use `autocomplete="one-time-code"`. This field is `false` by default + */ + otp?: boolean; + /** + * Uncontrolled pin input default value. + */ + defaultValue?: string; + /** + * Controlled pin input value. + */ + value?: string; + /** + * The type of value pin input should allow, `alphanumeric` by default + */ + type?: "numeric" | "alphanumeric"; + /** + * Placeholder for input fields, `â—‹` by default + */ + placeholder?: string; + /** + * If set, the user cannot set the value, `false` by default + */ + readOnly?: boolean; + /** + * If set, the input fields are disabled, `false` by default + */ + disabled?: boolean; + ref?: React.Ref; } -const PinInputContext = React.createContext(false) +const PinInputContext = React.createContext(false); const PinInput = ({ className, children, ref, ...props }: PinInputProps) => { - const { - defaultValue, - value, - onChange, - onComplete, - onIncomplete, - placeholder = 'â—‹', - type = 'alphanumeric', - name, - form, - otp = false, - mask = false, - disabled = false, - readOnly = false, - autoFocus = false, - ariaLabel = '', - ...rest - } = props + const { + defaultValue, + value, + onChange, + onComplete, + onIncomplete, + placeholder = "â—‹", + type = "alphanumeric", + name, + form, + otp = false, + mask = false, + disabled = false, + readOnly = false, + autoFocus = false, + ariaLabel = "", + ...rest + } = props; - const validChildren = getValidChildren(children) + const validChildren = getValidChildren(children); - const length = getInputFieldCount(children) + const length = getInputFieldCount(children); - // pins, pinValue, refMap, ...handlers - const { pins, pinValue, refMap, ...handlers } = usePinInput({ - value, - defaultValue, - placeholder, - type, - length, - readOnly, - }) + // pins, pinValue, refMap, ...handlers + const { pins, pinValue, refMap, ...handlers } = usePinInput({ + value, + defaultValue, + placeholder, + type, + length, + readOnly + }); - /* call onChange func if pinValue changes */ - React.useEffect(() => { - if (!onChange) return - onChange(pinValue) - }, [onChange, pinValue]) + /* call onChange func if pinValue changes */ + React.useEffect(() => { + if (!onChange) return; + onChange(pinValue); + }, [onChange, pinValue]); - /* call onComplete/onIncomplete func if pinValue is valid and completed/incompleted */ - const completeRef = React.useRef(pinValue.length === length) - React.useEffect(() => { - if (pinValue.length === length && completeRef.current === false) { - completeRef.current = true - if (onComplete) onComplete(pinValue) - } - if (pinValue.length !== length && completeRef.current === true) { - completeRef.current = false - if (onIncomplete) onIncomplete(pinValue) - } - }, [length, onComplete, onIncomplete, pinValue, pins, value]) + /* call onComplete/onIncomplete func if pinValue is valid and completed/incompleted */ + const completeRef = React.useRef(pinValue.length === length); + React.useEffect(() => { + if (pinValue.length === length && completeRef.current === false) { + completeRef.current = true; + if (onComplete) onComplete(pinValue); + } + if (pinValue.length !== length && completeRef.current === true) { + completeRef.current = false; + if (onIncomplete) onIncomplete(pinValue); + } + }, [length, onComplete, onIncomplete, pinValue, pins, value]); - /* focus on first input field if autoFocus is set */ - React.useEffect(() => { - if (!autoFocus) return - const node = refMap?.get(0) - if (node) { - node.focus() - } - }, [autoFocus, refMap]) + /* focus on first input field if autoFocus is set */ + React.useEffect(() => { + if (!autoFocus) return; + const node = refMap?.get(0); + if (node) { + node.focus(); + } + }, [autoFocus, refMap]); - const skipRef = React.useRef(0) - let counter = 0 - const clones = validChildren.map((child) => { - if (child.type === PinInputField) { - const pinIndex = counter - counter = counter + 1 - return React.cloneElement(child, { - name, - inputKey: `input-${pinIndex}`, - value: length > pinIndex ? pins[pinIndex] : '', - onChange: (e: React.ChangeEvent) => - handlers.handleChange(e, pinIndex), - onFocus: (e: React.FocusEvent) => - handlers.handleFocus(e, pinIndex), - onBlur: () => handlers.handleBlur(pinIndex), - onKeyDown: (e: React.KeyboardEvent) => - handlers.handleKeyDown(e, pinIndex), - onPaste: (e: React.ClipboardEvent) => - handlers.handlePaste(e), - placeholder: placeholder, - type: type, - mask: mask, - autoComplete: otp ? 'one-time-code' : 'off', - disabled: disabled, - readOnly: readOnly, - 'aria-label': ariaLabel - ? ariaLabel - : `Pin input ${counter} of ${length}`, - ref: (node: HTMLInputElement | null) => { - if (node) { - refMap?.set(pinIndex, node) - } else { - refMap?.delete(pinIndex) - } - }, - }) - } - skipRef.current = skipRef.current + 1 - return child - }) + const skipRef = React.useRef(0); + let counter = 0; + const clones = validChildren.map((child) => { + if (child.type === PinInputField) { + const pinIndex = counter; + counter = counter + 1; + return React.cloneElement(child, { + name, + inputKey: `input-${pinIndex}`, + value: length > pinIndex ? pins[pinIndex] : "", + onChange: (e: React.ChangeEvent) => + handlers.handleChange(e, pinIndex), + onFocus: (e: React.FocusEvent) => + handlers.handleFocus(e, pinIndex), + onBlur: () => handlers.handleBlur(pinIndex), + onKeyDown: (e: React.KeyboardEvent) => + handlers.handleKeyDown(e, pinIndex), + onPaste: (e: React.ClipboardEvent) => + handlers.handlePaste(e), + placeholder: placeholder, + type: type, + mask: mask, + autoComplete: otp ? "one-time-code" : "off", + disabled: disabled, + readOnly: readOnly, + "aria-label": ariaLabel + ? ariaLabel + : `Pin input ${counter} of ${length}`, + ref: (node: HTMLInputElement | null) => { + if (node) { + refMap?.set(pinIndex, node); + } else { + refMap?.delete(pinIndex); + } + } + }); + } + skipRef.current = skipRef.current + 1; + return child; + }); - return ( - -
- {clones} - -
-
- ) -} -PinInput.displayName = 'PinInput' + return ( + +
+ {clones} + +
+
+ ); +}; +PinInput.displayName = "PinInput"; /* ========== PinInputField ========== */ interface _PinInputFieldProps { - mask: boolean - inputKey: string - type: 'numeric' | 'alphanumeric' + mask: boolean; + inputKey: string; + type: "numeric" | "alphanumeric"; } interface PinInputFieldProps - extends Omit< - React.ComponentPropsWithoutRef<'input'>, - keyof _PinInputFieldProps - > { - component?: T + extends Omit< + React.ComponentPropsWithoutRef<"input">, + keyof _PinInputFieldProps + > { + component?: T; } -const PinInputField = ({ - className, - component, - ...props +const PinInputField = ({ + className, + component, + ...props }: PinInputFieldProps & - (React.ComponentType extends undefined - ? never - : React.ComponentProps)) => { - const { mask, type, inputKey, ...rest } = props as _PinInputFieldProps & - React.ComponentProps + (React.ComponentType extends undefined + ? never + : React.ComponentProps)) => { + const { mask, type, inputKey, ...rest } = props as _PinInputFieldProps & + React.ComponentProps; - // Check if PinInputField is used within PinInput - const isInsidePinInput = React.useContext(PinInputContext) - if (!isInsidePinInput) { - throw new Error( - `PinInputField must be used within ${PinInput.displayName}.` - ) - } + // Check if PinInputField is used within PinInput + const isInsidePinInput = React.useContext(PinInputContext); + if (!isInsidePinInput) { + throw new Error( + `PinInputField must be used within ${PinInput.displayName}.` + ); + } - const Element = component || 'input' + const Element = component || "input"; - return ( - - ) -} + return ( + + ); +}; /* ========== usePinInput custom hook ========== */ interface UsePinInputProps { - value: string | undefined - defaultValue: string | undefined - placeholder: string - type: 'numeric' | 'alphanumeric' - length: number - readOnly: boolean + value: string | undefined; + defaultValue: string | undefined; + placeholder: string; + type: "numeric" | "alphanumeric"; + length: number; + readOnly: boolean; } const usePinInput = ({ - value, - defaultValue, - placeholder, - type, - length, - readOnly, + value, + defaultValue, + placeholder, + type, + length, + readOnly }: UsePinInputProps) => { - const pinInputs = React.useMemo( - () => - Array.from({ length }, (_, index) => - defaultValue - ? defaultValue.charAt(index) - : value - ? value.charAt(index) - : '' - ), - [defaultValue, length, value] - ) + const pinInputs = React.useMemo( + () => + Array.from({ length }, (_, index) => + defaultValue + ? defaultValue.charAt(index) + : value + ? value.charAt(index) + : "" + ), + [defaultValue, length, value] + ); - const [pins, setPins] = React.useState(pinInputs) - const pinValue = pins.join('').trim() + const [pins, setPins] = React.useState(pinInputs); + const pinValue = pins.join("").trim(); - /** - * Update pins when values change programmatically. - * This syncs the pins if the `defaultValue` or `value` prop is updated. - */ - React.useEffect(() => { - setPins(pinInputs) - }, [pinInputs]) + /** + * Update pins when values change programmatically. + * This syncs the pins if the `defaultValue` or `value` prop is updated. + */ + React.useEffect(() => { + setPins(pinInputs); + }, [pinInputs]); - const itemsRef = React.useRef | null>(null) + const itemsRef = React.useRef | null>(null); - function getMap() { - if (!itemsRef.current) { - // Initialize the Map on first usage. - itemsRef.current = new Map() - } - return itemsRef.current - } + function getMap() { + if (!itemsRef.current) { + // Initialize the Map on first usage. + itemsRef.current = new Map(); + } + return itemsRef.current; + } - function getNode(index: number) { - const map = getMap() - const node = map?.get(index) - return node - } + function getNode(index: number) { + const map = getMap(); + const node = map?.get(index); + return node; + } - function focusInput(itemId: number) { - const node = getNode(itemId) - if (node) { - node.focus() - node.placeholder = '' - } - } + function focusInput(itemId: number) { + const node = getNode(itemId); + if (node) { + node.focus(); + node.placeholder = ""; + } + } - function handleFocus( - event: React.FocusEvent, - index: number - ) { - event.target.select() - focusInput(index) - } + function handleFocus( + event: React.FocusEvent, + index: number + ) { + event.target.select(); + focusInput(index); + } - function handleBlur(index: number) { - const node = getNode(index) - if (node) { - node.placeholder = placeholder - } - } + function handleBlur(index: number) { + const node = getNode(index); + if (node) { + node.placeholder = placeholder; + } + } - function updateInputField(val: string, index: number) { - const node = getNode(index) + function updateInputField(val: string, index: number) { + const node = getNode(index); - if (node) { - node.value = val - } + if (node) { + node.value = val; + } - setPins((prev) => - prev.map((p, i) => { - if (i === index) { - return val - } else { - return p - } - }) - ) - } + setPins((prev) => + prev.map((p, i) => { + if (i === index) { + return val; + } else { + return p; + } + }) + ); + } - function validate(value: string) { - const NUMERIC_REGEX = /^[0-9]+$/ - const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i - const regex = type === 'alphanumeric' ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX - return regex.test(value) - } + function validate(value: string) { + const NUMERIC_REGEX = /^[0-9]+$/; + const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i; + const regex = type === "alphanumeric" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX; + return regex.test(value); + } - const pastedVal = React.useRef(null) - function handleChange(e: React.ChangeEvent, index: number) { - const inputValue = e.target.value - const pastedValue = pastedVal.current - const inputChar = - pastedValue && pastedValue.length === length - ? pastedValue.charAt(length - 1) - : inputValue.slice(-1) + const pastedVal = React.useRef(null); + function handleChange(e: React.ChangeEvent, index: number) { + const inputValue = e.target.value; + const pastedValue = pastedVal.current; + const inputChar = + pastedValue && pastedValue.length === length + ? pastedValue.charAt(length - 1) + : inputValue.slice(-1); - if (validate(inputChar)) { - updateInputField(inputChar, index) - pastedVal.current = null - if (inputValue.length > 0) { - focusInput(index + 1) - } - } - } + if (validate(inputChar)) { + updateInputField(inputChar, index); + pastedVal.current = null; + if (inputValue.length > 0) { + focusInput(index + 1); + } + } + } - function handlePaste(event: React.ClipboardEvent) { - event.preventDefault() - const copyValue = event.clipboardData - .getData('text/plain') - .replace(/[\n\r\s]+/g, '') - const copyArr = copyValue.split('').slice(0, length) + function handlePaste(event: React.ClipboardEvent) { + event.preventDefault(); + const copyValue = event.clipboardData + .getData("text/plain") + .replace(/[\n\r\s]+/g, ""); + const copyArr = copyValue.split("").slice(0, length); - const isValid = copyArr.every((c) => validate(c)) + const isValid = copyArr.every((c) => validate(c)); - if (!isValid) return + if (!isValid) return; - for (let i = 0; i < length; i++) { - if (i < copyArr.length) { - updateInputField(copyArr[i], i) - } - } + for (let i = 0; i < length; i++) { + if (i < copyArr.length) { + updateInputField(copyArr[i], i); + } + } - pastedVal.current = copyValue - focusInput(copyArr.length < length ? copyArr.length : length - 1) - } + pastedVal.current = copyValue; + focusInput(copyArr.length < length ? copyArr.length : length - 1); + } - function handleKeyDown( - event: React.KeyboardEvent, - index: number - ) { - const { ctrlKey, key, shiftKey, metaKey } = event + function handleKeyDown( + event: React.KeyboardEvent, + index: number + ) { + const { ctrlKey, key, shiftKey, metaKey } = event; - if (type === 'numeric') { - const canTypeSign = - key === 'Backspace' || - key === 'Tab' || - key === 'Control' || - key === 'Delete' || - (ctrlKey && key === 'v') || - (metaKey && key === 'v') - ? true - : !Number.isNaN(Number(key)) + if (type === "numeric") { + const canTypeSign = + key === "Backspace" || + key === "Tab" || + key === "Control" || + key === "Delete" || + (ctrlKey && key === "v") || + (metaKey && key === "v") + ? true + : !Number.isNaN(Number(key)); - if (!canTypeSign || readOnly) { - event.preventDefault() - } - } + if (!canTypeSign || readOnly) { + event.preventDefault(); + } + } - if (key === 'ArrowLeft' || (shiftKey && key === 'Tab')) { - event.preventDefault() - focusInput(index - 1) - } else if (key === 'ArrowRight' || key === 'Tab' || key === ' ') { - event.preventDefault() - focusInput(index + 1) - } else if (key === 'Delete') { - event.preventDefault() - } else if (key === 'Backspace') { - event.preventDefault() - updateInputField('', index) - if ((event.target as HTMLInputElement).value === '') { - focusInput(index - 1) - } - } - } + if (key === "ArrowLeft" || (shiftKey && key === "Tab")) { + event.preventDefault(); + focusInput(index - 1); + } else if (key === "ArrowRight" || key === "Tab" || key === " ") { + event.preventDefault(); + focusInput(index + 1); + } else if (key === "Delete") { + event.preventDefault(); + } else if (key === "Backspace") { + event.preventDefault(); + updateInputField("", index); + if ((event.target as HTMLInputElement).value === "") { + focusInput(index - 1); + } + } + } - return { - pins, - pinValue, - refMap: getMap(), - handleFocus, - handleBlur, - handleChange, - handlePaste, - handleKeyDown, - } -} + return { + pins, + pinValue, + refMap: getMap(), + handleFocus, + handleBlur, + handleChange, + handlePaste, + handleKeyDown + }; +}; /* ========== Util Func ========== */ const getValidChildren = (children: React.ReactNode) => - React.Children.toArray(children).filter((child) => { - if (React.isValidElement(child)) { - return React.isValidElement(child) - } - throw new Error(`${PinInput.displayName} contains invalid children.`) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }) as React.ReactElement[] + React.Children.toArray(children).filter((child) => { + if (React.isValidElement(child)) { + return React.isValidElement(child); + } + throw new Error(`${PinInput.displayName} contains invalid children.`); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as React.ReactElement[]; const getInputFieldCount = (children: React.ReactNode) => - React.Children.toArray(children).filter((child) => { - if (React.isValidElement(child) && child.type === PinInputField) { - return React.isValidElement(child) - } - }).length + React.Children.toArray(children).filter((child) => { + if (React.isValidElement(child) && child.type === PinInputField) { + return React.isValidElement(child); + } + }).length; -export { PinInput, PinInputField } +export { PinInput, PinInputField }; diff --git a/frontend/src/components/profile-dropdown.tsx b/frontend/src/components/profile-dropdown.tsx index 8686bbf..c3e4012 100644 --- a/frontend/src/components/profile-dropdown.tsx +++ b/frontend/src/components/profile-dropdown.tsx @@ -1,65 +1,65 @@ -import { Link } from '@tanstack/react-router' -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import { Button } from '@/components/ui/button' +import { Link } from "@tanstack/react-router"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; export function ProfileDropdown() { - return ( - - - - - - -
-

satnaing

-

- satnaingdev@gmail.com -

-
-
- - - - - Profile - ⇧⌘P - - - - - Billing - ⌘B - - - - - Settings - ⌘S - - - New Team - - - - Log out - ⇧⌘Q - -
-
- ) + return ( + + + + + + +
+

satnaing

+

+ satnaingdev@gmail.com +

+
+
+ + + + + Profile + ⇧⌘P + + + + + Billing + ⌘B + + + + + Settings + ⌘S + + + New Team + + + + Log out + ⇧⌘Q + +
+
+ ); } diff --git a/frontend/src/components/search.tsx b/frontend/src/components/search.tsx index e3a32ef..6bec1e9 100644 --- a/frontend/src/components/search.tsx +++ b/frontend/src/components/search.tsx @@ -1,33 +1,32 @@ -import { IconSearch } from '@tabler/icons-react' -import { cn } from '@/lib/utils' -import { useSearch } from '@/context/search-context' -import { Button } from './ui/button' +import { IconSearch } from "@tabler/icons-react"; +import { cn } from "@/lib/utils"; +import { useSearch } from "@/context/search-context"; +import { Button } from "./ui/button"; interface Props { - className?: string - type?: React.HTMLInputTypeAttribute - placeholder?: string + className?: string; + type?: React.HTMLInputTypeAttribute; + placeholder?: string; } -export function Search({ className = '', placeholder = 'Search' }: Props) { - const { setOpen } = useSearch() - return ( - - ) +export function Search({ className = "", placeholder = "Search" }: Props) { + const { setOpen } = useSearch(); + return ( + + ); } diff --git a/frontend/src/components/select-dropdown.tsx b/frontend/src/components/select-dropdown.tsx index 1a4dbc3..53d7e82 100644 --- a/frontend/src/components/select-dropdown.tsx +++ b/frontend/src/components/select-dropdown.tsx @@ -1,62 +1,62 @@ -import { IconLoader } from '@tabler/icons-react' -import { cn } from '@/lib/utils' -import { FormControl } from '@/components/ui/form' +import { IconLoader } from "@tabler/icons-react"; +import { cn } from "@/lib/utils"; +import { FormControl } from "@/components/ui/form"; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@/components/ui/select"; interface SelectDropdownProps { - onValueChange?: (value: string) => void - defaultValue: string | undefined - placeholder?: string - isPending?: boolean - items: { label: string; value: string }[] | undefined - disabled?: boolean - className?: string - isControlled?: boolean + onValueChange?: (value: string) => void; + defaultValue: string | undefined; + placeholder?: string; + isPending?: boolean; + items: { label: string; value: string }[] | undefined; + disabled?: boolean; + className?: string; + isControlled?: boolean; } export function SelectDropdown({ - defaultValue, - onValueChange, - isPending, - items, - placeholder, - disabled, - className = '', - isControlled = false, + defaultValue, + onValueChange, + isPending, + items, + placeholder, + disabled, + className = "", + isControlled = false }: SelectDropdownProps) { - const defaultState = isControlled - ? { value: defaultValue, onValueChange } - : { defaultValue, onValueChange } - return ( - - ) + const defaultState = isControlled + ? { value: defaultValue, onValueChange } + : { defaultValue, onValueChange }; + return ( + + ); } diff --git a/frontend/src/components/skip-to-main.tsx b/frontend/src/components/skip-to-main.tsx index bb1f840..22ba65c 100644 --- a/frontend/src/components/skip-to-main.tsx +++ b/frontend/src/components/skip-to-main.tsx @@ -1,12 +1,11 @@ const SkipToMain = () => { - return ( - - Skip to Main - - ) -} + return ( + + Skip to Main + + ); +}; -export default SkipToMain +export default SkipToMain; diff --git a/frontend/src/components/team-switcher.tsx b/frontend/src/components/team-switcher.tsx index 2808e0a..65be2f2 100644 --- a/frontend/src/components/team-switcher.tsx +++ b/frontend/src/components/team-switcher.tsx @@ -1,89 +1,86 @@ -"use client" +"use client"; -import * as React from "react" -import { ChevronsUpDown, Plus } from "lucide-react" +import * as React from "react"; +import { ChevronsUpDown, Plus } from "lucide-react"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar"; export function TeamSwitcher({ - teams, + teams }: { - teams: { - name: string - logo: React.ElementType - plan: string - }[] + teams: { + name: string; + logo: React.ElementType; + plan: string; + }[]; }) { - const { isMobile } = useSidebar() - const [activeTeam, setActiveTeam] = React.useState(teams[0]) + const { isMobile } = useSidebar(); + const [activeTeam, setActiveTeam] = React.useState(teams[0]); - return ( - - - - - -
- -
-
- - {activeTeam.name} - - {activeTeam.plan} -
- -
-
- - - Teams - - {teams.map((team, index) => ( - setActiveTeam(team)} - className="gap-2 p-2" - > -
- -
- {team.name} - ⌘{index + 1} -
- ))} - - -
- -
-
Add team
-
-
-
-
-
- ) + return ( + + + + + +
+ +
+
+ + {activeTeam.name} + + {activeTeam.plan} +
+ +
+
+ + + Teams + + {teams.map((team, index) => ( + setActiveTeam(team)} + className="gap-2 p-2"> +
+ +
+ {team.name} + ⌘{index + 1} +
+ ))} + + +
+ +
+
Add team
+
+
+
+
+
+ ); } diff --git a/frontend/src/components/theme-switch.tsx b/frontend/src/components/theme-switch.tsx index c6d3fda..578f201 100644 --- a/frontend/src/components/theme-switch.tsx +++ b/frontend/src/components/theme-switch.tsx @@ -1,58 +1,58 @@ -import { useEffect } from 'react' -import { IconCheck, IconMoon, IconSun } from '@tabler/icons-react' -import { cn } from '@/lib/utils' -import { useTheme } from '@/context/theme-context' -import { Button } from '@/components/ui/button' +import { useEffect } from "react"; +import { IconCheck, IconMoon, IconSun } from "@tabler/icons-react"; +import { cn } from "@/lib/utils"; +import { useTheme } from "@/context/theme-context"; +import { Button } from "@/components/ui/button"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; export function ThemeSwitch() { - const { theme, setTheme } = useTheme() + const { theme, setTheme } = useTheme(); - /* Update theme-color meta tag - * when theme is updated */ - useEffect(() => { - const themeColor = theme === 'dark' ? '#020817' : '#fff' - const metaThemeColor = document.querySelector("meta[name='theme-color']") - if (metaThemeColor) metaThemeColor.setAttribute('content', themeColor) - }, [theme]) + /* Update theme-color meta tag + * when theme is updated */ + useEffect(() => { + const themeColor = theme === "dark" ? "#020817" : "#fff"; + const metaThemeColor = document.querySelector("meta[name='theme-color']"); + if (metaThemeColor) metaThemeColor.setAttribute("content", themeColor); + }, [theme]); - return ( - - - - - - setTheme('light')}> - Light{' '} - - - setTheme('dark')}> - Dark - - - setTheme('system')}> - System - - - - - ) + return ( + + + + + + setTheme("light")}> + Light{" "} + + + setTheme("dark")}> + Dark + + + setTheme("system")}> + System + + + + + ); } diff --git a/frontend/src/components/ui/alert-dialog.tsx b/frontend/src/components/ui/alert-dialog.tsx index 515bcff..96bbd76 100644 --- a/frontend/src/components/ui/alert-dialog.tsx +++ b/frontend/src/components/ui/alert-dialog.tsx @@ -1,138 +1,138 @@ -import * as React from 'react' -import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' -import { cn } from '@/lib/utils' -import { buttonVariants } from '@/components/ui/button' +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; -const AlertDialog = AlertDialogPrimitive.Root +const AlertDialog = AlertDialogPrimitive.Root; -const AlertDialogTrigger = AlertDialogPrimitive.Trigger +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; -const AlertDialogPortal = AlertDialogPrimitive.Portal +const AlertDialogPortal = AlertDialogPrimitive.Portal; const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - - -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; const AlertDialogHeader = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = 'AlertDialogHeader' +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; const AlertDialogFooter = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
-) -AlertDialogFooter.displayName = 'AlertDialogFooter' +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) + +)); AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName + AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { - AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, -} + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel +}; diff --git a/frontend/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx index d0a903d..3c731cb 100644 --- a/frontend/src/components/ui/alert.tsx +++ b/frontend/src/components/ui/alert.tsx @@ -1,58 +1,58 @@ -import * as React from 'react' -import { cva, type VariantProps } from 'class-variance-authority' -import { cn } from '@/lib/utils' +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; const alertVariants = cva( - 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7', - { - variants: { - variant: { - default: 'bg-background text-foreground', - destructive: - 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', - }, - }, - defaultVariants: { - variant: 'default', - }, - } -) + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive" + } + }, + defaultVariants: { + variant: "default" + } + } +); const Alert = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & VariantProps + HTMLDivElement, + React.HTMLAttributes & VariantProps >(({ className, variant, ...props }, ref) => ( -
-)) -Alert.displayName = 'Alert' +
+)); +Alert.displayName = "Alert"; const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
-)) -AlertTitle.displayName = 'AlertTitle' +
+)); +AlertTitle.displayName = "AlertTitle"; const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
-)) -AlertDescription.displayName = 'AlertDescription' +
+)); +AlertDescription.displayName = "AlertDescription"; -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertTitle, AlertDescription }; diff --git a/frontend/src/components/ui/avatar.tsx b/frontend/src/components/ui/avatar.tsx index 10129f5..b7f9284 100644 --- a/frontend/src/components/ui/avatar.tsx +++ b/frontend/src/components/ui/avatar.tsx @@ -1,47 +1,47 @@ -import * as React from 'react' -import * as AvatarPrimitive from '@radix-ui/react-avatar' -import { cn } from '@/lib/utils' +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import { cn } from "@/lib/utils"; const Avatar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -Avatar.displayName = AvatarPrimitive.Root.displayName + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx index ad7a83e..9236c57 100644 --- a/frontend/src/components/ui/badge.tsx +++ b/frontend/src/components/ui/badge.tsx @@ -1,35 +1,35 @@ -import * as React from 'react' -import { cva, type VariantProps } from 'class-variance-authority' -import { cn } from '@/lib/utils' +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; const badgeVariants = cva( - 'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', - { - variants: { - variant: { - default: - 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80', - secondary: - 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', - destructive: - 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80', - outline: 'text-foreground', - }, - }, - defaultVariants: { - variant: 'default', - }, - } -) + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground" + } + }, + defaultVariants: { + variant: "default" + } + } +); export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} + extends React.HTMLAttributes, + VariantProps {} function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) + return ( +
+ ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/frontend/src/components/ui/breadcrumb.tsx b/frontend/src/components/ui/breadcrumb.tsx index 60e6c96..c7a2ff3 100644 --- a/frontend/src/components/ui/breadcrumb.tsx +++ b/frontend/src/components/ui/breadcrumb.tsx @@ -1,115 +1,113 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Breadcrumb = React.forwardRef< - HTMLElement, - React.ComponentPropsWithoutRef<"nav"> & { - separator?: React.ReactNode - } ->(({ ...props }, ref) =>