@validation-os/dashboard 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -316,6 +316,9 @@ type Route = {
316
316
  } | {
317
317
  name: "records";
318
318
  register: Collection;
319
+ lens?: string;
320
+ stage?: string;
321
+ view?: "all";
319
322
  } | {
320
323
  name: "record";
321
324
  id: string;
@@ -1347,6 +1350,9 @@ interface RegisterBrowserProps {
1347
1350
  /** Open a record's canonical full page (story 12). When set, the drawer
1348
1351
  * offers a "Full page" link; reads/edits still happen in the peek drawer. */
1349
1352
  onOpenRecord?: (id: string) => void;
1353
+ /** Pre-filter applied before tab/free-text — Lens × Stage from the grid. */
1354
+ lens?: string;
1355
+ stage?: string;
1350
1356
  }
1351
1357
  /**
1352
1358
  * The browse-create-edit surface for one register. Above the flat table sits the
@@ -1362,7 +1368,7 @@ interface RegisterBrowserProps {
1362
1368
  * gated API (which recomputes derived fields on write). The canonical full record
1363
1369
  * page is reachable via the drawer's "Full page" link when `onOpenRecord` is set.
1364
1370
  */
1365
- declare function RegisterBrowser({ register, basePath, subtitle, onOpenRecord, }: RegisterBrowserProps): react.JSX.Element;
1371
+ declare function RegisterBrowser({ register, basePath, subtitle, onOpenRecord, lens, stage, }: RegisterBrowserProps): react.JSX.Element;
1366
1372
 
1367
1373
  /**
1368
1374
  * The front door — "what's my next move" (design OPS-1295, build OPS-1304): the
package/dist/index.js CHANGED
@@ -49,7 +49,6 @@ var REGISTER_GROUPS = [
49
49
  ];
50
50
  var WORKFLOW_NAV = [
51
51
  { route: "next", label: "Next move", icon: "\u25C8", isDefault: true },
52
- { route: "stage-grid", label: "Lens \xD7 Stage", icon: "\u25A6" },
53
52
  { route: "pipeline", label: "Pipeline", icon: "\u25A4" }
54
53
  ];
55
54
 
@@ -4782,7 +4781,9 @@ function RegisterBrowser({
4782
4781
  register,
4783
4782
  basePath,
4784
4783
  subtitle,
4785
- onOpenRecord
4784
+ onOpenRecord,
4785
+ lens,
4786
+ stage
4786
4787
  }) {
4787
4788
  const { records, loading, error, refresh: refreshList } = useList(
4788
4789
  register,
@@ -4804,18 +4805,25 @@ function RegisterBrowser({
4804
4805
  } = useRecord(register, openId, basePath);
4805
4806
  const [asOf] = useState10(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
4806
4807
  const rows = records ?? [];
4808
+ const prefiltered = useMemo4(() => {
4809
+ return rows.filter((r) => {
4810
+ if (lens !== void 0 && (r.Lens ?? "") !== lens) return false;
4811
+ if (stage !== void 0 && (r.Stage ?? "") !== stage) return false;
4812
+ return true;
4813
+ });
4814
+ }, [rows, lens, stage]);
4807
4815
  const ctx = {
4808
4816
  asOf,
4809
- assumptions: register === "assumptions" ? rows : assumptions.records ?? [],
4810
- experiments: register === "experiments" ? rows : experiments.records ?? [],
4811
- readings: register === "readings" ? rows : readings.records ?? [],
4812
- decisions: register === "decisions" ? rows : []
4817
+ assumptions: register === "assumptions" ? prefiltered : assumptions.records ?? [],
4818
+ experiments: register === "experiments" ? prefiltered : experiments.records ?? [],
4819
+ readings: register === "readings" ? prefiltered : readings.records ?? [],
4820
+ decisions: register === "decisions" ? prefiltered : []
4813
4821
  };
4814
4822
  const shaped = useMemo4(
4815
- () => shapeRegister(register, rows, descriptor, ctx),
4823
+ () => shapeRegister(register, prefiltered, descriptor, ctx),
4816
4824
  // ctx is derived from the same inputs; listing them keeps the memo honest.
4817
4825
  // eslint-disable-next-line react-hooks/exhaustive-deps
4818
- [register, rows, descriptor, ctx.assumptions, ctx.experiments, ctx.readings, asOf]
4826
+ [register, prefiltered, descriptor, ctx.assumptions, ctx.experiments, ctx.readings, asOf]
4819
4827
  );
4820
4828
  const counts = needsHumanCounts(ctx);
4821
4829
  const humanCount = {
@@ -5568,14 +5576,14 @@ function StageGridSurface({ basePath, onNavigate }) {
5568
5576
  {
5569
5577
  type: "button",
5570
5578
  className: "vos-btn",
5571
- onClick: () => onNavigate({ name: "records", register: "assumptions" }),
5579
+ onClick: () => onNavigate({ name: "records", register: "assumptions", view: "all" }),
5572
5580
  children: "Write your first bet"
5573
5581
  }
5574
5582
  )
5575
5583
  ] })
5576
5584
  ] });
5577
5585
  }
5578
- return /* @__PURE__ */ jsxs17(StageGridFrame, { total: view.total, onRefresh: refresh, children: [
5586
+ return /* @__PURE__ */ jsxs17(StageGridFrame, { total: view.total, onRefresh: refresh, onNavigate, children: [
5579
5587
  /* @__PURE__ */ jsxs17("div", { className: "vos-card vos-stage-grid-card", children: [
5580
5588
  /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-scroll", children: /* @__PURE__ */ jsxs17("table", { className: "vos-stage-grid", role: "grid", "aria-label": "Lens \xD7 Stage heatmap", children: [
5581
5589
  /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsxs17("tr", { children: [
@@ -5607,7 +5615,8 @@ function StageGridSurface({ basePath, onNavigate }) {
5607
5615
  {
5608
5616
  cell: open,
5609
5617
  onClose: () => setOpen(null),
5610
- onOpenRecord: openRecord
5618
+ onOpenRecord: openRecord,
5619
+ onNavigate
5611
5620
  }
5612
5621
  )
5613
5622
  ] });
@@ -5615,6 +5624,7 @@ function StageGridSurface({ basePath, onNavigate }) {
5615
5624
  function StageGridFrame({
5616
5625
  total,
5617
5626
  onRefresh,
5627
+ onNavigate,
5618
5628
  children
5619
5629
  }) {
5620
5630
  return /* @__PURE__ */ jsxs17("div", { children: [
@@ -5637,6 +5647,15 @@ function StageGridFrame({
5637
5647
  onClick: onRefresh,
5638
5648
  children: "\u21BB Refresh"
5639
5649
  }
5650
+ ) : null,
5651
+ onNavigate ? /* @__PURE__ */ jsx18(
5652
+ "button",
5653
+ {
5654
+ type: "button",
5655
+ className: "vos-btn vos-btn-ghost",
5656
+ onClick: () => onNavigate({ name: "records", register: "assumptions", view: "all" }),
5657
+ children: "View all \u2192"
5658
+ }
5640
5659
  ) : null
5641
5660
  ] }),
5642
5661
  /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-host", children })
@@ -5674,7 +5693,8 @@ function StageCell({
5674
5693
  function CellDrawer({
5675
5694
  cell,
5676
5695
  onClose,
5677
- onOpenRecord
5696
+ onOpenRecord,
5697
+ onNavigate
5678
5698
  }) {
5679
5699
  return /* @__PURE__ */ jsxs17(
5680
5700
  DrawerShell,
@@ -5683,15 +5703,31 @@ function CellDrawer({
5683
5703
  onClose,
5684
5704
  ariaLabel: cell ? `${cell.lens} \xD7 ${cell.stage} \u2014 ${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}` : "Cell drill-through",
5685
5705
  children: [
5686
- /* @__PURE__ */ jsx18("header", { className: "vos-drawer-header", children: /* @__PURE__ */ jsxs17("div", { children: [
5687
- /* @__PURE__ */ jsx18("p", { className: "vos-drawer-eyebrow", children: "Lens \xD7 Stage" }),
5688
- /* @__PURE__ */ jsxs17("h2", { className: "vos-drawer-title", children: [
5689
- cell?.lens,
5690
- " \xD7 ",
5691
- cell?.stage
5706
+ /* @__PURE__ */ jsxs17("header", { className: "vos-drawer-header", children: [
5707
+ /* @__PURE__ */ jsxs17("div", { children: [
5708
+ /* @__PURE__ */ jsx18("p", { className: "vos-drawer-eyebrow", children: "Lens \xD7 Stage" }),
5709
+ /* @__PURE__ */ jsxs17("h2", { className: "vos-drawer-title", children: [
5710
+ cell?.lens,
5711
+ " \xD7 ",
5712
+ cell?.stage
5713
+ ] }),
5714
+ /* @__PURE__ */ jsx18("p", { className: "vos-hint", children: cell && cell.count > 0 ? `${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}, ranked by Risk \u2014 riskiest first.` : "No beliefs in this cell." })
5692
5715
  ] }),
5693
- /* @__PURE__ */ jsx18("p", { className: "vos-hint", children: cell && cell.count > 0 ? `${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}, ranked by Risk \u2014 riskiest first.` : "No beliefs in this cell." })
5694
- ] }) }),
5716
+ cell && cell.stage !== "\u2014" && onNavigate && /* @__PURE__ */ jsx18(
5717
+ "button",
5718
+ {
5719
+ type: "button",
5720
+ className: "vos-btn vos-btn-secondary",
5721
+ onClick: () => onNavigate({
5722
+ name: "records",
5723
+ register: "assumptions",
5724
+ lens: cell.lens === "\u2014" ? void 0 : cell.lens,
5725
+ stage: cell.stage
5726
+ }),
5727
+ children: "Open in table \u2192"
5728
+ }
5729
+ )
5730
+ ] }),
5695
5731
  cell && cell.count > 0 ? /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-drawer-body", children: /* @__PURE__ */ jsx18(
5696
5732
  RegisterTable,
5697
5733
  {
@@ -5710,8 +5746,13 @@ var DEFAULT_ROUTE = { name: "next" };
5710
5746
  function parseRoute(hash, registers) {
5711
5747
  const h = hash.replace(/^#\/?/, "");
5712
5748
  if (!h) return DEFAULT_ROUTE;
5713
- const parts = h.split("/");
5749
+ const [pathPart = "", queryPart = ""] = h.split("?");
5750
+ const parts = pathPart.split("/");
5714
5751
  const head = parts[0] ?? "";
5752
+ const query = new URLSearchParams(queryPart ?? "");
5753
+ const lens = query.get("lens") ?? void 0;
5754
+ const stage = query.get("stage") ?? void 0;
5755
+ const view = query.get("view") ?? void 0;
5715
5756
  if (head === "next") return { name: "next" };
5716
5757
  if (head === "pipeline") return { name: "pipeline" };
5717
5758
  if (head === "stage-grid") return { name: "stage-grid" };
@@ -5720,14 +5761,20 @@ function parseRoute(hash, registers) {
5720
5761
  return id ? { name: "record", id } : DEFAULT_ROUTE;
5721
5762
  }
5722
5763
  if (registers.includes(head)) {
5723
- return { name: "records", register: head };
5764
+ return { name: "records", register: head, lens, stage, view };
5724
5765
  }
5725
5766
  return DEFAULT_ROUTE;
5726
5767
  }
5727
5768
  function formatRoute(route) {
5728
5769
  switch (route.name) {
5729
- case "records":
5730
- return route.register;
5770
+ case "records": {
5771
+ const q = new URLSearchParams();
5772
+ if (route.lens) q.set("lens", route.lens);
5773
+ if (route.stage) q.set("stage", route.stage);
5774
+ if (route.view) q.set("view", route.view);
5775
+ const qs = q.toString();
5776
+ return qs ? `${route.register}?${qs}` : route.register;
5777
+ }
5731
5778
  case "record":
5732
5779
  return `record/${route.id}`;
5733
5780
  default:
@@ -5928,15 +5975,22 @@ function ValidationOSDashboard({ config = {} }) {
5928
5975
  registers
5929
5976
  }
5930
5977
  ),
5931
- /* @__PURE__ */ jsx20("main", { className: "vos-main", children: route.name === "records" ? /* @__PURE__ */ jsx20(
5932
- RegisterBrowser,
5933
- {
5934
- register: route.register,
5935
- basePath,
5936
- subtitle: REGISTER_SUBTITLE[route.register],
5937
- onOpenRecord: (id) => navigate({ name: "record", id })
5938
- },
5939
- route.register
5978
+ /* @__PURE__ */ jsx20("main", { className: "vos-main", children: route.name === "records" ? (
5979
+ // The assumptions register renders the grid as its landing surface
5980
+ // (#assumptions with no query); cell-click and "view all" drill into
5981
+ // the filtered/unfiltered table by setting lens/stage/view on the route.
5982
+ route.register === "assumptions" && !route.lens && !route.stage && route.view !== "all" ? /* @__PURE__ */ jsx20(StageGridSurface, { basePath, onNavigate: navigate }, "stage-grid") : /* @__PURE__ */ jsx20(
5983
+ RegisterBrowser,
5984
+ {
5985
+ register: route.register,
5986
+ basePath,
5987
+ subtitle: REGISTER_SUBTITLE[route.register],
5988
+ onOpenRecord: (id) => navigate({ name: "record", id }),
5989
+ lens: route.lens,
5990
+ stage: route.stage
5991
+ },
5992
+ route.register + (route.lens ?? "") + (route.stage ?? "") + (route.view ?? "")
5993
+ )
5940
5994
  ) : route.name === "record" ? /* @__PURE__ */ jsx20(
5941
5995
  RecordPage,
5942
5996
  {