M2 checklists: note kind + items backend + editor/card UI
- Migration 0006: notes.kind ('text'|'list') + note_items (text, checked,
position). Item API: add/update(toggle)/delete/reorder; PATCH note kind; note
responses include kind + items[] (merged in one query alongside labels).
- notes store: kind/items on Note, setKind/addItem/updateItem/deleteItem.
- NoteChecklist component (toggle/add/edit/delete items); rendered read-only-ish
on cards (checkboxes toggle) and editable in the editor.
- Editor: convert text<->checklist (body lines become items on convert to list).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -4,21 +4,31 @@ import { api } from "../api/client";
|
||||
import type { NoteColor } from "../notes/colors";
|
||||
|
||||
export type NoteView = "active" | "archived" | "trash";
|
||||
export type NoteKind = "text" | "list";
|
||||
|
||||
export interface NoteLabel {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ChecklistItem {
|
||||
id: string;
|
||||
text: string;
|
||||
checked: boolean;
|
||||
position: number;
|
||||
}
|
||||
|
||||
export interface Note {
|
||||
id: string;
|
||||
title: string | null;
|
||||
body: string;
|
||||
color: NoteColor;
|
||||
kind: NoteKind;
|
||||
pinned: boolean;
|
||||
archived: boolean;
|
||||
trashed: boolean;
|
||||
labels: NoteLabel[];
|
||||
items: ChecklistItem[];
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
}
|
||||
@@ -76,7 +86,7 @@ export const useNotesStore = defineStore("notes", () => {
|
||||
|
||||
async function mutate(
|
||||
id: string,
|
||||
changes: Partial<Pick<Note, "title" | "body" | "color" | "pinned" | "archived">>,
|
||||
changes: Partial<Pick<Note, "title" | "body" | "color" | "kind" | "pinned" | "archived">>,
|
||||
): Promise<void> {
|
||||
reconcile(await api.patch<Note>(`/api/notes/${id}`, changes));
|
||||
}
|
||||
@@ -84,12 +94,25 @@ export const useNotesStore = defineStore("notes", () => {
|
||||
const setPinned = (id: string, pinned: boolean) => mutate(id, { pinned });
|
||||
const setArchived = (id: string, archived: boolean) => mutate(id, { archived });
|
||||
const setColor = (id: string, color: NoteColor) => mutate(id, { color });
|
||||
const setKind = (id: string, kind: NoteKind) => mutate(id, { kind });
|
||||
const saveEdit = (id: string, changes: { title: string; body: string; color: NoteColor }) => mutate(id, changes);
|
||||
|
||||
async function setLabels(id: string, labelIds: string[]): Promise<void> {
|
||||
reconcile(await api.put<Note>(`/api/notes/${id}/labels`, { label_ids: labelIds }));
|
||||
}
|
||||
|
||||
async function addItem(id: string, text: string): Promise<void> {
|
||||
reconcile(await api.post<Note>(`/api/notes/${id}/items`, { text }));
|
||||
}
|
||||
|
||||
async function updateItem(id: string, itemId: string, changes: { text?: string; checked?: boolean }): Promise<void> {
|
||||
reconcile(await api.patch<Note>(`/api/notes/${id}/items/${itemId}`, changes));
|
||||
}
|
||||
|
||||
async function deleteItem(id: string, itemId: string): Promise<void> {
|
||||
reconcile(await api.del<Note>(`/api/notes/${id}/items/${itemId}`));
|
||||
}
|
||||
|
||||
async function trash(id: string): Promise<void> {
|
||||
reconcile(await api.post<Note>(`/api/notes/${id}/trash`));
|
||||
}
|
||||
@@ -114,8 +137,12 @@ export const useNotesStore = defineStore("notes", () => {
|
||||
setPinned,
|
||||
setArchived,
|
||||
setColor,
|
||||
setKind,
|
||||
saveEdit,
|
||||
setLabels,
|
||||
addItem,
|
||||
updateItem,
|
||||
deleteItem,
|
||||
trash,
|
||||
restore,
|
||||
deleteForever,
|
||||
|
||||
Reference in New Issue
Block a user