feat: add card page
This commit is contained in:
65
src/app/_components/dashboard.tsx
Normal file
65
src/app/_components/dashboard.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import Image from "next/image";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerAuthSession } from "~/server/auth";
|
||||
|
||||
import boardsIcon from "~/app/assets/boards.json";
|
||||
|
||||
import ReactiveButton from "~/app/components/ReactiveButton";
|
||||
|
||||
const navigation = [
|
||||
{ name: "Boards", href: "/boards", icon: boardsIcon, current: true },
|
||||
];
|
||||
|
||||
export default async function Layout(props: { children: React.ReactNode }) {
|
||||
const session = await getServerAuthSession();
|
||||
|
||||
if (!session?.user) redirect("api/auth/signin");
|
||||
|
||||
return (
|
||||
<main className="flex h-screen flex-col items-center bg-dark-50">
|
||||
<div className="m-auto flex h-16 w-full justify-between border-b border-dark-600 px-5 py-2 align-middle">
|
||||
<div className="my-auto flex">
|
||||
<h1 className="text-lg font-normal tracking-tight text-dark-1000">
|
||||
貫 kan
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full w-full">
|
||||
<nav className="flex w-72 flex-col justify-between border-r border-dark-600 px-5 py-5">
|
||||
<div>
|
||||
<ul role="list" className="-mx-2 my-6 space-y-1">
|
||||
{navigation.map((item) => (
|
||||
<li key={item.name}>
|
||||
<ReactiveButton
|
||||
href={item.href}
|
||||
current={item.current}
|
||||
name={item.name}
|
||||
json={item.icon}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button className="flex items-center">
|
||||
{session?.user.image ? (
|
||||
<Image
|
||||
src={session?.user.image ?? ""}
|
||||
className="h-8 w-8 rounded-full bg-gray-50"
|
||||
width={30}
|
||||
height={30}
|
||||
alt=""
|
||||
/>
|
||||
) : null}
|
||||
<p className="ml-2 truncate text-sm text-dark-1000">
|
||||
{session?.user.email}
|
||||
</p>
|
||||
</button>
|
||||
</nav>
|
||||
<div className="w-full p-8">{props.children}</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { HiOutlinePlusSmall } from "react-icons/hi2";
|
||||
import {
|
||||
@@ -180,17 +180,16 @@ export default function BoardPage() {
|
||||
index={index}
|
||||
>
|
||||
{(provided) => (
|
||||
<div
|
||||
<Link
|
||||
key={card.publicId}
|
||||
className="mb-2 rounded-md border border-dark-200 bg-dark-500 px-3 py-2"
|
||||
href={`/cards/${card.publicId}`}
|
||||
className="mb-2 flex !cursor-pointer rounded-md border border-dark-200 bg-dark-500 px-3 py-2 text-sm text-dark-1000"
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
>
|
||||
<p className="text-sm text-dark-1000">
|
||||
{card.title}
|
||||
</p>
|
||||
</div>
|
||||
{card.title}
|
||||
</Link>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
|
||||
@@ -1,65 +1,5 @@
|
||||
import Image from "next/image";
|
||||
import { redirect } from "next/navigation";
|
||||
import Dashboard from "~/app/_components/dashboard";
|
||||
|
||||
import { getServerAuthSession } from "~/server/auth";
|
||||
|
||||
import boardsIcon from "~/app/assets/boards.json";
|
||||
|
||||
import ReactiveButton from "~/app/components/ReactiveButton";
|
||||
|
||||
const navigation = [
|
||||
{ name: "Boards", href: "/boards", icon: boardsIcon, current: true },
|
||||
];
|
||||
|
||||
export default async function Layout(props: { children: React.ReactNode }) {
|
||||
const session = await getServerAuthSession();
|
||||
|
||||
if (!session?.user) redirect("api/auth/signin");
|
||||
|
||||
return (
|
||||
<main className="flex h-screen flex-col items-center bg-dark-50">
|
||||
<div className="m-auto flex h-16 w-full justify-between border-b border-dark-600 px-5 py-2 align-middle">
|
||||
<div className="my-auto flex">
|
||||
<h1 className="text-lg font-normal tracking-tight text-dark-1000">
|
||||
貫 kan
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full w-full">
|
||||
<nav className="flex w-72 flex-col justify-between border-r border-dark-600 px-5 py-5">
|
||||
<div>
|
||||
<ul role="list" className="-mx-2 my-6 space-y-1">
|
||||
{navigation.map((item) => (
|
||||
<li key={item.name}>
|
||||
<ReactiveButton
|
||||
href={item.href}
|
||||
current={item.current}
|
||||
name={item.name}
|
||||
json={item.icon}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button className="flex items-center">
|
||||
{session?.user.image ? (
|
||||
<Image
|
||||
src={session?.user.image ?? ""}
|
||||
className="h-8 w-8 rounded-full bg-gray-50"
|
||||
width={30}
|
||||
height={30}
|
||||
alt=""
|
||||
/>
|
||||
) : null}
|
||||
<p className="ml-2 truncate text-sm text-dark-1000">
|
||||
{session?.user.email}
|
||||
</p>
|
||||
</button>
|
||||
</nav>
|
||||
<div className="w-full p-8">{props.children}</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
export default function Layout(props: { children: React.ReactNode }) {
|
||||
return <Dashboard>{props.children}</Dashboard>;
|
||||
}
|
||||
|
||||
85
src/app/cards/[...id]/page.tsx
Normal file
85
src/app/cards/[...id]/page.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import { useParams } from "next/navigation";
|
||||
import { HiOutlinePlusSmall } from "react-icons/hi2";
|
||||
import { Formik, Form, Field, type FieldProps } from "formik";
|
||||
|
||||
import { api } from "~/trpc/react";
|
||||
|
||||
interface FormValues {
|
||||
cardId: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default function CardPage() {
|
||||
const params = useParams();
|
||||
|
||||
const cardId = params?.id?.length && params.id[0];
|
||||
|
||||
if (!cardId) return <></>;
|
||||
|
||||
const { data } = api.card.byId.useQuery({ id: cardId });
|
||||
|
||||
const updateCard = api.card.updateDescription.useMutation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-8 flex w-full justify-between">
|
||||
<h1 className="font-medium tracking-tight text-dark-1000 sm:text-[1.2rem]">
|
||||
{data?.title}
|
||||
</h1>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center gap-x-1.5 rounded-md bg-dark-1000 px-3 py-2 text-sm font-semibold text-dark-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
|
||||
>
|
||||
<HiOutlinePlusSmall
|
||||
className="-mr-0.5 h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
New
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-8 flex w-full max-w-2xl justify-between">
|
||||
<Formik
|
||||
initialValues={{
|
||||
cardId,
|
||||
description: data?.description ? data.description : "",
|
||||
}}
|
||||
onSubmit={(values: FormValues) => {
|
||||
updateCard.mutate({
|
||||
cardId: values.cardId,
|
||||
description: values.description,
|
||||
});
|
||||
}}
|
||||
enableReinitialize
|
||||
>
|
||||
<Form className="w-full space-y-6">
|
||||
<div>
|
||||
<div className="mt-2">
|
||||
<Field
|
||||
id="description"
|
||||
name="description"
|
||||
render={({ field, form }: FieldProps) => (
|
||||
<div
|
||||
onBlur={() => form.submitForm()}
|
||||
onInput={async (e) => {
|
||||
const { innerText } = e.target as HTMLDivElement;
|
||||
await form.setFieldValue("description", innerText);
|
||||
}}
|
||||
contentEditable
|
||||
className="block w-full border-0 bg-transparent py-1.5 text-dark-1000 focus-visible:outline-none sm:text-sm sm:leading-6"
|
||||
>
|
||||
{field.value || "Add description..."}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
5
src/app/cards/layout.tsx
Normal file
5
src/app/cards/layout.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Dashboard from "~/app/_components/dashboard";
|
||||
|
||||
export default function Layout(props: { children: React.ReactNode }) {
|
||||
return <Dashboard>{props.children}</Dashboard>;
|
||||
}
|
||||
Reference in New Issue
Block a user