Description
The invoice description field is a plain <textarea> that cannot format text, embed links, or organize long notes, limiting the usefulness of invoice descriptions for complex engagements. A lightweight rich text editor that serializes to a safe HTML or Markdown string should replace the plain textarea.
Technical Context
src/components/invoice/NotesEditor.tsx (new) integrates the @tiptap/react editor with the StarterKit extension for bold, italic, lists, and links. The editor stores its output as a sanitized HTML string using DOMPurify before saving to the API via src/hooks/useInvoiceForm.ts. The public invoice status page renders the notes as sanitized HTML using dangerouslySetInnerHTML with the same DOMPurify sanitization. Toolbar buttons use accessible aria-label attributes and role="toolbar".
Acceptance Criteria
Description
The invoice description field is a plain
<textarea>that cannot format text, embed links, or organize long notes, limiting the usefulness of invoice descriptions for complex engagements. A lightweight rich text editor that serializes to a safe HTML or Markdown string should replace the plain textarea.Technical Context
src/components/invoice/NotesEditor.tsx(new) integrates the@tiptap/reacteditor with theStarterKitextension for bold, italic, lists, and links. The editor stores its output as a sanitized HTML string usingDOMPurifybefore saving to the API viasrc/hooks/useInvoiceForm.ts. The public invoice status page renders the notes as sanitized HTML usingdangerouslySetInnerHTMLwith the sameDOMPurifysanitization. Toolbar buttons use accessiblearia-labelattributes androle="toolbar".Acceptance Criteria
DOMPurifybefore storage and before rendering on the public pagesrc/app/invoice/[id]/page.tsxand the public status page