Repository navigation
Closed
Labels
Description
Summary
Home currently loads ALL tags, tasks, and notes on every mount, which will not scale as user data grows. Change the default Home load to a windowed payload (high-priority incomplete tasks + anything touched in the last 24h), keep loading all tags, and move tag filtering and text search to the backend. No pagination for now.
This design was fully grilled and settled with the product owner. All decisions below are final; do not re-open them without new information.
Decisions (settled via grilling session)
Default window (Q1, Q2, Q9, Q11)
- "Touched" = the existing
last_updatecolumn (last_update >= now() - interval '24 hours'). There is nocreated_atcolumn and none will be added. - Write-path audit confirmed every task/note mutation already bumps
last_update(tag link/unlink only happens insidepatchTask/patchNote), so no write-path changes are needed. - High-priority tasks are always loaded, but ONLY when
highPriority = true AND completed = false. Completed HP tasks appear only via the window, search, or tag queries. - Archived notes follow the same 24h window (archiving bumps
last_update, so a just-archived note still appears in the archived section). Do NOT always-load all archived notes, and do NOT remove the archived section from Home. - No mid-session eviction, polling, or TTL timers. Data refreshes on reload, navigation back to Home, mutation, or filter/search change. (Session expiry kicks inactive users well before 24h anyway.)
Query mode (Q3, Q4, Q5, Q6, Q7)
- One new aggregated endpoint:
GET /rest/home/items- No params: returns the default window payload described above.
?q=<text>: backend search replicating today's client-side semantics exactly: notes matched on title, description, url, AND tags; tasks matched on description, tags, AND urls; completed tasks and archived notes INCLUDED in results.?tag=<tag>: full-database (unbounded, accepted) query for items with that tag, including theuntaggedpseudo-tag (items with zero tags).?type=all|tasks|notes: maps to the dropdown's everything/onlyTasks/onlyNotes modes.- Params compose with AND semantics (
?q=foo&tag=work= both). - Response shape:
{ "tasks": TaskResponse[], "notes": NoteResponse[] }using the existing DTOs unchanged.
- Client search UX: debounce ~300ms, min 1 char; clearing the search box re-fetches the default window.
- Sorting stays client-side exactly as today.
Supporting changes (Q8, Q10, Q12, Q13)
- Rewrite
GET /rest/home/tasks/tagsto query the tag tables directly (union of distinct tags +untaggedpseudo-tag injection). It currently scans ALL tasks and notes server-side in Java, which would defeat the purpose. Must stay response-compatible: it is also consumed by TaskAdd and NoteAdd views. - New Flyway migration adding indexes on
task.last_updateandnote.last_update. - After every mutation from Home (create/edit/complete/archive/restore/delete), refetch the CURRENT view (default window, or re-run the active search/tag query). Server is the source of truth; no optimistic local patching for list membership.
- localStorage filter persistence (
FILTER_TEXT,FILTER_OPTION) stays as-is; on reload with a persisted filter, fire the backend query on mount instead of the default window load. - Full-list
GET /rest/tasksandGET /rest/notesstay untouched (only Home consumes them today; account deletion uses the service layer directly).
Technical notes (for the implementing agent)
Backend facts (verified against the code)
- Entities are plain Java records +
NamedParameterJdbcTemplate, NOT JPA. Hand-written SQL lives inserver/src/main/java/br/com/tasknoteapp/server/repository/TaskRepository.javaandNoteRepository.java. Taskrecord (entity/Task.java):id, userId, description, completed, lastUpdate (LocalDateTime), dueDate, dueDateNotify, dueDateNotifySent, highPriority (Boolean). Priority is a boolean, not an enum.Noterecord (entity/Note.java):id, userId, description, title, lastUpdate (LocalDateTime), shared, shareToken, archived.last_updateis set manually viaLocalDateTime.now()in:TaskServicecreate (:144) and patch (:219);NoteServicecreate (:140), patch (:211), share (:338), unshare (:381), archive (:490), restore (:533).TaskResponse.lastUpdateandNoteResponse.lastUpdateare pre-formatted time-ago strings (viaTimeAgoUtil), NOT raw timestamps. The window filtering must happen in SQL against thelast_updatecolumn, not against DTO fields.- Unused search methods already exist and can be adapted, but their semantics do NOT match the required behavior and must be fixed:
TaskRepository.findAllBySearchTerm(:200-222) forcescompleted = falseand misses nothing else;NoteRepository.findAllBySearchTerm(:198-215) missesurland tags. Neither is wired to any controller. HomeService.getTopTasksTag(:60-73) currently builds the tag list by fetching all tasks+notes and extracting tags in Java, and injects theuntaggedpseudo-tag there. Replace with direct SQL overtags,task_tags,note_tagstables.- Tag mutations live in
TagRepository(updateTagForTask:90,updateTagForNote:111,deleteTagFromTask:133,deleteTagFromNote:155) and are only invoked from task/note create/patch flows; no standalone tag endpoints exist. - Flyway convention:
V<yyyyMMddHHmm>__description.sqlinserver/src/main/resources/db/migration/; latest isV202609171142__drop_tag_from_notes_table.sql. Never edit old migrations. - Integration tests (
*IntTest.java, Failsafe) use Testcontainers Postgres (jdbc:tc:postgresql:15.8-bookworm) with real Flyway migrations; Docker required. Runbash tools/check-backend.shfor the full gate (tests need-P testsprofile, the script handles it).
Frontend facts (verified against the code)
client/src/views/Home/index.tsx:useEffectat :491-497 callsloadTags(),loadAllTasks(),loadAllNotes(). Tag dropdown options built at :556-620 (everything/onlyTasks/onlyNotes/radio_<tag>/radio_untagged). Client-side filtering inapplyFilterat :250-339; archived partition viapartitionNotesat :136; archived section rendered at :880.client/src/context/FilterProvider.tsx:filterText+selectedOptionpersisted to localStorage asFILTER_TEXT/FILTER_OPTION. Not keyed per user (pre-existing quirk, out of scope).- API layer:
client/src/api-service/api.ts(api.getJSON(url)etc., Bearer token from localStorage); base URLs inclient/src/api-service/apiConfig.ts(homeUrl,tasksUrl,notesUrl). No query-param helper exists; interpolate query strings into the URL. - Frontend gate:
bash tools/check-frontend.sh(npm ci, lint:fix, build, test:no-watch).
Implementation checklist
- Flyway migration: indexes on
task.last_updateandnote.last_update. - Backend: new
GET /rest/home/itemsinHomeController+HomeService, with SQL for (a) default window, (b) search across the required fields including tag joins, (c) tag filter includinguntagged, (d)typeparam; params compose with AND. - Backend: rewrite
getTopTasksTagSQL to hit tag tables directly; keepuntaggedinjection; keep response shape for TaskAdd/NoteAdd compatibility. - Frontend: replace
loadAllTasks/loadAllNoteson Home with the windowed fetch; wire search (debounced) and tag/type dropdown to/rest/home/itemswith params; clearing search re-fetches the window. - Frontend: refetch current view after every mutation originating from Home.
- Frontend: on mount, if persisted filter exists, fire the corresponding backend query instead of the default window.
- Tests: backend integration tests for the new endpoint (window boundary at 24h, HP-completed exclusion, untagged, archived inclusion, composed params); frontend tests for debounce/clear behavior and mutation refetch.
Explicitly out of scope
- Pagination.
- Per-user localStorage filter keying.
- Removing or changing the full-list
/rest/tasksand/rest/notesendpoints. - Adding
created_atortouched_atcolumns. - Any TTL/eviction of aged-out items during an open session.
Activity
feat: load windowed tasks and notes. (#80)