Implement lists module and dev login setup
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Plus } from "lucide-react";
|
||||
import { useMemo, useState, useTransition } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import type { ListDto } from "../server/queries";
|
||||
import { createList } from "../server/actions";
|
||||
|
||||
export function ListsIndex({ lists }: { lists: ListDto[] }) {
|
||||
const [listRows, setListRows] = useState(lists);
|
||||
const [type, setType] = useState("shopping");
|
||||
const [name, setName] = useState("");
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
const grouped = useMemo(() => {
|
||||
const groups = new Map<string, ListDto[]>();
|
||||
for (const list of listRows) {
|
||||
groups.set(list.type, [...(groups.get(list.type) ?? []), list]);
|
||||
}
|
||||
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b));
|
||||
}, [listRows]);
|
||||
|
||||
function addList() {
|
||||
startTransition(async () => {
|
||||
const created = await createList({ type, name });
|
||||
setListRows((current) => [
|
||||
...current,
|
||||
{
|
||||
id: created.id,
|
||||
type: created.type,
|
||||
name: created.name,
|
||||
archived: created.archived,
|
||||
openCount: 0,
|
||||
doneCount: 0,
|
||||
createdAt: created.createdAt.toISOString(),
|
||||
},
|
||||
]);
|
||||
setName("");
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto grid w-full max-w-5xl gap-6 p-4">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Lists</h1>
|
||||
<p className="text-sm text-muted-foreground">Shopping, tasks, and whatever comes next.</p>
|
||||
</div>
|
||||
<div className="grid gap-2 rounded-lg border bg-background p-3 sm:grid-cols-[140px_220px_auto]">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="new-list-type">Type</Label>
|
||||
<Input
|
||||
id="new-list-type"
|
||||
value={type}
|
||||
onChange={(event) => setType(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="new-list-name">Name</Label>
|
||||
<Input
|
||||
id="new-list-name"
|
||||
value={name}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button className="self-end" onClick={addList} disabled={!type || !name}>
|
||||
<Plus />
|
||||
New list
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6">
|
||||
{grouped.map(([groupType, groupLists]) => (
|
||||
<section key={groupType} className="grid gap-3">
|
||||
<h2 className="text-sm font-medium uppercase tracking-normal text-muted-foreground">
|
||||
{groupType}
|
||||
</h2>
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{groupLists.map((list) => (
|
||||
<Link
|
||||
key={list.id}
|
||||
href={`/lists/${list.id}`}
|
||||
className="rounded-lg border bg-card p-4 text-card-foreground transition-colors hover:bg-muted"
|
||||
>
|
||||
<div className="font-medium">{list.name}</div>
|
||||
<div className="mt-2 text-sm text-muted-foreground">
|
||||
{list.openCount} open / {list.doneCount} done
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user