motir.co serves the five tabs — Board, Items, Tree, Roadmap and Changelog
Opened by Zhu Yue ·
The five tab routes under app/p/[identifier]/, rendered inside the shell the page card ships, each against its endpoint on motir-core's public contract:
| route | endpoint | shape |
|---|---|---|
board | GET /api/public/p/{identifier}/board (new) | the public board's columns |
items | GET …/items?cursor= (shipped) | the lazy list + "Load more" |
tree | GET …/tree?parentId=&offset= (shipped) | one level at a time; expand fetches the next |
roadmap | GET …/roadmap for the four columns (new), ?bucket=&cursor= per column (shipped) | four columns, each paging independently |
changelog | GET …/changelog?cursor= (shipped) | the paged log |
Paging must survive without JavaScript wherever the shipped precedent does. app/explore/_components/Gallery.tsx pages with <a href="?cursor=…"> deliberately; follow that for the flat lists. The tree's expand and the roadmap's per-column "Load more" are genuinely interactive — draw the line where the design asset draws it, and say in the code which side each control is on.
Every tab owes its empty, loading and error states, from the design. The error state is the unreachable-public-API case (public-surface-hosts.md §8 cost 1), and it is per-tab: a roadmap column that fails to page must not blank the page.
Plan for real data, not a demo. The items list, the changelog and each roadmap column page; the tree levels page; a project with two thousand work items is the case the mirror products design for, and the endpoints already return cursors precisely so this surface can. A tab that loads everything is mis-built even where it currently looks fine.
Scope boundary: this card renders and pages. It adds no control that writes — the roadmap's VOTE is the act affordances card's, even though it appears on the roadmap. Where the design draws a vote control, this card renders the surface it will attach to and nothing else, and says so.
Acceptance criteria
/p/<identifier>/{board,items,tree,roadmap,changelog}each render for a logged-out visitor, inside the shell, with the correct tab marked current.- Each tab pages against its endpoint: Items and Changelog by cursor, the Tree one level at a time with expand, the Roadmap per column. A project with more rows than one page returns more on the next request, asserted in a test per tab.
- The flat-list pagers work with JavaScript disabled, matching the
/exploreprecedent, and the interactive ones are the ones the design asset marks interactive. - Each tab renders its empty, loading and API-unreachable states as the design draws them, and a failure inside one roadmap column does not blank the page.
- Every read goes through
lib/publicProject.ts; no directfetchofAPP_ORIGINis written into a component, and no database client appears. - No write control ships in this card — a grep of the diff finds no
POST— and the surfaces the vote will attach to are rendered. - Each tab's canonical and
og:urlnameSITE_ORIGINand its own path. - No file outside
motir-marketingis touched.
Context refs
motir-marketing/app/explore/_components/Gallery.tsx— no-JS cursor paging, the precedent for the flat listsmotir-marketing/lib/explore.ts— the params/href model and thefailedstatemotir-core/app/api/public/p/[identifier]/{items,tree,roadmap,changelog}/route.ts— the shipped reads and their cursor errorsmotir-core/lib/dto/publicProjects.ts—PublicBoardDto,PublicRoadmapDto,PUBLIC_ROADMAP_BUCKET_KEYS, the item/changelog page shapesmotir-core/app/(public)/p/[identifier]/{board,items,tree,roadmap,changelog}/page.tsx@95a2d4468^— prior art onlymotir-marketing/design/public-projects/— the five tab boards and their states
Discussion
No comments yet.
Adding to this discussion signs you in on app.motir.co and brings you back to this request.