Files
famapp/src/modules/notes/server/queries.ts
T

121 lines
3.1 KiB
TypeScript

"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<NoteDto[]> {
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<NoteDto[]> {
const { household } = await getCurrentSession();
return listNotesForScope(household.id);
}
export async function getNoteForScope(householdId: string, id: string): Promise<NoteDto> {
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<NoteDto> {
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(),
};
}