Skip to content

Commit

Permalink
🎮 WOR-7 sign-in/out & create/switch workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
steeeee0223 committed Dec 6, 2024
1 parent f5c8068 commit c1dcb4d
Show file tree
Hide file tree
Showing 21 changed files with 620 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
"use client";

import Image from "next/image";

import { cn } from "@swy/ui/lib";

export interface CardProps {
id: number;
checked: boolean;
image: string;
title: string;
description: string;
onClick?: () => void;
}

export const Card = ({
id,
checked,
image,
title,
description,
onClick,
}: CardProps) => {
return (
<div
id={`${id}`}
role="button"
tabIndex={0}
className={cn(
"relative m-3 h-[280px] w-[230px] cursor-pointer select-none items-center justify-center whitespace-normal rounded-md border border-primary/20 bg-white py-10 text-center text-sm/[1.2] opacity-70 hover:opacity-100",
checked && "border-2 border-blue opacity-100",
)}
onClick={onClick}
>
<Image
src="https://www.notion.so/images/onboarding/unchecked.svg"
alt=""
className={cn("absolute right-3 top-3", checked && "hidden")}
height={24}
width={24}
/>
<Image
src="https://www.notion.so/images/onboarding/checked.svg"
alt=""
className={cn("absolute right-3 top-3", !checked && "hidden")}
height={24}
width={24}
/>
<div className="flex h-[90px] justify-center">
<Image src={image} alt="" className="h-full" />
</div>
<div className="m-4 flex-grow basis-full">
<header className="mt-[30px] text-lg font-semibold text-black">
{title}
</header>
<p className="mb-2 mt-3 text-sm/snug text-black/70">{description}</p>
</div>
</div>
);
};
137 changes: 137 additions & 0 deletions apps/playground/src/app/(platform)/(auth)/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client";

import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { v4 } from "uuid";

import { usePlatformStore } from "@swy/notion";
import { Button } from "@swy/ui/shadcn";
import { Plan, Role } from "@swy/validators";

import { WorkspaceModel } from "~/db";
import { useAppActions, useAppState, useMockDB } from "~/hooks";
import { Card, type CardProps } from "./_components/card";

const data: Omit<CardProps, "checked">[] = [
{
id: 0,
image:
"https://www.notion.so/images/onboarding/team-features-illustration.png",
title: "For my team",
description: "Collaborate on your docs, projects, and wikis.",
},
{
id: 1,
image: "https://www.notion.so/images/onboarding/use-case-note.png",
title: "For personal use",
description: "Write better. Think more clearly. Stay organized.",
},
{
id: 2,
image: "https://www.notion.so/images/onboarding/use-case-wiki.png",
title: "For school",
description: "Keep your notes, research, and tasks all in one place.",
},
];

export default function Page() {
const [checked, setChecked] = useState(-1);
const router = useRouter();
const { updateDB } = useMockDB();
const [isSignedIn, accountId] = useAppState((state) => [
state.isSignedIn,
state.userId,
]);
const { selectWorkspace } = useAppActions();
const addWorkspace = usePlatformStore((state) => state.addWorkspace);

const createWorkspace = async () => {
if (!accountId) {
router.push("/sign-in");
return;
}
const wid = v4();
const w: WorkspaceModel = {
id: wid,
name: "New Workspace",
createdBy: accountId,
domain: `workspace-${accountId.slice(0, 5)}`,
inviteToken: wid,
plan: Plan.FREE,
icon: { type: "text", text: "N" },
lastEditedAt: Date.now(),
};
await updateDB("workspaces", { [wid]: w });
const mem = {
id: v4(),
accountId,
workspaceId: w.id,
joinedAt: Date.now(),
role: Role.OWNER,
};
await updateDB("memberships", [mem]);
addWorkspace({
id: wid,
name: w.name,
icon: w.icon ?? { type: "text", text: w.name },
members: 1,
plan: w.plan,
role: mem.role,
});
console.log(`create success, redirecting to ${wid}`);
selectWorkspace(w.id);
};

useEffect(() => {
if (!isSignedIn) router.push("/sign-in");
}, [isSignedIn, router]);

return (
<>
<div className="flex min-h-screen flex-col justify-center">
<div className="relative mx-auto max-w-[520px] py-8 text-center">
<div>
<div className="font-sans text-3xl/tight font-semibold">
How are you planning to use Notion?
</div>
<div className="pt-0.5 text-lg/tight font-normal text-secondary dark:text-secondary-dark">
We’ll streamline your setup experience accordingly.
</div>
</div>
</div>
<div className="flex flex-col items-center pb-8">
<div className="flex flex-col items-center justify-center">
<div className="mb-8 mt-2.5 inline-flex w-full justify-center">
{data.map((props) => (
<Card
key={props.id}
{...props}
checked={checked === props.id}
onClick={() => setChecked(props.id)}
/>
))}
</div>
<Button
variant="blue"
size="sm"
className="w-[280px]"
disabled={!(checked in ([0, 1, 2] as const))}
onClick={() => void createWorkspace()}
>
Continue
</Button>
</div>
</div>
</div>
<Button
variant="hint"
size="sm"
tabIndex={0}
className="absolute right-[18px] top-[18px] text-primary dark:text-primary/80"
onClick={() => router.back()}
>
Cancel
</Button>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import { Button } from "@swy/ui/shadcn";
interface SignInButtonProps {
name: string;
avatarUrl: string;
onClick: () => void;
}

export const SignInButton: React.FC<SignInButtonProps> = ({
name,
avatarUrl,
onClick,
}) => {
return (
<Button variant="hint" className="flex h-fit w-full justify-between">
<Button
variant="hint"
className="flex h-fit w-full justify-between"
onClick={onClick}
>
<div className="flex w-full items-center gap-2.5">
<div className="relative flex-shrink-0">
<Image
Expand Down
41 changes: 38 additions & 3 deletions apps/playground/src/app/(platform)/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
"use client";

import { useRouter } from "next/navigation";

import { usePlatformStore } from "@swy/notion";
import { IconBlock } from "@swy/ui/shared";

import { accounts } from "~/db/accounts";
import { accounts } from "~/db/data/accounts";
import { useAppActions, useAppState, useMockDB } from "~/hooks";
import { SignInButton } from "./_components/sign-in-button";

export default function Page() {
const router = useRouter();
const { signIn } = useAppState();
const { goToOnboarding, selectWorkspace } = useAppActions();
const { findAccount, findAccountMemberships } = useMockDB();
/** Store */
const setUser = usePlatformStore((state) => state.setUser);
const setWorkspaces = usePlatformStore((state) => state.setWorkspaces);
const login = async (userId: string) => {
const u = await findAccount(userId);
if (!u) return router.push("/sign-in");

console.log(`set app state: ${userId}`);
setUser({
id: userId,
name: u.name,
email: u.email,
avatarUrl: u.avatarUrl,
});
signIn(userId);
const workspaces = await findAccountMemberships(userId);
if (workspaces.length === 0) return goToOnboarding();
setWorkspaces(workspaces);
selectWorkspace(workspaces[0]!.id);
};

return (
<div className="flex flex-col items-center gap-12 p-10">
<div className="flex w-[320px] flex-col items-center gap-4">
Expand All @@ -15,8 +46,12 @@ export default function Page() {
Sign in with ...
</h1>
<div className="flex w-[240px] flex-col items-center gap-3">
{Object.values(accounts).map(({ name, avatarUrl }) => (
<SignInButton name={name} avatarUrl={avatarUrl} />
{Object.values(accounts).map(({ id, name, avatarUrl }) => (
<SignInButton
name={name}
avatarUrl={avatarUrl}
onClick={() => login(id)}
/>
))}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import Image from "next/image";
import { PlusCircle } from "lucide-react";

import { usePlatformStore } from "@swy/notion";
import { Button } from "@swy/ui/shadcn";

interface Params {
params: { workspaceId: string };
}

const HomePage = ({ params: _ }: Params) => {
const activeWorkspace = usePlatformStore(
(state) => state.workspaces[state.activeWorkspace ?? ""],
);
/** Action */
const onSubmit = () => {
// TODO create doc
};

return (
<div className="flex h-full flex-col items-center justify-center space-y-4">
<Image
src="/empty.png"
height="300"
width="300"
alt="Empty"
className="dark:hidden"
/>
<Image
src="/empty-dark.png"
height="300"
width="300"
alt="Empty"
className="hidden dark:block"
/>
<h2 className="text-lg font-medium">
Welcome to {activeWorkspace?.name ?? "WorXpace"}
</h2>
<form action={onSubmit}>
<Button type="submit">
<PlusCircle className="mr-2 size-4" />
Create a note
</Button>
</form>
</div>
);
};

export default HomePage;
Loading

0 comments on commit c1dcb4d

Please sign in to comment.