Remove completed todo.md + html-css-foundation mockups; strip dead §N phase refs from comments/docs (simplify visual E2E to drop the mockup-comparison oracle)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Reference plugin (todo §7): a worked example of the contract — a list page that fetches upstream
|
||||
// Reference plugin: a worked example of the contract — a list page that fetches upstream
|
||||
// data, a CSRF-guarded form that forwards a write upstream, and permission-gated nav. Copy this
|
||||
// folder, rename it, point it at your own backend. Full contract: docs/plugin-contract.md.
|
||||
|
||||
@@ -19,7 +19,7 @@ export default definePlugin({
|
||||
|
||||
// Merged into the global menu + filtered per user. "Overview" is `public`, so the "Scheduling"
|
||||
// header shows for everyone (even signed out); "Shifts" needs `scheduling:read`, so the gated data
|
||||
// stays hidden until a reader signs in (§10 — a plugin may make a page + its menu option public).
|
||||
// stays hidden until a reader signs in (a plugin may make a page + its menu option public).
|
||||
nav: [{
|
||||
children: [
|
||||
{ href: SCHEDULING_PATH, id: "scheduling:overview", label: "Overview", public: true },
|
||||
|
||||
@@ -112,7 +112,7 @@ test("listShifts degrades to a recoverable error page when the upstream is down
|
||||
assert.deepEqual((r.data["table"] as { rows: unknown[] }).rows, []);
|
||||
});
|
||||
|
||||
// ---- public overview handler (§10: a page anyone can reach, gated data stays behind the role) ----
|
||||
// ---- public overview handler (a page anyone can reach, gated data stays behind the role) ----
|
||||
|
||||
test("overview renders a public page for anyone; it links straight to Shifts only for a reader", async () => {
|
||||
const anon = asView(await overview()(fakeCtx())); // user null, no roles
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Reference plugin (todo §7) — Scheduling/Shifts handlers + the upstream client. Shows the blessed
|
||||
// Reference plugin — Scheduling/Shifts handlers + the upstream client. Shows the blessed
|
||||
// shape: a thin handler parses ctx, calls an upstream REST service, and returns a RouteResult the
|
||||
// host renders. The plugin holds no state of its own (README "Stateless") — data lives upstream.
|
||||
//
|
||||
@@ -8,7 +8,7 @@
|
||||
// One import from the host's plugin-api barrel — the stable author surface (see docs/plugin-contract.md).
|
||||
import { can, CSRF_FIELD, GuardError, type PageChrome, parseListQuery, readFormBody, type RouteHandler, tracedFetch } from "../../src/plugin-api.ts";
|
||||
|
||||
export const SCHEDULING_PATH = "/scheduling"; // the plugin's public overview page (§10)
|
||||
export const SCHEDULING_PATH = "/scheduling"; // the plugin's public overview page
|
||||
export const SHIFTS_PATH = "/scheduling/shifts";
|
||||
export const READ = "scheduling:read"; // permission token gating the list + nav
|
||||
export const WRITE = "scheduling:write"; // permission token gating create
|
||||
@@ -56,7 +56,7 @@ export function assertHttpUrl(value: string, name: string): void {
|
||||
}
|
||||
|
||||
// REST client over the upstream service (a stand-in for the customer's real backend). `fetch`
|
||||
// defaults to the host's tracedFetch (§9), so each upstream call joins the request's trace (a client
|
||||
// defaults to the host's tracedFetch, so each upstream call joins the request's trace (a client
|
||||
// span + a propagated traceparent); it's injectable so handlers unit-test against a mock, no network.
|
||||
export function createUpstream(baseUrl: string, fetchImpl: typeof fetch = tracedFetch): ShiftsUpstream {
|
||||
const base = baseUrl.replace(/\/+$/, "");
|
||||
@@ -172,7 +172,7 @@ export function listShifts(upstream: ShiftsUpstream): RouteHandler {
|
||||
try {
|
||||
shifts = await upstream.list();
|
||||
} catch (err) {
|
||||
ctx.log.warn("scheduling upstream unreachable", { error: String(err) }); // plugin logging via ctx.log (§9)
|
||||
ctx.log.warn("scheduling upstream unreachable", { error: String(err) }); // plugin logging via ctx.log
|
||||
error = "Couldn't reach the scheduling service — try again shortly.";
|
||||
}
|
||||
const needle = q.toLowerCase();
|
||||
@@ -185,7 +185,7 @@ export function newShiftForm(): RouteHandler {
|
||||
return (ctx) => ({ data: buildFormModel({ chrome: ctx.chrome }), view: "shift-new" });
|
||||
}
|
||||
|
||||
// Public overview (§10): a page anyone may reach — its route + nav node are marked `public`, so the
|
||||
// Public overview: a page anyone may reach — its route + nav node are marked `public`, so the
|
||||
// gate lets an anonymous visitor through and the menu option shows for everyone. The real data
|
||||
// (the shifts list) stays behind `scheduling:read`; a reader gets a link straight to it, anyone
|
||||
// else a prompt to sign in. ctx.user may be null here, so read the role via can() (zero I/O).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%#
|
||||
Scheduling · public overview (reference plugin, §10). A page ANYONE may reach — the route and its
|
||||
Scheduling · public overview (reference plugin). A page ANYONE may reach — the route and its
|
||||
nav node are marked `public`, so an anonymous visitor is let through and the menu option shows for
|
||||
everyone. The actual shifts data stays behind `scheduling:read`: a reader gets a link straight to
|
||||
it, anyone else a prompt to sign in. Rendered in the native shell via ctx.chrome.
|
||||
|
||||
Reference in New Issue
Block a user