@volter/twin-stripe 2.0.33 → 2.0.35

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.
@@ -273,3 +273,5 @@ h1 .pill { font-size: 13px; height: 22px; }
273
273
  .popover-item:hover { background: var(--hover); }
274
274
  .popover-radio { width: 14px; height: 14px; border-radius: 50%; border: 1px solid #a3acba; flex: none; }
275
275
  .popover-item.checked .popover-radio { border: 4px solid var(--brand); }
276
+ .show-note { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 16px; padding: 10px 12px; border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text); font-size: 13px; }
277
+ .show-note button { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 2px; }
@@ -1494,14 +1494,39 @@ export function Page({ route, search, go }: { route: string[]; search: string; g
1494
1494
  return <div className="page"><EmptyState title="Page not found" body="This page doesn’t exist in the Dashboard." /></div>;
1495
1495
  }
1496
1496
 
1497
+ /** A record a World's console asks to see (`?show=<type>:<id>`, docs/contributing/architecture.md "Screens"): where the
1498
+ * Dashboard shows it. The console names a record, never a Stripe path. A type with no page of its own opens its list,
1499
+ * and one with no list stays on Home; either says so, never showing another page as if it were the record. */
1500
+ const SHOW_AT: Record<string, (id: string) => string> = { customer: (id) => `customers/${id}`, payment_intent: (id) => `payments/${id}`, charge: (id) => `payments/${id}`, subscription: (id) => `subscriptions/${id}`, invoice: (id) => `invoices/${id}` };
1501
+ /** The sections the Dashboard routes to, by the object type their rows are (a section's key is Stripe's plural). */
1502
+ const LISTED: Record<string, { path: string; label: string }> = {
1503
+ product: { path: 'products', label: 'Products' },
1504
+ // (not `accounts`: Connect lists connected accounts, never the platform's own account record)
1505
+ ...Object.fromEntries(SECTIONS.filter((s) => s.page && s.key !== 'accounts').map((s) => [s.key.replace(/s$/, '').replace(/\//g, '.'), { path: s.path, label: s.label }])),
1506
+ };
1507
+ function shown(search: string): { to: string; note?: string } | null {
1508
+ const asked = new URLSearchParams(search).get('show'); if (!asked) return null;
1509
+ const at = asked.indexOf(':'); const type = asked.slice(0, at); const id = asked.slice(at + 1);
1510
+ if (at <= 0 || !id) return null;
1511
+ const words = type.replace(/[._]/g, ' ');
1512
+ if (SHOW_AT[type]) return { to: hrefOf(SHOW_AT[type](id)) };
1513
+ if (LISTED[type]) return { to: hrefOf(LISTED[type].path), note: `These screens have no page for one ${words} yet; ${LISTED[type].label.toLowerCase()} are listed here. Look for ${id}.` };
1514
+ return { to: hrefOf('dashboard'), note: `These screens have no page for ${words} records yet, so ${id} can't be shown here.` };
1515
+ }
1497
1516
  export function App() {
1498
- const [loc, setLoc] = useState(() => ({ path: window.location.pathname, search: window.location.search }));
1517
+ const [asked] = useState(() => shown(window.location.search));
1518
+ const [note, setNote] = useState(asked?.note ?? '');
1519
+ const [loc, setLoc] = useState(() => {
1520
+ if (asked) { window.history.replaceState(null, '', asked.to); const u = new URL(asked.to, window.location.origin); return { path: u.pathname, search: u.search }; }
1521
+ return { path: window.location.pathname, search: window.location.search };
1522
+ });
1499
1523
  useEffect(() => {
1500
1524
  const onPop = () => setLoc({ path: window.location.pathname, search: window.location.search });
1501
1525
  window.addEventListener('popstate', onPop);
1502
1526
  return () => window.removeEventListener('popstate', onPop);
1503
1527
  }, []);
1504
1528
  const go: Go = useCallback((to) => {
1529
+ setNote('');
1505
1530
  const href = hrefOf(to);
1506
1531
  window.history.pushState(null, '', href);
1507
1532
  const u = new URL(href, window.location.origin);
@@ -1522,7 +1547,7 @@ export function App() {
1522
1547
  <SideNav route={route} go={go} />
1523
1548
  <div className="main">
1524
1549
  <TopBar go={go} />
1525
- <main className="content"><Page route={route} search={loc.search} go={go} /></main>
1550
+ <main className="content">{note ? <div className="show-note" role="status"><span>{note}</span><button type="button" aria-label="Dismiss" onClick={() => setNote('')}><Icon name="x" size={12} /></button></div> : null}<Page route={route} search={loc.search} go={go} /></main>
1526
1551
  </div>
1527
1552
  </div>
1528
1553
  </div>