@tokenoftrust/storefront-runner 2.0.2 → 2.0.3

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.
Files changed (117) hide show
  1. package/package.json +1 -1
  2. package/scripts/lib/tenant-dirs.mjs +80 -0
  3. package/scripts/tenant/lint-embeds.mjs +180 -0
  4. package/scripts/tenant/materialize-tenant-content.mjs +533 -0
  5. package/scripts/tenant/validate.mjs +837 -0
  6. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +0 -68
  7. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +0 -111
  8. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +0 -2
  9. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +0 -247
  10. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +0 -73
  11. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +0 -79
  12. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +0 -10982
  13. package/apps/storefront/scripts/import-v84-through-ws2.ts +0 -38
  14. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +0 -2
  15. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +0 -4
  16. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +0 -185
  17. package/apps/storefront/scripts/prove-clone-isolation.ts +0 -506
  18. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +0 -148
  19. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +0 -35
  20. package/apps/storefront/scripts/replay-v84-clean-clone.ts +0 -214
  21. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +0 -18
  22. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +0 -18
  23. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +0 -18
  24. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +0 -17
  25. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +0 -15
  26. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +0 -21
  27. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +0 -42
  28. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +0 -145
  29. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +0 -18
  30. package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +0 -531
  31. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +0 -878
  32. package/apps/storefront/src/components/admin/AdminBlogTab.astro +0 -1435
  33. package/apps/storefront/src/components/admin/AdminCheckoutSettingsPanel.astro +0 -226
  34. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +0 -593
  35. package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +0 -465
  36. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +0 -105
  37. package/apps/storefront/src/components/admin/AdminProductsTab.astro +0 -901
  38. package/apps/storefront/src/components/admin/AdminPublishTab.astro +0 -2983
  39. package/apps/storefront/src/components/admin/AdminReportingTab.astro +0 -435
  40. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +0 -782
  41. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +0 -611
  42. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +0 -1223
  43. package/apps/storefront/src/lib/dev/cockpitStore.ts +0 -57
  44. package/apps/storefront/src/lib/publish/adminPublishTab.ts +0 -4625
  45. package/apps/storefront/src/lib/publish/apex-readiness.ts +0 -360
  46. package/apps/storefront/src/lib/publish/buildStream.ts +0 -269
  47. package/apps/storefront/src/lib/publish/collapsePersistence.ts +0 -72
  48. package/apps/storefront/src/lib/publish/dispatchHealth.ts +0 -267
  49. package/apps/storefront/src/lib/publish/domainState.ts +0 -360
  50. package/apps/storefront/src/lib/publish/lifecycleState.ts +0 -369
  51. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +0 -29
  52. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +0 -78
  53. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +0 -62
  54. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +0 -124
  55. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +0 -75
  56. package/apps/storefront/src/lib/publish/panelPolling.ts +0 -78
  57. package/apps/storefront/src/lib/publish/publish-guard.ts +0 -88
  58. package/apps/storefront/src/lib/publish/publishFormat.ts +0 -165
  59. package/apps/storefront/src/lib/publish/publishHero.ts +0 -177
  60. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +0 -66
  61. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +0 -141
  62. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +0 -41
  63. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +0 -47
  64. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +0 -42
  65. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +0 -59
  66. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +0 -440
  67. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +0 -161
  68. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +0 -325
  69. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +0 -241
  70. package/apps/storefront/src/lib/publish/shipWorkspace.ts +0 -439
  71. package/apps/storefront/src/lib/publish/sseRoute.ts +0 -119
  72. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +0 -215
  73. package/apps/storefront/src/lib/publish/streamLogConsole.ts +0 -142
  74. package/apps/storefront/src/lib/publish/versionNumber.ts +0 -52
  75. package/apps/storefront/src/pages/admin/ops-timeline.astro +0 -420
  76. package/apps/storefront/src/pages/admin/review-release.astro +0 -265
  77. package/apps/storefront/src/pages/admin/select.astro +0 -96
  78. package/apps/storefront/src/pages/admin.astro +0 -570
  79. package/apps/storefront/src/pages/api/apps/admin/credentials/rotate.ts +0 -86
  80. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +0 -174
  81. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +0 -96
  82. package/apps/storefront/src/pages/api/apps/admin/health.ts +0 -44
  83. package/apps/storefront/src/pages/api/apps/admin/install.ts +0 -125
  84. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +0 -118
  85. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +0 -89
  86. package/apps/storefront/src/pages/api/apps/admin/list.ts +0 -26
  87. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +0 -85
  88. package/apps/storefront/src/pages/api/apps/admin/resume.ts +0 -79
  89. package/apps/storefront/src/pages/api/apps/admin/suspend.ts +0 -80
  90. package/apps/storefront/src/pages/api/apps/admin/uninstall.ts +0 -98
  91. package/apps/storefront/src/pages/api/apps/admin/update.ts +0 -126
  92. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries/[deliveryId]/replay.ts +0 -70
  93. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries.ts +0 -51
  94. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +0 -255
  95. package/apps/storefront/src/pages/api/apps/oauth/token.ts +0 -87
  96. package/apps/storefront/src/pages/api/apps/v1/attribution.ts +0 -162
  97. package/apps/storefront/src/pages/api/apps/v1/catalog/products/[handle].ts +0 -39
  98. package/apps/storefront/src/pages/api/apps/v1/catalog/products.ts +0 -49
  99. package/apps/storefront/src/pages/api/apps/v1/health.ts +0 -32
  100. package/apps/storefront/src/pages/api/apps/v1/inventory.ts +0 -59
  101. package/apps/storefront/src/pages/api/apps/v1/orders/[id].ts +0 -48
  102. package/apps/storefront/src/pages/api/apps/v1/orders.ts +0 -73
  103. package/apps/storefront/src/pages/api/apps/v1/reports.ts +0 -21
  104. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries/[deliveryId]/replay.ts +0 -74
  105. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries.ts +0 -47
  106. package/apps/storefront/src/pages/api/cache-purge.ts +0 -55
  107. package/apps/storefront/src/pages/api/compliance/tamper.ts +0 -88
  108. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +0 -154
  109. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +0 -120
  110. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +0 -108
  111. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +0 -70
  112. package/apps/storefront/src/pages/api/test/dev-session.ts +0 -133
  113. package/apps/storefront/src/pages/cockpit.astro +0 -1548
  114. package/apps/storefront/src/pages/dashboard/[appDomain]/apps/index.astro +0 -119
  115. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +0 -105
  116. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +0 -177
  117. package/apps/storefront/src/pages/dashboard/index.astro +0 -160
@@ -1,465 +0,0 @@
1
- ---
2
- import { getFulfillmentViewModel, getSettingsViewModel } from "@/lib/admin/services/mock";
3
-
4
- const { envelope } = Astro.props;
5
- const fulfillment = getFulfillmentViewModel(envelope);
6
- const settings = getSettingsViewModel(envelope);
7
-
8
- const QUEUE_TONE: Record<string, string> = {
9
- ready_to_ship: "green",
10
- holds: "amber",
11
- picked: "blue",
12
- packed: "blue",
13
- label_needed: "amber",
14
- shipped: "teal",
15
- exceptions: "red",
16
- };
17
-
18
- const readyToShipCount = fulfillment.queues.find((q) => q.key === "ready_to_ship")?.count ?? 0;
19
- const holdsCount = fulfillment.queues.find((q) => q.key === "holds")?.count ?? 0;
20
- const exceptionsCount = fulfillment.queues.find((q) => q.key === "exceptions")?.count ?? 0;
21
- const labelsLockedCount = fulfillment.shipments.filter((s) => s.labelLocked).length;
22
- const oldestSlaHours = fulfillment.shipments.reduce((max, s) => Math.max(max, s.slaAgeHours), 0);
23
-
24
- const selectedShipment = fulfillment.shipments.find((s) => s.labelLocked) ?? fulfillment.shipments[0] ?? null;
25
- const selectedAssistDraft = selectedShipment
26
- ? fulfillment.assistDrafts.find((d) => d.orderId === selectedShipment.orderId) ?? null
27
- : null;
28
-
29
- function queueLabel(key: string): string {
30
- return fulfillment.queues.find((q) => q.key === key)?.label ?? key;
31
- }
32
- ---
33
-
34
- <!-- agent-hook:fulfillment-tab -->
35
- <section id="fulfillment" class="band fulfillment-tab" aria-labelledby="admin-tab-fulfillment fulfillment-title" data-admin-panel data-agent-scope="fulfillment" role="tabpanel">
36
- <div class="section-title">
37
- <div>
38
- <h2 id="fulfillment-title">Fulfillment</h2>
39
- <p>Ship paid orders, clear exceptions, print labels, and keep fulfillment moving.</p>
40
- </div>
41
- <div class="actions">
42
- <button class="btn" type="button"><svg><use href="#i-export"></use></svg>Export queue</button>
43
- <button class="btn primary" type="button"><svg><use href="#i-truck"></use></svg>Create batch</button>
44
- </div>
45
- </div>
46
-
47
- {holdsCount + exceptionsCount > 0 && (
48
- <div class="ship-gate amber fulfillment-gate" aria-labelledby="fulfillment-gate-title">
49
- <div class="ship-gate-main">
50
- <span class="seal"><svg><use href="#i-alert"></use></svg></span>
51
- <div>
52
- <b id="fulfillment-gate-title">Hold: Review Required</b>
53
- <span>{holdsCount + exceptionsCount} shipment{holdsCount + exceptionsCount === 1 ? "" : "s"} need merchant action. Ready-to-ship orders stay visible and fast to label.</span>
54
- </div>
55
- </div>
56
- <div class="actions">
57
- <a class="btn" href="#fulfillment-blockers">Open blockers</a>
58
- <a class="btn primary" href="#fulfillment-ready">Ship ready orders</a>
59
- </div>
60
- </div>
61
- )}
62
-
63
- <div class="grid cols-4">
64
- <div class="metric">
65
- <span>Ready to ship</span>
66
- <strong>{readyToShipCount}</strong>
67
- <small>Paid, allocated, and checked</small>
68
- </div>
69
- <div class="metric">
70
- <span>Labels locked</span>
71
- <strong>{labelsLockedCount}</strong>
72
- <small>Blocked until the stated reason clears</small>
73
- </div>
74
- <div class="metric">
75
- <span>Holds</span>
76
- <strong>{holdsCount}</strong>
77
- <small>Action required before label creation</small>
78
- </div>
79
- <div class="metric">
80
- <span>Oldest SLA age</span>
81
- <strong>{oldestSlaHours}h</strong>
82
- <small>Since payment was captured</small>
83
- </div>
84
- </div>
85
-
86
- <div class="panel fulfillment-assist-panel">
87
- <div class="panel-head fulfillment-panel-head">
88
- <h3><svg><use href="#i-ask"></use></svg>Fulfillment Assist</h3>
89
- <span class="chip blue">Operator support, not autopilot — draft-only, evidence-linked</span>
90
- </div>
91
- <div class="panel-body grid">
92
- {fulfillment.assistDrafts.length === 0 ? (
93
- <p class="note">No exceptions or label locks right now — nothing for Assist to explain.</p>
94
- ) : (
95
- <div class="assist-signal-grid">
96
- {fulfillment.assistDrafts.map((draft) => (
97
- <div class="assist-signal">
98
- <span class="chip amber">{draft.orderId}</span>
99
- <p>{draft.summary}</p>
100
- </div>
101
- ))}
102
- </div>
103
- )}
104
- <p class="note">Assist never automates a shipping decision — every output here is a draft or explanation an operator reviews before acting.</p>
105
- </div>
106
- </div>
107
-
108
- <div class="fulfillment-workspace">
109
- <div class="panel fulfillment-queue-panel">
110
- <div class="panel-head fulfillment-panel-head">
111
- <h3><svg><use href="#i-truck"></use></svg>Fulfillment Queue</h3>
112
- <span class="chip teal">{fulfillment.shipments.length} shipments</span>
113
- </div>
114
- <div class="panel-body grid">
115
- <div class="tabs" role="tablist" aria-label="Fulfillment queues">
116
- {fulfillment.queues.map((queue, index) => (
117
- <button class:list={["tab", { active: index === 0 }]} type="button">{queue.label} ({queue.count})</button>
118
- ))}
119
- </div>
120
-
121
- <div class="table-wrap fulfillment-table-wrap" id="fulfillment-ready">
122
- <table class="fulfillment-table">
123
- <thead>
124
- <tr>
125
- <th>Order</th>
126
- <th>Destination</th>
127
- <th>Queue</th>
128
- <th>SLA age</th>
129
- <th>Carrier</th>
130
- <th>Label</th>
131
- <th class="action">Action</th>
132
- </tr>
133
- </thead>
134
- <tbody>
135
- {fulfillment.shipments.map((shipment) => (
136
- <tr class:list={[{ selected: shipment.orderId === selectedShipment?.orderId }]}>
137
- <td><strong>{shipment.orderId}</strong></td>
138
- <td><strong>{shipment.destination}</strong><span class="subtle">{shipment.destinationReady ? "Serviceable" : "Needs review"}</span></td>
139
- <td><span class:list={["chip", QUEUE_TONE[shipment.queue] ?? ""]}>{queueLabel(shipment.queue)}</span></td>
140
- <td><strong>{shipment.slaAgeHours}h</strong></td>
141
- <td><span class:list={["chip", shipment.carrierReady ? "green" : "amber"]}>{shipment.carrier}</span></td>
142
- <td><span class:list={["chip", shipment.labelLocked ? "amber" : "green"]}>{shipment.labelLocked ? "Locked" : "Unlocked"}</span></td>
143
- <td class="action"><a class="btn" href={shipment.labelLocked ? "#fulfillment-blockers" : "#fulfillment-detail"}>{shipment.labelLocked ? "Resolve" : "Create label"}</a></td>
144
- </tr>
145
- ))}
146
- </tbody>
147
- </table>
148
- </div>
149
- </div>
150
- </div>
151
-
152
- <aside class="panel fulfillment-detail-panel" id="fulfillment-detail" aria-labelledby="fulfillment-detail-title">
153
- {selectedShipment ? (
154
- <>
155
- <div class="panel-head fulfillment-panel-head">
156
- <h3 id="fulfillment-detail-title"><svg><use href="#i-file"></use></svg>{selectedShipment.orderId}</h3>
157
- <span class:list={["chip", selectedShipment.labelLocked ? "amber" : "green"]}>
158
- {selectedShipment.labelLocked ? "Shipping Label Locked" : "Shipping Label Unlocked"}
159
- </span>
160
- </div>
161
- <div class="panel-body grid">
162
- <div class="detail-summary">
163
- <div>
164
- <span class="eyebrow">Destination</span>
165
- <strong>{selectedShipment.destination}</strong>
166
- <span class="subtle">{selectedShipment.destinationReady ? "Serviceable" : "Needs review"}</span>
167
- </div>
168
- <div>
169
- <span class="eyebrow">Carrier</span>
170
- <strong>{selectedShipment.carrier}</strong>
171
- <span class="subtle">{selectedShipment.carrierReady ? "Eligible" : "Not eligible yet"}</span>
172
- </div>
173
- <div>
174
- <span class="eyebrow">SLA age</span>
175
- <strong>{selectedShipment.slaAgeHours}h</strong>
176
- <span class="subtle">Since payment captured</span>
177
- </div>
178
- </div>
179
-
180
- <div class:list={["label-card", selectedShipment.labelLocked ? "locked" : "unlocked"]}>
181
- <div>
182
- <span class="eyebrow">Shipping label</span>
183
- <strong>{selectedShipment.labelLocked ? "Locked until the blocker clears" : "Ready to create"}</strong>
184
- <p>
185
- {selectedShipment.labelLocked
186
- ? selectedShipment.labelLockReason ?? "Resolve the outstanding blocker to unlock this label."
187
- : "Carrier and destination checks are clear — create the label whenever you're ready."}
188
- </p>
189
- </div>
190
- <button class="btn primary" type="button" disabled={selectedShipment.labelLocked}>
191
- {selectedShipment.labelLocked ? "Label locked" : "Create label"}
192
- </button>
193
- </div>
194
-
195
- {selectedShipment.labelLocked && (
196
- <div class="action-strip" id="fulfillment-blockers">
197
- <a class="btn primary" href="#fulfillment-readiness">Resolve blocker</a>
198
- <button class="btn" type="button">Contact customer</button>
199
- </div>
200
- )}
201
-
202
- <div class="detail-section" id="fulfillment-readiness">
203
- <div class="detail-section-head">
204
- <h4>Readiness Checks</h4>
205
- <span class:list={["chip", selectedShipment.carrierReady && selectedShipment.destinationReady ? "green" : "amber"]}>
206
- {[!selectedShipment.carrierReady && "carrier", !selectedShipment.destinationReady && "destination"].filter(Boolean).length || "0"} blocker(s)
207
- </span>
208
- </div>
209
- <div class:list={["check-row", { blocking: !selectedShipment.carrierReady }]}>
210
- <div><b>Carrier eligibility</b><p>Carrier options unlock once the shipment's carrier check passes.</p></div>
211
- <strong>{selectedShipment.carrier}</strong>
212
- <span class:list={["chip", selectedShipment.carrierReady ? "green" : "amber"]}>{selectedShipment.carrierReady ? "OK" : "Waiting"}</span>
213
- </div>
214
- <div class:list={["check-row", { blocking: !selectedShipment.destinationReady }]}>
215
- <div><b>Address and jurisdiction</b><p>Destination is serviceable once any jurisdiction or address concerns clear.</p></div>
216
- <strong>{selectedShipment.destination}</strong>
217
- <span class:list={["chip", selectedShipment.destinationReady ? "green" : "amber"]}>{selectedShipment.destinationReady ? "Ready" : "Review"}</span>
218
- </div>
219
- </div>
220
-
221
- {selectedAssistDraft && (
222
- <div class="detail-section">
223
- <div class="detail-section-head">
224
- <h4>Fulfillment Assist</h4>
225
- <span class="chip blue">Draft</span>
226
- </div>
227
- <p class="note">{selectedAssistDraft.summary}</p>
228
- </div>
229
- )}
230
- </div>
231
- </>
232
- ) : (
233
- <div class="panel-body">
234
- <p class="note">No shipments yet for this tenant.</p>
235
- </div>
236
- )}
237
- </aside>
238
- </div>
239
-
240
- <div class="panel fulfillment-policy-panel">
241
- <div class="panel-head fulfillment-panel-head">
242
- <h3><svg><use href="#i-shield"></use></svg>Shipping Policy Context</h3>
243
- <span class:list={["chip", settings.shippingRestrictions.length > 0 ? "amber" : "green"]}>
244
- {settings.shippingRestrictions.length > 0 ? `${settings.shippingRestrictions.length} region(s) restricted` : "No active restrictions"}
245
- </span>
246
- </div>
247
- <div class="panel-body grid">
248
- <p class="note">Per-shipment destination readiness (above) reflects that shipment's own carrier/jurisdiction check. This panel surfaces the tenant's current shipping-restriction policy from Settings for context — see the Settings tab to manage it.</p>
249
- {settings.shippingRestrictions.length > 0 && (
250
- <div class="readiness-list" aria-label="Restricted regions">
251
- {settings.shippingRestrictions.map((restriction) => (
252
- <div class="check-row">
253
- <div><b>{restriction.region}</b><p>{restriction.reason}</p></div>
254
- <span class="chip amber">Restricted</span>
255
- </div>
256
- ))}
257
- </div>
258
- )}
259
- </div>
260
- </div>
261
- </section>
262
-
263
- <style>
264
- .fulfillment-tab {
265
- --fulfillment-split: minmax(0, 1.2fr) minmax(360px, 0.8fr);
266
- }
267
-
268
- .fulfillment-gate {
269
- scroll-margin-top: 92px;
270
- }
271
-
272
- .fulfillment-assist-panel {
273
- border-color: #b9c9ec;
274
- background: #fbfcff;
275
- }
276
-
277
- .assist-signal-grid {
278
- display: grid;
279
- grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
280
- gap: 10px;
281
- }
282
-
283
- .assist-signal {
284
- display: grid;
285
- gap: 7px;
286
- min-width: 0;
287
- padding: 12px;
288
- border: 1px solid var(--admin-line);
289
- border-radius: var(--admin-radius-sm);
290
- background: var(--admin-surface);
291
- }
292
-
293
- .assist-signal p {
294
- margin: 0;
295
- color: var(--admin-muted);
296
- font-size: 12px;
297
- }
298
-
299
- .fulfillment-workspace {
300
- display: grid;
301
- grid-template-columns: var(--fulfillment-split);
302
- gap: 14px;
303
- align-items: start;
304
- min-width: 0;
305
- }
306
-
307
- .fulfillment-queue-panel,
308
- .fulfillment-detail-panel {
309
- min-width: 0;
310
- }
311
-
312
- .fulfillment-detail-panel {
313
- position: sticky;
314
- top: 86px;
315
- box-shadow: var(--admin-shadow);
316
- }
317
-
318
- .fulfillment-panel-head {
319
- min-width: 0;
320
- }
321
-
322
- .fulfillment-panel-head h3 {
323
- min-width: 0;
324
- overflow-wrap: anywhere;
325
- }
326
-
327
- .fulfillment-table {
328
- min-width: 900px;
329
- }
330
-
331
- .fulfillment-table td {
332
- height: 58px;
333
- }
334
-
335
- .fulfillment-table tr.selected {
336
- background: #fff8ec;
337
- box-shadow: inset 3px 0 0 var(--admin-amber);
338
- }
339
-
340
- .detail-summary {
341
- display: grid;
342
- grid-template-columns: repeat(3, minmax(0, 1fr));
343
- gap: 10px;
344
- }
345
-
346
- .detail-summary > div {
347
- min-width: 0;
348
- padding-bottom: 10px;
349
- border-bottom: 1px solid var(--admin-line);
350
- }
351
-
352
- .detail-summary strong {
353
- display: block;
354
- margin-top: 3px;
355
- overflow-wrap: anywhere;
356
- }
357
-
358
- .eyebrow {
359
- color: var(--admin-muted);
360
- font-size: 11px;
361
- font-weight: 760;
362
- text-transform: uppercase;
363
- }
364
-
365
- .label-card {
366
- display: flex;
367
- align-items: center;
368
- justify-content: space-between;
369
- gap: 12px;
370
- padding: 12px;
371
- border: 1px solid var(--admin-line);
372
- border-radius: var(--admin-radius);
373
- background: var(--admin-surface-soft);
374
- }
375
-
376
- .label-card.locked {
377
- border-color: #f1c883;
378
- background: #fff8ec;
379
- }
380
-
381
- .label-card.unlocked {
382
- border-color: #a6d8c0;
383
- background: #eefaf3;
384
- }
385
-
386
- .label-card p {
387
- margin: 4px 0 0;
388
- color: var(--admin-muted);
389
- font-size: 12px;
390
- }
391
-
392
- .label-card .btn[disabled] {
393
- border-color: #d9b978;
394
- background: #f5e7c8;
395
- color: var(--admin-amber);
396
- cursor: not-allowed;
397
- }
398
-
399
- .action-strip {
400
- display: flex;
401
- flex-wrap: wrap;
402
- gap: 8px;
403
- scroll-margin-top: 92px;
404
- }
405
-
406
- .detail-section {
407
- display: grid;
408
- gap: 10px;
409
- padding-top: 2px;
410
- scroll-margin-top: 92px;
411
- }
412
-
413
- .detail-section + .detail-section {
414
- padding-top: 12px;
415
- border-top: 1px solid var(--admin-line);
416
- }
417
-
418
- .detail-section-head {
419
- display: flex;
420
- align-items: center;
421
- justify-content: space-between;
422
- gap: 10px;
423
- }
424
-
425
- .detail-section-head h4 {
426
- margin: 0;
427
- font-size: 13px;
428
- }
429
-
430
- .fulfillment-policy-panel .readiness-list {
431
- display: grid;
432
- gap: 0;
433
- }
434
-
435
- @media (max-width: 1280px) {
436
- .fulfillment-workspace {
437
- grid-template-columns: minmax(0, 1fr);
438
- }
439
-
440
- .fulfillment-detail-panel {
441
- position: static;
442
- }
443
- }
444
-
445
- @media (max-width: 920px) {
446
- .detail-summary {
447
- grid-template-columns: repeat(2, minmax(0, 1fr));
448
- }
449
-
450
- .label-card {
451
- align-items: flex-start;
452
- flex-direction: column;
453
- }
454
- }
455
-
456
- @media (max-width: 640px) {
457
- .detail-summary {
458
- grid-template-columns: 1fr;
459
- }
460
-
461
- .label-card .btn {
462
- width: 100%;
463
- }
464
- }
465
- </style>
@@ -1,105 +0,0 @@
1
- ---
2
- /** Managed rows use only the frozen ManagedAnalyticsApiV1 transport. */
3
- import { withBase } from "@/lib/basePath";
4
- const { envelope } = Astro.props;
5
- const { tenant, basePath } = Astro.locals;
6
- const viewerCapability = Astro.locals.viewer?.capability ?? "none";
7
- const canManage = viewerCapability === "owner" || viewerCapability === "admin";
8
- const managedEndpoint = withBase(basePath || "", "/api/integrations/v1");
9
- const localReadModelEndpoint = withBase(basePath || "", "/api/apps/admin/integrations");
10
- const gtmMigrationEndpoint = withBase(basePath || "", "/api/apps/admin/gtm-migration");
11
- const targetTenant = envelope.targetTenant ?? tenant.appDomain;
12
- ---
13
-
14
- <!-- agent-hook:integrations-tab -->
15
- <section id="integrations" class="band integrations-tab" aria-labelledby="admin-tab-integrations integrations-title" data-admin-panel data-agent-scope="integrations" role="tabpanel" data-integrations-panel data-managed-endpoint={managedEndpoint} data-local-readmodel-endpoint={localReadModelEndpoint} data-target-tenant={targetTenant} data-stage="staged" data-can-manage={canManage ? "true" : "false"}>
16
- <div class="section-title">
17
- <div><h2 id="integrations-title">Managed analytics</h2><p>Configure reviewed, native analytics adapters. New and revised configurations remain disabled until you explicitly publish them.</p></div>
18
- <div class="actions"><button class="btn" type="button" data-integrations-refresh><svg><use href="#i-refresh"></use></svg>Refresh</button></div>
19
- </div>
20
- {!canManage && <div class="ship-gate amber"><div class="ship-gate-main"><span class="seal"><svg><use href="#i-shield"></use></svg></span><div><b>Read-only view</b><span>Only an owner or admin can change managed analytics.</span></div></div></div>}
21
- <p class="chip" data-integrations-status role="status" aria-live="polite" tabindex="-1">Loading managed analytics…</p>
22
-
23
- <div class="panel" id="managed-analytics-inventory">
24
- <div class="panel-head"><h3><svg><use href="#i-workflows"></use></svg>Managed installs</h3><span class="chip teal">Native adapters</span></div>
25
- <div class="panel-body"><div class="table-wrap"><table class="integrations-table"><thead><tr><th>Integration</th><th>Status</th><th>Revisions</th><th>Configuration</th><th class="action">Actions</th></tr></thead><tbody data-managed-integrations-body><tr><td colspan="5" class="subtle">Loading installs…</td></tr></tbody></table></div></div>
26
- </div>
27
-
28
- <div class="panel" id="integrations-catalog">
29
- <div class="panel-head"><h3><svg><use href="#i-box"></use></svg>Add managed analytics</h3><span class="chip">Reviewed catalog</span></div>
30
- <div class="panel-body"><p class="note">Collection origins, consent purposes, and manual event types are fixed by the reviewed adapter. Nothing is enabled until Publish.</p><div class="integrations-catalog-grid" data-managed-integrations-catalog><p class="subtle">Loading catalog…</p></div></div>
31
- </div>
32
-
33
- <!-- READ-ONLY diagnostic: the uploaded GTM export is parsed only as inert data;
34
- analysis never publishes, activates, renders, or executes imported code. -->
35
- <div class="panel" id="integrations-gtm-migration" data-gtm-migration-panel data-gtm-endpoint={gtmMigrationEndpoint} data-target-tenant={targetTenant}>
36
- <div class="panel-head"><h3><svg><use href="#i-file"></use></svg>Migrate from GTM</h3><span class="chip teal">Read-only report</span></div>
37
- <div class="panel-body grid">
38
- <p class="note">Upload a Google Tag Manager container export. It is parsed as inert data to produce an inventory, dependency map, and migration dispositions. Nothing is published or activated; the file is never executed.</p>
39
- <div class="gtm-upload">
40
- <label class="gtm-upload-label" for="gtm-migration-file">GTM container export (JSON)</label>
41
- <input id="gtm-migration-file" type="file" accept="application/json,.json" data-gtm-file />
42
- <div class="actions">
43
- <button class="btn primary" type="button" data-gtm-analyze><svg><use href="#i-file"></use></svg>Analyze</button>
44
- <button class="btn" type="button" data-gtm-download disabled><svg><use href="#i-export"></use></svg>Download support bundle</button>
45
- </div>
46
- <p class="chip" data-gtm-status role="status" aria-live="polite">Choose a GTM export file to begin.</p>
47
- </div>
48
- <div class="gtm-results" data-gtm-results hidden>
49
- <section class="gtm-section" aria-labelledby="gtm-inventory-title"><h4 id="gtm-inventory-title">Inventory</h4><div data-gtm-inventory></div></section>
50
- <section class="gtm-section" aria-labelledby="gtm-dispositions-title"><h4 id="gtm-dispositions-title">Dispositions</h4><div data-gtm-dispositions></div></section>
51
- <section class="gtm-section" aria-labelledby="gtm-graph-title"><h4 id="gtm-graph-title">Dependency map</h4><div data-gtm-graph></div></section>
52
- <section class="gtm-section" aria-labelledby="gtm-findings-title"><h4 id="gtm-findings-title">Unresolved findings</h4><div data-gtm-findings></div></section>
53
- <section class="gtm-section" aria-labelledby="gtm-recommendations-title"><h4 id="gtm-recommendations-title">Recommendations</h4><div data-gtm-recommendations></div></section>
54
- </div>
55
- </div>
56
- </div>
57
-
58
- <details class="panel" id="tenant-local-integrations">
59
- <summary class="panel-head"><h3>Other tenant-local integrations</h3><span class="chip">Read only</span></summary>
60
- <div class="panel-body"><div class="table-wrap"><table class="integrations-table local"><thead><tr><th>Integration</th><th>Status</th><th>Authority</th><th>State</th><th>Consent</th></tr></thead><tbody data-local-integrations-body><tr><td colspan="5" class="subtle">Loading tenant-local integrations…</td></tr></tbody></table></div></div>
61
- </details>
62
- </section>
63
-
64
- <script>
65
- import { initIntegrationsTab } from "@/lib/apps/integrationsTab";
66
- import { initGtmMigrationTab } from "@/lib/apps/gtmMigrationTab";
67
- initIntegrationsTab();
68
- initGtmMigrationTab();
69
- </script>
70
-
71
- <style>
72
- .integrations-table { min-width: 760px; width: 100%; border-collapse: collapse; }
73
- .integrations-table th, .integrations-table td { text-align: left; vertical-align: top; padding: 8px 10px; border-bottom: 1px solid var(--admin-line); }
74
- .integrations-table td .subtle { display: block; color: var(--admin-muted); font-size: 12px; }
75
- .integrations-actions, .integrations-inline { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
76
- .integrations-inline input, .integrations-inline select { min-width: 0; max-width: 210px; padding: 7px 9px; border: 1px solid var(--admin-line); border-radius: var(--admin-radius-sm); }
77
- [data-integrations-status][data-tone="error"] { background: var(--admin-red); color: #fff; }
78
- .integrations-catalog-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 12px; }
79
- .integrations-adapter-card { display: grid; gap: 8px; min-width: 0; padding: 12px; border: 1px solid var(--admin-line); border-radius: var(--admin-radius); background: var(--admin-surface); }
80
- .integrations-adapter-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
81
- .integrations-adapter-meta { display: grid; grid-template-columns: minmax(90px, auto) minmax(0, 1fr); gap: 2px 10px; margin: 0; font-size: 12px; }
82
- .integrations-adapter-meta dt { color: var(--admin-muted); }
83
- .integrations-adapter-meta dd { margin: 0; overflow-wrap: anywhere; }
84
- .integrations-adapter-config { display: flex; flex-wrap: wrap; gap: 8px; }
85
- .integrations-adapter-config input { flex: 1 1 160px; min-width: 0; padding: 8px 10px; border: 1px solid var(--admin-line); border-radius: var(--admin-radius-sm); }
86
- .integrations-adapter-result[data-tone="error"] { color: var(--admin-red); }
87
- .gtm-upload { display: grid; gap: 8px; }
88
- .gtm-upload-label { font-size: 12px; color: var(--admin-muted); }
89
- .gtm-upload input[type="file"] { max-width: 100%; }
90
- [data-gtm-status][data-tone="error"] { background: var(--admin-red); color: #fff; }
91
- .gtm-results { display: grid; gap: 16px; margin-top: 8px; }
92
- .gtm-section h4 { margin: 0 0 6px; }
93
- .gtm-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
94
- .gtm-stat { display: grid; gap: 2px; padding: 8px 10px; border: 1px solid var(--admin-line); border-radius: var(--admin-radius-sm); background: var(--admin-surface); }
95
- .gtm-stat strong { font-size: 18px; }
96
- .gtm-stat span { font-size: 12px; color: var(--admin-muted); overflow-wrap: anywhere; }
97
- .gtm-disposition-group { margin-bottom: 12px; }
98
- .gtm-disposition-group h4 { display: flex; align-items: center; gap: 8px; }
99
- .gtm-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
100
- .gtm-list li { padding: 6px 8px; border-bottom: 1px solid var(--admin-line); overflow-wrap: anywhere; }
101
- .gtm-list li .note { margin: 2px 0 0; color: var(--admin-muted); font-size: 12px; }
102
- details.panel > summary { cursor: pointer; list-style-position: inside; }
103
- details.panel > summary h3 { display: inline-flex; }
104
- @media (max-width: 760px) { .integrations-adapter-config, .integrations-inline { flex-direction: column; align-items: stretch; } .integrations-inline input, .integrations-inline select { max-width: none; } .table-wrap { max-width: 100%; overflow-x: auto; } }
105
- </style>