create-bluecopa-react-app 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +47 -10
  2. package/bin/create-bluecopa-react-app.js +257 -51
  3. package/package.json +6 -5
  4. package/templates/latest/Agent.md +254 -0
  5. package/templates/latest/Dockerfile +22 -0
  6. package/templates/latest/README.md +157 -221
  7. package/templates/latest/app/app.css +134 -0
  8. package/templates/latest/app/app.tsx +46 -0
  9. package/templates/latest/app/components/app-sidebar.tsx +174 -0
  10. package/templates/latest/app/components/chart-area-interactive.tsx +290 -0
  11. package/templates/latest/app/components/data-table.tsx +807 -0
  12. package/templates/latest/app/components/nav-documents.tsx +92 -0
  13. package/templates/latest/app/components/nav-main.tsx +56 -0
  14. package/templates/latest/app/components/nav-secondary.tsx +42 -0
  15. package/templates/latest/app/components/nav-user.tsx +112 -0
  16. package/templates/latest/app/components/section-cards.tsx +102 -0
  17. package/templates/latest/app/components/site-header.tsx +19 -0
  18. package/templates/latest/app/components/ui/avatar.tsx +53 -0
  19. package/templates/latest/app/components/ui/badge.tsx +46 -0
  20. package/templates/latest/app/components/ui/breadcrumb.tsx +109 -0
  21. package/templates/latest/app/components/ui/button.tsx +58 -0
  22. package/templates/latest/app/components/ui/card.tsx +92 -0
  23. package/templates/latest/app/components/ui/chart.tsx +352 -0
  24. package/templates/latest/app/components/ui/checkbox.tsx +30 -0
  25. package/templates/latest/app/components/ui/drawer.tsx +139 -0
  26. package/templates/latest/app/components/ui/dropdown-menu.tsx +258 -0
  27. package/templates/latest/app/components/ui/input.tsx +21 -0
  28. package/templates/latest/app/components/ui/label.tsx +24 -0
  29. package/templates/latest/app/components/ui/select.tsx +183 -0
  30. package/templates/latest/app/components/ui/separator.tsx +26 -0
  31. package/templates/latest/app/components/ui/sheet.tsx +139 -0
  32. package/templates/latest/app/components/ui/sidebar.tsx +731 -0
  33. package/templates/latest/app/components/ui/skeleton.tsx +13 -0
  34. package/templates/latest/app/components/ui/sonner.tsx +23 -0
  35. package/templates/latest/app/components/ui/table.tsx +117 -0
  36. package/templates/latest/app/components/ui/tabs.tsx +66 -0
  37. package/templates/latest/app/components/ui/toggle-group.tsx +73 -0
  38. package/templates/latest/app/components/ui/toggle.tsx +47 -0
  39. package/templates/latest/app/components/ui/tooltip.tsx +59 -0
  40. package/templates/latest/app/dashboard/data.json +614 -0
  41. package/templates/latest/app/hooks/use-bluecopa-user.ts +37 -0
  42. package/templates/latest/app/hooks/use-mobile.ts +19 -0
  43. package/templates/latest/{src → app}/lib/utils.ts +1 -1
  44. package/templates/latest/app/main.tsx +12 -0
  45. package/templates/latest/app/routes/home.tsx +40 -0
  46. package/templates/latest/app/routes.tsx +15 -0
  47. package/templates/latest/{src → app}/single-spa.tsx +38 -28
  48. package/templates/latest/components.json +22 -0
  49. package/templates/latest/dist/assets/__federation_expose_App-DRwKKpS2.js +91 -0
  50. package/templates/latest/dist/assets/__federation_fn_import-CzfA7kmP.js +438 -0
  51. package/templates/latest/dist/assets/__federation_shared_react-Bp6HVBS4.js +16 -0
  52. package/templates/latest/dist/assets/__federation_shared_react-dom-BCcRGiYp.js +17 -0
  53. package/templates/latest/dist/assets/client-DgSav55y.js +12658 -0
  54. package/templates/latest/dist/assets/home-DOL6GrYV.js +54951 -0
  55. package/templates/latest/dist/assets/index-BzNimew1.js +69 -0
  56. package/templates/latest/dist/assets/index-DMFtQdNS.js +412 -0
  57. package/templates/latest/dist/assets/index-DdYpcDMk.js +60 -0
  58. package/templates/latest/dist/assets/remoteEntry.js +88 -0
  59. package/templates/latest/dist/assets/style-36A39bNN.css +3683 -0
  60. package/templates/latest/dist/avatars/shadcn.svg +6 -0
  61. package/templates/latest/dist/favicon.ico +0 -0
  62. package/templates/latest/dist/index.html +19 -0
  63. package/templates/latest/index.html +1 -1
  64. package/templates/latest/package-lock.json +1227 -3353
  65. package/templates/latest/package.json +47 -43
  66. package/templates/latest/pnpm-lock.yaml +4767 -0
  67. package/templates/latest/preview/index.html +32 -2
  68. package/templates/latest/public/avatars/shadcn.svg +6 -0
  69. package/templates/latest/public/favicon.ico +0 -0
  70. package/templates/latest/tsconfig.json +18 -11
  71. package/templates/latest/vite.config.ts +41 -41
  72. package/templates/latest/.env.example +0 -14
  73. package/templates/latest/.eslintrc.cjs +0 -42
  74. package/templates/latest/AGENT.md +0 -284
  75. package/templates/latest/clean.sh +0 -39
  76. package/templates/latest/postcss.config.cjs +0 -6
  77. package/templates/latest/public/bluecopa-logo.svg +0 -30
  78. package/templates/latest/public/favicon-32x32.png +0 -0
  79. package/templates/latest/public/favicon-96x96.png +0 -0
  80. package/templates/latest/setup.sh +0 -55
  81. package/templates/latest/src/App.tsx +0 -15
  82. package/templates/latest/src/components/layout/dashboard-header.tsx +0 -139
  83. package/templates/latest/src/components/layout/dashboard-layout.tsx +0 -29
  84. package/templates/latest/src/components/layout/sidebar.tsx +0 -54
  85. package/templates/latest/src/components/page/dashboard.tsx +0 -1506
  86. package/templates/latest/src/components/page/navbar.tsx +0 -104
  87. package/templates/latest/src/components/tables/data-grid.tsx +0 -439
  88. package/templates/latest/src/components/ui/alert.tsx +0 -59
  89. package/templates/latest/src/components/ui/avatar.tsx +0 -50
  90. package/templates/latest/src/components/ui/badge.tsx +0 -36
  91. package/templates/latest/src/components/ui/bluecopa-logo.tsx +0 -54
  92. package/templates/latest/src/components/ui/button.tsx +0 -58
  93. package/templates/latest/src/components/ui/card.tsx +0 -79
  94. package/templates/latest/src/components/ui/dropdown-menu.tsx +0 -200
  95. package/templates/latest/src/components/ui/input.tsx +0 -24
  96. package/templates/latest/src/components/ui/label.tsx +0 -23
  97. package/templates/latest/src/components/ui/select.tsx +0 -29
  98. package/templates/latest/src/hooks/use-api.ts +0 -55
  99. package/templates/latest/src/index.css +0 -59
  100. package/templates/latest/src/main.tsx +0 -13
  101. package/templates/latest/src/pages/Dashboard.tsx +0 -13
  102. package/templates/latest/src/pages/Home.tsx +0 -622
  103. package/templates/latest/src/providers/query-provider.tsx +0 -48
  104. package/templates/latest/src/types/api.ts +0 -78
  105. package/templates/latest/src/vite-env.d.ts +0 -11
  106. package/templates/latest/tailwind.config.js +0 -88
  107. package/templates/latest/tsconfig.app.json +0 -26
  108. package/templates/latest/tsconfig.node.json +0 -10
@@ -0,0 +1,92 @@
1
+ "use client"
2
+
3
+ import {
4
+ IconDots,
5
+ IconFolder,
6
+ IconShare3,
7
+ IconTrash,
8
+ type Icon,
9
+ } from "@tabler/icons-react"
10
+
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuItem,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger,
17
+ } from "~/components/ui/dropdown-menu"
18
+ import {
19
+ SidebarGroup,
20
+ SidebarGroupLabel,
21
+ SidebarMenu,
22
+ SidebarMenuAction,
23
+ SidebarMenuButton,
24
+ SidebarMenuItem,
25
+ useSidebar,
26
+ } from "~/components/ui/sidebar"
27
+
28
+ export function NavDocuments({
29
+ items,
30
+ }: {
31
+ items: {
32
+ name: string
33
+ url: string
34
+ icon: Icon
35
+ }[]
36
+ }) {
37
+ const { isMobile } = useSidebar()
38
+
39
+ return (
40
+ <SidebarGroup className="group-data-[collapsible=icon]:hidden">
41
+ <SidebarGroupLabel>Documents</SidebarGroupLabel>
42
+ <SidebarMenu>
43
+ {items.map((item) => (
44
+ <SidebarMenuItem key={item.name}>
45
+ <SidebarMenuButton asChild>
46
+ <a href={item.url}>
47
+ <item.icon />
48
+ <span>{item.name}</span>
49
+ </a>
50
+ </SidebarMenuButton>
51
+ <DropdownMenu>
52
+ <DropdownMenuTrigger asChild>
53
+ <SidebarMenuAction
54
+ showOnHover
55
+ className="data-[state=open]:bg-accent rounded-sm"
56
+ >
57
+ <IconDots />
58
+ <span className="sr-only">More</span>
59
+ </SidebarMenuAction>
60
+ </DropdownMenuTrigger>
61
+ <DropdownMenuContent
62
+ className="w-24 rounded-lg"
63
+ side={isMobile ? "bottom" : "right"}
64
+ align={isMobile ? "end" : "start"}
65
+ >
66
+ <DropdownMenuItem>
67
+ <IconFolder />
68
+ <span>Open</span>
69
+ </DropdownMenuItem>
70
+ <DropdownMenuItem>
71
+ <IconShare3 />
72
+ <span>Share</span>
73
+ </DropdownMenuItem>
74
+ <DropdownMenuSeparator />
75
+ <DropdownMenuItem variant="destructive">
76
+ <IconTrash />
77
+ <span>Delete</span>
78
+ </DropdownMenuItem>
79
+ </DropdownMenuContent>
80
+ </DropdownMenu>
81
+ </SidebarMenuItem>
82
+ ))}
83
+ <SidebarMenuItem>
84
+ <SidebarMenuButton className="text-sidebar-foreground/70">
85
+ <IconDots className="text-sidebar-foreground/70" />
86
+ <span>More</span>
87
+ </SidebarMenuButton>
88
+ </SidebarMenuItem>
89
+ </SidebarMenu>
90
+ </SidebarGroup>
91
+ )
92
+ }
@@ -0,0 +1,56 @@
1
+ import { IconCirclePlusFilled, IconMail, type Icon } from "@tabler/icons-react"
2
+
3
+ import { Button } from "~/components/ui/button"
4
+ import {
5
+ SidebarGroup,
6
+ SidebarGroupContent,
7
+ SidebarMenu,
8
+ SidebarMenuButton,
9
+ SidebarMenuItem,
10
+ } from "~/components/ui/sidebar"
11
+
12
+ export function NavMain({
13
+ items,
14
+ }: {
15
+ items: {
16
+ title: string
17
+ url: string
18
+ icon?: Icon
19
+ }[]
20
+ }) {
21
+ return (
22
+ <SidebarGroup>
23
+ <SidebarGroupContent className="flex flex-col gap-2">
24
+ <SidebarMenu>
25
+ <SidebarMenuItem className="flex items-center gap-2">
26
+ <SidebarMenuButton
27
+ tooltip="Quick Create"
28
+ className="bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground min-w-8 duration-200 ease-linear"
29
+ >
30
+ <IconCirclePlusFilled />
31
+ <span>Quick Create</span>
32
+ </SidebarMenuButton>
33
+ <Button
34
+ size="icon"
35
+ className="size-8 group-data-[collapsible=icon]:opacity-0"
36
+ variant="outline"
37
+ >
38
+ <IconMail />
39
+ <span className="sr-only">Inbox</span>
40
+ </Button>
41
+ </SidebarMenuItem>
42
+ </SidebarMenu>
43
+ <SidebarMenu>
44
+ {items.map((item) => (
45
+ <SidebarMenuItem key={item.title}>
46
+ <SidebarMenuButton tooltip={item.title}>
47
+ {item.icon && <item.icon />}
48
+ <span>{item.title}</span>
49
+ </SidebarMenuButton>
50
+ </SidebarMenuItem>
51
+ ))}
52
+ </SidebarMenu>
53
+ </SidebarGroupContent>
54
+ </SidebarGroup>
55
+ )
56
+ }
@@ -0,0 +1,42 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { type Icon } from "@tabler/icons-react"
5
+
6
+ import {
7
+ SidebarGroup,
8
+ SidebarGroupContent,
9
+ SidebarMenu,
10
+ SidebarMenuButton,
11
+ SidebarMenuItem,
12
+ } from "~/components/ui/sidebar"
13
+
14
+ export function NavSecondary({
15
+ items,
16
+ ...props
17
+ }: {
18
+ items: {
19
+ title: string
20
+ url: string
21
+ icon: Icon
22
+ }[]
23
+ } & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
24
+ return (
25
+ <SidebarGroup {...props}>
26
+ <SidebarGroupContent>
27
+ <SidebarMenu>
28
+ {items.map((item) => (
29
+ <SidebarMenuItem key={item.title}>
30
+ <SidebarMenuButton asChild>
31
+ <a href={item.url}>
32
+ <item.icon />
33
+ <span>{item.title}</span>
34
+ </a>
35
+ </SidebarMenuButton>
36
+ </SidebarMenuItem>
37
+ ))}
38
+ </SidebarMenu>
39
+ </SidebarGroupContent>
40
+ </SidebarGroup>
41
+ )
42
+ }
@@ -0,0 +1,112 @@
1
+ import {
2
+ IconCreditCard,
3
+ IconDotsVertical,
4
+ IconLogout,
5
+ IconNotification,
6
+ IconUserCircle,
7
+ } from "@tabler/icons-react"
8
+
9
+ import {
10
+ Avatar,
11
+ AvatarFallback,
12
+ AvatarImage,
13
+ } from "~/components/ui/avatar"
14
+ import {
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuGroup,
18
+ DropdownMenuItem,
19
+ DropdownMenuLabel,
20
+ DropdownMenuSeparator,
21
+ DropdownMenuTrigger,
22
+ } from "~/components/ui/dropdown-menu"
23
+ import {
24
+ SidebarMenu,
25
+ SidebarMenuButton,
26
+ SidebarMenuItem,
27
+ useSidebar,
28
+ } from "~/components/ui/sidebar"
29
+ import { useBluecopaUser } from "~/hooks/use-bluecopa-user"
30
+
31
+ export function NavUser() {
32
+ const { isMobile } = useSidebar()
33
+ const { user, isLoading } = useBluecopaUser()
34
+ const { firstName, email } = user?.user || {}
35
+ // Show loading state while fetching user data
36
+ if (isLoading) {
37
+ return (
38
+ <SidebarMenu>
39
+ <SidebarMenuItem>
40
+ <SidebarMenuButton size="lg" className="animate-pulse">
41
+ <div className="h-8 w-8 rounded-lg bg-muted" />
42
+ <div className="grid flex-1 text-left text-sm leading-tight">
43
+ <div className="h-4 w-20 bg-muted rounded" />
44
+ <div className="h-3 w-32 bg-muted rounded" />
45
+ </div>
46
+ </SidebarMenuButton>
47
+ </SidebarMenuItem>
48
+ </SidebarMenu>
49
+ )
50
+ }
51
+
52
+ return (
53
+ <SidebarMenu>
54
+ <SidebarMenuItem>
55
+ <DropdownMenu>
56
+ <DropdownMenuTrigger asChild>
57
+ <SidebarMenuButton
58
+ size="lg"
59
+ className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
60
+ >
61
+ <div className="grid flex-1 text-left text-sm leading-tight">
62
+ <span className="truncate font-medium">{firstName}</span>
63
+ <span className="text-muted-foreground truncate text-xs">
64
+ {email}
65
+ </span>
66
+ </div>
67
+ <IconDotsVertical className="ml-auto size-4" />
68
+ </SidebarMenuButton>
69
+ </DropdownMenuTrigger>
70
+ <DropdownMenuContent
71
+ className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
72
+ side={isMobile ? "bottom" : "right"}
73
+ align="end"
74
+ sideOffset={4}
75
+ >
76
+ <DropdownMenuLabel className="p-0 font-normal">
77
+ <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
78
+
79
+ <div className="grid flex-1 text-left text-sm leading-tight">
80
+ <span className="truncate font-medium">{firstName}</span>
81
+ <span className="text-muted-foreground truncate text-xs">
82
+ {email}
83
+ </span>
84
+ </div>
85
+ </div>
86
+ </DropdownMenuLabel>
87
+ <DropdownMenuSeparator />
88
+ <DropdownMenuGroup>
89
+ <DropdownMenuItem>
90
+ <IconUserCircle />
91
+ Account
92
+ </DropdownMenuItem>
93
+ <DropdownMenuItem>
94
+ <IconCreditCard />
95
+ Billing
96
+ </DropdownMenuItem>
97
+ <DropdownMenuItem>
98
+ <IconNotification />
99
+ Notifications
100
+ </DropdownMenuItem>
101
+ </DropdownMenuGroup>
102
+ <DropdownMenuSeparator />
103
+ <DropdownMenuItem>
104
+ <IconLogout />
105
+ Log out
106
+ </DropdownMenuItem>
107
+ </DropdownMenuContent>
108
+ </DropdownMenu>
109
+ </SidebarMenuItem>
110
+ </SidebarMenu>
111
+ )
112
+ }
@@ -0,0 +1,102 @@
1
+ import { IconTrendingDown, IconTrendingUp } from "@tabler/icons-react"
2
+
3
+ import { Badge } from "~/components/ui/badge"
4
+ import {
5
+ Card,
6
+ CardAction,
7
+ CardDescription,
8
+ CardFooter,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "~/components/ui/card"
12
+
13
+ export function SectionCards() {
14
+ return (
15
+ <div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
16
+ <Card className="@container/card">
17
+ <CardHeader>
18
+ <CardDescription>Total Revenue</CardDescription>
19
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
20
+ $1,250.00
21
+ </CardTitle>
22
+ <CardAction>
23
+ <Badge variant="outline">
24
+ <IconTrendingUp />
25
+ +12.5%
26
+ </Badge>
27
+ </CardAction>
28
+ </CardHeader>
29
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
30
+ <div className="line-clamp-1 flex gap-2 font-medium">
31
+ Trending up this month <IconTrendingUp className="size-4" />
32
+ </div>
33
+ <div className="text-muted-foreground">
34
+ Visitors for the last 6 months
35
+ </div>
36
+ </CardFooter>
37
+ </Card>
38
+ <Card className="@container/card">
39
+ <CardHeader>
40
+ <CardDescription>New Customers</CardDescription>
41
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
42
+ 1,234
43
+ </CardTitle>
44
+ <CardAction>
45
+ <Badge variant="outline">
46
+ <IconTrendingDown />
47
+ -20%
48
+ </Badge>
49
+ </CardAction>
50
+ </CardHeader>
51
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
52
+ <div className="line-clamp-1 flex gap-2 font-medium">
53
+ Down 20% this period <IconTrendingDown className="size-4" />
54
+ </div>
55
+ <div className="text-muted-foreground">
56
+ Acquisition needs attention
57
+ </div>
58
+ </CardFooter>
59
+ </Card>
60
+ <Card className="@container/card">
61
+ <CardHeader>
62
+ <CardDescription>Active Accounts</CardDescription>
63
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
64
+ 45,678
65
+ </CardTitle>
66
+ <CardAction>
67
+ <Badge variant="outline">
68
+ <IconTrendingUp />
69
+ +12.5%
70
+ </Badge>
71
+ </CardAction>
72
+ </CardHeader>
73
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
74
+ <div className="line-clamp-1 flex gap-2 font-medium">
75
+ Strong user retention <IconTrendingUp className="size-4" />
76
+ </div>
77
+ <div className="text-muted-foreground">Engagement exceed targets</div>
78
+ </CardFooter>
79
+ </Card>
80
+ <Card className="@container/card">
81
+ <CardHeader>
82
+ <CardDescription>Growth Rate</CardDescription>
83
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
84
+ 4.5%
85
+ </CardTitle>
86
+ <CardAction>
87
+ <Badge variant="outline">
88
+ <IconTrendingUp />
89
+ +4.5%
90
+ </Badge>
91
+ </CardAction>
92
+ </CardHeader>
93
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
94
+ <div className="line-clamp-1 flex gap-2 font-medium">
95
+ Steady performance increase <IconTrendingUp className="size-4" />
96
+ </div>
97
+ <div className="text-muted-foreground">Meets growth projections</div>
98
+ </CardFooter>
99
+ </Card>
100
+ </div>
101
+ )
102
+ }
@@ -0,0 +1,19 @@
1
+ import { Button } from "~/components/ui/button"
2
+ import { Separator } from "~/components/ui/separator"
3
+ import { SidebarTrigger } from "~/components/ui/sidebar"
4
+
5
+ export function SiteHeader() {
6
+ return (
7
+ <header className="flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)">
8
+ <div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
9
+ <SidebarTrigger className="-ml-1" />
10
+ <Separator
11
+ orientation="vertical"
12
+ className="mx-2 data-[orientation=vertical]:h-4"
13
+ />
14
+ <h1 className="text-base font-medium">Documents</h1>
15
+
16
+ </div>
17
+ </header>
18
+ )
19
+ }
@@ -0,0 +1,53 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
5
+
6
+ import { cn } from "~/lib/utils"
7
+
8
+ function Avatar({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
12
+ return (
13
+ <AvatarPrimitive.Root
14
+ data-slot="avatar"
15
+ className={cn(
16
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function AvatarImage({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
28
+ return (
29
+ <AvatarPrimitive.Image
30
+ data-slot="avatar-image"
31
+ className={cn("aspect-square size-full", className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AvatarFallback({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
41
+ return (
42
+ <AvatarPrimitive.Fallback
43
+ data-slot="avatar-fallback"
44
+ className={cn(
45
+ "bg-muted flex size-full items-center justify-center rounded-full",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ export { Avatar, AvatarImage, AvatarFallback }
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "~/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16
+ destructive:
17
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ outline:
19
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ }
26
+ )
27
+
28
+ function Badge({
29
+ className,
30
+ variant,
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35
+ const Comp = asChild ? Slot : "span"
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ className={cn(badgeVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ export { Badge, badgeVariants }
@@ -0,0 +1,109 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { ChevronRight, MoreHorizontal } from "lucide-react"
4
+
5
+ import { cn } from "~/lib/utils"
6
+
7
+ function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
8
+ return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
9
+ }
10
+
11
+ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
12
+ return (
13
+ <ol
14
+ data-slot="breadcrumb-list"
15
+ className={cn(
16
+ "text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
25
+ return (
26
+ <li
27
+ data-slot="breadcrumb-item"
28
+ className={cn("inline-flex items-center gap-1.5", className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function BreadcrumbLink({
35
+ asChild,
36
+ className,
37
+ ...props
38
+ }: React.ComponentProps<"a"> & {
39
+ asChild?: boolean
40
+ }) {
41
+ const Comp = asChild ? Slot : "a"
42
+
43
+ return (
44
+ <Comp
45
+ data-slot="breadcrumb-link"
46
+ className={cn("hover:text-foreground transition-colors", className)}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+
52
+ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
53
+ return (
54
+ <span
55
+ data-slot="breadcrumb-page"
56
+ role="link"
57
+ aria-disabled="true"
58
+ aria-current="page"
59
+ className={cn("text-foreground font-normal", className)}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function BreadcrumbSeparator({
66
+ children,
67
+ className,
68
+ ...props
69
+ }: React.ComponentProps<"li">) {
70
+ return (
71
+ <li
72
+ data-slot="breadcrumb-separator"
73
+ role="presentation"
74
+ aria-hidden="true"
75
+ className={cn("[&>svg]:size-3.5", className)}
76
+ {...props}
77
+ >
78
+ {children ?? <ChevronRight />}
79
+ </li>
80
+ )
81
+ }
82
+
83
+ function BreadcrumbEllipsis({
84
+ className,
85
+ ...props
86
+ }: React.ComponentProps<"span">) {
87
+ return (
88
+ <span
89
+ data-slot="breadcrumb-ellipsis"
90
+ role="presentation"
91
+ aria-hidden="true"
92
+ className={cn("flex size-9 items-center justify-center", className)}
93
+ {...props}
94
+ >
95
+ <MoreHorizontal className="size-4" />
96
+ <span className="sr-only">More</span>
97
+ </span>
98
+ )
99
+ }
100
+
101
+ export {
102
+ Breadcrumb,
103
+ BreadcrumbList,
104
+ BreadcrumbItem,
105
+ BreadcrumbLink,
106
+ BreadcrumbPage,
107
+ BreadcrumbSeparator,
108
+ BreadcrumbEllipsis,
109
+ }
@@ -0,0 +1,58 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "~/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16
+ outline:
17
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
20
+ ghost:
21
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22
+ link: "text-primary underline-offset-4 hover:underline",
23
+ },
24
+ size: {
25
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
26
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28
+ icon: "size-9",
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ variant: "default",
33
+ size: "default",
34
+ },
35
+ }
36
+ )
37
+
38
+ const Button = React.forwardRef<
39
+ HTMLButtonElement,
40
+ React.ComponentProps<"button"> &
41
+ VariantProps<typeof buttonVariants> & {
42
+ asChild?: boolean
43
+ }
44
+ >(({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button"
46
+
47
+ return (
48
+ <Comp
49
+ data-slot="button"
50
+ className={cn(buttonVariants({ variant, size, className }))}
51
+ ref={ref}
52
+ {...props}
53
+ />
54
+ )
55
+ })
56
+ Button.displayName = "Button"
57
+
58
+ export { Button, buttonVariants }