import Link from "next/link"; import { and, desc, eq, inArray, isNull, sql } from "drizzle-orm"; import { auth } from "@/auth"; import { db } from "@/lib/db/client"; import { memories, projects } from "@/lib/db/schema"; import { getAccessibleProjects, getUserGroupNames } from "@/lib/access"; import { Container, PageHeader } from "@/app/_components/ui/container"; import { Card } from "@/app/_components/ui/card"; import { Badge } from "@/app/_components/ui/badge"; import { EmptyState } from "@/app/_components/ui/empty-state"; export const dynamic = "force-dynamic"; export default async function ProjectsPage() { const session = await auth(); const userId = session!.user.id; const groupNames = await getUserGroupNames(userId); // The project list is owned ∪ shared: projects the user owns PLUS // projects shared with one of their groups (any access). Visibility was // previously owner-only (`eq(projects.userId, userId)`), which hid // projects another user shared in via project_shares even though // project.identify already reported them as {shared, access}. const accessible = await getAccessibleProjects(userId, groupNames); const accessById = new Map(accessible.map((p) => [p.projectId, p.access])); const accessibleIds = accessible.map((p) => p.projectId); const rows = accessibleIds.length === 0 ? [] : await db .select({ id: projects.id, key: projects.key, displayName: projects.displayName, createdAt: projects.createdAt, memoryCount: sql`count(${memories.id})::int`, lastActivity: sql`max(${memories.createdAt})`, }) .from(projects) .leftJoin( memories, and(eq(memories.projectId, projects.id), isNull(memories.deletedAt)), ) .where(inArray(projects.id, accessibleIds)) .groupBy(projects.id) .orderBy(desc(sql`max(${memories.createdAt})`)); return ( {rows.length === 0 ? ( ) : ( {rows.map((p, i) => ( 0 ? "border-t border-border" : ""}`} >
{p.key} {p.memoryCount} {accessById.get(p.id) !== "owner" ? ( shared · {accessById.get(p.id)} ) : null}
{p.displayName && p.displayName !== p.key ? (
{p.displayName}
) : null}
{p.lastActivity ? `last write ${new Date(p.lastActivity).toLocaleDateString()}` : "empty"}
))}
)}
); }