- Node service layer connecting Zustand stores to REST API - NotesPage fetches notes from API on mount with loading state - NoteEditor auto-saves title and content with 1.5s debounce - Optimistic updates with rollback on API errors - Fallback to local-only mode when API is unavailable - Proper save status indicators (Unsaved → Saving → Saved)
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { Plus } from 'lucide-react';
|
|
import { useNotesStore } from '../stores/notes';
|
|
import { NoteList } from './NoteList';
|
|
import { NoteEditor } from './NoteEditor';
|
|
import { Button } from '@yetanother/ui';
|
|
import { loadNotes, createNote } from '../lib/node-service';
|
|
|
|
export function NotesPage() {
|
|
const notes = useNotesStore((s) => s.notes);
|
|
const setSelectedNoteId = useNotesStore((s) => s.setSelectedNoteId);
|
|
const loading = useNotesStore((s) => s.loading);
|
|
|
|
useEffect(() => {
|
|
loadNotes();
|
|
}, []);
|
|
|
|
const handleCreate = async () => {
|
|
try {
|
|
const note = await createNote({
|
|
title: '',
|
|
workspaceId: notes[0]?.workspaceId ?? crypto.randomUUID(),
|
|
});
|
|
setSelectedNoteId(note.id as string);
|
|
} catch (err) {
|
|
const now = new Date().toISOString();
|
|
const fallback = {
|
|
id: crypto.randomUUID(),
|
|
title: '',
|
|
type: 'note' as const,
|
|
content: { type: 'doc', content: [{ type: 'paragraph' }] },
|
|
plainText: '',
|
|
status: 'active' as const,
|
|
workspaceId: crypto.randomUUID(),
|
|
tags: [],
|
|
folderId: null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
};
|
|
useNotesStore.getState().addNote(fallback);
|
|
setSelectedNoteId(fallback.id);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-full">
|
|
<div className="w-72 border-r flex flex-col bg-background">
|
|
<div className="p-3 border-b">
|
|
<Button onClick={handleCreate} size="sm" className="w-full gap-2" disabled={loading}>
|
|
<Plus className="size-4" />
|
|
{loading ? 'Loading...' : 'New Note'}
|
|
</Button>
|
|
</div>
|
|
<NoteList />
|
|
</div>
|
|
|
|
<div className="flex-1">
|
|
<NoteEditor />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|