@thinhnguyencth1204/nextcli 0.2.1 → 0.4.0

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 (98) hide show
  1. package/README.md +6 -2
  2. package/dist/cli.js +778 -101
  3. package/package.json +2 -1
  4. package/templates/next-base/PROJECT_STRUCTURE.md +88 -0
  5. package/templates/next-base/SETUP.md +86 -0
  6. package/templates/next-base/bun.lock +1443 -0
  7. package/templates/next-base/components.json +21 -0
  8. package/templates/next-base/messages/vi/auth.json +42 -0
  9. package/templates/next-base/messages/vi/common.json +34 -0
  10. package/templates/next-base/messages/vi/example.json +10 -0
  11. package/templates/next-base/next-env.d.ts +3 -1
  12. package/templates/next-base/next.config.ts +11 -1
  13. package/templates/next-base/nextcli.json +8 -0
  14. package/templates/next-base/package.json +21 -1
  15. package/templates/next-base/postcss.config.mjs +5 -0
  16. package/templates/next-base/prisma/migrations/20260612000000_init/migration.sql +104 -0
  17. package/templates/next-base/prisma/migrations/migration_lock.toml +3 -0
  18. package/templates/next-base/prisma/schema.prisma +23 -9
  19. package/templates/next-base/public/logo.svg +4 -0
  20. package/templates/next-base/src/app/(auth)/change-password/layout.tsx +21 -0
  21. package/templates/next-base/src/app/(auth)/change-password/page.tsx +14 -0
  22. package/templates/next-base/src/app/(auth)/layout.tsx +9 -0
  23. package/templates/next-base/src/app/(auth)/sign-in/layout.tsx +17 -0
  24. package/templates/next-base/src/app/(auth)/sign-in/page.tsx +6 -3
  25. package/templates/next-base/src/app/(dashboard)/account/page.tsx +9 -5
  26. package/templates/next-base/src/app/(dashboard)/dashboard/page.tsx +17 -0
  27. package/templates/next-base/src/app/(dashboard)/example/page.tsx +5 -2
  28. package/templates/next-base/src/app/(dashboard)/layout.tsx +22 -0
  29. package/templates/next-base/src/app/api/v1/auth/change-password/route.ts +55 -0
  30. package/templates/next-base/src/app/api/v1/auth/login/route.ts +15 -5
  31. package/templates/next-base/src/app/api/v1/auth/me/route.ts +17 -19
  32. package/templates/next-base/src/app/api/v1/users/[id]/route.ts +104 -0
  33. package/templates/next-base/src/app/api/v1/users/route.ts +58 -0
  34. package/templates/next-base/src/app/globals.css +111 -0
  35. package/templates/next-base/src/app/layout.tsx +24 -10
  36. package/templates/next-base/src/app/page.tsx +2 -18
  37. package/templates/next-base/src/components/branding/logo.tsx +27 -0
  38. package/templates/next-base/src/components/layout/private/app-sidebar.tsx +44 -0
  39. package/templates/next-base/src/components/layout/private/dashboard-layout.tsx +54 -0
  40. package/templates/next-base/src/components/layout/private/locale-switcher.tsx +45 -0
  41. package/templates/next-base/src/components/layout/private/nav-sidebar.tsx +55 -0
  42. package/templates/next-base/src/components/layout/private/nav-user.tsx +99 -0
  43. package/templates/next-base/src/components/providers/theme-provider.tsx +11 -0
  44. package/templates/next-base/src/components/ui/alert-dialog.tsx +11 -0
  45. package/templates/next-base/src/components/ui/avatar.tsx +45 -0
  46. package/templates/next-base/src/components/ui/badge.tsx +29 -0
  47. package/templates/next-base/src/components/ui/button.tsx +47 -7
  48. package/templates/next-base/src/components/ui/card.tsx +54 -0
  49. package/templates/next-base/src/components/ui/data-table/data-table-column-header.tsx +23 -0
  50. package/templates/next-base/src/components/ui/data-table/data-table-filter-list.tsx +3 -0
  51. package/templates/next-base/src/components/ui/data-table/data-table-pagination.tsx +35 -0
  52. package/templates/next-base/src/components/ui/data-table/data-table-skeleton.tsx +11 -0
  53. package/templates/next-base/src/components/ui/data-table/data-table-toolbar.tsx +14 -0
  54. package/templates/next-base/src/components/ui/data-table/data-table-view-options.tsx +3 -0
  55. package/templates/next-base/src/components/ui/data-table/data-table.tsx +72 -0
  56. package/templates/next-base/src/components/ui/dialog.tsx +105 -0
  57. package/templates/next-base/src/components/ui/dropdown-menu.tsx +44 -0
  58. package/templates/next-base/src/components/ui/input.tsx +19 -0
  59. package/templates/next-base/src/components/ui/label.tsx +15 -0
  60. package/templates/next-base/src/components/ui/popover.tsx +30 -0
  61. package/templates/next-base/src/components/ui/scroll-area.tsx +47 -0
  62. package/templates/next-base/src/components/ui/select.tsx +76 -0
  63. package/templates/next-base/src/components/ui/separator.tsx +23 -0
  64. package/templates/next-base/src/components/ui/sheet.tsx +117 -0
  65. package/templates/next-base/src/components/ui/sidebar.tsx +215 -0
  66. package/templates/next-base/src/components/ui/skeleton.tsx +10 -0
  67. package/templates/next-base/src/components/ui/sonner.tsx +3 -0
  68. package/templates/next-base/src/components/ui/table.tsx +54 -0
  69. package/templates/next-base/src/components/ui/tabs.tsx +52 -0
  70. package/templates/next-base/src/components/ui/textarea.tsx +17 -0
  71. package/templates/next-base/src/components/ui/tooltip.tsx +26 -0
  72. package/templates/next-base/src/config/branding.ts +14 -0
  73. package/templates/next-base/src/data/sidebar-modules.ts +11 -0
  74. package/templates/next-base/src/example/components/example-table.tsx +25 -40
  75. package/templates/next-base/src/features/auth/components/account-panel.tsx +32 -14
  76. package/templates/next-base/src/features/auth/components/change-password-form.tsx +82 -0
  77. package/templates/next-base/src/features/auth/components/sign-in-form.tsx +53 -35
  78. package/templates/next-base/src/features/auth/validations.ts +7 -1
  79. package/templates/next-base/src/features/users/services.ts +132 -0
  80. package/templates/next-base/src/features/users/validations.ts +21 -0
  81. package/templates/next-base/src/hooks/index.ts +1 -1
  82. package/templates/next-base/src/hooks/table/use-data-table.ts +33 -0
  83. package/templates/next-base/src/hooks/use-mobile.ts +25 -0
  84. package/templates/next-base/src/i18n/config.ts +7 -0
  85. package/templates/next-base/src/i18n/namespaces.ts +5 -0
  86. package/templates/next-base/src/i18n/request.ts +19 -2
  87. package/templates/next-base/src/instrumentation.ts +14 -0
  88. package/templates/next-base/src/lib/auth-client.ts +2 -2
  89. package/templates/next-base/src/lib/auth.ts +2 -2
  90. package/templates/next-base/src/lib/bootstrap.ts +96 -0
  91. package/templates/next-base/src/lib/constants.ts +7 -0
  92. package/templates/next-base/src/lib/prisma.ts +11 -1
  93. package/templates/next-base/src/lib/rbac.ts +62 -0
  94. package/templates/next-base/src/types/data-table.ts +4 -0
  95. package/templates/next-base/src/types/index.ts +2 -0
  96. package/templates/next-base/tsconfig.json +29 -7
  97. package/templates/next-base/middleware.ts +0 -10
  98. package/templates/next-base/src/app/styles.css +0 -12
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
5
+ import { cn } from "@/utils/cn";
6
+
7
+ export const Popover = PopoverPrimitive.Root;
8
+ export const PopoverTrigger = PopoverPrimitive.Trigger;
9
+ export const PopoverAnchor = PopoverPrimitive.Anchor;
10
+
11
+ export function PopoverContent({
12
+ className,
13
+ align = "center",
14
+ sideOffset = 4,
15
+ ...props
16
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
17
+ return (
18
+ <PopoverPrimitive.Portal>
19
+ <PopoverPrimitive.Content
20
+ align={align}
21
+ sideOffset={sideOffset}
22
+ className={cn(
23
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md",
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ </PopoverPrimitive.Portal>
29
+ );
30
+ }
@@ -0,0 +1,47 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
+ import { cn } from "@/utils/cn";
6
+
7
+ export function ScrollArea({
8
+ className,
9
+ children,
10
+ ...props
11
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
12
+ return (
13
+ <ScrollAreaPrimitive.Root
14
+ className={cn("relative overflow-hidden", className)}
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
18
+ {children}
19
+ </ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ );
24
+ }
25
+
26
+ export function ScrollBar({
27
+ className,
28
+ orientation = "vertical",
29
+ ...props
30
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
31
+ return (
32
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
33
+ orientation={orientation}
34
+ className={cn(
35
+ "flex touch-none select-none transition-colors",
36
+ orientation === "vertical" &&
37
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
38
+ orientation === "horizontal" &&
39
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
40
+ className,
41
+ )}
42
+ {...props}
43
+ >
44
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
45
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
46
+ );
47
+ }
@@ -0,0 +1,76 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as SelectPrimitive from "@radix-ui/react-select";
5
+ import { Check, ChevronDown } from "lucide-react";
6
+ import { cn } from "@/utils/cn";
7
+
8
+ export const Select = SelectPrimitive.Root;
9
+ export const SelectGroup = SelectPrimitive.Group;
10
+ export const SelectValue = SelectPrimitive.Value;
11
+
12
+ export function SelectTrigger({
13
+ className,
14
+ children,
15
+ ...props
16
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger>) {
17
+ return (
18
+ <SelectPrimitive.Trigger
19
+ className={cn(
20
+ "flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm",
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ {children}
26
+ <SelectPrimitive.Icon asChild>
27
+ <ChevronDown className="h-4 w-4 opacity-50" />
28
+ </SelectPrimitive.Icon>
29
+ </SelectPrimitive.Trigger>
30
+ );
31
+ }
32
+
33
+ export function SelectContent({
34
+ className,
35
+ children,
36
+ ...props
37
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
38
+ return (
39
+ <SelectPrimitive.Portal>
40
+ <SelectPrimitive.Content
41
+ className={cn(
42
+ "z-50 min-w-[8rem] rounded-md border bg-popover text-popover-foreground shadow-md",
43
+ className,
44
+ )}
45
+ {...props}
46
+ >
47
+ <SelectPrimitive.Viewport className="p-1">
48
+ {children}
49
+ </SelectPrimitive.Viewport>
50
+ </SelectPrimitive.Content>
51
+ </SelectPrimitive.Portal>
52
+ );
53
+ }
54
+
55
+ export function SelectItem({
56
+ className,
57
+ children,
58
+ ...props
59
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
60
+ return (
61
+ <SelectPrimitive.Item
62
+ className={cn(
63
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground",
64
+ className,
65
+ )}
66
+ {...props}
67
+ >
68
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
69
+ <SelectPrimitive.ItemIndicator>
70
+ <Check className="h-4 w-4" />
71
+ </SelectPrimitive.ItemIndicator>
72
+ </span>
73
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
74
+ </SelectPrimitive.Item>
75
+ );
76
+ }
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
3
+ import { cn } from "@/utils/cn";
4
+
5
+ export function Separator({
6
+ className,
7
+ orientation = "horizontal",
8
+ decorative = true,
9
+ ...props
10
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
11
+ return (
12
+ <SeparatorPrimitive.Root
13
+ decorative={decorative}
14
+ orientation={orientation}
15
+ className={cn(
16
+ "shrink-0 bg-border",
17
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ );
23
+ }
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { X } from "lucide-react";
6
+ import { cn } from "@/utils/cn";
7
+ import { buttonVariants } from "@/components/ui/button";
8
+
9
+ export const Sheet = DialogPrimitive.Root;
10
+ export const SheetTrigger = DialogPrimitive.Trigger;
11
+ export const SheetClose = DialogPrimitive.Close;
12
+ export const SheetPortal = DialogPrimitive.Portal;
13
+
14
+ export function SheetOverlay({
15
+ className,
16
+ ...props
17
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
18
+ return (
19
+ <DialogPrimitive.Overlay
20
+ className={cn("fixed inset-0 z-50 bg-black/50", className)}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ export function SheetContent({
27
+ className,
28
+ children,
29
+ side = "right",
30
+ ...props
31
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
32
+ side?: "top" | "right" | "bottom" | "left";
33
+ }) {
34
+ return (
35
+ <SheetPortal>
36
+ <SheetOverlay />
37
+ <DialogPrimitive.Content
38
+ className={cn(
39
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition",
40
+ side === "right" &&
41
+ "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
42
+ side === "left" &&
43
+ "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
44
+ side === "top" && "inset-x-0 top-0 border-b",
45
+ side === "bottom" && "inset-x-0 bottom-0 border-t",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ <DialogPrimitive.Close
52
+ className={cn(
53
+ buttonVariants({ variant: "ghost", size: "icon" }),
54
+ "absolute right-4 top-4",
55
+ )}
56
+ >
57
+ <X className="h-4 w-4" />
58
+ <span className="sr-only">Close</span>
59
+ </DialogPrimitive.Close>
60
+ </DialogPrimitive.Content>
61
+ </SheetPortal>
62
+ );
63
+ }
64
+
65
+ export function SheetHeader({
66
+ className,
67
+ ...props
68
+ }: React.ComponentProps<"div">) {
69
+ return (
70
+ <div
71
+ className={cn(
72
+ "flex flex-col space-y-2 text-center sm:text-left",
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ export function SheetFooter({
81
+ className,
82
+ ...props
83
+ }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ className={cn(
87
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
88
+ className,
89
+ )}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ export function SheetTitle({
96
+ className,
97
+ ...props
98
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
99
+ return (
100
+ <DialogPrimitive.Title
101
+ className={cn("text-lg font-semibold", className)}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ export function SheetDescription({
108
+ className,
109
+ ...props
110
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
111
+ return (
112
+ <DialogPrimitive.Description
113
+ className={cn("text-sm text-muted-foreground", className)}
114
+ {...props}
115
+ />
116
+ );
117
+ }
@@ -0,0 +1,215 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { PanelLeft } from "lucide-react";
5
+ import { Slot } from "@radix-ui/react-slot";
6
+ import { cn } from "@/utils/cn";
7
+ import { Button } from "@/components/ui/button";
8
+ import { Sheet, SheetContent } from "@/components/ui/sheet";
9
+ import { useIsMobile } from "@/hooks/use-mobile";
10
+
11
+ type SidebarContextValue = {
12
+ open: boolean;
13
+ setOpen: (value: boolean) => void;
14
+ openMobile: boolean;
15
+ setOpenMobile: (value: boolean) => void;
16
+ isMobile: boolean;
17
+ state: "expanded" | "collapsed";
18
+ toggleSidebar: () => void;
19
+ };
20
+
21
+ const SidebarContext = React.createContext<SidebarContextValue | null>(null);
22
+
23
+ export function useSidebar() {
24
+ const context = React.useContext(SidebarContext);
25
+ if (!context) {
26
+ throw new Error("useSidebar must be used within SidebarProvider");
27
+ }
28
+ return context;
29
+ }
30
+
31
+ export function SidebarProvider({
32
+ defaultOpen = true,
33
+ children,
34
+ }: {
35
+ defaultOpen?: boolean;
36
+ children: React.ReactNode;
37
+ }) {
38
+ const isMobile = useIsMobile();
39
+ const [open, setOpen] = React.useState(defaultOpen);
40
+ const [openMobile, setOpenMobile] = React.useState(false);
41
+ const state = open ? "expanded" : "collapsed";
42
+
43
+ const toggleSidebar = React.useCallback(() => {
44
+ if (isMobile) {
45
+ setOpenMobile((value) => !value);
46
+ return;
47
+ }
48
+ setOpen((value) => !value);
49
+ }, [isMobile]);
50
+
51
+ return (
52
+ <SidebarContext.Provider
53
+ value={{
54
+ open,
55
+ setOpen,
56
+ openMobile,
57
+ setOpenMobile,
58
+ isMobile,
59
+ state,
60
+ toggleSidebar,
61
+ }}
62
+ >
63
+ <div className="group/sidebar-wrapper flex min-h-svh w-full">
64
+ {children}
65
+ </div>
66
+ </SidebarContext.Provider>
67
+ );
68
+ }
69
+
70
+ export function Sidebar({
71
+ className,
72
+ children,
73
+ collapsible = "offcanvas",
74
+ }: React.ComponentProps<"div"> & { collapsible?: "offcanvas" | "none" }) {
75
+ const { isMobile, openMobile, setOpenMobile, state } = useSidebar();
76
+
77
+ if (collapsible === "none") {
78
+ return (
79
+ <aside
80
+ className={cn(
81
+ "flex h-full w-64 flex-col border-r bg-sidebar text-sidebar-foreground",
82
+ className,
83
+ )}
84
+ >
85
+ {children}
86
+ </aside>
87
+ );
88
+ }
89
+
90
+ if (isMobile) {
91
+ return (
92
+ <Sheet open={openMobile} onOpenChange={setOpenMobile}>
93
+ <SheetContent side="left" className={cn("w-[18rem] p-0", className)}>
94
+ <div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
95
+ {children}
96
+ </div>
97
+ </SheetContent>
98
+ </Sheet>
99
+ );
100
+ }
101
+
102
+ return (
103
+ <aside
104
+ data-state={state}
105
+ className={cn(
106
+ "hidden h-screen border-r bg-sidebar text-sidebar-foreground md:flex md:flex-col",
107
+ state === "collapsed" ? "w-14" : "w-64",
108
+ className,
109
+ )}
110
+ >
111
+ {children}
112
+ </aside>
113
+ );
114
+ }
115
+
116
+ export function SidebarTrigger({
117
+ className,
118
+ children,
119
+ ...props
120
+ }: React.ComponentProps<typeof Button>) {
121
+ const { toggleSidebar } = useSidebar();
122
+ return (
123
+ <Button
124
+ variant="ghost"
125
+ size="icon"
126
+ className={cn("h-8 w-8", className)}
127
+ onClick={(event) => {
128
+ props.onClick?.(event);
129
+ toggleSidebar();
130
+ }}
131
+ {...props}
132
+ >
133
+ {children ?? <PanelLeft className="h-4 w-4" />}
134
+ </Button>
135
+ );
136
+ }
137
+
138
+ export function SidebarHeader({
139
+ className,
140
+ ...props
141
+ }: React.ComponentProps<"div">) {
142
+ return <div className={cn("border-b p-3", className)} {...props} />;
143
+ }
144
+
145
+ export function SidebarContent({
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<"div">) {
149
+ return (
150
+ <div className={cn("flex-1 overflow-y-auto p-2", className)} {...props} />
151
+ );
152
+ }
153
+
154
+ export function SidebarGroup({
155
+ className,
156
+ ...props
157
+ }: React.ComponentProps<"div">) {
158
+ return <div className={cn("mb-2", className)} {...props} />;
159
+ }
160
+
161
+ export function SidebarGroupLabel({
162
+ className,
163
+ ...props
164
+ }: React.ComponentProps<"div">) {
165
+ return (
166
+ <div
167
+ className={cn(
168
+ "px-2 py-1 text-xs font-medium text-muted-foreground",
169
+ className,
170
+ )}
171
+ {...props}
172
+ />
173
+ );
174
+ }
175
+
176
+ export function SidebarGroupContent({
177
+ className,
178
+ ...props
179
+ }: React.ComponentProps<"div">) {
180
+ return <div className={cn("space-y-1", className)} {...props} />;
181
+ }
182
+
183
+ export function SidebarMenu({
184
+ className,
185
+ ...props
186
+ }: React.ComponentProps<"ul">) {
187
+ return <ul className={cn("space-y-1", className)} {...props} />;
188
+ }
189
+
190
+ export function SidebarMenuItem({
191
+ className,
192
+ ...props
193
+ }: React.ComponentProps<"li">) {
194
+ return <li className={cn("list-none", className)} {...props} />;
195
+ }
196
+
197
+ export function SidebarMenuButton({
198
+ className,
199
+ asChild = false,
200
+ isActive = false,
201
+ ...props
202
+ }: React.ComponentProps<"button"> & { asChild?: boolean; isActive?: boolean }) {
203
+ const Comp = asChild ? Slot : "button";
204
+ return (
205
+ <Comp
206
+ className={cn(
207
+ "flex h-9 w-full items-center gap-2 rounded-md px-2 text-sm hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
208
+ isActive &&
209
+ "bg-sidebar-primary text-sidebar-primary-foreground hover:bg-sidebar-primary/90",
210
+ className,
211
+ )}
212
+ {...props}
213
+ />
214
+ );
215
+ }
@@ -0,0 +1,10 @@
1
+ import { cn } from "@/utils/cn";
2
+
3
+ export function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ className={cn("animate-pulse rounded-md bg-muted", className)}
7
+ {...props}
8
+ />
9
+ );
10
+ }
@@ -0,0 +1,3 @@
1
+ "use client";
2
+
3
+ export { Toaster } from "sonner";
@@ -0,0 +1,54 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/utils/cn";
3
+
4
+ export function Table({ className, ...props }: React.ComponentProps<"table">) {
5
+ return (
6
+ <div className="w-full overflow-auto">
7
+ <table
8
+ className={cn("w-full caption-bottom text-sm", className)}
9
+ {...props}
10
+ />
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export function TableHeader({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<"thead">) {
19
+ return <thead className={cn("[&_tr]:border-b", className)} {...props} />;
20
+ }
21
+
22
+ export function TableBody({
23
+ className,
24
+ ...props
25
+ }: React.ComponentProps<"tbody">) {
26
+ return (
27
+ <tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />
28
+ );
29
+ }
30
+
31
+ export function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
32
+ return (
33
+ <tr
34
+ className={cn("border-b transition-colors hover:bg-muted/50", className)}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ export function TableHead({ className, ...props }: React.ComponentProps<"th">) {
41
+ return (
42
+ <th
43
+ className={cn(
44
+ "h-10 px-2 text-left align-middle font-medium text-muted-foreground",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ export function TableCell({ className, ...props }: React.ComponentProps<"td">) {
53
+ return <td className={cn("p-2 align-middle", className)} {...props} />;
54
+ }
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
5
+ import { cn } from "@/utils/cn";
6
+
7
+ export const Tabs = TabsPrimitive.Root;
8
+
9
+ export function TabsList({
10
+ className,
11
+ ...props
12
+ }: React.ComponentProps<typeof TabsPrimitive.List>) {
13
+ return (
14
+ <TabsPrimitive.List
15
+ className={cn(
16
+ "inline-flex h-9 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ export function TabsTrigger({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
28
+ return (
29
+ <TabsPrimitive.Trigger
30
+ className={cn(
31
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ export function TabsContent({
40
+ className,
41
+ ...props
42
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
43
+ return (
44
+ <TabsPrimitive.Content
45
+ className={cn(
46
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
47
+ className,
48
+ )}
49
+ {...props}
50
+ />
51
+ );
52
+ }
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/utils/cn";
3
+
4
+ export function Textarea({
5
+ className,
6
+ ...props
7
+ }: React.ComponentProps<"textarea">) {
8
+ return (
9
+ <textarea
10
+ className={cn(
11
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
12
+ className,
13
+ )}
14
+ {...props}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
+ import { cn } from "@/utils/cn";
6
+
7
+ export const TooltipProvider = TooltipPrimitive.Provider;
8
+ export const Tooltip = TooltipPrimitive.Root;
9
+ export const TooltipTrigger = TooltipPrimitive.Trigger;
10
+
11
+ export function TooltipContent({
12
+ className,
13
+ sideOffset = 4,
14
+ ...props
15
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
16
+ return (
17
+ <TooltipPrimitive.Content
18
+ sideOffset={sideOffset}
19
+ className={cn(
20
+ "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Central branding config — edit these values after scaffolding.
3
+ * See SETUP.md § Branding for env vs display settings.
4
+ */
5
+ export const branding = {
6
+ /** Display name shown in UI (header, metadata, sidebar). */
7
+ projectName: "__PROJECT_NAME__",
8
+ /** URL-safe slug (package name, DB name). Set at create time. */
9
+ projectSlug: "__PROJECT_NAME__",
10
+ /** Short app description for metadata. */
11
+ description: "Outsource-ready Next.js scaffolded by NexTCLI",
12
+ /** Public path to logo asset under /public. */
13
+ logoPath: "/logo.svg",
14
+ } as const;