From 5d03b2c41414a6ad6d519d189d911fcc88b8a444 Mon Sep 17 00:00:00 2001 From: Yehoshua Sandler Date: Mon, 19 May 2025 14:05:59 -0500 Subject: [PATCH] feat: added zustand global store, LeftSidebar set to tenant + user data --- package-lock.json | 32 +- package.json | 3 +- .../[tenant]/dashboard/page.client.tsx | 22 + .../{ => [tenant]}/dashboard/page.tsx | 11 +- .../(frontend)/[tenant]/layout.suspense.tsx | 48 ++ src/app/(frontend)/[tenant]/layout.tsx | 60 ++ .../{[slug] => [tenant]}/page.client.tsx | 0 .../(frontend)/{[slug] => [tenant]}/page.tsx | 0 src/app/(frontend)/dashboard/layout.tsx | 16 - src/app/(frontend)/globals.css | 51 +- src/app/(frontend)/layout.tsx | 18 +- src/app/(frontend)/page.client.tsx | 0 src/app/(frontend)/page.tsx | 2 +- src/components/nav-favorites.tsx | 33 +- src/components/nav-main.tsx | 30 +- src/components/nav-secondary.tsx | 27 +- src/components/nav-workspaces.tsx | 28 +- src/components/sidebar-left.tsx | 380 ++++------- src/components/team-switcher.tsx | 59 +- src/components/ui/sidebar.tsx | 623 +++++++++--------- src/stores/index.ts | 23 + tailwind.config.mjs | 204 +++--- 22 files changed, 854 insertions(+), 816 deletions(-) create mode 100644 src/app/(frontend)/[tenant]/dashboard/page.client.tsx rename src/app/(frontend)/{ => [tenant]}/dashboard/page.tsx (82%) create mode 100644 src/app/(frontend)/[tenant]/layout.suspense.tsx create mode 100644 src/app/(frontend)/[tenant]/layout.tsx rename src/app/(frontend)/{[slug] => [tenant]}/page.client.tsx (100%) rename src/app/(frontend)/{[slug] => [tenant]}/page.tsx (100%) delete mode 100644 src/app/(frontend)/dashboard/layout.tsx create mode 100644 src/app/(frontend)/page.client.tsx create mode 100644 src/stores/index.ts diff --git a/package-lock.json b/package-lock.json index 8246793..12c47a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,8 @@ "react-hook-form": "7.45.4", "sharp": "0.32.6", "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.4" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -15390,6 +15391,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 9125b37..6d99584 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "react-hook-form": "7.45.4", "sharp": "0.32.6", "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.4" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/src/app/(frontend)/[tenant]/dashboard/page.client.tsx b/src/app/(frontend)/[tenant]/dashboard/page.client.tsx new file mode 100644 index 0000000..a4968ec --- /dev/null +++ b/src/app/(frontend)/[tenant]/dashboard/page.client.tsx @@ -0,0 +1,22 @@ +'use client' + +import { Tenant, User } from '@/payload-types' +import useGlobal from '@/stores' + +type Props = { + user?: User + tenant?: Tenant +} + +const DashboardPageClient = (props?: Props) => { + const { user, tenant } = useGlobal() + return ( +
+

Testing Dashboard Zustand Data Here

+

{user?.email}

+

{tenant?.name}

+
+ ) +} + +export default DashboardPageClient diff --git a/src/app/(frontend)/dashboard/page.tsx b/src/app/(frontend)/[tenant]/dashboard/page.tsx similarity index 82% rename from src/app/(frontend)/dashboard/page.tsx rename to src/app/(frontend)/[tenant]/dashboard/page.tsx index 5b2eb80..eb77a25 100644 --- a/src/app/(frontend)/dashboard/page.tsx +++ b/src/app/(frontend)/[tenant]/dashboard/page.tsx @@ -8,6 +8,7 @@ import { } from '@/components/ui/breadcrumb' import { Separator } from '@/components/ui/separator' import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar' +import DashboardPageClient from './page.client' export default function Page() { return ( @@ -21,18 +22,18 @@ export default function Page() { - - Project Management & Task Tracking - + Dashboard -
+
+ +
-
+
diff --git a/src/app/(frontend)/[tenant]/layout.suspense.tsx b/src/app/(frontend)/[tenant]/layout.suspense.tsx new file mode 100644 index 0000000..1989acd --- /dev/null +++ b/src/app/(frontend)/[tenant]/layout.suspense.tsx @@ -0,0 +1,48 @@ +'use client' + +import { Tenant } from '@/payload-types' +import useGlobal from '@/stores' +import { redirect } from 'next/navigation' +import { AuthResult } from 'node_modules/payload/dist/auth/operations/auth' +import { PaginatedDocs } from 'payload' +import { ReactNode, use, useEffect } from 'react' + +type SuspenseProps = { + getUserPromise: Promise + getTenantPromise: Promise> + children: ReactNode +} +const RootLayoutSuspenseFrontend = (props: SuspenseProps) => { + const { getUserPromise, getTenantPromise, children } = props + + const { user: authedUser } = use(getUserPromise) + const foundTenants = use(getTenantPromise) + + const { setUser, setTenant } = useGlobal() + + useEffect(() => { + try { + if (!authedUser || !authedUser?.id) return redirect('/login') + + setUser(authedUser) + } catch (err) { + return redirect('/login') + } + }, [authedUser]) + + useEffect(() => { + try { + if (!foundTenants?.docs || !foundTenants.docs.length || !foundTenants.docs[0]) { + return redirect('/') + } + + setTenant(foundTenants.docs[0]) + } catch (err) { + return redirect('/') + } + }, [foundTenants]) + + return <>{children} +} + +export default RootLayoutSuspenseFrontend diff --git a/src/app/(frontend)/[tenant]/layout.tsx b/src/app/(frontend)/[tenant]/layout.tsx new file mode 100644 index 0000000..2ed9a19 --- /dev/null +++ b/src/app/(frontend)/[tenant]/layout.tsx @@ -0,0 +1,60 @@ +import configPromise from '@payload-config' +import { getPayload, PaginatedDocs } from 'payload' +import { headers as getHeaders } from 'next/headers.js' +import { ReactNode, Suspense } from 'react' +import RootLayoutSuspenseFrontend from './layout.suspense' +import { Tenant } from '@/payload-types' + +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +type Props = { + params: Promise<{ + tenant?: string + }> + children: ReactNode +} + +const RootLayoutFrontend = async (props: Props) => { + const { params, children } = props + const { tenant: tenantSlug } = await params + + const payload = await getPayload({ config: configPromise }) + const headers = await getHeaders() + + const getUserPromise = payload.auth({ headers }) + + const getTenantPromise = payload.find({ + collection: 'tenants', + limit: 1, + select: { + name: true, + domain: true, + slug: true, + }, + where: { + slug: { + equals: tenantSlug, + }, + }, + }) as Promise> + + return ( + + + Loading layout...

}> + + {children} + +
+ + + ) +} + +export default RootLayoutFrontend diff --git a/src/app/(frontend)/[slug]/page.client.tsx b/src/app/(frontend)/[tenant]/page.client.tsx similarity index 100% rename from src/app/(frontend)/[slug]/page.client.tsx rename to src/app/(frontend)/[tenant]/page.client.tsx diff --git a/src/app/(frontend)/[slug]/page.tsx b/src/app/(frontend)/[tenant]/page.tsx similarity index 100% rename from src/app/(frontend)/[slug]/page.tsx rename to src/app/(frontend)/[tenant]/page.tsx diff --git a/src/app/(frontend)/dashboard/layout.tsx b/src/app/(frontend)/dashboard/layout.tsx deleted file mode 100644 index a14e64f..0000000 --- a/src/app/(frontend)/dashboard/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export const metadata = { - title: 'Next.js', - description: 'Generated by Next.js', -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - {children} - - ) -} diff --git a/src/app/(frontend)/globals.css b/src/app/(frontend)/globals.css index 7178694..9d69cc1 100644 --- a/src/app/(frontend)/globals.css +++ b/src/app/(frontend)/globals.css @@ -2,6 +2,8 @@ @tailwind components; @tailwind utilities; +@custom-variant dark (&:is(.dark *)); + @layer base { h1, h2, @@ -89,8 +91,8 @@ --success: 196 100% 14%; --warning: 34 51% 25%; --error: 10 39% 43%; - } - .dark { + + --sidebar-background: 240 5.9% 10%; --sidebar-foreground: 240 4.8% 95.9%; --sidebar-primary: 224.3 76.3% 48%; @@ -100,6 +102,51 @@ --sidebar-border: 240 3.7% 15.9%; --sidebar-ring: 217.2 91.2% 59.8%; } + + .dark { + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + + --background: 0 0% 0%; + --foreground: 210 40% 98%; + + --card: 0 0% 4%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 0, 0%, 15%, 0.8; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + + --success: 196 100% 14%; + --warning: 34 51% 25%; + --error: 10 39% 43%; + } + + } @layer base { diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index e2e2af0..f58b836 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -2,20 +2,14 @@ import type { Metadata } from 'next' import React from 'react' -import { AdminBar } from '@/components/AdminBar' -import { Footer } from '@/Footer/Component' -import { Header } from '@/Header/Component' import { Providers } from '@/providers' import { InitTheme } from '@/providers/Theme/InitTheme' import { mergeOpenGraph } from '@/utilities/mergeOpenGraph' -import { draftMode } from 'next/headers' import './globals.css' import { getServerSideURL } from '@/utilities/getURL' export default async function RootLayout({ children }: { children: React.ReactNode }) { - const { isEnabled } = await draftMode() - return ( @@ -24,17 +18,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo - - - -
- {children} -