diff --git a/apps/web/src/app/(dashboard)/api-keys/page.tsx b/apps/web/src/app/(dashboard)/api-keys/page.tsx index a38d994..c9de74c 100644 --- a/apps/web/src/app/(dashboard)/api-keys/page.tsx +++ b/apps/web/src/app/(dashboard)/api-keys/page.tsx @@ -1,7 +1,9 @@ +"use client"; + import ApiList from "./api-list"; import AddApiKey from "./add-api-key"; -export default async function ApiKeysPage() { +export default function ApiKeysPage() { return (
diff --git a/apps/web/src/app/(dashboard)/domains/page.tsx b/apps/web/src/app/(dashboard)/domains/page.tsx index 2db5ba5..c22dd84 100644 --- a/apps/web/src/app/(dashboard)/domains/page.tsx +++ b/apps/web/src/app/(dashboard)/domains/page.tsx @@ -1,7 +1,9 @@ +"use client"; + import DomainsList from "./domain-list"; import AddDomain from "./add-domain"; -export default async function DomainsPage() { +export default function DomainsPage() { return (
diff --git a/apps/web/src/app/(dashboard)/emails/page.tsx b/apps/web/src/app/(dashboard)/emails/page.tsx index f798358..ebd200b 100644 --- a/apps/web/src/app/(dashboard)/emails/page.tsx +++ b/apps/web/src/app/(dashboard)/emails/page.tsx @@ -1,10 +1,9 @@ -import dynamic from "next/dynamic"; +"use client"; -const EmailList = dynamic( - () => import("./email-list").then((mod) => mod.default), - { ssr: false } -); -export default async function EmailsPage() { +import dynamic from "next/dynamic"; +import EmailList from "./email-list"; + +export default function EmailsPage() { return (
diff --git a/apps/web/src/app/(dashboard)/layout.tsx b/apps/web/src/app/(dashboard)/layout.tsx index 565eb65..67a50be 100644 --- a/apps/web/src/app/(dashboard)/layout.tsx +++ b/apps/web/src/app/(dashboard)/layout.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { + BookOpenText, BookUser, CircleUser, Code, @@ -29,10 +30,12 @@ import { } from "@unsend/ui/src/dropdown-menu"; import { Sheet, SheetContent, SheetTrigger } from "@unsend/ui/src/sheet"; -import { NextAuthProvider } from "~/providers/next-auth"; import { getServerAuthSession } from "~/server/auth"; import { NavButton } from "./nav-button"; import { db } from "~/server/db"; +import { SessionProvider } from "next-auth/react"; +import { DashboardProvider } from "~/providers/dashboard-provider"; +import { NextAuthProvider } from "~/providers/next-auth"; export const metadata = { title: "Unsend", @@ -45,176 +48,161 @@ export default async function AuthenticatedDashboardLayout({ }: { children: React.ReactNode; }) { - const session = await getServerAuthSession(); - - if (!session?.user) { - redirect("/"); - } - - if (!session.user.isBetaUser) { - redirect("/wait-list"); - } - - const teamUser = await db.teamUser.findFirst({ - where: { - userId: session.user.id, - }, - }); - - if (!teamUser) { - redirect("/create-team"); - } - return ( - -
-
-
-
- - Unsend - - - Early access - + + +
+
+
+
+ + Unsend + + + Early access + +
+
+ +
+
-
- -
-
+ + Toggle navigation menu + + + + +
+
+ + + + + + + My Account + + Settings + Support + + Logout + + + +
+
+ {children} +
+
-
-
- - - - - - -
-
-
- - - - - - My Account - - Settings - Support - - Logout - - -
-
-
- {children} -
-
-
-
+ ); } diff --git a/apps/web/src/components/FullScreenLoading.tsx b/apps/web/src/components/FullScreenLoading.tsx new file mode 100644 index 0000000..b6711a3 --- /dev/null +++ b/apps/web/src/components/FullScreenLoading.tsx @@ -0,0 +1,9 @@ +import { Logo } from "@unsend/ui/src/logo"; + +export const FullScreenLoading = () => { + return ( +
+ +
+ ); +}; diff --git a/apps/web/src/app/create-team/page.tsx b/apps/web/src/components/team/CreateTeam.tsx similarity index 97% rename from apps/web/src/app/create-team/page.tsx rename to apps/web/src/components/team/CreateTeam.tsx index 17e1721..504a894 100644 --- a/apps/web/src/app/create-team/page.tsx +++ b/apps/web/src/components/team/CreateTeam.tsx @@ -26,6 +26,7 @@ const FormSchema = z.object({ export default function CreateTeam() { const createTeam = api.team.createTeam.useMutation(); + const utils = api.useUtils(); const router = useRouter(); @@ -39,6 +40,7 @@ export default function CreateTeam() { function onSubmit(data: z.infer) { createTeam.mutate(data, { onSuccess: () => { + utils.team.invalidate(); router.replace("/dashboard"); }, }); diff --git a/apps/web/src/providers/dashboard-provider.tsx b/apps/web/src/providers/dashboard-provider.tsx new file mode 100644 index 0000000..e0395d1 --- /dev/null +++ b/apps/web/src/providers/dashboard-provider.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { FullScreenLoading } from "~/components/FullScreenLoading"; +import CreateTeam from "~/components/team/CreateTeam"; +import { api } from "~/trpc/react"; + +export const DashboardProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const { data: teams, status } = api.team.getTeams.useQuery(); + + if (status === "pending") { + return ; + } + + if (!teams || teams.length === 0) { + return ; + } + + return <>{children}; +}; diff --git a/apps/web/src/providers/next-auth.tsx b/apps/web/src/providers/next-auth.tsx index 4ee71a6..d18b362 100644 --- a/apps/web/src/providers/next-auth.tsx +++ b/apps/web/src/providers/next-auth.tsx @@ -3,10 +3,13 @@ import React from "react"; import type { Session } from "next-auth"; -import { SessionProvider } from "next-auth/react"; +import { SessionProvider, useSession } from "next-auth/react"; +import LoginPage from "~/app/login/login-page"; +import { Rocket } from "lucide-react"; +import { FullScreenLoading } from "~/components/FullScreenLoading"; export type NextAuthProviderProps = { - session?: Session | null; + session?: Session | null | undefined; children: React.ReactNode; }; @@ -14,5 +17,37 @@ export const NextAuthProvider = ({ session, children, }: NextAuthProviderProps) => { - return {children}; + return ( + + {children} + + ); +}; + +const AppAuthProvider = ({ children }: { children: React.ReactNode }) => { + const { data: session, status } = useSession({ required: true }); + + if (status === "loading") { + return ; + } + + if (!session) { + return ; + } + + if (!session.user.isBetaUser) { + return ( +
+
+ +

You're on the Waitlist!

+

+ Hang tight, we'll get to you as soon as possible. +

+
+
+ ); + } + + return <>{children}; }; diff --git a/packages/ui/src/logo.tsx b/packages/ui/src/logo.tsx new file mode 100644 index 0000000..9fc8f16 --- /dev/null +++ b/packages/ui/src/logo.tsx @@ -0,0 +1,59 @@ +import React from "react"; + +export const Logo: React.FC> = ({ ...props }) => { + return ( + + + + + + + + + + + + + + + ); +}; diff --git a/packages/ui/styles/globals.css b/packages/ui/styles/globals.css index 00a22c3..b455c80 100644 --- a/packages/ui/styles/globals.css +++ b/packages/ui/styles/globals.css @@ -57,7 +57,7 @@ --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; + --destructive: 0 68% 41%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%;