import Link from "next/link"; import { and, desc, eq, inArray, isNull, or, sql, count } from "drizzle-orm"; import { auth } from "@/auth"; import { db } from "@/lib/db/client"; import { memories, projects, projectShares } from "@/lib/db/schema"; import { getUserGroupNames, readableProjectIds } from "@/lib/access"; import { Container, PageHeader } from "@/app/_components/ui/container"; import { Card, CardBody, CardHeader } from "@/app/_components/ui/card"; import { Badge } from "@/app/_components/ui/badge"; import { Button } from "@/app/_components/ui/button"; import { EmptyState } from "@/app/_components/ui/empty-state"; export const dynamic = "force-dynamic"; export default async function DashboardPage() { const session = await auth(); const userId = session!.user.id; const groupNames = await getUserGroupNames(userId); // Visibility widening โ€” recent + counts include memories under // projects shared with the user's groups. const accessibleIds = await readableProjectIds(userId, groupNames); const visibility = accessibleIds.length > 0 ? or(eq(memories.userId, userId), inArray(memories.projectId, accessibleIds)) : eq(memories.userId, userId); // Dashboard's "Projects" card stays owned-only โ€” the list of projects // you actively own. Shared projects show up via the memory list and // the per-project page; surfacing them here would make the panel // confusing about who owns what. const [counts, recent, topProjects] = await Promise.all([ db .select({ total: count(memories.id), }) .from(memories) .where(and(visibility!, isNull(memories.deletedAt))), db .select({ id: memories.id, content: memories.content, scope: memories.scope, tags: memories.tags, createdAt: memories.createdAt, projectId: memories.projectId, projectKey: projects.key, }) .from(memories) .leftJoin(projects, eq(memories.projectId, projects.id)) .where(and(visibility!, isNull(memories.deletedAt))) .orderBy(desc(memories.createdAt)) .limit(5), db .select({ id: projects.id, key: projects.key, displayName: projects.displayName, memoryCount: sql`count(${memories.id})::int`, }) .from(projects) .leftJoin( memories, and(eq(memories.projectId, projects.id), isNull(memories.deletedAt)), ) .where(eq(projects.userId, userId)) .groupBy(projects.id) .orderBy(desc(sql`count(${memories.id})`)) .limit(4), ]); // Annotate "Shared" chips on the recent panel. const projectIds = recent .map((r) => r.projectId) .filter((p): p is string => p !== null); const sharedProjects = projectIds.length > 0 ? new Set( ( await db .selectDistinct({ projectId: projectShares.projectId }) .from(projectShares) .where(inArray(projectShares.projectId, projectIds)) ).map((r) => r.projectId), ) : new Set(); const memoryTotal = counts[0]?.total ?? 0; return ( } />

Recent

{recent.length === 0 ? ( } /> ) : ( recent.map((m) => (
{m.scope} {m.projectId && sharedProjects.has(m.projectId) ? ( Shared ) : null} {m.projectKey ? ยท {m.projectKey} : null} {new Date(m.createdAt).toLocaleDateString()}

{m.content}

{m.tags.length ? (
{m.tags.slice(0, 6).map((t) => ( {t} ))}
) : null}
)) )}

Projects

{topProjects.length === 0 ? (

No projects yet.

) : ( {topProjects.map((p, i) => ( 0 ? "border-t border-border" : ""}`} >
{p.key} {p.memoryCount}
{p.displayName && p.displayName !== p.key ? ( {p.displayName} ) : null} ))} All projects โ†’
)}
); }