Move dashboard to client components. (#26)

* Move to client components

* Move to client components

* Fix create team
This commit is contained in:
KM Koushik
2024-06-02 11:03:50 +10:00
committed by GitHub
parent f183905c9f
commit 4a37c66865
10 changed files with 297 additions and 178 deletions

View File

@@ -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 (
<div>
<div className="flex justify-between items-center">

View File

@@ -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 (
<div>
<div className="flex justify-between items-center">

View File

@@ -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 (
<div>
<div className="flex justify-between items-center">

View File

@@ -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 (
<NextAuthProvider session={session}>
<div className="flex min-h-screen w-full h-full">
<div className="hidden bg-muted/20 md:block md:w-[280px]">
<div className="flex h-full max-h-screen flex-col gap-2">
<div className="flex h-14 gap-4 items-center px-4 lg:h-[60px] lg:px-6">
<Link href="/" className="flex items-center gap-2 font-semibold">
<span className=" text-lg">Unsend</span>
</Link>
<span className="text-[10px] text-muted-foreground bg-muted p-0.5 px-2 rounded-full">
Early access
</span>
<NextAuthProvider>
<DashboardProvider>
<div className="flex min-h-screen w-full h-full">
<div className="hidden bg-muted/20 md:block md:w-[280px]">
<div className="flex h-full max-h-screen flex-col gap-2">
<div className="flex h-14 gap-4 items-center px-4 lg:h-[60px] lg:px-6">
<Link
href="/"
className="flex items-center gap-2 font-semibold"
>
<span className=" text-lg">Unsend</span>
</Link>
<span className="text-[10px] text-muted-foreground bg-muted p-0.5 px-2 rounded-full">
Early access
</span>
</div>
<div className="flex-1 h-full">
<nav className=" flex-1 h-full flex-col justify-between items-center px-2 text-sm font-medium lg:px-4">
<div>
<NavButton href="/dashboard">
<LayoutDashboard className="h-4 w-4" />
Dashboard
</NavButton>
<NavButton href="/emails">
<Mail className="h-4 w-4" />
Emails
</NavButton>
<NavButton href="/domains">
<Globe className="h-4 w-4" />
Domains
</NavButton>
<NavButton href="/contacts" comingSoon>
<BookUser className="h-4 w-4" />
Contacts
</NavButton>
<NavButton href="/contacts" comingSoon>
<Volume2 className="h-4 w-4" />
Marketing
</NavButton>
<NavButton href="/api-keys">
<Code className="h-4 w-4" />
Developer settings
</NavButton>
</div>
<div className=" absolute bottom-10 p-4">
<Link
href="https://docs.unsend.dev"
target="_blank"
className="flex gap-2 items-center"
>
<BookOpenText className="h-4 w-4" />
<span className="">Docs</span>
</Link>
</div>
</nav>
</div>
<div className="mt-auto p-4"></div>
</div>
<div className="flex-1 h-full">
<nav className=" flex-1 h-full flex-col justify-between items-center px-2 text-sm font-medium lg:px-4">
<div>
<NavButton href="/dashboard">
<LayoutDashboard className="h-4 w-4" />
Dashboard
</NavButton>
<NavButton href="/emails">
<Mail className="h-4 w-4" />
Emails
</NavButton>
<NavButton href="/domains">
<Globe className="h-4 w-4" />
Domains
</NavButton>
<NavButton href="/contacts" comingSoon>
<BookUser className="h-4 w-4" />
Contacts
</NavButton>
<NavButton href="/contacts" comingSoon>
<Volume2 className="h-4 w-4" />
Marketing
</NavButton>
<NavButton href="/api-keys">
<Code className="h-4 w-4" />
Developer settings
</NavButton>
</div>
<div className=" absolute bottom-10 p-4">
<Link
href="https://docs.unsend.dev"
target="_blank"
className="flex gap-2 items-center"
</div>
<div className="flex flex-1 flex-col">
<header className="flex h-14 items-center gap-4 md:hidden bg-muted/20 px-4 lg:h-[60px] lg:px-6">
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<ExternalLink className="h-4 w-4" />
<span className="">Docs</span>
</Link>
</div>
</nav>
</div>
<div className="mt-auto p-4"></div>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="flex flex-col">
<nav className="grid gap-2 text-lg font-medium">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl bg-muted px-3 py-2 text-foreground hover:text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Users className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Analytics
</Link>
</nav>
<div className="mt-auto"></div>
</SheetContent>
</Sheet>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="secondary"
size="icon"
className="rounded-full"
>
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="flex-1 overflow-y-auto h-full">
<div className="flex flex-col gap-4 p-4 w-full lg:max-w-6xl mx-auto lg:gap-6 lg:p-6">
{children}
</div>
</main>
</div>
</div>
<div className="flex flex-1 flex-col">
<header className="flex h-14 items-center gap-4 md:hidden bg-muted/20 px-4 lg:h-[60px] lg:px-6">
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="flex flex-col">
<nav className="grid gap-2 text-lg font-medium">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl bg-muted px-3 py-2 text-foreground hover:text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Users className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Analytics
</Link>
</nav>
<div className="mt-auto"></div>
</SheetContent>
</Sheet>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="secondary"
size="icon"
className="rounded-full"
>
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="flex-1 overflow-y-auto h-full">
<div className="flex flex-col gap-4 p-4 w-full lg:max-w-6xl mx-auto lg:gap-6 lg:p-6">
{children}
</div>
</main>
</div>
</div>
</DashboardProvider>
</NextAuthProvider>
);
}

View File

@@ -0,0 +1,9 @@
import { Logo } from "@unsend/ui/src/logo";
export const FullScreenLoading = () => {
return (
<div className="flex items-center justify-center min-h-screen">
<Logo className="w-10 h-10" />
</div>
);
};

View File

@@ -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<typeof FormSchema>) {
createTeam.mutate(data, {
onSuccess: () => {
utils.team.invalidate();
router.replace("/dashboard");
},
});

View File

@@ -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 <FullScreenLoading />;
}
if (!teams || teams.length === 0) {
return <CreateTeam />;
}
return <>{children}</>;
};

View File

@@ -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 <SessionProvider session={session}>{children}</SessionProvider>;
return (
<SessionProvider session={session}>
<AppAuthProvider>{children}</AppAuthProvider>
</SessionProvider>
);
};
const AppAuthProvider = ({ children }: { children: React.ReactNode }) => {
const { data: session, status } = useSession({ required: true });
if (status === "loading") {
return <FullScreenLoading />;
}
if (!session) {
return <LoginPage />;
}
if (!session.user.isBetaUser) {
return (
<div className="flex items-center justify-center min-h-screen ">
<div className="p-8 shadow-lg rounded-lg flex flex-col gap-4">
<Rocket />
<h1 className="text-2xl font-bold">You're on the Waitlist!</h1>
<p className=" text-secondary-muted">
Hang tight, we'll get to you as soon as possible.
</p>
</div>
</div>
);
}
return <>{children}</>;
};