121 lines
3.1 KiB
TypeScript
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(),
|
|
};
|
|
}
|