"use client"; import { useState } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; import { ArrowRight, CheckCircle2, CircleDashed, ClipboardList, FileText, FolderKanban, LayoutDashboard, LayoutGrid, Plus, Presentation, Sparkles, type LucideIcon, } from "lucide-react"; import { CreateObjectDialog } from "@/components/objects"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; import { api } from "@/lib/trpc"; import { usePanelStore } from "@/lib/stores/panel-store"; import { useWorkspaceStore } from "@/lib/stores/workspace-store"; import { cn } from "@/lib/utils"; /** * Minimal "X ago" formatter. We avoid pulling in date-fns or a Yjs-aware * relative-time helper for a single use site; the home page just needs * coarse-grained recency labels. */ function relativeTime(value: Date | string): string { const date = value instanceof Date ? value : new Date(value); const ms = Date.now() - date.getTime(); if (Number.isNaN(ms)) return String(value); const secs = Math.max(1, Math.round(ms / 1000)); if (secs < 60) return `${secs}s ago`; const mins = Math.round(secs / 60); if (mins < 60) return `${mins}m ago`; const hours = Math.round(mins / 60); if (hours < 24) return `${hours}h ago`; const days = Math.round(hours / 24); if (days < 7) return `${days}d ago`; return date.toLocaleDateString(); } const TYPE_LABELS: Record = { task: "Task", document: "Document", whiteboard: "Whiteboard", space: "Space", project: "Project", group: "Group", form: "Form", }; const TYPE_ICONS: Record = { task: ClipboardList, document: FileText, whiteboard: Presentation, space: LayoutGrid, project: FolderKanban, group: FolderKanban, form: ClipboardList, }; function isDoneStatus(status: string | null): boolean { return status === "done" || status === "closed"; } export default function WorkspaceHomePage() { const params = useParams(); const workspaceSlug = typeof params?.workspaceSlug === "string" ? params.workspaceSlug : undefined; const workspace = useWorkspaceStore((s) => s.currentWorkspace); const openPanel = usePanelStore((s) => s.open); const [createOpen, setCreateOpen] = useState(false); const [createType, setCreateType] = useState(undefined); const statsQuery = api.objects.stats.useQuery( { workspace: workspaceSlug! }, { enabled: Boolean(workspaceSlug) }, ); const recentQuery = api.objects.listRecent.useQuery( { workspace: workspaceSlug!, limit: 5 }, { enabled: Boolean(workspaceSlug) }, ); const name = workspace?.name ?? "Workspace"; const recent = recentQuery.data?.objects ?? []; return (
Home

{name}

Your command center for tasks, docs, and boards. Pick up where you left off or open the assistant to plan the day.

Quick stats

A snapshot of what's live in this workspace right now.

Recent activity

Latest updates across this workspace.

Live
{recentQuery.isLoading ? (
    {[0, 1, 2].map((i) => (
  • ))}
) : recent.length === 0 ? ( { setCreateType("task"); setCreateOpen(true); }} /> ) : (
    {recent.map((item) => { if (!workspaceSlug) return null; const Icon = TYPE_ICONS[item.type] ?? ClipboardList; const typeLabel = TYPE_LABELS[item.type] ?? item.type; const done = isDoneStatus(item.status); return (
  • {done ? ( ) : ( )}

    {item.title || "Untitled"}

    {typeLabel} ยท Updated {relativeTime(item.updatedAt)}

    {done ? "Done" : item.status?.replace("_", " ") ?? "Active"}
  • ); })}
)}
); } function StatCard({ label, value, hint, isLoading, }: { label: string; value: number | undefined; hint: string; isLoading: boolean; }) { return (

{label}

{isLoading ? ( ) : (

{value ?? 0}

)}

{hint}

); } function EmptyRecent({ onCreate }: { onCreate: () => void }) { return (

Nothing here yet

Create your first task to see recent activity here.

); }