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.
Files changed (219) hide show
  1. package/README.md +42 -0
  2. package/apps/api/.prettierrc +4 -0
  3. package/apps/api/README.md +98 -0
  4. package/apps/api/drizzle/0000_right_drax.sql +87 -0
  5. package/apps/api/drizzle/meta/0000_snapshot.json +588 -0
  6. package/apps/api/drizzle/meta/_journal.json +13 -0
  7. package/apps/api/drizzle.config.ts +12 -0
  8. package/apps/api/eslint.config.mjs +35 -0
  9. package/apps/api/nest-cli.json +8 -0
  10. package/apps/api/package.json +99 -0
  11. package/apps/api/src/app.controller.spec.ts +25 -0
  12. package/apps/api/src/app.controller.ts +9 -0
  13. package/apps/api/src/app.module.ts +37 -0
  14. package/apps/api/src/app.service.ts +8 -0
  15. package/apps/api/src/auth/auth.controller.ts +113 -0
  16. package/apps/api/src/auth/auth.module.ts +15 -0
  17. package/apps/api/src/auth/auth.service.ts +309 -0
  18. package/apps/api/src/auth/decorators/current-user-decorator.ts +10 -0
  19. package/apps/api/src/auth/decorators/require-org-role-decorator.ts +7 -0
  20. package/apps/api/src/auth/dto/login.dto.ts +10 -0
  21. package/apps/api/src/auth/dto/register.dto.ts +14 -0
  22. package/apps/api/src/auth/guards/jwt-auth-guard.ts +40 -0
  23. package/apps/api/src/auth/guards/org-role.guards.ts +60 -0
  24. package/apps/api/src/db/db.module.ts +9 -0
  25. package/apps/api/src/db/drizzle.service.ts +16 -0
  26. package/apps/api/src/db/schema/index.ts +6 -0
  27. package/apps/api/src/db/schema/organizations.ts +30 -0
  28. package/apps/api/src/db/schema/projects.ts +26 -0
  29. package/apps/api/src/db/schema/query-history.ts +16 -0
  30. package/apps/api/src/db/schema/storage-buckets.ts +17 -0
  31. package/apps/api/src/db/schema/storage-objects.ts +18 -0
  32. package/apps/api/src/db/schema/users.ts +14 -0
  33. package/apps/api/src/main.ts +51 -0
  34. package/apps/api/src/members/dto/invite-member-dto.ts +6 -0
  35. package/apps/api/src/members/dto/update-role.dto.ts +8 -0
  36. package/apps/api/src/members/invite.service.ts +147 -0
  37. package/apps/api/src/members/members.controller.ts +56 -0
  38. package/apps/api/src/members/members.module.ts +13 -0
  39. package/apps/api/src/members/members.service.ts +85 -0
  40. package/apps/api/src/orgs/dto/create-org.dto.ts +8 -0
  41. package/apps/api/src/orgs/orgs.controller.ts +27 -0
  42. package/apps/api/src/orgs/orgs.module.ts +12 -0
  43. package/apps/api/src/orgs/orgs.service.ts +86 -0
  44. package/apps/api/src/project-api/project-api.controller.ts +95 -0
  45. package/apps/api/src/project-api/project-api.module.ts +12 -0
  46. package/apps/api/src/project-api/project-api.service.ts +196 -0
  47. package/apps/api/src/project-api/project-key.guard.ts +45 -0
  48. package/apps/api/src/project-api/query-parser.ts +129 -0
  49. package/apps/api/src/project-auth/dto/magic-link.dto.ts +7 -0
  50. package/apps/api/src/project-auth/dto/signin.dto.ts +10 -0
  51. package/apps/api/src/project-auth/dto/signup.dto.ts +11 -0
  52. package/apps/api/src/project-auth/project-auth-dashboard.controller.ts +35 -0
  53. package/apps/api/src/project-auth/project-auth.controller.ts +111 -0
  54. package/apps/api/src/project-auth/project-auth.module.ts +13 -0
  55. package/apps/api/src/project-auth/project-auth.service.ts +440 -0
  56. package/apps/api/src/projects/dto/create-project.dto.ts +9 -0
  57. package/apps/api/src/projects/projects.controller.ts +34 -0
  58. package/apps/api/src/projects/projects.module.ts +12 -0
  59. package/apps/api/src/projects/projects.service.ts +136 -0
  60. package/apps/api/src/realtime/realtime-socket.type.ts +14 -0
  61. package/apps/api/src/realtime/realtime.controller.ts +63 -0
  62. package/apps/api/src/realtime/realtime.gateway.ts +150 -0
  63. package/apps/api/src/realtime/realtime.module.ts +14 -0
  64. package/apps/api/src/realtime/realtime.service.ts +101 -0
  65. package/apps/api/src/realtime/trigger.service.ts +95 -0
  66. package/apps/api/src/sql-editor/dto/execute-query.dto.ts +7 -0
  67. package/apps/api/src/sql-editor/sql-editor.controller.ts +28 -0
  68. package/apps/api/src/sql-editor/sql-editor.module.ts +12 -0
  69. package/apps/api/src/sql-editor/sql-editor.service.ts +154 -0
  70. package/apps/api/src/storage/project-storage.controller.ts +140 -0
  71. package/apps/api/src/storage/storage.controller.ts +118 -0
  72. package/apps/api/src/storage/storage.module.ts +15 -0
  73. package/apps/api/src/storage/storage.service.ts +191 -0
  74. package/apps/api/src/storage/uploadthing.ts +23 -0
  75. package/apps/api/src/table-editor/dto/alter-table.dto.ts +15 -0
  76. package/apps/api/src/table-editor/dto/create-table.dto.ts +54 -0
  77. package/apps/api/src/table-editor/table-editor.controller.ts +117 -0
  78. package/apps/api/src/table-editor/table-editor.module.ts +12 -0
  79. package/apps/api/src/table-editor/table-editor.service.ts +366 -0
  80. package/apps/api/test/app.e2e-spec.ts +29 -0
  81. package/apps/api/test/jest-e2e.json +9 -0
  82. package/apps/api/tsconfig.build.json +4 -0
  83. package/apps/api/tsconfig.json +25 -0
  84. package/apps/web/.env.example +12 -0
  85. package/apps/web/AGENTS.md +9 -0
  86. package/apps/web/CLAUDE.md +1 -0
  87. package/apps/web/README.md +36 -0
  88. package/apps/web/components.json +25 -0
  89. package/apps/web/eslint.config.mjs +18 -0
  90. package/apps/web/next.config.ts +11 -0
  91. package/apps/web/package.json +48 -0
  92. package/apps/web/postcss.config.mjs +7 -0
  93. package/apps/web/public/file.svg +1 -0
  94. package/apps/web/public/globe.svg +1 -0
  95. package/apps/web/public/logo.svg +4 -0
  96. package/apps/web/public/next.svg +1 -0
  97. package/apps/web/public/vercel.svg +1 -0
  98. package/apps/web/public/window.svg +1 -0
  99. package/apps/web/src/app/(dashboard)/layout.tsx +33 -0
  100. package/apps/web/src/app/(dashboard)/organizations/(list)/layout.tsx +7 -0
  101. package/apps/web/src/app/(dashboard)/organizations/(list)/new/page.tsx +26 -0
  102. package/apps/web/src/app/(dashboard)/organizations/(list)/page.tsx +44 -0
  103. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/api/page.tsx +13 -0
  104. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/layout.tsx +19 -0
  105. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/page.tsx +10 -0
  106. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/providers/page.tsx +19 -0
  107. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/url-configuration/page.tsx +24 -0
  108. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/users/page.tsx +13 -0
  109. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/database/page.tsx +29 -0
  110. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/realtime/page.tsx +28 -0
  111. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/sql/page.tsx +21 -0
  112. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/storage/page.tsx +28 -0
  113. package/apps/web/src/app/(dashboard)/organizations/[slug]/layout.tsx +20 -0
  114. package/apps/web/src/app/(dashboard)/organizations/[slug]/projects/page.tsx +62 -0
  115. package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/members/page.tsx +28 -0
  116. package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/page.tsx +10 -0
  117. package/apps/web/src/app/(dashboard)/settings/page.tsx +12 -0
  118. package/apps/web/src/app/dashboard/page.tsx +14 -0
  119. package/apps/web/src/app/favicon.ico +0 -0
  120. package/apps/web/src/app/globals.css +115 -0
  121. package/apps/web/src/app/layout.tsx +36 -0
  122. package/apps/web/src/app/login/page.tsx +199 -0
  123. package/apps/web/src/app/page.tsx +100 -0
  124. package/apps/web/src/app/register/page.tsx +218 -0
  125. package/apps/web/src/components/app-sidebar.tsx +301 -0
  126. package/apps/web/src/components/navbar.tsx +63 -0
  127. package/apps/web/src/components/topNav.tsx +68 -0
  128. package/apps/web/src/components/ui/avatar.tsx +112 -0
  129. package/apps/web/src/components/ui/badge.tsx +48 -0
  130. package/apps/web/src/components/ui/button.tsx +66 -0
  131. package/apps/web/src/components/ui/card.tsx +102 -0
  132. package/apps/web/src/components/ui/checkbox.tsx +32 -0
  133. package/apps/web/src/components/ui/dialog.tsx +168 -0
  134. package/apps/web/src/components/ui/dropdown-menu.tsx +269 -0
  135. package/apps/web/src/components/ui/field.tsx +238 -0
  136. package/apps/web/src/components/ui/input.tsx +19 -0
  137. package/apps/web/src/components/ui/label.tsx +24 -0
  138. package/apps/web/src/components/ui/resizable.tsx +50 -0
  139. package/apps/web/src/components/ui/scroll-area.tsx +55 -0
  140. package/apps/web/src/components/ui/select.tsx +192 -0
  141. package/apps/web/src/components/ui/separator.tsx +28 -0
  142. package/apps/web/src/components/ui/sheet.tsx +147 -0
  143. package/apps/web/src/components/ui/sidebar.tsx +703 -0
  144. package/apps/web/src/components/ui/skeleton.tsx +13 -0
  145. package/apps/web/src/components/ui/switch.tsx +33 -0
  146. package/apps/web/src/components/ui/table.tsx +116 -0
  147. package/apps/web/src/components/ui/tooltip.tsx +57 -0
  148. package/apps/web/src/features/api-docs/api-docs-client.tsx +227 -0
  149. package/apps/web/src/features/api-docs/api-docs-helpers.server.ts +86 -0
  150. package/apps/web/src/features/auth/action.ts +67 -0
  151. package/apps/web/src/features/auth/client.schema.ts +13 -0
  152. package/apps/web/src/features/auth/constants.ts +6 -0
  153. package/apps/web/src/features/auth/cookies.ts +86 -0
  154. package/apps/web/src/features/auth/server.schema.ts +11 -0
  155. package/apps/web/src/features/auth/sign-out.ts +17 -0
  156. package/apps/web/src/features/members/action.ts +66 -0
  157. package/apps/web/src/features/members/client.schema.ts +11 -0
  158. package/apps/web/src/features/members/constants.ts +8 -0
  159. package/apps/web/src/features/members/members-client.tsx +198 -0
  160. package/apps/web/src/features/members/members-helpers.server.ts +21 -0
  161. package/apps/web/src/features/members/server.schema.ts +19 -0
  162. package/apps/web/src/features/organization/action.ts +45 -0
  163. package/apps/web/src/features/organization/client.schema.ts +5 -0
  164. package/apps/web/src/features/organization/constants.tsx +3 -0
  165. package/apps/web/src/features/organization/create-org-form.tsx +66 -0
  166. package/apps/web/src/features/organization/organization-helpers.server.ts +35 -0
  167. package/apps/web/src/features/organization/server.schema.ts +10 -0
  168. package/apps/web/src/features/project-auth/project-auth-action.ts +78 -0
  169. package/apps/web/src/features/project-auth/project-auth-client.schema.ts +18 -0
  170. package/apps/web/src/features/project-auth/project-auth-constants.ts +8 -0
  171. package/apps/web/src/features/project-auth/project-auth-helpers.server.ts +39 -0
  172. package/apps/web/src/features/project-auth/project-auth-providers.tsx +222 -0
  173. package/apps/web/src/features/project-auth/project-auth-server.schema.ts +22 -0
  174. package/apps/web/src/features/project-auth/project-auth-shell.tsx +106 -0
  175. package/apps/web/src/features/project-auth/project-auth-url-config.tsx +151 -0
  176. package/apps/web/src/features/project-auth/project-auth-users.tsx +138 -0
  177. package/apps/web/src/features/projects/action.ts +46 -0
  178. package/apps/web/src/features/projects/client.schema.ts +9 -0
  179. package/apps/web/src/features/projects/constants.ts +6 -0
  180. package/apps/web/src/features/projects/create-project-modal.tsx +117 -0
  181. package/apps/web/src/features/projects/project-helpers.server.ts +38 -0
  182. package/apps/web/src/features/projects/server.schema.ts +10 -0
  183. package/apps/web/src/features/realtime/realtime-client.tsx +294 -0
  184. package/apps/web/src/features/realtime/realtime-helpers.server.ts +40 -0
  185. package/apps/web/src/features/sql-editor/sql-editor-client.tsx +324 -0
  186. package/apps/web/src/features/sql-editor/sql-editor-helpers.server.ts +22 -0
  187. package/apps/web/src/features/storage/storage-client.tsx +500 -0
  188. package/apps/web/src/features/storage/storage-helpers.server.ts +36 -0
  189. package/apps/web/src/features/table-editor/action.ts +97 -0
  190. package/apps/web/src/features/table-editor/add-column-dialog.tsx +184 -0
  191. package/apps/web/src/features/table-editor/client.schema.ts +36 -0
  192. package/apps/web/src/features/table-editor/create-table-drawer.tsx +542 -0
  193. package/apps/web/src/features/table-editor/server.schema.ts +41 -0
  194. package/apps/web/src/features/table-editor/table-editor-client.tsx +737 -0
  195. package/apps/web/src/features/table-editor/table-editor-helpers.server.ts +39 -0
  196. package/apps/web/src/hooks/use-mobile.ts +19 -0
  197. package/apps/web/src/lib/axios.ts +8 -0
  198. package/apps/web/src/lib/utils.ts +6 -0
  199. package/apps/web/src/proxy.ts +72 -0
  200. package/apps/web/src/server-utils/utils.ts +10 -0
  201. package/apps/web/tsconfig.json +34 -0
  202. package/package.json +22 -0
  203. package/packages/apiDatabase-js/package.json +39 -0
  204. package/packages/apiDatabase-js/src/auth/index.ts +121 -0
  205. package/packages/apiDatabase-js/src/client.ts +25 -0
  206. package/packages/apiDatabase-js/src/db/index.ts +15 -0
  207. package/packages/apiDatabase-js/src/db/query-builder.ts +194 -0
  208. package/packages/apiDatabase-js/src/index.ts +16 -0
  209. package/packages/apiDatabase-js/src/realtime/index.ts +84 -0
  210. package/packages/apiDatabase-js/src/storage/index.ts +130 -0
  211. package/packages/apiDatabase-js/tsconfig.json +13 -0
  212. package/packages/apiDatabase-js/tsdown.config.ts +20 -0
  213. package/packages/constants/package.json +6 -0
  214. package/packages/constants/src/index.ts +76 -0
  215. package/packages/tsconfig.json +13 -0
  216. package/packages/tsdown.config.ts +20 -0
  217. package/packages/types/package.json +6 -0
  218. package/packages/types/src/index.ts +254 -0
  219. 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 }