feat: quick-jot note creation modal #21
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/pal-e-docs-app!21
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "18-feat-quick-jot-note-creation-modal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
nor click the FAB to open a modal that creates notes via the pal-e-docs APIChanges
src/lib/api.ts: AddedcreateNote()function andCreateNotePayloadinterface for POST /notessrc/routes/api/notes/+server.ts: New API proxy route that forwards note creation to the backend, with validation and 409 handlingsrc/lib/components/QuickJot.svelte: New modal component with title (required), body (optional), project dropdown, note type selector (todo/doc/reference/journal), slug preview, focus trap, escape-to-close, backdrop click, error display, and success toast with linksrc/routes/+layout.svelte: Added FAB (bottom-right, #e94560, hover scale), keyboard shortcut (nkey when not in input), and QuickJot mountsrc/routes/+layout.server.ts: New layout server load that fetches projects list for the modal dropdownTest Plan
non any page (not in a text field) -- modal opens+FAB in bottom-right -- modal opensnpm run buildpasses,npm run checkhas 0 errorsReview Checklist
Related
plan-2026-03-13-pal-e-frontendSelf-Review
Files reviewed: 5 changed files, +416/-8 lines
Findings
No blocking issues found.
Code quality:
api/notes/+server.ts) follows the established pattern fromapi/boards/[slug]/items/[id]/+server.tscreateNote()inapi.tsfollows the existingmoveItem()patternBuild verification:
npm run build-- passesnpm run check-- 0 errors, 1 pre-existing warning (autofocus in search page)npm run lint-- 0 new errors (2 pre-existing on main in search page)Minor notes (non-blocking):
message.includes('409')which is slightly fragile but adequate sinceapiFetchembeds the status code in the error message<p>tags client-side; backend is responsible for sanitization (DOMPurify already in use for rendering)Verdict: LGTM -- ready for manual QA.
PR #21 Review
BLOCKERS
1. Dashboard nav link removed (scope creep / regression)
The current
+layout.svelteonmain(line 43) includes a/dashboardnav link:The PR's version of the nav section reformats the links (Prettier-style line breaks) but silently drops the Dashboard link entirely. The
dashboard/route and its+page.server.ts/+page.sveltestill exist on disk. This is an unrelated change that removes a working navigation entry and constitutes both scope creep and a user-facing regression. Must be restored.NITS
1. Body content wraps in
<p>tags via raw string interpolation (defense-in-depth gap)QuickJot.svelteline ~62:If a user types
<img src=x onerror=alert(1)>in the body field, it gets sent raw to the backend. The existing rendering pipeline sanitizes on output via DOMPurify, so this is not an active XSS vulnerability. However, best practice is to sanitize or HTML-escape on input as well (defense in depth). Consider using a simple HTML escape function before wrapping in<p>tags, or importing the existingsanitize()from$lib/sanitize-- though note that module usesisomorphic-dompurifywhich works server-side too.2. Fragile 409 detection in API proxy route
src/routes/api/notes/+server.tsline ~46:The
apiFetcherror message format is"API request failed: 409 Conflict (http://...)", so this works today. But it's fragile -- if the URL ever contains "409" it would false-positive. A more robust approach: checkres.statusdirectly inapiFetchor parse the status code from the error message pattern.3. FAB visible behind modal backdrop
The FAB button has
z-50and is always rendered. The modal backdrop also hasz-50. When the modal is open, the red FAB button bleeds through the semi-transparent black backdrop (bg-black/50). Consider hiding the FAB when the modal is open:4.
createNote()andCreateNotePayloadinapi.ts-- partial dead codeThe
createNote()function is added toapi.tsand used by the API proxy route (+server.ts), which is correct. However,CreateNotePayloadis exported but never imported by any other module (the QuickJot component builds its own payload asRecord<string, unknown>). Minor -- not blocking, just noting the type could be reused by the component for type safety.5. Root layout server load on every page
+layout.server.tsfetcheslistProjects()on every page load since it's the root layout. For the current app size this is fine. If the project list grows or latency becomes noticeable, consider moving the fetch to a dedicated page or adding caching. Not blocking.SOP COMPLIANCE
18-feat-quick-jot-note-creation-modalmatches issue #18)plan-2026-03-13-pal-e-frontend)Closes #18present in Related section.envfiles, or credentials committedVERDICT: NOT APPROVED
One blocker: the
/dashboardnav link was silently removed from the layout, which is unrelated to quick-jot and breaks existing navigation. Restore the Dashboard link, then this is ready to merge. The nits are non-blocking suggestions for follow-up.524a36b0a6624de27f64PR #21 Review (Re-review)
BLOCKERS
None. All three previously identified issues have been resolved:
{#if !quickJotOpen}and usesz-40. Modal backdrop sits atz-50, toast atz-[100]. No overlap possible.message.match(/API request failed: (\d+)/)to extract the numeric status code. Client checksres.status === 409directly on the fetch response. Both are robust.NITS
Duplicate
listProjects()call on home page -- The new+layout.server.tscallslistProjects()on every route. The existing+page.server.ts(home page) also callslistProjects(). This means the home page fetches the project list twice per load. SvelteKit does not deduplicate layout vs page server loads. Consider removing thelistProjects()call from+page.server.tsand readingdata.projectsfrom the parent layout data instead, or accepting the minor redundancy.Unsanitized HTML in body --
QuickJot.svelteline 65 wraps raw user input in a<p>tag:`<p>${body.trim()}</p>`. If a user types HTML or script tags in the body field, they are sent verbatim ashtml_content. This relies entirely on the backend (pal-e-docs) to sanitize on ingestion. If the backend does not sanitize, this is a stored XSS vector. Worth confirming backend sanitization exists, or adding client-side escaping here.SELECTadded to isInput check -- Good fix (preventsnshortcut from firing in dropdowns), but this change also affects the/search shortcut behavior. Previously, pressing/while focused on a<select>would trigger search focus. Now it won't. This is probably the correct behavior, just noting the scope extends beyond the QuickJot feature.ACCEPTANCE CRITERIA
nkey opens modal (when not in input/textarea/select)SOP COMPLIANCE
18-feat-quick-jot-note-creation-modal-> Issue #18)plan-2026-03-13-pal-e-frontend).env.examplewith placeholder)VERDICT: APPROVED