@basezero/roadmap
v0.1.0
Published
Feature request roadmap for Shopify apps, backed by BaseZero.
Readme
@basezero/roadmap
A feature request roadmap for Shopify apps, backed by BaseZero. Merchants suggest features, upvote, comment in Markdown, and subscribe to status updates from inside your app's admin.
Browser (your app's admin) Your app server BaseZero
<Roadmap endpoint="/api/roadmap"> ──► handleRoadmapRequest(request, ──► /api/sdk/roadmap/*
{ apiKey, shop, baseUrl })The browser never sees your bz_live_ key. Your server authenticates the shop and forwards the call with the key and the verified shop domain.
Install
npm install @basezero/roadmapRequires react 18 or 19.
Shopify app integration
Call billing identify for the shop first (the roadmap uses the same BaseZero Subscriber).
1. One proxy route (Remix / React Router, app/routes/api.roadmap.$.ts):
import { handleRoadmapRequest } from "@basezero/roadmap/server";
import { authenticate } from "../shopify.server";
export const loader = async ({ request }) => {
const { session } = await authenticate.admin(request);
return handleRoadmapRequest(request, {
apiKey: process.env.BZ_KEY!,
shop: session.shop,
baseUrl: "https://<your-basezero-host>",
});
};
export const action = loader;2. One component and one stylesheet on any page:
import { Roadmap } from "@basezero/roadmap/react";
import "@basezero/roadmap/styles.css";
export default function RoadmapPage() {
return (
<Roadmap
endpoint="/api/roadmap"
publicBoardUrl="https://<your-basezero-host>/roadmap-board/<projectId>"
/>
);
}<Roadmap> uses the global fetch, which App Bridge patches to attach the session token to same-origin requests. Pass fetch to override.
Server: handleRoadmapRequest(request, options)
| Option | Type | Description |
| --------- | -------------- | ------------------------------------------------------------ |
| apiKey | string | The Project's bz_live_ billing API key. |
| shop | string | The shop domain your app already authenticated. |
| baseUrl | string | Your BaseZero origin. |
| fetch | typeof fetch | Optional fetch implementation. |
It keeps the path from the last /requests segment, so it works under any mount path. Only /requests, /requests/:id, and /requests/:id/(vote|follow|comments) are forwarded; anything else returns 404 { error: { code: "not_found" } }. Cookies and the incoming Authorization header are never forwarded.
React
<Roadmap> (container)
| Prop | Type | Default | Description |
| ----------------- | ------------------------------ | ---------------- | ------------------------------------------------------------------------ |
| endpoint | string | "/api/roadmap" | Your proxy route. |
| fetch | typeof fetch | global fetch | Custom fetch. |
| publicBoardUrl | string | | Enables "Copy link" (<publicBoardUrl>?request=<id>). |
| requestId | string \| null | | Controls the open request (e.g. synced to the URL). |
| onRequestChange | (id: string \| null) => void | | Called when a request opens or closes, and when a merged id redirects. |
| className | string | | Added to the root .bz-roadmap element. |
It loads the board, sorts by votes or newest, votes and subscribes optimistically (rolling back on failure), opens request details in a modal, follows merged requests to the surviving one, and refreshes the board after suggestions and comments.
Views
The views are pure: data in, callbacks out. Leave out a callback and that control renders read-only. Wrap standalone views in an element with the bz-roadmap class so the theme variables apply.
RoadmapBoard({ requests, sort?, onSortChange?, onOpenRequest?, onVote?, onSuggest?, title? }): status columns (Pending 👀, Approved 👍, In progress 🛠, Done ✅, Rejected). The viewer's own pending suggestions lead the first column marked "Awaiting review".RequestCard({ request, onOpen?, onVote? })RequestDetail({ request, onVote?, onFollow?, onComment?, publicUrl?, onClose? })SuggestDialog({ open, onClose, onSubmit })RoadmapDialog({ open, onClose, label, children }): the native<dialog>modal used by the container.createRoadmapClient({ endpoint, fetch? })andRoadmapClientError(status,code,mergedIntoId?).
Wire types live in @basezero/roadmap/contracts and are re-exported from @basezero/roadmap/react.
Theming
All classes are prefixed bz-. Override the variables on .bz-roadmap:
| Variable | Default (light) | Used for |
| -------------- | --------------- | ------------------------------ |
| --bz-bg | #f1f1f1 | Page background, pills |
| --bz-surface | #ffffff | Columns, cards, dialogs |
| --bz-border | #e3e3e3 | Card and input borders |
| --bz-text | #1a1a1a | Body text |
| --bz-muted | #6b6b6b | Descriptions, dates, hints |
| --bz-accent | #1a1a1a | Primary buttons, active votes |
| --bz-accent-text | #ffffff | Text on accent backgrounds |
| --bz-radius | 12px | Column and dialog corners |
| --bz-font | system stack | Font family |
Dark values apply under prefers-color-scheme: dark. Columns scroll horizontally below 900px.
.bz-roadmap {
--bz-accent: #008060;
--bz-radius: 8px;
}Releasing
- Create the npm org
basezero(once). - Add an
NPM_TOKENrepo secret on github.com/panikkastudio/basezero (once). - Bump
versioninpackages/roadmap/package.jsonand commit. - Tag and push; the
publish-roadmapworkflow publishes to npm:
git tag roadmap-v<version> && git push origin roadmap-v<version>The tag must match the package version or the workflow fails.
License
MIT
