ta_data_mas 0.0.2
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.
- package/README.md +42 -0
- package/apps/api/.prettierrc +4 -0
- package/apps/api/README.md +98 -0
- package/apps/api/drizzle/0000_right_drax.sql +87 -0
- package/apps/api/drizzle/meta/0000_snapshot.json +588 -0
- package/apps/api/drizzle/meta/_journal.json +13 -0
- package/apps/api/drizzle.config.ts +12 -0
- package/apps/api/eslint.config.mjs +35 -0
- package/apps/api/nest-cli.json +8 -0
- package/apps/api/package.json +99 -0
- package/apps/api/src/app.controller.spec.ts +25 -0
- package/apps/api/src/app.controller.ts +9 -0
- package/apps/api/src/app.module.ts +37 -0
- package/apps/api/src/app.service.ts +8 -0
- package/apps/api/src/auth/auth.controller.ts +113 -0
- package/apps/api/src/auth/auth.module.ts +15 -0
- package/apps/api/src/auth/auth.service.ts +309 -0
- package/apps/api/src/auth/decorators/current-user-decorator.ts +10 -0
- package/apps/api/src/auth/decorators/require-org-role-decorator.ts +7 -0
- package/apps/api/src/auth/dto/login.dto.ts +10 -0
- package/apps/api/src/auth/dto/register.dto.ts +14 -0
- package/apps/api/src/auth/guards/jwt-auth-guard.ts +40 -0
- package/apps/api/src/auth/guards/org-role.guards.ts +60 -0
- package/apps/api/src/db/db.module.ts +9 -0
- package/apps/api/src/db/drizzle.service.ts +16 -0
- package/apps/api/src/db/schema/index.ts +6 -0
- package/apps/api/src/db/schema/organizations.ts +30 -0
- package/apps/api/src/db/schema/projects.ts +26 -0
- package/apps/api/src/db/schema/query-history.ts +16 -0
- package/apps/api/src/db/schema/storage-buckets.ts +17 -0
- package/apps/api/src/db/schema/storage-objects.ts +18 -0
- package/apps/api/src/db/schema/users.ts +14 -0
- package/apps/api/src/main.ts +51 -0
- package/apps/api/src/members/dto/invite-member-dto.ts +6 -0
- package/apps/api/src/members/dto/update-role.dto.ts +8 -0
- package/apps/api/src/members/invite.service.ts +147 -0
- package/apps/api/src/members/members.controller.ts +56 -0
- package/apps/api/src/members/members.module.ts +13 -0
- package/apps/api/src/members/members.service.ts +85 -0
- package/apps/api/src/orgs/dto/create-org.dto.ts +8 -0
- package/apps/api/src/orgs/orgs.controller.ts +27 -0
- package/apps/api/src/orgs/orgs.module.ts +12 -0
- package/apps/api/src/orgs/orgs.service.ts +86 -0
- package/apps/api/src/project-api/project-api.controller.ts +95 -0
- package/apps/api/src/project-api/project-api.module.ts +12 -0
- package/apps/api/src/project-api/project-api.service.ts +196 -0
- package/apps/api/src/project-api/project-key.guard.ts +45 -0
- package/apps/api/src/project-api/query-parser.ts +129 -0
- package/apps/api/src/project-auth/dto/magic-link.dto.ts +7 -0
- package/apps/api/src/project-auth/dto/signin.dto.ts +10 -0
- package/apps/api/src/project-auth/dto/signup.dto.ts +11 -0
- package/apps/api/src/project-auth/project-auth-dashboard.controller.ts +35 -0
- package/apps/api/src/project-auth/project-auth.controller.ts +111 -0
- package/apps/api/src/project-auth/project-auth.module.ts +13 -0
- package/apps/api/src/project-auth/project-auth.service.ts +440 -0
- package/apps/api/src/projects/dto/create-project.dto.ts +9 -0
- package/apps/api/src/projects/projects.controller.ts +34 -0
- package/apps/api/src/projects/projects.module.ts +12 -0
- package/apps/api/src/projects/projects.service.ts +136 -0
- package/apps/api/src/realtime/realtime-socket.type.ts +14 -0
- package/apps/api/src/realtime/realtime.controller.ts +63 -0
- package/apps/api/src/realtime/realtime.gateway.ts +150 -0
- package/apps/api/src/realtime/realtime.module.ts +14 -0
- package/apps/api/src/realtime/realtime.service.ts +101 -0
- package/apps/api/src/realtime/trigger.service.ts +95 -0
- package/apps/api/src/sql-editor/dto/execute-query.dto.ts +7 -0
- package/apps/api/src/sql-editor/sql-editor.controller.ts +28 -0
- package/apps/api/src/sql-editor/sql-editor.module.ts +12 -0
- package/apps/api/src/sql-editor/sql-editor.service.ts +154 -0
- package/apps/api/src/storage/project-storage.controller.ts +140 -0
- package/apps/api/src/storage/storage.controller.ts +118 -0
- package/apps/api/src/storage/storage.module.ts +15 -0
- package/apps/api/src/storage/storage.service.ts +191 -0
- package/apps/api/src/storage/uploadthing.ts +23 -0
- package/apps/api/src/table-editor/dto/alter-table.dto.ts +15 -0
- package/apps/api/src/table-editor/dto/create-table.dto.ts +54 -0
- package/apps/api/src/table-editor/table-editor.controller.ts +117 -0
- package/apps/api/src/table-editor/table-editor.module.ts +12 -0
- package/apps/api/src/table-editor/table-editor.service.ts +366 -0
- package/apps/api/test/app.e2e-spec.ts +29 -0
- package/apps/api/test/jest-e2e.json +9 -0
- package/apps/api/tsconfig.build.json +4 -0
- package/apps/api/tsconfig.json +25 -0
- package/apps/web/.env.example +12 -0
- package/apps/web/AGENTS.md +9 -0
- package/apps/web/CLAUDE.md +1 -0
- package/apps/web/README.md +36 -0
- package/apps/web/components.json +25 -0
- package/apps/web/eslint.config.mjs +18 -0
- package/apps/web/next.config.ts +11 -0
- package/apps/web/package.json +48 -0
- package/apps/web/postcss.config.mjs +7 -0
- package/apps/web/public/file.svg +1 -0
- package/apps/web/public/globe.svg +1 -0
- package/apps/web/public/logo.svg +4 -0
- package/apps/web/public/next.svg +1 -0
- package/apps/web/public/vercel.svg +1 -0
- package/apps/web/public/window.svg +1 -0
- package/apps/web/src/app/(dashboard)/layout.tsx +33 -0
- package/apps/web/src/app/(dashboard)/organizations/(list)/layout.tsx +7 -0
- package/apps/web/src/app/(dashboard)/organizations/(list)/new/page.tsx +26 -0
- package/apps/web/src/app/(dashboard)/organizations/(list)/page.tsx +44 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/api/page.tsx +13 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/layout.tsx +19 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/page.tsx +10 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/providers/page.tsx +19 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/url-configuration/page.tsx +24 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/users/page.tsx +13 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/database/page.tsx +29 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/realtime/page.tsx +28 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/sql/page.tsx +21 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/storage/page.tsx +28 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/layout.tsx +20 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/projects/page.tsx +62 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/members/page.tsx +28 -0
- package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/page.tsx +10 -0
- package/apps/web/src/app/(dashboard)/settings/page.tsx +12 -0
- package/apps/web/src/app/dashboard/page.tsx +14 -0
- package/apps/web/src/app/favicon.ico +0 -0
- package/apps/web/src/app/globals.css +115 -0
- package/apps/web/src/app/layout.tsx +36 -0
- package/apps/web/src/app/login/page.tsx +199 -0
- package/apps/web/src/app/page.tsx +100 -0
- package/apps/web/src/app/register/page.tsx +218 -0
- package/apps/web/src/components/app-sidebar.tsx +301 -0
- package/apps/web/src/components/navbar.tsx +63 -0
- package/apps/web/src/components/topNav.tsx +68 -0
- package/apps/web/src/components/ui/avatar.tsx +112 -0
- package/apps/web/src/components/ui/badge.tsx +48 -0
- package/apps/web/src/components/ui/button.tsx +66 -0
- package/apps/web/src/components/ui/card.tsx +102 -0
- package/apps/web/src/components/ui/checkbox.tsx +32 -0
- package/apps/web/src/components/ui/dialog.tsx +168 -0
- package/apps/web/src/components/ui/dropdown-menu.tsx +269 -0
- package/apps/web/src/components/ui/field.tsx +238 -0
- package/apps/web/src/components/ui/input.tsx +19 -0
- package/apps/web/src/components/ui/label.tsx +24 -0
- package/apps/web/src/components/ui/resizable.tsx +50 -0
- package/apps/web/src/components/ui/scroll-area.tsx +55 -0
- package/apps/web/src/components/ui/select.tsx +192 -0
- package/apps/web/src/components/ui/separator.tsx +28 -0
- package/apps/web/src/components/ui/sheet.tsx +147 -0
- package/apps/web/src/components/ui/sidebar.tsx +703 -0
- package/apps/web/src/components/ui/skeleton.tsx +13 -0
- package/apps/web/src/components/ui/switch.tsx +33 -0
- package/apps/web/src/components/ui/table.tsx +116 -0
- package/apps/web/src/components/ui/tooltip.tsx +57 -0
- package/apps/web/src/features/api-docs/api-docs-client.tsx +227 -0
- package/apps/web/src/features/api-docs/api-docs-helpers.server.ts +86 -0
- package/apps/web/src/features/auth/action.ts +67 -0
- package/apps/web/src/features/auth/client.schema.ts +13 -0
- package/apps/web/src/features/auth/constants.ts +6 -0
- package/apps/web/src/features/auth/cookies.ts +86 -0
- package/apps/web/src/features/auth/server.schema.ts +11 -0
- package/apps/web/src/features/auth/sign-out.ts +17 -0
- package/apps/web/src/features/members/action.ts +66 -0
- package/apps/web/src/features/members/client.schema.ts +11 -0
- package/apps/web/src/features/members/constants.ts +8 -0
- package/apps/web/src/features/members/members-client.tsx +198 -0
- package/apps/web/src/features/members/members-helpers.server.ts +21 -0
- package/apps/web/src/features/members/server.schema.ts +19 -0
- package/apps/web/src/features/organization/action.ts +45 -0
- package/apps/web/src/features/organization/client.schema.ts +5 -0
- package/apps/web/src/features/organization/constants.tsx +3 -0
- package/apps/web/src/features/organization/create-org-form.tsx +66 -0
- package/apps/web/src/features/organization/organization-helpers.server.ts +35 -0
- package/apps/web/src/features/organization/server.schema.ts +10 -0
- package/apps/web/src/features/project-auth/project-auth-action.ts +78 -0
- package/apps/web/src/features/project-auth/project-auth-client.schema.ts +18 -0
- package/apps/web/src/features/project-auth/project-auth-constants.ts +8 -0
- package/apps/web/src/features/project-auth/project-auth-helpers.server.ts +39 -0
- package/apps/web/src/features/project-auth/project-auth-providers.tsx +222 -0
- package/apps/web/src/features/project-auth/project-auth-server.schema.ts +22 -0
- package/apps/web/src/features/project-auth/project-auth-shell.tsx +106 -0
- package/apps/web/src/features/project-auth/project-auth-url-config.tsx +151 -0
- package/apps/web/src/features/project-auth/project-auth-users.tsx +138 -0
- package/apps/web/src/features/projects/action.ts +46 -0
- package/apps/web/src/features/projects/client.schema.ts +9 -0
- package/apps/web/src/features/projects/constants.ts +6 -0
- package/apps/web/src/features/projects/create-project-modal.tsx +117 -0
- package/apps/web/src/features/projects/project-helpers.server.ts +38 -0
- package/apps/web/src/features/projects/server.schema.ts +10 -0
- package/apps/web/src/features/realtime/realtime-client.tsx +294 -0
- package/apps/web/src/features/realtime/realtime-helpers.server.ts +40 -0
- package/apps/web/src/features/sql-editor/sql-editor-client.tsx +324 -0
- package/apps/web/src/features/sql-editor/sql-editor-helpers.server.ts +22 -0
- package/apps/web/src/features/storage/storage-client.tsx +500 -0
- package/apps/web/src/features/storage/storage-helpers.server.ts +36 -0
- package/apps/web/src/features/table-editor/action.ts +97 -0
- package/apps/web/src/features/table-editor/add-column-dialog.tsx +184 -0
- package/apps/web/src/features/table-editor/client.schema.ts +36 -0
- package/apps/web/src/features/table-editor/create-table-drawer.tsx +542 -0
- package/apps/web/src/features/table-editor/server.schema.ts +41 -0
- package/apps/web/src/features/table-editor/table-editor-client.tsx +737 -0
- package/apps/web/src/features/table-editor/table-editor-helpers.server.ts +39 -0
- package/apps/web/src/hooks/use-mobile.ts +19 -0
- package/apps/web/src/lib/axios.ts +8 -0
- package/apps/web/src/lib/utils.ts +6 -0
- package/apps/web/src/proxy.ts +72 -0
- package/apps/web/src/server-utils/utils.ts +10 -0
- package/apps/web/tsconfig.json +34 -0
- package/package.json +22 -0
- package/packages/apiDatabase-js/package.json +39 -0
- package/packages/apiDatabase-js/src/auth/index.ts +121 -0
- package/packages/apiDatabase-js/src/client.ts +25 -0
- package/packages/apiDatabase-js/src/db/index.ts +15 -0
- package/packages/apiDatabase-js/src/db/query-builder.ts +194 -0
- package/packages/apiDatabase-js/src/index.ts +16 -0
- package/packages/apiDatabase-js/src/realtime/index.ts +84 -0
- package/packages/apiDatabase-js/src/storage/index.ts +130 -0
- package/packages/apiDatabase-js/tsconfig.json +13 -0
- package/packages/apiDatabase-js/tsdown.config.ts +20 -0
- package/packages/constants/package.json +6 -0
- package/packages/constants/src/index.ts +76 -0
- package/packages/tsconfig.json +13 -0
- package/packages/tsdown.config.ts +20 -0
- package/packages/types/package.json +6 -0
- package/packages/types/src/index.ts +254 -0
- package/pnpm-workspace.yaml +10 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useParams, usePathname } from "next/navigation";
|
|
5
|
+
import {
|
|
6
|
+
Home,
|
|
7
|
+
Table2,
|
|
8
|
+
ShieldCheck,
|
|
9
|
+
FolderOpen,
|
|
10
|
+
Code2,
|
|
11
|
+
Radio,
|
|
12
|
+
Building2,
|
|
13
|
+
ChevronsUpDown,
|
|
14
|
+
Plus,
|
|
15
|
+
Check,
|
|
16
|
+
LogOut,
|
|
17
|
+
TerminalIcon,
|
|
18
|
+
} from "lucide-react";
|
|
19
|
+
import {
|
|
20
|
+
Sidebar,
|
|
21
|
+
SidebarContent,
|
|
22
|
+
SidebarFooter,
|
|
23
|
+
SidebarGroup,
|
|
24
|
+
SidebarGroupContent,
|
|
25
|
+
SidebarHeader,
|
|
26
|
+
SidebarMenu,
|
|
27
|
+
SidebarMenuButton,
|
|
28
|
+
SidebarMenuItem,
|
|
29
|
+
SidebarRail,
|
|
30
|
+
SidebarSeparator,
|
|
31
|
+
} from "@/components/ui/sidebar";
|
|
32
|
+
import {
|
|
33
|
+
DropdownMenu,
|
|
34
|
+
DropdownMenuContent,
|
|
35
|
+
DropdownMenuItem,
|
|
36
|
+
DropdownMenuSeparator,
|
|
37
|
+
DropdownMenuTrigger,
|
|
38
|
+
} from "@/components/ui/dropdown-menu";
|
|
39
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
40
|
+
import { signOut } from "@/features/auth/sign-out";
|
|
41
|
+
import { cn } from "@/lib/utils";
|
|
42
|
+
import { OrganizationWithMeta } from "@apiDatabase/types";
|
|
43
|
+
|
|
44
|
+
const orgNavItem = { icon: Home, label: "Home", segment: "projects" } as const;
|
|
45
|
+
|
|
46
|
+
const projectNavItems = [
|
|
47
|
+
{ icon: Table2, label: "Database", segment: "database" },
|
|
48
|
+
{ icon: TerminalIcon, label: "SQL", segment: "sql" },
|
|
49
|
+
{ icon: ShieldCheck, label: "Auth", segment: "auth" },
|
|
50
|
+
{ icon: FolderOpen, label: "Storage", segment: "storage" },
|
|
51
|
+
{ icon: Code2, label: "API", segment: "api" },
|
|
52
|
+
{ icon: Radio, label: "Realtime", segment: "realtime" },
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
function buildNavHref(
|
|
56
|
+
orgSlug: string | undefined,
|
|
57
|
+
projectSlug: string | undefined,
|
|
58
|
+
segment: string,
|
|
59
|
+
scope: "org" | "project",
|
|
60
|
+
): string {
|
|
61
|
+
if (!orgSlug) return "/organizations";
|
|
62
|
+
|
|
63
|
+
if (scope === "org") {
|
|
64
|
+
return `/organizations/${orgSlug}/projects`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (projectSlug) {
|
|
68
|
+
return `/organizations/${orgSlug}/${projectSlug}/${segment}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return `/organizations/${orgSlug}/projects`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isNavItemActive(
|
|
75
|
+
pathname: string,
|
|
76
|
+
orgSlug: string | undefined,
|
|
77
|
+
projectSlug: string | undefined,
|
|
78
|
+
segment: string,
|
|
79
|
+
scope: "org" | "project",
|
|
80
|
+
): boolean {
|
|
81
|
+
if (!orgSlug) return false;
|
|
82
|
+
|
|
83
|
+
if (scope === "org") {
|
|
84
|
+
return pathname === `/organizations/${orgSlug}/projects`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!projectSlug) return false;
|
|
88
|
+
|
|
89
|
+
const base = `/organizations/${orgSlug}/${projectSlug}/${segment}`;
|
|
90
|
+
return pathname === base || pathname.startsWith(`${base}/`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function navLinkClass(isActive: boolean) {
|
|
94
|
+
return cn(
|
|
95
|
+
"bg-transparent text-muted-foreground shadow-none",
|
|
96
|
+
"hover:bg-muted/60 hover:text-foreground",
|
|
97
|
+
isActive && "bg-muted font-medium text-foreground hover:bg-muted",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface AppSidebarProps {
|
|
102
|
+
orgs: OrganizationWithMeta[];
|
|
103
|
+
user: { email: string; name: string | null };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function AppSidebar({ orgs, user }: AppSidebarProps) {
|
|
107
|
+
const pathname = usePathname();
|
|
108
|
+
const params = useParams<{ slug: string; projectSlug?: string }>();
|
|
109
|
+
const currentOrg = orgs.find((org) => org.slug === params?.slug) ?? orgs[0];
|
|
110
|
+
const orgSlug = params?.slug;
|
|
111
|
+
const projectSlug = params?.projectSlug;
|
|
112
|
+
|
|
113
|
+
const homeHref = buildNavHref(
|
|
114
|
+
orgSlug,
|
|
115
|
+
projectSlug,
|
|
116
|
+
orgNavItem.segment,
|
|
117
|
+
"org",
|
|
118
|
+
);
|
|
119
|
+
const isHomeActive = isNavItemActive(
|
|
120
|
+
pathname,
|
|
121
|
+
orgSlug,
|
|
122
|
+
projectSlug,
|
|
123
|
+
orgNavItem.segment,
|
|
124
|
+
"org",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<Sidebar collapsible="icon">
|
|
129
|
+
<SidebarHeader>
|
|
130
|
+
<SidebarMenu>
|
|
131
|
+
<SidebarMenuItem>
|
|
132
|
+
<DropdownMenu>
|
|
133
|
+
<DropdownMenuTrigger asChild>
|
|
134
|
+
<SidebarMenuButton
|
|
135
|
+
size="lg"
|
|
136
|
+
className="data-[state=open]:bg-sidebar-accent"
|
|
137
|
+
>
|
|
138
|
+
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
|
|
139
|
+
<Building2 size={16} />
|
|
140
|
+
</div>
|
|
141
|
+
<div className="flex flex-col gap-0.5 leading-none">
|
|
142
|
+
<span className="font-semibold">{currentOrg?.name}</span>
|
|
143
|
+
<span className="text-xs text-muted-foreground capitalize">
|
|
144
|
+
{currentOrg?.role}
|
|
145
|
+
</span>
|
|
146
|
+
</div>
|
|
147
|
+
<ChevronsUpDown className="ml-auto" />
|
|
148
|
+
</SidebarMenuButton>
|
|
149
|
+
</DropdownMenuTrigger>
|
|
150
|
+
<DropdownMenuContent
|
|
151
|
+
className="w-[--radix-popper-anchor-width]"
|
|
152
|
+
align="start"
|
|
153
|
+
>
|
|
154
|
+
{orgs.map((org) => (
|
|
155
|
+
<DropdownMenuItem key={org.id} asChild>
|
|
156
|
+
<Link
|
|
157
|
+
href={`/organizations/${org.slug}/projects`}
|
|
158
|
+
className="flex items-center justify-between"
|
|
159
|
+
>
|
|
160
|
+
<span>{org.name}</span>
|
|
161
|
+
{org.slug === params?.slug && <Check size={14} />}
|
|
162
|
+
</Link>
|
|
163
|
+
</DropdownMenuItem>
|
|
164
|
+
))}
|
|
165
|
+
<DropdownMenuSeparator />
|
|
166
|
+
<DropdownMenuItem asChild>
|
|
167
|
+
<Link href="/organizations">All organizations</Link>
|
|
168
|
+
</DropdownMenuItem>
|
|
169
|
+
<DropdownMenuItem asChild>
|
|
170
|
+
<Link
|
|
171
|
+
href="/organizations/new"
|
|
172
|
+
className="flex items-center gap-1.5"
|
|
173
|
+
>
|
|
174
|
+
<Plus size={14} />
|
|
175
|
+
New organization
|
|
176
|
+
</Link>
|
|
177
|
+
</DropdownMenuItem>
|
|
178
|
+
</DropdownMenuContent>
|
|
179
|
+
</DropdownMenu>
|
|
180
|
+
</SidebarMenuItem>
|
|
181
|
+
</SidebarMenu>
|
|
182
|
+
</SidebarHeader>
|
|
183
|
+
|
|
184
|
+
<SidebarContent>
|
|
185
|
+
<SidebarGroup>
|
|
186
|
+
<SidebarGroupContent>
|
|
187
|
+
<SidebarMenu className="gap-1 px-0 py-1">
|
|
188
|
+
<SidebarMenuItem>
|
|
189
|
+
<SidebarMenuButton
|
|
190
|
+
asChild
|
|
191
|
+
isActive={false}
|
|
192
|
+
tooltip={orgNavItem.label}
|
|
193
|
+
className={navLinkClass(isHomeActive)}
|
|
194
|
+
>
|
|
195
|
+
<Link href={homeHref}>
|
|
196
|
+
<orgNavItem.icon />
|
|
197
|
+
<span>{orgNavItem.label}</span>
|
|
198
|
+
</Link>
|
|
199
|
+
</SidebarMenuButton>
|
|
200
|
+
</SidebarMenuItem>
|
|
201
|
+
|
|
202
|
+
<SidebarSeparator className="my-2 group-data-[collapsible=icon]:hidden" />
|
|
203
|
+
|
|
204
|
+
{projectNavItems.map(({ icon: Icon, label, segment }) => {
|
|
205
|
+
const href = buildNavHref(
|
|
206
|
+
orgSlug,
|
|
207
|
+
projectSlug,
|
|
208
|
+
segment,
|
|
209
|
+
"project",
|
|
210
|
+
);
|
|
211
|
+
const isActive = isNavItemActive(
|
|
212
|
+
pathname,
|
|
213
|
+
orgSlug,
|
|
214
|
+
projectSlug,
|
|
215
|
+
segment,
|
|
216
|
+
"project",
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<SidebarMenuItem key={label}>
|
|
221
|
+
<SidebarMenuButton
|
|
222
|
+
asChild
|
|
223
|
+
isActive={false}
|
|
224
|
+
tooltip={label}
|
|
225
|
+
className={navLinkClass(isActive)}
|
|
226
|
+
>
|
|
227
|
+
<Link href={href}>
|
|
228
|
+
<Icon />
|
|
229
|
+
<span>{label}</span>
|
|
230
|
+
</Link>
|
|
231
|
+
</SidebarMenuButton>
|
|
232
|
+
</SidebarMenuItem>
|
|
233
|
+
);
|
|
234
|
+
})}
|
|
235
|
+
</SidebarMenu>
|
|
236
|
+
</SidebarGroupContent>
|
|
237
|
+
</SidebarGroup>
|
|
238
|
+
</SidebarContent>
|
|
239
|
+
|
|
240
|
+
<SidebarFooter>
|
|
241
|
+
<SidebarMenu>
|
|
242
|
+
<SidebarMenuItem>
|
|
243
|
+
<DropdownMenu>
|
|
244
|
+
<DropdownMenuTrigger asChild>
|
|
245
|
+
<SidebarMenuButton
|
|
246
|
+
size="lg"
|
|
247
|
+
className="data-[state=open]:bg-sidebar-accent"
|
|
248
|
+
>
|
|
249
|
+
<Avatar className="size-8 rounded-lg">
|
|
250
|
+
<AvatarFallback className="rounded-lg">
|
|
251
|
+
{(user.name ?? user.email).charAt(0).toUpperCase()}
|
|
252
|
+
</AvatarFallback>
|
|
253
|
+
</Avatar>
|
|
254
|
+
<div className="flex flex-col gap-0.5 leading-none">
|
|
255
|
+
<span className="font-semibold">
|
|
256
|
+
{user.name ?? "Account"}
|
|
257
|
+
</span>
|
|
258
|
+
<span className="text-xs text-muted-foreground truncate">
|
|
259
|
+
{user.email}
|
|
260
|
+
</span>
|
|
261
|
+
</div>
|
|
262
|
+
<ChevronsUpDown className="ml-auto" />
|
|
263
|
+
</SidebarMenuButton>
|
|
264
|
+
</DropdownMenuTrigger>
|
|
265
|
+
<DropdownMenuContent
|
|
266
|
+
className="w-[--radix-popper-anchor-width]"
|
|
267
|
+
side="top"
|
|
268
|
+
align="start"
|
|
269
|
+
>
|
|
270
|
+
<DropdownMenuItem asChild>
|
|
271
|
+
<Link
|
|
272
|
+
href={`/organizations/${orgSlug ?? currentOrg?.slug}/settings/members`}
|
|
273
|
+
>
|
|
274
|
+
Settings
|
|
275
|
+
</Link>
|
|
276
|
+
</DropdownMenuItem>
|
|
277
|
+
<DropdownMenuSeparator />
|
|
278
|
+
<DropdownMenuItem
|
|
279
|
+
asChild
|
|
280
|
+
onSelect={(event) => event.preventDefault()}
|
|
281
|
+
>
|
|
282
|
+
<form action={signOut}>
|
|
283
|
+
<button
|
|
284
|
+
type="submit"
|
|
285
|
+
className="flex w-full items-center gap-1.5 text-destructive text-sm"
|
|
286
|
+
>
|
|
287
|
+
<LogOut size={14} />
|
|
288
|
+
Sign out
|
|
289
|
+
</button>
|
|
290
|
+
</form>
|
|
291
|
+
</DropdownMenuItem>
|
|
292
|
+
</DropdownMenuContent>
|
|
293
|
+
</DropdownMenu>
|
|
294
|
+
</SidebarMenuItem>
|
|
295
|
+
</SidebarMenu>
|
|
296
|
+
</SidebarFooter>
|
|
297
|
+
|
|
298
|
+
<SidebarRail />
|
|
299
|
+
</Sidebar>
|
|
300
|
+
);
|
|
301
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { MenuIcon, XIcon } from "lucide-react";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
7
|
+
|
|
8
|
+
export default function Navbar() {
|
|
9
|
+
const [openMobileMenu, setOpenMobileMenu] = useState(false);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (openMobileMenu) {
|
|
13
|
+
document.body.classList.add("max-md:overflow-hidden");
|
|
14
|
+
} else {
|
|
15
|
+
document.body.classList.remove("max-md:overflow-hidden");
|
|
16
|
+
}
|
|
17
|
+
}, [openMobileMenu]);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<nav
|
|
21
|
+
className={`flex items-center justify-between w-full px-6 md:px-16 lg:px-24 xl:px-32 py-4 ${openMobileMenu ? "" : "backdrop-blur"}`}
|
|
22
|
+
>
|
|
23
|
+
<Link className="flex items-center gap-2 italic font-semibold" href="/">
|
|
24
|
+
<Image
|
|
25
|
+
className="h-9 md:h-9.5 w-auto shrink-0 "
|
|
26
|
+
src="/logo.svg"
|
|
27
|
+
alt="Logo"
|
|
28
|
+
width={140}
|
|
29
|
+
height={40}
|
|
30
|
+
priority
|
|
31
|
+
fetchPriority="high"
|
|
32
|
+
/>
|
|
33
|
+
ta_data_mas
|
|
34
|
+
</Link>
|
|
35
|
+
{/* Mobile menu */}
|
|
36
|
+
<div
|
|
37
|
+
className={`fixed inset-0 flex flex-col items-center justify-center gap-6 text-lg font-medium bg-white/60 dark:bg-black/40 backdrop-blur-md md:hidden transition duration-300 ${openMobileMenu ? "translate-x-0" : "-translate-x-full"}`}
|
|
38
|
+
>
|
|
39
|
+
<button className="italic font-semibold">
|
|
40
|
+
<a href="/login">Sign in</a>
|
|
41
|
+
</button>
|
|
42
|
+
<button
|
|
43
|
+
className="aspect-square size-10 p-1 items-center justify-center bg-grey-800 hover:bg-grey-800 transition text-black rounded-md flex"
|
|
44
|
+
onClick={() => setOpenMobileMenu(false)}
|
|
45
|
+
>
|
|
46
|
+
<XIcon />
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="flex items-center gap-4">
|
|
50
|
+
<button className="hidden md:block hover:bg-slate-100 dark:hover:bg-slate-950 transition px-4 py-2 border border-slate-600 rounded-md italic font-semibold">
|
|
51
|
+
<a href="/login">Sign in</a>
|
|
52
|
+
</button>
|
|
53
|
+
|
|
54
|
+
<button
|
|
55
|
+
onClick={() => setOpenMobileMenu(!openMobileMenu)}
|
|
56
|
+
className="md:hidden"
|
|
57
|
+
>
|
|
58
|
+
<MenuIcon size={26} className="active:scale-90 transition" />
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
</nav>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useParams } from "next/navigation";
|
|
4
|
+
import { SidebarTrigger } from "@/components/ui/sidebar";
|
|
5
|
+
import { Separator } from "@/components/ui/separator";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import { Check, ChevronsUpDown } from "lucide-react";
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuItem,
|
|
12
|
+
DropdownMenuSeparator,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from "./ui/dropdown-menu";
|
|
15
|
+
import { Project } from "@apiDatabase/types";
|
|
16
|
+
import Link from "next/link";
|
|
17
|
+
|
|
18
|
+
interface TopNavProps {
|
|
19
|
+
projects: Project[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function TopNav({ projects }: TopNavProps) {
|
|
23
|
+
const params = useParams<{ slug: string; projectSlug: string }>();
|
|
24
|
+
const currentProject = projects.find((p) => p.slug === params?.projectSlug);
|
|
25
|
+
|
|
26
|
+
console.log("projects", projects);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<header className="flex h-12 shrink-0 items-center gap-2 border-b px-4">
|
|
30
|
+
<SidebarTrigger className="-ml-1" />
|
|
31
|
+
<Separator orientation="vertical" className="mr-2 h-4" />
|
|
32
|
+
|
|
33
|
+
{params?.slug && (
|
|
34
|
+
<DropdownMenu>
|
|
35
|
+
<DropdownMenuTrigger asChild>
|
|
36
|
+
<Button variant="ghost" size="sm" className="gap-1.5 font-normal">
|
|
37
|
+
<span className="text-sm">
|
|
38
|
+
{currentProject?.name ?? "Select project"}
|
|
39
|
+
</span>
|
|
40
|
+
<ChevronsUpDown size={12} className="text-muted-foreground" />
|
|
41
|
+
</Button>
|
|
42
|
+
</DropdownMenuTrigger>
|
|
43
|
+
<DropdownMenuContent align="start" className="w-56">
|
|
44
|
+
{projects.map((project) => (
|
|
45
|
+
<DropdownMenuItem key={project.id} asChild>
|
|
46
|
+
<Link
|
|
47
|
+
href={`/organizations/${params.slug}/${project.slug}/database`}
|
|
48
|
+
className="flex items-center justify-between"
|
|
49
|
+
>
|
|
50
|
+
<span>{project.name}</span>
|
|
51
|
+
{project.slug === params?.projectSlug && (
|
|
52
|
+
<Check size={14} className="text-muted-foreground" />
|
|
53
|
+
)}
|
|
54
|
+
</Link>
|
|
55
|
+
</DropdownMenuItem>
|
|
56
|
+
))}
|
|
57
|
+
<DropdownMenuSeparator />
|
|
58
|
+
<DropdownMenuItem asChild>
|
|
59
|
+
<Link href={`/organizations/${params.slug}/projects`}>
|
|
60
|
+
All projects
|
|
61
|
+
</Link>
|
|
62
|
+
</DropdownMenuItem>
|
|
63
|
+
</DropdownMenuContent>
|
|
64
|
+
</DropdownMenu>
|
|
65
|
+
)}
|
|
66
|
+
</header>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Avatar as AvatarPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
size = "default",
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
|
13
|
+
size?: "default" | "sm" | "lg"
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<AvatarPrimitive.Root
|
|
17
|
+
data-slot="avatar"
|
|
18
|
+
data-size={size}
|
|
19
|
+
className={cn(
|
|
20
|
+
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AvatarImage({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
32
|
+
return (
|
|
33
|
+
<AvatarPrimitive.Image
|
|
34
|
+
data-slot="avatar-image"
|
|
35
|
+
className={cn(
|
|
36
|
+
"aspect-square size-full rounded-full object-cover",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function AvatarFallback({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
48
|
+
return (
|
|
49
|
+
<AvatarPrimitive.Fallback
|
|
50
|
+
data-slot="avatar-fallback"
|
|
51
|
+
className={cn(
|
|
52
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
61
|
+
return (
|
|
62
|
+
<span
|
|
63
|
+
data-slot="avatar-badge"
|
|
64
|
+
className={cn(
|
|
65
|
+
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
|
|
66
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
67
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
68
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
data-slot="avatar-group"
|
|
80
|
+
className={cn(
|
|
81
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
82
|
+
className
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function AvatarGroupCount({
|
|
90
|
+
className,
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<"div">) {
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
data-slot="avatar-group-count"
|
|
96
|
+
className={cn(
|
|
97
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
|
98
|
+
className
|
|
99
|
+
)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
Avatar,
|
|
107
|
+
AvatarImage,
|
|
108
|
+
AvatarFallback,
|
|
109
|
+
AvatarGroup,
|
|
110
|
+
AvatarGroupCount,
|
|
111
|
+
AvatarBadge,
|
|
112
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
import { Slot } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
12
|
+
secondary:
|
|
13
|
+
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
16
|
+
outline:
|
|
17
|
+
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
20
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
function Badge({
|
|
30
|
+
className,
|
|
31
|
+
variant = "default",
|
|
32
|
+
asChild = false,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<"span"> &
|
|
35
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
36
|
+
const Comp = asChild ? Slot.Root : "span"
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Comp
|
|
40
|
+
data-slot="badge"
|
|
41
|
+
data-variant={variant}
|
|
42
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
import { Slot } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
12
|
+
outline:
|
|
13
|
+
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
14
|
+
secondary:
|
|
15
|
+
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
16
|
+
ghost:
|
|
17
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
18
|
+
destructive:
|
|
19
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
20
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default:
|
|
24
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
25
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
26
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
27
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
28
|
+
icon: "size-8",
|
|
29
|
+
"icon-xs":
|
|
30
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
31
|
+
"icon-sm":
|
|
32
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
33
|
+
"icon-lg": "size-9",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
variant: "default",
|
|
38
|
+
size: "default",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
function Button({
|
|
44
|
+
className,
|
|
45
|
+
variant = "default",
|
|
46
|
+
size = "default",
|
|
47
|
+
asChild = false,
|
|
48
|
+
...props
|
|
49
|
+
}: React.ComponentProps<"button"> &
|
|
50
|
+
VariantProps<typeof buttonVariants> & {
|
|
51
|
+
asChild?: boolean
|
|
52
|
+
}) {
|
|
53
|
+
const Comp = asChild ? Slot.Root : "button"
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Comp
|
|
57
|
+
data-slot="button"
|
|
58
|
+
data-variant={variant}
|
|
59
|
+
data-size={size}
|
|
60
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Button, buttonVariants }
|