@volter/twin-stripe 2.0.32 → 2.0.34
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/client/stripe-mirror.css
CHANGED
|
@@ -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; }
|
package/client/stripe-mirror.tsx
CHANGED
|
@@ -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 [
|
|
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>
|