157 lines
5.8 KiB
TypeScript
157 lines
5.8 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import {
|
||
|
|
ArrowRight,
|
||
|
|
CheckCircle2,
|
||
|
|
CircleDashed,
|
||
|
|
LayoutDashboard,
|
||
|
|
Sparkles,
|
||
|
|
} from "lucide-react";
|
||
|
|
|
||
|
|
import { Badge } from "@/components/ui/badge";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import { Separator } from "@/components/ui/separator";
|
||
|
|
import { usePanelStore } from "@/lib/stores/panel-store";
|
||
|
|
import { useWorkspaceStore } from "@/lib/stores/workspace-store";
|
||
|
|
import { cn } from "@/lib/utils";
|
||
|
|
|
||
|
|
const stats = [
|
||
|
|
{ label: "Open tasks", value: "24", delta: "+3 this week" },
|
||
|
|
{ label: "Due this week", value: "8", delta: "2 overdue" },
|
||
|
|
{ label: "Lists", value: "12", delta: "Across teams" },
|
||
|
|
];
|
||
|
|
|
||
|
|
const recent = [
|
||
|
|
{ title: "Sprint planning", meta: "List · Updated 2h ago", status: "done" as const },
|
||
|
|
{ title: "Design review — navigation", meta: "Task · Updated yesterday", status: "progress" as const },
|
||
|
|
{ title: "Q1 roadmap doc", meta: "Doc · Edited 3d ago", status: "progress" as const },
|
||
|
|
];
|
||
|
|
|
||
|
|
export default function WorkspaceHomePage() {
|
||
|
|
const workspace = useWorkspaceStore((s) => s.currentWorkspace);
|
||
|
|
const openPanel = usePanelStore((s) => s.open);
|
||
|
|
|
||
|
|
const name = workspace?.name ?? "Workspace";
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex min-h-full flex-col bg-background">
|
||
|
|
<header className="relative overflow-hidden border-b border-border px-6 py-10 sm:px-10">
|
||
|
|
<div
|
||
|
|
className={cn(
|
||
|
|
"absolute inset-0 bg-gradient-primary opacity-90",
|
||
|
|
"dark:opacity-100",
|
||
|
|
)}
|
||
|
|
aria-hidden
|
||
|
|
/>
|
||
|
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_rgba(255,255,255,0.12),_transparent_55%)]" />
|
||
|
|
<div className="relative flex flex-col gap-3">
|
||
|
|
<div className="flex flex-wrap items-center gap-2 text-primary-foreground/90">
|
||
|
|
<LayoutDashboard className="size-5" />
|
||
|
|
<span className="text-xs font-semibold uppercase tracking-widest">
|
||
|
|
Home
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<h1 className="text-3xl font-bold tracking-tight text-primary-foreground sm:text-4xl">
|
||
|
|
{name}
|
||
|
|
</h1>
|
||
|
|
<p className="max-w-2xl text-sm text-primary-foreground/85 sm:text-base">
|
||
|
|
Your command center for tasks, docs, and boards. Pick up where you
|
||
|
|
left off or open the assistant to plan the day.
|
||
|
|
</p>
|
||
|
|
<div className="flex flex-wrap gap-2 pt-2">
|
||
|
|
<Button
|
||
|
|
type="button"
|
||
|
|
size="sm"
|
||
|
|
variant="secondary"
|
||
|
|
className="gap-2 bg-primary-foreground/15 text-primary-foreground hover:bg-primary-foreground/25"
|
||
|
|
onClick={() => openPanel("ai-chat")}
|
||
|
|
>
|
||
|
|
<Sparkles className="size-4" />
|
||
|
|
Open AI assistant
|
||
|
|
</Button>
|
||
|
|
<Button
|
||
|
|
type="button"
|
||
|
|
size="sm"
|
||
|
|
variant="secondary"
|
||
|
|
className="gap-2 bg-primary-foreground/15 text-primary-foreground hover:bg-primary-foreground/25"
|
||
|
|
onClick={() => openPanel("object-detail", "demo-object")}
|
||
|
|
>
|
||
|
|
Sample side panel
|
||
|
|
<ArrowRight className="size-4" />
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<div className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-8 px-6 py-8 sm:px-10">
|
||
|
|
<section>
|
||
|
|
<h2 className="text-sm font-semibold text-foreground">Quick stats</h2>
|
||
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
||
|
|
Placeholder metrics until your data layer is connected.
|
||
|
|
</p>
|
||
|
|
<div className="mt-4 grid gap-3 sm:grid-cols-3">
|
||
|
|
{stats.map((s) => (
|
||
|
|
<div
|
||
|
|
key={s.label}
|
||
|
|
className="rounded-lg border border-border bg-card p-4 shadow-sm"
|
||
|
|
>
|
||
|
|
<p className="text-xs font-medium text-muted-foreground">
|
||
|
|
{s.label}
|
||
|
|
</p>
|
||
|
|
<p className="mt-2 text-2xl font-semibold tabular-nums text-foreground">
|
||
|
|
{s.value}
|
||
|
|
</p>
|
||
|
|
<p className="mt-1 text-xs text-muted-foreground">{s.delta}</p>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<Separator />
|
||
|
|
|
||
|
|
<section>
|
||
|
|
<div className="flex items-center justify-between gap-2">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-sm font-semibold text-foreground">
|
||
|
|
Recent activity
|
||
|
|
</h2>
|
||
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
||
|
|
Latest updates across this workspace (sample rows).
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<Badge variant="secondary" className="shrink-0">
|
||
|
|
Beta
|
||
|
|
</Badge>
|
||
|
|
</div>
|
||
|
|
<ul className="mt-4 divide-y divide-border rounded-lg border border-border bg-card">
|
||
|
|
{recent.map((item) => (
|
||
|
|
<li
|
||
|
|
key={item.title}
|
||
|
|
className="flex items-start gap-3 px-4 py-3 first:rounded-t-lg last:rounded-b-lg"
|
||
|
|
>
|
||
|
|
<span className="mt-0.5 text-muted-foreground">
|
||
|
|
{item.status === "done" ? (
|
||
|
|
<CheckCircle2 className="size-4 text-teal" />
|
||
|
|
) : (
|
||
|
|
<CircleDashed className="size-4" />
|
||
|
|
)}
|
||
|
|
</span>
|
||
|
|
<div className="min-w-0 flex-1">
|
||
|
|
<p className="font-medium text-foreground">{item.title}</p>
|
||
|
|
<p className="text-xs text-muted-foreground">{item.meta}</p>
|
||
|
|
</div>
|
||
|
|
<Badge
|
||
|
|
variant={item.status === "done" ? "secondary" : "outline"}
|
||
|
|
className="shrink-0 capitalize"
|
||
|
|
>
|
||
|
|
{item.status === "done" ? "Done" : "In progress"}
|
||
|
|
</Badge>
|
||
|
|
</li>
|
||
|
|
))}
|
||
|
|
</ul>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|