"use server"; import { and, desc, eq, or, sql } from "drizzle-orm"; import { z } from "zod"; import { db } from "@/lib/db"; import { getCurrentSession } from "@/lib/session"; import { notes } from "../schema"; export type NoteDto = { id: string; householdId: string; authorId: string; title: string; body: string; pinned: boolean; remindAt: string | null; createdAt: string; updatedAt: string; }; export async function listNotesForScope(householdId: string): Promise { const rows = await db .select() .from(notes) .where(eq(notes.householdId, householdId)) .orderBy(desc(notes.pinned), desc(notes.updatedAt)); return rows.map(toNoteDto); } export async function listNotes(): Promise { const { household } = await getCurrentSession(); return listNotesForScope(household.id); } export async function getNoteForScope(householdId: string, id: string): Promise { const parsed = z.string().uuid().parse(id); const [note] = await db .select() .from(notes) .where(and(eq(notes.id, parsed), eq(notes.householdId, householdId))) .limit(1); if (!note) throw new Error("Note not found"); return toNoteDto(note); } export async function getNote(id: string): Promise { const { household } = await getCurrentSession(); return getNoteForScope(household.id, id); } export async function canAccessNote(noteId: string, householdId: string) { const [note] = await db .select({ id: notes.id }) .from(notes) .where(and(eq(notes.id, noteId), eq(notes.householdId, householdId))) .limit(1); return !!note; } export async function searchNotes(query: string, householdId: string) { const rows = await db .select({ id: notes.id, title: notes.title, body: notes.body, }) .from(notes) .where( and( eq(notes.householdId, householdId), or(sql`${notes.title} ilike ${`%${query}%`}`, sql`${notes.body} ilike ${`%${query}%`}`), ), ) .limit(10); return rows.map((row) => ({ id: row.id, title: row.title, url: `/notes/${row.id}`, excerpt: row.body.slice(0, 160), })); } export async function listWidgetNotes(input: { filter: "pinned" | "all"; limit?: number }) { const parsed = z .object({ filter: z.enum(["pinned", "all"]), limit: z.number().int().min(1).max(50).optional(), }) .parse(input); const { household } = await getCurrentSession(); const conditions = [eq(notes.householdId, household.id)]; if (parsed.filter === "pinned") conditions.push(eq(notes.pinned, true)); const rows = await db .select() .from(notes) .where(and(...conditions)) .orderBy(desc(notes.pinned), desc(notes.updatedAt)) .limit(parsed.limit ?? 10); return rows.map(toNoteDto); } function toNoteDto(note: typeof notes.$inferSelect): NoteDto { return { id: note.id, householdId: note.householdId, authorId: note.authorId, title: note.title, body: note.body, pinned: note.pinned, remindAt: note.remindAt?.toISOString() ?? null, createdAt: note.createdAt.toISOString(), updatedAt: note.updatedAt.toISOString(), }; }