Map Edit View Sidebar: Points of Interest List¶
Goal¶
In edit mode, show every point of interest on the current map in the left sidebar's existing "Points of interest" section, grouped by type and paginated per group, so a GM can find and select one without hunting for its marker on the map.
Decisions¶
| Decision | Choice | Why |
|---|---|---|
| Plan file location | docs/v0.0.2/map-edit-sidebar-poi-list.md |
next release |
| List location | Existing left sidebar "Points of interest" section in project-map.tsx, below the "Add point of interest" button | Confirmed with user — no new sidebar section |
| Selection behavior | Clicking a list entry calls the same handleSelectRequest(poiId) already wired to marker clicks |
Confirmed with user — one selection path, not a second one |
| Grouping | By PoiType, in POI_TYPES order (from poi.ts); a type with zero POIs renders no heading |
Confirmed with user |
| Pagination | Per group, 10 items per page, using a new shadcn pagination component |
Confirmed with user; no pagination component exists in app/components/ui/ yet |
| Empty state | "No points of interest yet." replaces all group headings when pois is empty |
Confirmed with user |
| Grouping logic placement | A pure helper in app/lib/poi.ts, not a .server.ts module |
No filesystem access involved — grouping/pagination is in-memory list shaping over data the loader already returned, so it doesn't cross the server boundary described in the project instructions |
Scope¶
In:
- app/lib/poi.ts — a grouping helper used by the sidebar
- app/components/ui/pagination.tsx — added via
pnpm dlx shadcn@latest add pagination, not hand-written - app/routes/project-map.tsx — left sidebar "Points of interest" section grows the grouped, paginated list and per-group page state
Out:
- Configurable/custom POI types or a type "group" field — separate backlog item ("Point of Interest Types")
- Dragging POIs or highlighting the selected one on the map itself — separate backlog item ("Edit mode: allow dragging points of interest")
- Search/filter controls over the list
- Any change to
addPointOfInterest/updatePointOfInterest/removePointOfInterestin projects.server.ts, or to the loader/action shape inproject-map.tsx—poisis already returned today
Why this shape¶
- The grouping helper is a small pure function colocated with
PoiType/POI_TYPESinpoi.ts, which is already the shared module both the route andmap.tsximport from — no new shared module needed for one function. - Pagination is per-group (not whole-list) state, kept as local
useStateinproject-map.tsxalongside the route's other mode-local state (placingPoi,editingPoiId, etc.) — it's view-only state, not data that needs to survive a navigation or be shared withMap. - Using the shadcn CLI for
paginationmatches the project convention of never hand-writingapp/components/ui/*.
Tasks¶
Ordered so each task type-checks and is shippable on its own; later tasks depend on earlier ones.
T1 — Grouping helper¶
Goal: A single pure function turns a flat PointOfInterest[] into
ordered, non-empty groups by type.
Acceptance criteria:
app/lib/poi.tsexports a function, e.g.groupPointsOfInterest(pois: PointOfInterest[]): { type: PoiType; items: PointOfInterest[] }[].- Groups are ordered per
POI_TYPES; a type with no matching entries is omitted from the result entirely (no empty-array group). - Within a group, items preserve the order they appear in the input array.
pnpm typecheckpasses.
T2 — Add the shadcn pagination component¶
Goal: A Pagination component is available for the sidebar to use.
Acceptance criteria:
pnpm dlx shadcn@latest add paginationhas been run, producingapp/components/ui/pagination.tsxunmodified from the CLI output.pnpm typecheckpasses.
T3 — Render the grouped, paginated list in the sidebar¶
Goal: The left sidebar's "Points of interest" section shows every POI on the map, grouped and paginated, selectable via the existing click path.
Acceptance criteria:
- Below the existing "Add point of interest" button, the section renders
the output of
groupPointsOfInterest(pois)— one heading per group (group label is thePoiTypevalue), followed by that group's items. - When
poisis empty, the section shows the text "No points of interest yet." and no group headings. - Each item renders as a clickable row (name, and visually distinguished
by type consistent with existing marker styling); clicking it calls
handleSelectRequest(poi.id)— the same function already invoked byMap'sonSelectRequest. - A group with more than 10 items paginates using the
Paginationcomponent from T2, 10 items per page; each group tracks its own current page (e.g.Record<PoiType, number>local state, default page 1). - If a group's item count drops (e.g. after a
remove) such that the tracked current page no longer exists, the page state clamps to the last valid page rather than rendering an empty page. - The list re-renders with updated data after any add/update/remove
fetcher submit, consistent with the existing revalidation-driven
behavior of
poisin this route — no new fetcher or loader change needed. - The "Add point of interest" button's position and the button's own behavior are unchanged.
pnpm typecheckpasses.- Manual check:
pnpm dev, open a map with more than 10 POIs of one type, confirm that type's group paginates and other groups render in full; confirm clicking a list entry opens it in the right-hand edit form.