From 8290f1bbee466bebaae41fa9c62d74aa04838b62 Mon Sep 17 00:00:00 2001 From: Dev Talan Date: Fri, 5 Dec 2025 22:19:36 +0530 Subject: [PATCH 001/137] updated api to take folder ID --- apps/api/src/modules/v1/videos/model.ts | 1 + apps/api/src/modules/v1/videos/service.ts | 4 ++-- apps/dashboard/next-env.d.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/api/src/modules/v1/videos/model.ts b/apps/api/src/modules/v1/videos/model.ts index 72b1640..280b022 100644 --- a/apps/api/src/modules/v1/videos/model.ts +++ b/apps/api/src/modules/v1/videos/model.ts @@ -21,6 +21,7 @@ export const VideoModel = { // 3. Create Draft create: z.object({ title: z.string().min(3).max(100).optional(), + folderId: nanoIdValidation, filename: z.string().min(1), contentType: z.string().regex(/^video\//, "Must be a video file"), description: z.string().optional(), diff --git a/apps/api/src/modules/v1/videos/service.ts b/apps/api/src/modules/v1/videos/service.ts index 2fcd289..dc00724 100644 --- a/apps/api/src/modules/v1/videos/service.ts +++ b/apps/api/src/modules/v1/videos/service.ts @@ -3,7 +3,6 @@ import { db } from "@workspace/database/client"; import { videos } from "@workspace/database/schema/video-schema"; import { generateId } from "@workspace/database/utils/id"; import { - abortMultipartUpload, completeMultipartUpload, initMultipartUpload, signMultipartPart, @@ -16,7 +15,7 @@ export class VideoService { static async createDraft( userId: string, orgId: string, - data: { filename: string; title?: string }, + data: { filename: string; title?: string; folderId?: string | null }, ) { const videoId = generateId("vid"); const extension = data.filename.split(".").pop(); @@ -31,6 +30,7 @@ export class VideoService { title: data.title || data.filename, status: "waiting_upload", masterAccessUrl: s3Key, + folderId: data.folderId || null, }) .returning(); diff --git a/apps/dashboard/next-env.d.ts b/apps/dashboard/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/dashboard/next-env.d.ts +++ b/apps/dashboard/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. From bec7324e7b4b00bdc8ab38f348359f1824231d5e Mon Sep 17 00:00:00 2001 From: Dev Talan Date: Fri, 5 Dec 2025 22:51:56 +0530 Subject: [PATCH 002/137] implemented tanstack react query --- apps/dashboard/package.json | 1 + apps/dashboard/src/app/layout.tsx | 4 ++- .../src/components/query-provider.tsx | 21 +++++++++++++ apps/dashboard/src/lib/query-client.ts | 27 +++++++++++++++++ pnpm-lock.yaml | 30 +++++++++++++++---- 5 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 apps/dashboard/src/components/query-provider.tsx create mode 100644 apps/dashboard/src/lib/query-client.ts diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index c998570..d2e5048 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -18,6 +18,7 @@ "@hugeicons/core-free-icons": "^2.0.0", "@hugeicons/react": "^1.1.1", "@tanstack/react-query": "^5.90.12", + "@tanstack/react-query-devtools": "^5.91.1", "@workspace/database": "workspace:^", "@workspace/seo": "workspace:^", "@workspace/ui": "workspace:*", diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index 9ff292c..d44255e 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -3,6 +3,8 @@ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import QueryProvider from "../components/query-provider"; + const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], @@ -28,7 +30,7 @@ export default function RootLayout({ - {children} + {children} ); diff --git a/apps/dashboard/src/components/query-provider.tsx b/apps/dashboard/src/components/query-provider.tsx new file mode 100644 index 0000000..a7f8951 --- /dev/null +++ b/apps/dashboard/src/components/query-provider.tsx @@ -0,0 +1,21 @@ +"use client"; + +import type { PropsWithChildren } from "react"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + +import { getQueryClient } from "../lib/query-client"; + +const QueryProvider = ({ children }: PropsWithChildren) => { + const queryClient = getQueryClient(); + + return ( + + {children} + + + + ); +}; + +export default QueryProvider; diff --git a/apps/dashboard/src/lib/query-client.ts b/apps/dashboard/src/lib/query-client.ts new file mode 100644 index 0000000..d30acd0 --- /dev/null +++ b/apps/dashboard/src/lib/query-client.ts @@ -0,0 +1,27 @@ +import { isServer, QueryClient } from "@tanstack/react-query"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // With SSR, we usually want to set some default staleTime + // above 0 to avoid refetching immediately on the client + staleTime: 60 * 1000, + }, + }, + }); +} + +let browserQueryClient: QueryClient | undefined = undefined; + +export function getQueryClient() { + if (isServer) { + // Server: always make a new query client + return makeQueryClient(); + } else { + // Browser: make a new query client if we don't already have one + // This is very important, so we don't re-make a new client if React suspends during the initial render. + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7e37e6..ba5c50f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@tanstack/react-query': specifier: ^5.90.12 version: 5.90.12(react@19.2.1) + '@tanstack/react-query-devtools': + specifier: ^5.91.1 + version: 5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1) '@workspace/database': specifier: workspace:^ version: link:../../packages/database @@ -2843,6 +2846,15 @@ packages: '@tanstack/query-core@5.90.12': resolution: {integrity: sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==} + '@tanstack/query-devtools@5.91.1': + resolution: {integrity: sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg==} + + '@tanstack/react-query-devtools@5.91.1': + resolution: {integrity: sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ==} + peerDependencies: + '@tanstack/react-query': ^5.90.10 + react: ^18 || ^19 + '@tanstack/react-query@5.90.12': resolution: {integrity: sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==} peerDependencies: @@ -8348,6 +8360,14 @@ snapshots: '@tanstack/query-core@5.90.12': {} + '@tanstack/query-devtools@5.91.1': {} + + '@tanstack/react-query-devtools@5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1)': + dependencies: + '@tanstack/query-devtools': 5.91.1 + '@tanstack/react-query': 5.90.12(react@19.2.1) + react: 19.2.1 + '@tanstack/react-query@5.90.12(react@19.2.1)': dependencies: '@tanstack/query-core': 5.90.12 @@ -9417,7 +9437,7 @@ snapshots: '@next/eslint-plugin-next': 16.0.7 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) @@ -9444,7 +9464,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -9459,13 +9479,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -9480,7 +9500,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 From f5d29dfd2def5094f0ea5fec3a41fc5566648699 Mon Sep 17 00:00:00 2001 From: Dev Talan Date: Fri, 5 Dec 2025 22:57:17 +0530 Subject: [PATCH 003/137] improved path --- apps/dashboard/next-env.d.ts | 2 +- apps/dashboard/src/app/auth/login/page.tsx | 2 +- apps/dashboard/src/app/auth/signup/page.tsx | 2 +- apps/dashboard/src/app/dashboard/layout.tsx | 2 +- apps/dashboard/src/features/auth/components/login-form.tsx | 2 +- apps/dashboard/src/features/dashboard/components/nav-user.tsx | 2 +- apps/dashboard/src/proxy.ts | 2 +- apps/dashboard/src/utils/provider-signin.ts | 4 ++-- apps/dashboard/tsconfig.json | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/dashboard/next-env.d.ts b/apps/dashboard/next-env.d.ts index c4b7818..9edff1c 100644 --- a/apps/dashboard/next-env.d.ts +++ b/apps/dashboard/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/dashboard/src/app/auth/login/page.tsx b/apps/dashboard/src/app/auth/login/page.tsx index 5530e1b..4b39986 100644 --- a/apps/dashboard/src/app/auth/login/page.tsx +++ b/apps/dashboard/src/app/auth/login/page.tsx @@ -1,4 +1,4 @@ -import { AuthHeader, LoginForm } from "@/features/auth"; +import { AuthHeader, LoginForm } from "@dashboard/features/auth"; export default function LoginPage() { return ( diff --git a/apps/dashboard/src/app/auth/signup/page.tsx b/apps/dashboard/src/app/auth/signup/page.tsx index 91fe8cb..df447b9 100644 --- a/apps/dashboard/src/app/auth/signup/page.tsx +++ b/apps/dashboard/src/app/auth/signup/page.tsx @@ -1,4 +1,4 @@ -import { AuthHeader, SignupForm } from "@/features/auth"; +import { AuthHeader, SignupForm } from "@dashboard/features/auth"; export default function SignupPage() { return ( diff --git a/apps/dashboard/src/app/dashboard/layout.tsx b/apps/dashboard/src/app/dashboard/layout.tsx index a77e1ac..bc72620 100644 --- a/apps/dashboard/src/app/dashboard/layout.tsx +++ b/apps/dashboard/src/app/dashboard/layout.tsx @@ -1,4 +1,4 @@ -import { AppSidebar } from "@/features/dashboard"; +import { AppSidebar } from "@dashboard/features/dashboard"; import { Breadcrumb, diff --git a/apps/dashboard/src/features/auth/components/login-form.tsx b/apps/dashboard/src/features/auth/components/login-form.tsx index fd0f62f..1ca2e84 100644 --- a/apps/dashboard/src/features/auth/components/login-form.tsx +++ b/apps/dashboard/src/features/auth/components/login-form.tsx @@ -1,6 +1,6 @@ "use client"; -import { providerSignIn } from "@/utils/provider-signin"; +import { providerSignIn } from "@dashboard/utils/provider-signin"; import { DiscordFreeIcons, GithubIcon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; diff --git a/apps/dashboard/src/features/dashboard/components/nav-user.tsx b/apps/dashboard/src/features/dashboard/components/nav-user.tsx index f4f0a98..99e5176 100644 --- a/apps/dashboard/src/features/dashboard/components/nav-user.tsx +++ b/apps/dashboard/src/features/dashboard/components/nav-user.tsx @@ -1,7 +1,7 @@ "use client"; import { useRouter } from "next/navigation"; -import { auth } from "@/lib/auth"; +import { auth } from "@dashboard/lib/auth"; import { BadgeCheck, Bell, diff --git a/apps/dashboard/src/proxy.ts b/apps/dashboard/src/proxy.ts index 4af1f63..226c406 100644 --- a/apps/dashboard/src/proxy.ts +++ b/apps/dashboard/src/proxy.ts @@ -4,7 +4,7 @@ import { authRoutes, DEFAULT_LOGIN_REDIRECT, protectedRoutes, -} from "@/constants/route"; +} from "@dashboard/constants/route"; import { getSessionCookie } from "better-auth/cookies"; export async function proxy(request: NextRequest) { diff --git a/apps/dashboard/src/utils/provider-signin.ts b/apps/dashboard/src/utils/provider-signin.ts index b3c50dd..55a17a1 100644 --- a/apps/dashboard/src/utils/provider-signin.ts +++ b/apps/dashboard/src/utils/provider-signin.ts @@ -1,5 +1,5 @@ -import { DEFAULT_LOGIN_REDIRECT } from "@/constants/route"; -import { auth } from "@/lib/auth"; +import { DEFAULT_LOGIN_REDIRECT } from "@dashboard/constants/route"; +import { auth } from "@dashboard/lib/auth"; export const providerSignIn = async (provider: "github" | "discord") => { await auth.signIn.social({ provider, callbackURL: DEFAULT_LOGIN_REDIRECT }); diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index c4682b3..32a3308 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["./src/*"], + "@dashboard/*": ["./src/*"], "@workspace/ui/*": ["../../packages/ui/src/*"], "@server/*": ["../api/src/*"] }, From 0d3d1cb391024ac4f11b9869c4e694b4e52038a4 Mon Sep 17 00:00:00 2001 From: Dev Talan Date: Fri, 5 Dec 2025 23:55:41 +0530 Subject: [PATCH 004/137] new sidebar --- apps/dashboard/package.json | 2 + .../dashboard/components/app-sidebar.tsx | 97 +++++++++--- .../features/dashboard/components/logo.tsx | 23 +++ .../dashboard/components/nav-main.tsx | 142 ++++++++++++------ .../components/nav-notifications.tsx | 67 +++++++++ .../dashboard/components/team-switcher.tsx | 91 +++++++++++ pnpm-lock.yaml | 22 +++ 7 files changed, 380 insertions(+), 64 deletions(-) create mode 100644 apps/dashboard/src/features/dashboard/components/logo.tsx create mode 100644 apps/dashboard/src/features/dashboard/components/nav-notifications.tsx create mode 100644 apps/dashboard/src/features/dashboard/components/team-switcher.tsx diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index d2e5048..4b43980 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@elysiajs/eden": "^1.4.5", + "@gsap/react": "^2.1.2", "@hugeicons/core-free-icons": "^2.0.0", "@hugeicons/react": "^1.1.1", "@tanstack/react-query": "^5.90.12", @@ -23,6 +24,7 @@ "@workspace/seo": "workspace:^", "@workspace/ui": "workspace:*", "better-auth": "^1.4.5", + "gsap": "^3.13.0", "lucide-react": "^0.556.0", "next": "16.0.7", "react": "19.2.1", diff --git a/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx b/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx index 47fb791..1a9394d 100644 --- a/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx +++ b/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx @@ -1,6 +1,9 @@ "use client"; import * as React from "react"; +import { useRef } from "react"; +import { useGSAP } from "@gsap/react"; +import gsap from "gsap"; import { BarChart2, Clapperboard, @@ -22,12 +25,41 @@ import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, + SidebarTrigger, + useSidebar, } from "@workspace/ui/components/sidebar"; +import { cn } from "@workspace/ui/lib/utils"; +import { Logo } from "./logo"; import { NavMain } from "./nav-main"; +import { NotificationsPopover } from "./nav-notifications"; import { NavSecondary } from "./nav-secondary"; import { NavUser } from "./nav-user"; +const sampleNotifications = [ + { + id: "1", + avatar: "/avatars/01.png", + fallback: "OM", + text: "New order received.", + time: "10m ago", + }, + { + id: "2", + avatar: "/avatars/02.png", + fallback: "JL", + text: "Server upgrade completed.", + time: "1h ago", + }, + { + id: "3", + avatar: "/avatars/03.png", + fallback: "HH", + text: "New user signed up.", + time: "2h ago", + }, +]; + const data = { user: { name: "shadcn", @@ -168,27 +200,56 @@ const data = { }, ], }; + export function AppSidebar({ ...props }: React.ComponentProps) { + const { state } = useSidebar(); + const isCollapsed = state === "collapsed"; + const headerRef = useRef(null); + + useGSAP(() => { + gsap.fromTo( + headerRef.current, + { opacity: 0 }, + { opacity: 1, duration: 0.8 }, + ); + }, [isCollapsed]); + return ( - - - - - - -
- -
-
- VidCastX - Studio -
-
-
-
-
+ + + + + {!isCollapsed && ( +
+ + VidCastX + + + Studio + +
+ )} +
+ +
+ + +
- + diff --git a/apps/dashboard/src/features/dashboard/components/logo.tsx b/apps/dashboard/src/features/dashboard/components/logo.tsx new file mode 100644 index 0000000..00eb8d6 --- /dev/null +++ b/apps/dashboard/src/features/dashboard/components/logo.tsx @@ -0,0 +1,23 @@ +import { SVGProps } from "react"; + +export const Logo = (props: SVGProps) => ( + + + + + + + + + + + + + +); diff --git a/apps/dashboard/src/features/dashboard/components/nav-main.tsx b/apps/dashboard/src/features/dashboard/components/nav-main.tsx index 3ead148..5bbd2ec 100644 --- a/apps/dashboard/src/features/dashboard/components/nav-main.tsx +++ b/apps/dashboard/src/features/dashboard/components/nav-main.tsx @@ -1,7 +1,9 @@ "use client"; import type { LucideIcon } from "lucide-react"; -import { ChevronRight } from "lucide-react"; +import { useState } from "react"; +import Link from "next/link"; +import { ChevronDown, ChevronUp } from "lucide-react"; import { Collapsible, @@ -9,20 +11,19 @@ import { CollapsibleTrigger, } from "@workspace/ui/components/collapsible"; import { - SidebarGroup, - SidebarGroupLabel, SidebarMenu, - SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, + useSidebar, } from "@workspace/ui/components/sidebar"; +import { cn } from "@workspace/ui/lib/utils"; export function NavMain({ items, - label = "Platform", + label, }: { items: { title: string; @@ -38,62 +39,111 @@ export function NavMain({ }[]; label?: string; }) { + const { state } = useSidebar(); + const isCollapsed = state === "collapsed"; + const [openCollapsible, setOpenCollapsible] = useState(null); + return ( - - {label} - - {items.map((item) => ( - - - + {label && !isCollapsed && ( +
+ {label} +
+ )} + {items.map((item) => { + const isOpen = !isCollapsed && openCollapsible === item.title; + const hasSubRoutes = !!item.items?.length; + + return ( + + {hasSubRoutes ? ( + + setOpenCollapsible(open ? item.title : null) + } + className="w-full" > - - - {item.title} - -
- {item.items?.length ? ( - <> - - - - Toggle - - + + + + {!isCollapsed && ( + + {item.title} + + )} + {!isCollapsed && ( + + {isOpen ? ( + + ) : ( + + )} + + )} + + + + {!isCollapsed && ( - + {item.items?.map((subItem) => ( - - + + {subItem.title} {subItem.badge && ( {subItem.badge} )} - + ))} - - ) : null} -
-
- ))} -
-
+ )} + + ) : ( + + + + {!isCollapsed && ( + + {item.title} + + )} + + + )} + + ); + })} + ); } diff --git a/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx b/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx new file mode 100644 index 0000000..e7bc44d --- /dev/null +++ b/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { BellIcon } from "lucide-react"; + +import { + Avatar, + AvatarFallback, + AvatarImage, +} from "@workspace/ui/components/avatar"; +import { Button } from "@workspace/ui/components/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@workspace/ui/components/dropdown-menu"; + +type Notification = { + id: string; + avatar: string; + fallback: string; + text: string; + time: string; +}; + +export function NotificationsPopover({ + notifications, +}: { + notifications: Notification[]; +}) { + return ( + + + + + + Notifications + + {notifications.map(({ id, avatar, fallback, text, time }) => ( + + + + {fallback} + +
+ {text} + {time} +
+
+ ))} + + + View all notifications + +
+
+ ); +} diff --git a/apps/dashboard/src/features/dashboard/components/team-switcher.tsx b/apps/dashboard/src/features/dashboard/components/team-switcher.tsx new file mode 100644 index 0000000..1781dc5 --- /dev/null +++ b/apps/dashboard/src/features/dashboard/components/team-switcher.tsx @@ -0,0 +1,91 @@ +"use client"; + +import * as React from "react"; +import { ChevronsUpDown, Plus } from "lucide-react"; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@workspace/ui/components/dropdown-menu"; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@workspace/ui/components/sidebar"; + +type Team = { + name: string; + logo: React.ElementType; + plan: string; +}; + +export function TeamSwitcher({ teams }: { teams: Team[] }) { + const { isMobile } = useSidebar(); + const [activeTeam, setActiveTeam] = React.useState(teams[0]); + + if (!activeTeam) return null; + + const Logo = activeTeam.logo; + + return ( + + + + + +
+ +
+
+ + {activeTeam.name} + + {activeTeam.plan} +
+ +
+
+ + + Teams + + {teams.map((team, index) => ( + setActiveTeam(team)} + className="gap-2 p-2" + > +
+ +
+ {team.name} + ⌘{index + 1} +
+ ))} + + +
+ +
+
Add team
+
+
+
+
+
+ ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba5c50f..519b53e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,9 @@ importers: '@elysiajs/eden': specifier: ^1.4.5 version: 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)) + '@gsap/react': + specifier: ^2.1.2 + version: 2.1.2(gsap@3.13.0)(react@19.2.1) '@hugeicons/core-free-icons': specifier: ^2.0.0 version: 2.0.0 @@ -123,6 +126,9 @@ importers: better-auth: specifier: ^1.4.5 version: 1.4.5(next@16.0.7(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + gsap: + specifier: ^3.13.0 + version: 3.13.0 lucide-react: specifier: ^0.556.0 version: 0.556.0(react@19.2.1) @@ -1361,6 +1367,12 @@ packages: engines: {node: '>=6'} hasBin: true + '@gsap/react@2.1.2': + resolution: {integrity: sha512-JqliybO1837UcgH2hVOM4VO+38APk3ECNrsuSM4MuXp+rbf+/2IG2K1YJiqfTcXQHH7XlA0m3ykniFYstfq0Iw==} + peerDependencies: + gsap: ^3.12.5 + react: '>=17' + '@hookform/resolvers@5.2.2': resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} peerDependencies: @@ -4214,6 +4226,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gsap@3.13.0: + resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==} + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -6775,6 +6790,11 @@ snapshots: protobufjs: 7.5.4 yargs: 17.7.2 + '@gsap/react@2.1.2(gsap@3.13.0)(react@19.2.1)': + dependencies: + gsap: 3.13.0 + react: 19.2.1 + '@hookform/resolvers@5.2.2(react-hook-form@7.68.0(react@19.2.1))': dependencies: '@standard-schema/utils': 0.3.0 @@ -9866,6 +9886,8 @@ snapshots: graphemer@1.4.0: {} + gsap@3.13.0: {} + handlebars@4.7.8: dependencies: minimist: 1.2.8 From 6eb0c95555588e6493e771538ddef85a7972ba00 Mon Sep 17 00:00:00 2001 From: Dev Talan Date: Sat, 6 Dec 2025 00:27:58 +0530 Subject: [PATCH 005/137] sidebar optimized --- apps/dashboard/next-env.d.ts | 2 +- .../dashboard/components/app-sidebar.tsx | 17 +++++++++-------- .../dashboard/components/nav-notifications.tsx | 5 ++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/dashboard/next-env.d.ts b/apps/dashboard/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/dashboard/next-env.d.ts +++ b/apps/dashboard/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx b/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx index 1a9394d..bb31fb2 100644 --- a/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx +++ b/apps/dashboard/src/features/dashboard/components/app-sidebar.tsx @@ -22,9 +22,6 @@ import { SidebarContent, SidebarFooter, SidebarHeader, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, SidebarTrigger, useSidebar, } from "@workspace/ui/components/sidebar"; @@ -218,7 +215,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { ) { ref={headerRef} className={cn( "flex items-center gap-2", - isCollapsed ? "flex-row md:flex-col-reverse" : "flex-row", + isCollapsed + ? "flex-row md:flex-col-reverse md:items-start" + : "flex-row", )} > - - + + - diff --git a/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx b/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx index e7bc44d..25b8d2b 100644 --- a/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx +++ b/apps/dashboard/src/features/dashboard/components/nav-notifications.tsx @@ -16,6 +16,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@workspace/ui/components/dropdown-menu"; +import { cn } from "@workspace/ui/lib/utils"; type Notification = { id: string; @@ -27,8 +28,10 @@ type Notification = { export function NotificationsPopover({ notifications, + className, }: { notifications: Notification[]; + className?: string; }) { return ( @@ -36,7 +39,7 @@ export function NotificationsPopover({