@tokenoftrust/storefront-runner 1.3.2

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 (310) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +42 -0
  3. package/apps/storefront/astro.config.mjs +66 -0
  4. package/apps/storefront/dev-plugins/dev-loop-state.mjs +147 -0
  5. package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +238 -0
  6. package/apps/storefront/env.d.ts +162 -0
  7. package/apps/storefront/migrations/0000_baseline.sql +54 -0
  8. package/apps/storefront/migrations/0001_products_fts.sql +9 -0
  9. package/apps/storefront/migrations/README.md +54 -0
  10. package/apps/storefront/migrations/meta/0000_snapshot.json +347 -0
  11. package/apps/storefront/migrations/meta/0001_snapshot.json +347 -0
  12. package/apps/storefront/migrations/meta/_journal.json +20 -0
  13. package/apps/storefront/package.json +42 -0
  14. package/apps/storefront/public/brand/northwind-logo-dark.svg +4 -0
  15. package/apps/storefront/public/brand/northwind-logo-light.svg +4 -0
  16. package/apps/storefront/public/brand/northwind-og.svg +13 -0
  17. package/apps/storefront/public/favicon.svg +5 -0
  18. package/apps/storefront/public/js/dashboard-team.js +147 -0
  19. package/apps/storefront/public/js/dashboard-vendor-search.js +115 -0
  20. package/apps/storefront/public/shared/commerce-chrome.css +369 -0
  21. package/apps/storefront/src/components/Badge.astro +34 -0
  22. package/apps/storefront/src/components/Breadcrumbs.astro +45 -0
  23. package/apps/storefront/src/components/CollectionCard.astro +56 -0
  24. package/apps/storefront/src/components/EmptyState.astro +55 -0
  25. package/apps/storefront/src/components/Img.astro +68 -0
  26. package/apps/storefront/src/components/ProductCard.astro +228 -0
  27. package/apps/storefront/src/components/Section.astro +39 -0
  28. package/apps/storefront/src/components/Seo.astro +70 -0
  29. package/apps/storefront/src/components/WidgetFrame.astro +34 -0
  30. package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +531 -0
  31. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +597 -0
  32. package/apps/storefront/src/components/admin/AdminFoxyCartSettingsPanel.astro +227 -0
  33. package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +465 -0
  34. package/apps/storefront/src/components/admin/AdminProductsTab.astro +901 -0
  35. package/apps/storefront/src/components/admin/AdminReportingTab.astro +435 -0
  36. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +782 -0
  37. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +1223 -0
  38. package/apps/storefront/src/components/auth/AuthBadge.astro +44 -0
  39. package/apps/storefront/src/components/chrome/AnnouncementBar.astro +108 -0
  40. package/apps/storefront/src/components/chrome/MegaMenu.astro +26 -0
  41. package/apps/storefront/src/components/chrome/NavDropdown.astro +201 -0
  42. package/apps/storefront/src/components/chrome/SiteFooter.astro +201 -0
  43. package/apps/storefront/src/components/chrome/SiteHeader.astro +234 -0
  44. package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
  45. package/apps/storefront/src/components/commerce/ArticleCard.astro +242 -0
  46. package/apps/storefront/src/components/commerce/BrandGrid.astro +175 -0
  47. package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
  48. package/apps/storefront/src/components/commerce/FoxySidecartStyles.astro +61 -0
  49. package/apps/storefront/src/components/commerce/PriceDisplay.astro +91 -0
  50. package/apps/storefront/src/components/commerce/ProductCarousel.astro +197 -0
  51. package/apps/storefront/src/components/commerce/ProductGrid.astro +75 -0
  52. package/apps/storefront/src/components/commerce/RatingStars.astro +58 -0
  53. package/apps/storefront/src/components/commerce/ReviewList.astro +54 -0
  54. package/apps/storefront/src/components/commerce/ReviewSummary.astro +44 -0
  55. package/apps/storefront/src/components/commerce/TagFilterCloud.astro +149 -0
  56. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +37 -0
  57. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +99 -0
  58. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +45 -0
  59. package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
  60. package/apps/storefront/src/components/compliance/PactActNotice.astro +35 -0
  61. package/apps/storefront/src/components/compliance/Prop65Warning.astro +80 -0
  62. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +38 -0
  63. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +35 -0
  64. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +53 -0
  65. package/apps/storefront/src/components/home/Hero.astro +164 -0
  66. package/apps/storefront/src/components/islands/AgeGate.tsx +109 -0
  67. package/apps/storefront/src/components/islands/ImageGallery.tsx +237 -0
  68. package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +73 -0
  69. package/apps/storefront/src/components/islands/MobileNav.tsx +239 -0
  70. package/apps/storefront/src/components/islands/QuickView.tsx +138 -0
  71. package/apps/storefront/src/components/islands/RecentlyViewed.tsx +122 -0
  72. package/apps/storefront/src/components/islands/SavedList.tsx +99 -0
  73. package/apps/storefront/src/components/islands/SearchTypeahead.tsx +248 -0
  74. package/apps/storefront/src/components/islands/VariantSelector.tsx +375 -0
  75. package/apps/storefront/src/components/marketing/CardGrid.astro +72 -0
  76. package/apps/storefront/src/components/marketing/CodeSample.astro +20 -0
  77. package/apps/storefront/src/components/marketing/CtaBand.astro +24 -0
  78. package/apps/storefront/src/components/marketing/Faq.astro +37 -0
  79. package/apps/storefront/src/components/marketing/Integrations.astro +63 -0
  80. package/apps/storefront/src/components/marketing/MarketingBlocks.astro +53 -0
  81. package/apps/storefront/src/components/marketing/MarketingCtas.astro +48 -0
  82. package/apps/storefront/src/components/marketing/MarketingHero.astro +72 -0
  83. package/apps/storefront/src/components/marketing/ProofStrip.astro +37 -0
  84. package/apps/storefront/src/components/marketing/SplitCompare.astro +53 -0
  85. package/apps/storefront/src/components/marketing/Steps.astro +36 -0
  86. package/apps/storefront/src/components/marketing/Testimonials.astro +38 -0
  87. package/apps/storefront/src/components/marketing/TrustBar.astro +22 -0
  88. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +56 -0
  89. package/apps/storefront/src/components/membership/TierCards.astro +164 -0
  90. package/apps/storefront/src/components/membership/TierComparisonTable.astro +148 -0
  91. package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
  92. package/apps/storefront/src/components/plp/FacetSidebar.astro +199 -0
  93. package/apps/storefront/src/components/plp/Pagination.astro +99 -0
  94. package/apps/storefront/src/components/plp/SortSelect.astro +64 -0
  95. package/apps/storefront/src/components/style-guide/StyleGuideNav.astro +95 -0
  96. package/apps/storefront/src/components/subscription/HowItWorksSteps.astro +67 -0
  97. package/apps/storefront/src/components/subscription/ManageSubscriptionEntry.astro +67 -0
  98. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +101 -0
  99. package/apps/storefront/src/config/adminTargetTenant.ts +28 -0
  100. package/apps/storefront/src/config/devTenantSeed.ts +78 -0
  101. package/apps/storefront/src/config/devTenants.ts +16 -0
  102. package/apps/storefront/src/config/kvTenantResolver.ts +87 -0
  103. package/apps/storefront/src/config/resolver.ts +102 -0
  104. package/apps/storefront/src/config/tenants.ts +87 -0
  105. package/apps/storefront/src/layouts/Layout.astro +368 -0
  106. package/apps/storefront/src/lib/admin/README.md +122 -0
  107. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +21 -0
  108. package/apps/storefront/src/lib/admin/ai/askEnvelope.ts +48 -0
  109. package/apps/storefront/src/lib/admin/ai/audit.ts +14 -0
  110. package/apps/storefront/src/lib/admin/ai/index.ts +6 -0
  111. package/apps/storefront/src/lib/admin/ai/operationalRuns.ts +155 -0
  112. package/apps/storefront/src/lib/admin/ai/recommendation.ts +20 -0
  113. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +172 -0
  114. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +65 -0
  115. package/apps/storefront/src/lib/admin/envelope.ts +10 -0
  116. package/apps/storefront/src/lib/admin/evidence/evidenceReference.ts +32 -0
  117. package/apps/storefront/src/lib/admin/memoizeByEnvelope.ts +27 -0
  118. package/apps/storefront/src/lib/admin/orderClient.ts +146 -0
  119. package/apps/storefront/src/lib/admin/seed.ts +54 -0
  120. package/apps/storefront/src/lib/admin/services/mock/aiWorkflows.ts +59 -0
  121. package/apps/storefront/src/lib/admin/services/mock/customers.ts +86 -0
  122. package/apps/storefront/src/lib/admin/services/mock/evidence.ts +99 -0
  123. package/apps/storefront/src/lib/admin/services/mock/foxyCartSettings.ts +78 -0
  124. package/apps/storefront/src/lib/admin/services/mock/fulfillment.ts +62 -0
  125. package/apps/storefront/src/lib/admin/services/mock/index.ts +12 -0
  126. package/apps/storefront/src/lib/admin/services/mock/orders.ts +184 -0
  127. package/apps/storefront/src/lib/admin/services/mock/products.ts +105 -0
  128. package/apps/storefront/src/lib/admin/services/mock/reporting.ts +51 -0
  129. package/apps/storefront/src/lib/admin/services/mock/settings.ts +70 -0
  130. package/apps/storefront/src/lib/admin/services/mock/tenants.ts +29 -0
  131. package/apps/storefront/src/lib/admin/services/orders.ts +300 -0
  132. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +67 -0
  133. package/apps/storefront/src/lib/admin/viewmodels/customers.ts +56 -0
  134. package/apps/storefront/src/lib/admin/viewmodels/fulfillment.ts +49 -0
  135. package/apps/storefront/src/lib/admin/viewmodels/orders.ts +92 -0
  136. package/apps/storefront/src/lib/admin/viewmodels/products.ts +74 -0
  137. package/apps/storefront/src/lib/admin/viewmodels/reporting.ts +34 -0
  138. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +64 -0
  139. package/apps/storefront/src/lib/adminTenantEnvelope.ts +90 -0
  140. package/apps/storefront/src/lib/analytics/index.ts +47 -0
  141. package/apps/storefront/src/lib/auth/brokerAssertion.ts +136 -0
  142. package/apps/storefront/src/lib/auth/devAuth.ts +90 -0
  143. package/apps/storefront/src/lib/auth/gatePage.ts +81 -0
  144. package/apps/storefront/src/lib/auth/identityToken.ts +341 -0
  145. package/apps/storefront/src/lib/auth/loginGate.ts +119 -0
  146. package/apps/storefront/src/lib/auth/route.ts +146 -0
  147. package/apps/storefront/src/lib/auth/session.ts +203 -0
  148. package/apps/storefront/src/lib/auth/totAccessClient.ts +503 -0
  149. package/apps/storefront/src/lib/basePath.ts +41 -0
  150. package/apps/storefront/src/lib/cfImage.ts +55 -0
  151. package/apps/storefront/src/lib/chrome/model.ts +244 -0
  152. package/apps/storefront/src/lib/chrome/parseConfig.ts +34 -0
  153. package/apps/storefront/src/lib/colors.ts +97 -0
  154. package/apps/storefront/src/lib/commerce/foxyCartSettings.ts +253 -0
  155. package/apps/storefront/src/lib/content-edit/applyDiff.ts +91 -0
  156. package/apps/storefront/src/lib/content-edit/client.ts +230 -0
  157. package/apps/storefront/src/lib/content-edit/index.ts +14 -0
  158. package/apps/storefront/src/lib/content-edit/route.ts +25 -0
  159. package/apps/storefront/src/lib/content-edit/types.ts +19 -0
  160. package/apps/storefront/src/lib/cspStaticHashes.ts +24 -0
  161. package/apps/storefront/src/lib/d1/catalog.ts +366 -0
  162. package/apps/storefront/src/lib/dashboard/staffAdmission.ts +43 -0
  163. package/apps/storefront/src/lib/dashboard/staffVendorAccess.ts +449 -0
  164. package/apps/storefront/src/lib/dashboard/tenantSelection.ts +136 -0
  165. package/apps/storefront/src/lib/dashboard/vendorQuery.ts +61 -0
  166. package/apps/storefront/src/lib/demoGate.ts +113 -0
  167. package/apps/storefront/src/lib/dev/activityIngestToken.ts +78 -0
  168. package/apps/storefront/src/lib/dev/activityStore.ts +72 -0
  169. package/apps/storefront/src/lib/dev/ai-widget-client.js +308 -0
  170. package/apps/storefront/src/lib/dev/apiBase.ts +14 -0
  171. package/apps/storefront/src/lib/dev/cliSignInCode.ts +155 -0
  172. package/apps/storefront/src/lib/dev/devDraft.ts +133 -0
  173. package/apps/storefront/src/lib/dev/hostedCockpit.ts +169 -0
  174. package/apps/storefront/src/lib/dev/livePreview.ts +77 -0
  175. package/apps/storefront/src/lib/dev/runtimeStore.ts +120 -0
  176. package/apps/storefront/src/lib/dev/storeStatus.ts +142 -0
  177. package/apps/storefront/src/lib/diag/redact.ts +20 -0
  178. package/apps/storefront/src/lib/edgeCache.ts +128 -0
  179. package/apps/storefront/src/lib/email/magicLinkInviteEmail.ts +62 -0
  180. package/apps/storefront/src/lib/env.ts +60 -0
  181. package/apps/storefront/src/lib/fixtures/sampleProducts.ts +130 -0
  182. package/apps/storefront/src/lib/format.ts +38 -0
  183. package/apps/storefront/src/lib/foxyCommerce.ts +49 -0
  184. package/apps/storefront/src/lib/imageAttrs.ts +55 -0
  185. package/apps/storefront/src/lib/jsonld.ts +166 -0
  186. package/apps/storefront/src/lib/maintenance.ts +71 -0
  187. package/apps/storefront/src/lib/membership/model.ts +97 -0
  188. package/apps/storefront/src/lib/memoryKv.ts +32 -0
  189. package/apps/storefront/src/lib/messaging/messagesClient.ts +143 -0
  190. package/apps/storefront/src/lib/placeholder.ts +99 -0
  191. package/apps/storefront/src/lib/previewReload.ts +125 -0
  192. package/apps/storefront/src/lib/priceBuckets.ts +33 -0
  193. package/apps/storefront/src/lib/rawChrome.ts +505 -0
  194. package/apps/storefront/src/lib/rawMarketingHtml.ts +136 -0
  195. package/apps/storefront/src/lib/search/index.ts +74 -0
  196. package/apps/storefront/src/lib/sections.ts +129 -0
  197. package/apps/storefront/src/lib/securityHeaders.ts +31 -0
  198. package/apps/storefront/src/lib/storyblok/content-model.ts +289 -0
  199. package/apps/storefront/src/lib/storyblok/provider.ts +607 -0
  200. package/apps/storefront/src/lib/subscription/model.ts +68 -0
  201. package/apps/storefront/src/lib/tenantRouting.ts +91 -0
  202. package/apps/storefront/src/lib/tot/ToTClient.ts +177 -0
  203. package/apps/storefront/src/lib/tot/d1Client.ts +62 -0
  204. package/apps/storefront/src/lib/tot/query.ts +249 -0
  205. package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
  206. package/apps/storefront/src/lib/url.ts +142 -0
  207. package/apps/storefront/src/lib/wishlist.ts +63 -0
  208. package/apps/storefront/src/middleware/index.ts +455 -0
  209. package/apps/storefront/src/pages/404.astro +61 -0
  210. package/apps/storefront/src/pages/[...slug].astro +90 -0
  211. package/apps/storefront/src/pages/admin.astro +1675 -0
  212. package/apps/storefront/src/pages/api/admin/orders/[id].ts +53 -0
  213. package/apps/storefront/src/pages/api/admin/orders.ts +71 -0
  214. package/apps/storefront/src/pages/api/auth/logout.ts +21 -0
  215. package/apps/storefront/src/pages/api/auth/magic-exchange.ts +107 -0
  216. package/apps/storefront/src/pages/api/auth/send.ts +85 -0
  217. package/apps/storefront/src/pages/api/auth/verify.ts +135 -0
  218. package/apps/storefront/src/pages/api/cache-purge.ts +44 -0
  219. package/apps/storefront/src/pages/api/checkout-style.css.ts +43 -0
  220. package/apps/storefront/src/pages/api/cli/policy.ts +70 -0
  221. package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +164 -0
  222. package/apps/storefront/src/pages/api/csp-report.ts +25 -0
  223. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +124 -0
  224. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +120 -0
  225. package/apps/storefront/src/pages/api/dev/activity.ts +124 -0
  226. package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +73 -0
  227. package/apps/storefront/src/pages/api/dev/diff.ts +68 -0
  228. package/apps/storefront/src/pages/api/dev/file.ts +57 -0
  229. package/apps/storefront/src/pages/api/dev/locate.ts +125 -0
  230. package/apps/storefront/src/pages/api/dev/publish.ts +128 -0
  231. package/apps/storefront/src/pages/api/dev/tree.ts +70 -0
  232. package/apps/storefront/src/pages/api/dev/write.ts +116 -0
  233. package/apps/storefront/src/pages/api/newsletter.ts +104 -0
  234. package/apps/storefront/src/pages/api/request-access.ts +172 -0
  235. package/apps/storefront/src/pages/auth/login.astro +149 -0
  236. package/apps/storefront/src/pages/auth/magic.astro +105 -0
  237. package/apps/storefront/src/pages/capabilities.astro +292 -0
  238. package/apps/storefront/src/pages/collections/[handle].astro +177 -0
  239. package/apps/storefront/src/pages/collections/index.astro +59 -0
  240. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +100 -0
  241. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +154 -0
  242. package/apps/storefront/src/pages/dashboard/index.astro +160 -0
  243. package/apps/storefront/src/pages/index.astro +290 -0
  244. package/apps/storefront/src/pages/llms.txt.ts +92 -0
  245. package/apps/storefront/src/pages/products/[handle].astro +402 -0
  246. package/apps/storefront/src/pages/robots.txt.ts +24 -0
  247. package/apps/storefront/src/pages/saved.astro +35 -0
  248. package/apps/storefront/src/pages/search.astro +150 -0
  249. package/apps/storefront/src/pages/sitemap.xml.ts +78 -0
  250. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +1280 -0
  251. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +229 -0
  252. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +144 -0
  253. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +116 -0
  254. package/apps/storefront/src/pages/style-guide/index.astro +98 -0
  255. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
  256. package/apps/storefront/src/styles/global.css +292 -0
  257. package/apps/storefront/src/themes/reference.ts +68 -0
  258. package/apps/storefront/src/themes/schema.ts +111 -0
  259. package/apps/storefront/src/themes/tenants/index.ts +51 -0
  260. package/apps/storefront/tsconfig.json +16 -0
  261. package/apps/storefront/vitest.config.ts +22 -0
  262. package/package.json +26 -0
  263. package/packages/public-runtime/LICENSE +58 -0
  264. package/packages/public-runtime/package.json +22 -0
  265. package/packages/public-runtime/src/binary-path.ts +68 -0
  266. package/packages/public-runtime/src/capabilities.ts +89 -0
  267. package/packages/public-runtime/src/catalog-api.ts +81 -0
  268. package/packages/public-runtime/src/catalog-d1.ts +192 -0
  269. package/packages/public-runtime/src/checkout-style-publish.ts +200 -0
  270. package/packages/public-runtime/src/checkout-style.ts +375 -0
  271. package/packages/public-runtime/src/csp.ts +109 -0
  272. package/packages/public-runtime/src/customization-preview.ts +197 -0
  273. package/packages/public-runtime/src/customization-reconcile.ts +63 -0
  274. package/packages/public-runtime/src/customization-runtime.ts +157 -0
  275. package/packages/public-runtime/src/customization-scripts.ts +64 -0
  276. package/packages/public-runtime/src/customization-versioning.ts +95 -0
  277. package/packages/public-runtime/src/foxy-orders.ts +136 -0
  278. package/packages/public-runtime/src/foxy.ts +196 -0
  279. package/packages/public-runtime/src/hot-standby-readiness.ts +140 -0
  280. package/packages/public-runtime/src/index.ts +34 -0
  281. package/packages/public-runtime/src/product.ts +159 -0
  282. package/packages/public-runtime/src/search.ts +45 -0
  283. package/packages/public-runtime/src/tenant-assets.ts +444 -0
  284. package/packages/public-runtime/src/tenant.ts +384 -0
  285. package/packages/public-runtime/tsconfig.json +8 -0
  286. package/packages/public-runtime/vitest.config.ts +8 -0
  287. package/pnpm-workspace.yaml +7 -0
  288. package/scripts/build/copy-tenant-assets.mjs +69 -0
  289. package/scripts/dev/ai-edit.mjs +183 -0
  290. package/scripts/dev/checkout-watch.mjs +116 -0
  291. package/scripts/dev/file-browser.mjs +159 -0
  292. package/scripts/dev/file-writer.mjs +141 -0
  293. package/scripts/dev/git-status.mjs +171 -0
  294. package/scripts/dev/locate-handler.mjs +163 -0
  295. package/scripts/dev/locate.mjs +112 -0
  296. package/scripts/dev/port-check.mjs +48 -0
  297. package/scripts/dev/safe-path.mjs +109 -0
  298. package/scripts/dev/shot.mjs +46 -0
  299. package/scripts/dev/tenant-source-root.mjs +76 -0
  300. package/scripts/dev/unified-diff.mjs +81 -0
  301. package/scripts/tot-dev.mjs +376 -0
  302. package/tenants/README.md +14 -0
  303. package/tenants/home/content/chrome.json +7 -0
  304. package/tenants/home/content/home.html +58 -0
  305. package/tenants/home/content/home.json +11 -0
  306. package/tenants/home/public/js/storefront.js +210 -0
  307. package/tenants/home/public/logo-wordmark.png +0 -0
  308. package/tenants/home/public/pages/storefront.css +212 -0
  309. package/tenants/home/theme.json +6 -0
  310. package/tsconfig.base.json +20 -0
@@ -0,0 +1,1223 @@
1
+ ---
2
+ /**
3
+ * Orders tab (baseline §Orders), wired to the live orders view model
4
+ * (lib/admin/services/mock, from data-view-contracts). Built as a standalone
5
+ * component under components/admin/orders/** per decision
6
+ * `parallelize-orders-read-surface` — the integrator wires the tab mount in
7
+ * admin.astro at consolidation, not this unit.
8
+ *
9
+ * SHELL CONTRACT (see admin.astro's doc comment): this component owns the
10
+ * `#orders` / data-agent-scope="orders" section root — copy its markup
11
+ * wholesale into that role, do not nest it inside another tab-panel root.
12
+ *
13
+ * Order Assist (baseline §Orders) uses the ai-evidence-substrate model
14
+ * directly (AiRecommendation + EvidenceReference) rather than inventing a
15
+ * parallel "suggestion" shape, per lib/admin/README's own guidance. It is
16
+ * explanation-only: hold explanation, safest next action, and an early
17
+ * dispute-evidence timeline — never an auto-action, so approvalMode is
18
+ * always draft_only or approval_required, never guarded_automation.
19
+ */
20
+ import { getEvidenceViewModel } from "@/lib/admin/services/mock";
21
+ import {
22
+ getOrdersViewModelResult,
23
+ emptyOrdersViewModel,
24
+ parseOrderFilters,
25
+ type OrdersLoadStatus,
26
+ } from "@/lib/admin/services/orders";
27
+ import { createFoxyOrderClient, orderApiBaseUrl } from "@/lib/admin/orderClient";
28
+ import { readEnv } from "@/lib/env";
29
+ import { ORDER_SHIP_STATE_LABEL, type OrderRow } from "@/lib/admin/viewmodels/orders";
30
+ import type { AdminTenantEnvelope } from "@/lib/admin/envelope";
31
+ import type { AiRecommendation } from "@/lib/admin/ai/recommendation";
32
+ import type { EvidenceReference } from "@/lib/admin/evidence/evidenceReference";
33
+
34
+ interface Props {
35
+ envelope: AdminTenantEnvelope;
36
+ }
37
+
38
+ const { envelope } = Astro.props;
39
+ const cspNonce = Astro.locals.cspNonce;
40
+
41
+ // Real orders (U3): fetch this tenant's orders from tot-foxycart via U2's client
42
+ // and map to the view model. The per-tenant bearer is a server-side Worker secret;
43
+ // absent (or any upstream error) → an empty-but-valid model so the tab still
44
+ // renders (rich empty/error UX is U5). Live data is gated on the ToT-core orders
45
+ // endpoint (see U1/U2) — until then this resolves empty on qa.
46
+ const orderBearer = await readEnv("FOXY_ORDER_API_KEY");
47
+ // Server-backed filters + pagination (U5): the queue filter + cursor come from
48
+ // the URL and are applied by the order API server-side; payment/search remain
49
+ // client-side refinements on the returned page.
50
+ const orderFilters = parseOrderFilters(Astro.url.searchParams);
51
+ const ordersResult = orderBearer
52
+ ? await getOrdersViewModelResult({
53
+ targetTenant: envelope.targetTenant,
54
+ client: createFoxyOrderClient({
55
+ baseUrl: orderApiBaseUrl(Astro.locals.customizationEnv),
56
+ appDomain: envelope.targetTenant,
57
+ bearer: orderBearer,
58
+ }),
59
+ filters: orderFilters,
60
+ })
61
+ : { status: "not_configured" as OrdersLoadStatus, vm: emptyOrdersViewModel(envelope.targetTenant), page: null };
62
+ const vm = ordersResult.vm;
63
+ const ordersStatus = ordersResult.status;
64
+ // Preserve current query params (queue/payment/etc.) when building the next page link.
65
+ const nextPageHref = ordersResult.page?.nextCursor
66
+ ? (() => {
67
+ const p = new URLSearchParams(Astro.url.search);
68
+ p.set("cursor", ordersResult.page.nextCursor);
69
+ return `?${p.toString()}`;
70
+ })()
71
+ : null;
72
+ const evidenceGraph = getEvidenceViewModel(envelope);
73
+
74
+ function evidenceForOrder(orderId: string): EvidenceReference[] {
75
+ const orderRef = evidenceGraph.find((e) => e.kind === "order" && e.recordId === orderId);
76
+ if (!orderRef) return [];
77
+ const related = evidenceGraph.filter((e) => orderRef.relatedRefs.includes(e.id));
78
+ const timeline = evidenceGraph.filter((e) => e.relatedRefs.includes(orderRef.id));
79
+ return [orderRef, ...related, ...timeline];
80
+ }
81
+
82
+ function buildOrderAssist(row: OrderRow, evidenceRefs: EvidenceReference[]): AiRecommendation {
83
+ const refIds = evidenceRefs.map((e) => e.id);
84
+ if (!row.blocker) {
85
+ return {
86
+ summary: `No blockers — ${row.nextStep.toLowerCase()} to move this order along.`,
87
+ confidence: 0.95,
88
+ evidenceRefs: refIds,
89
+ owner: "Ops",
90
+ approvalMode: "draft_only",
91
+ auditTrail: [],
92
+ };
93
+ }
94
+ const owner = row.blocker === "Age verification hold" ? "Trust & Safety" : row.blocker === "Manual review" ? "Risk" : "Ops";
95
+ return {
96
+ summary: `${row.blocker} is blocking shipment. Safest next action: ${row.nextStep.toLowerCase()}.`,
97
+ confidence: 0.7,
98
+ evidenceRefs: refIds,
99
+ owner,
100
+ approvalMode: "approval_required",
101
+ auditTrail: [],
102
+ };
103
+ }
104
+
105
+ const SHIP_TONE: Record<OrderRow["shipState"], string> = {
106
+ hold_review_required: "amber",
107
+ ready_to_ship: "green",
108
+ unfulfilled: "teal",
109
+ completed: "teal",
110
+ };
111
+
112
+ const PAYMENT_TONE: Record<OrderRow["payment"], string> = {
113
+ authorized: "amber",
114
+ captured: "green",
115
+ refunded: "red",
116
+ };
117
+
118
+ const QUEUE_TONE: Record<string, string> = {
119
+ needs_attention: "amber",
120
+ ready_to_ship: "green",
121
+ unfulfilled: "teal",
122
+ all: "",
123
+ completed: "",
124
+ };
125
+
126
+ function queueKeysFor(row: OrderRow): string[] {
127
+ const keys = ["all"];
128
+ if (row.shipState === "hold_review_required") keys.push("needs_attention", "unfulfilled");
129
+ if (row.shipState === "ready_to_ship") keys.push("ready_to_ship", "unfulfilled");
130
+ if (row.shipState === "completed") keys.push("completed");
131
+ return keys;
132
+ }
133
+
134
+ const defaultQueue = vm.queues.find((q) => q.key === "needs_attention" && q.count > 0) ? "needs_attention" : "all";
135
+ const defaultRow =
136
+ vm.rows.find((r) => r.shipState === "hold_review_required") ?? vm.rows[0] ?? null;
137
+
138
+ const holdCount = vm.rows.filter((r) => r.shipState === "hold_review_required").length;
139
+ ---
140
+
141
+ <section id="orders" class="band orders-tab" aria-labelledby="admin-tab-orders orders-title" data-admin-panel data-agent-scope="orders" role="tabpanel" data-orders-target-tenant={vm.targetTenant}>
142
+ <div class="section-title orders-title-row">
143
+ <div>
144
+ <h2 id="orders-title">Orders</h2>
145
+ <p>Work the exceptions first, then clear ready shipments without opening every record.</p>
146
+ </div>
147
+ <div class="actions orders-primary-actions">
148
+ <button class="icon-btn" type="button" aria-label="Refresh orders" title="Refresh orders" data-orders-refresh><svg><use href="#i-refresh"></use></svg></button>
149
+ <button class="btn" type="button"><svg><use href="#i-export"></use></svg>Export</button>
150
+ <button class="btn primary" type="button"><svg><use href="#i-truck"></use></svg>Ship ready</button>
151
+ </div>
152
+ </div>
153
+
154
+ <div class="orders-command-strip" aria-label="Order queues">
155
+ <div class="orders-queue-list" role="list" data-orders-queue-list>
156
+ {vm.queues.map((queue) => (
157
+ <button
158
+ type="button"
159
+ class:list={["orders-queue-card", { active: queue.key === defaultQueue }]}
160
+ data-orders-queue-button
161
+ data-queue-key={queue.key}
162
+ >
163
+ <span class:list={["chip", QUEUE_TONE[queue.key] ?? ""]}>{queue.label}</span>
164
+ <strong>{queue.count}</strong>
165
+ <small>{queue.detail}</small>
166
+ </button>
167
+ ))}
168
+ </div>
169
+ <details class="orders-today-summary">
170
+ <summary>
171
+ <span>Today</span>
172
+ <strong>${vm.today.grossSales.toFixed(2)}</strong>
173
+ <small>{vm.today.paidOrders} paid orders</small>
174
+ </summary>
175
+ <div>
176
+ <p><b>Average order</b><span>${vm.today.averageOrder.toFixed(2)}</span></p>
177
+ <p><b>Refunds</b><span>${vm.today.refunds.toFixed(2)}</span></p>
178
+ <p><b>Oldest open</b><span>{vm.today.oldestOpenAt ? new Date(vm.today.oldestOpenAt).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) : "—"}</span></p>
179
+ </div>
180
+ </details>
181
+ </div>
182
+
183
+ <div class="orders-workspace">
184
+ <div class="panel orders-list-panel" id="orders-list">
185
+ <div class="orders-list-tools">
186
+ <label class="orders-search">
187
+ <span>Search</span>
188
+ <input type="search" placeholder="Order, customer, email, SKU" aria-label="Search orders" data-orders-search />
189
+ </label>
190
+ <label>
191
+ <span>Queue</span>
192
+ <select aria-label="Filter by queue" data-orders-queue-select>
193
+ {vm.queues.map((queue) => (
194
+ <option value={queue.key} selected={queue.key === defaultQueue}>{queue.label}</option>
195
+ ))}
196
+ </select>
197
+ </label>
198
+ <label>
199
+ <span>Payment</span>
200
+ <select aria-label="Filter by payment" data-orders-payment-select>
201
+ <option value="">Any payment</option>
202
+ <option value="authorized">Authorized</option>
203
+ <option value="captured">Captured</option>
204
+ <option value="refunded">Refunded</option>
205
+ </select>
206
+ </label>
207
+ <button class="btn" type="button"><svg><use href="#i-file"></use></svg>Columns</button>
208
+ </div>
209
+
210
+ {holdCount > 0 && (
211
+ <div class="orders-attention-bar">
212
+ <div>
213
+ <span class="chip amber">{holdCount} hold{holdCount === 1 ? "" : "s"}</span>
214
+ <strong>Resolve the selected hold to unlock label creation.</strong>
215
+ </div>
216
+ <button type="button" class="btn primary" data-orders-resolve-hold>Resolve hold</button>
217
+ </div>
218
+ )}
219
+
220
+ {ordersStatus === "error" && (
221
+ <p class="orders-load-state error" role="alert" data-orders-load-state="error">
222
+ Couldn't load orders right now.
223
+ <button type="button" class="btn" data-orders-refresh>Retry</button>
224
+ </p>
225
+ )}
226
+ {ordersStatus === "not_configured" && (
227
+ <p class="orders-load-state" data-orders-load-state="not_configured">
228
+ Orders aren't connected for this store yet.
229
+ </p>
230
+ )}
231
+ {ordersStatus === "empty" && (
232
+ <p class="orders-load-state" data-orders-load-state="empty">
233
+ No orders yet{orderFilters.queue ? " in this queue" : ""}.
234
+ </p>
235
+ )}
236
+ <div class="table-wrap orders-table-wrap" hidden={vm.rows.length === 0}>
237
+ <table class="orders-table">
238
+ <thead>
239
+ <tr>
240
+ <th>Order</th>
241
+ <th>Customer</th>
242
+ <th>Payment</th>
243
+ <th>Ship status</th>
244
+ <th class="action">Next</th>
245
+ </tr>
246
+ </thead>
247
+ <tbody data-orders-tbody>
248
+ {vm.rows.map((row) => (
249
+ <tr
250
+ class:list={[{ selected: row.id === defaultRow?.id }]}
251
+ data-orders-row
252
+ data-order-id={row.id}
253
+ data-queue-keys={queueKeysFor(row).join(" ")}
254
+ data-payment={row.payment}
255
+ data-search-haystack={`${row.id} ${row.customerName} ${row.itemsSummary} ${row.customerNote}`.toLowerCase()}
256
+ >
257
+ <td data-label="Order"><strong>{row.id}</strong><span class="subtle">${row.total.toFixed(2)} - {row.itemsSummary}</span><span class="subtle">{new Date(row.placedAt).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })}</span></td>
258
+ <td data-label="Customer"><strong>{row.customerName}</strong><span class="subtle">{row.customerNote}</span></td>
259
+ <td data-label="Payment"><span class:list={["chip", PAYMENT_TONE[row.payment]]}>{row.payment[0]!.toUpperCase() + row.payment.slice(1)}</span></td>
260
+ <td data-label="Ship status"><span class:list={["chip", SHIP_TONE[row.shipState]]}>{ORDER_SHIP_STATE_LABEL[row.shipState]}</span><span class="subtle">{row.blocker ?? "No blocker"}</span></td>
261
+ <td data-label="Next" class="action"><button type="button" class="btn" data-orders-select-order={row.id}>{row.nextStep}</button></td>
262
+ </tr>
263
+ ))}
264
+ </tbody>
265
+ </table>
266
+ <p class="orders-empty-state" data-orders-empty hidden>No orders match the current filters.</p>
267
+ </div>
268
+ {nextPageHref && (
269
+ <div class="orders-pagination">
270
+ <a class="btn" href={nextPageHref} data-orders-loadmore>Load more</a>
271
+ </div>
272
+ )}
273
+ </div>
274
+
275
+ <aside class="panel orders-detail-sideout" id="orders-detail" aria-labelledby="orders-detail-title" data-orders-detail-root data-admin-selected-id={defaultRow?.id ?? ""}>
276
+ {vm.rows.map((row) => {
277
+ const detail = vm.detailsById[row.id]!;
278
+ const evidenceRefs = evidenceForOrder(row.id);
279
+ const assist = buildOrderAssist(row, evidenceRefs);
280
+ return (
281
+ <div
282
+ class="orders-detail-record"
283
+ data-orders-detail-record={row.id}
284
+ hidden={row.id !== defaultRow?.id}
285
+ >
286
+ <div class="orders-detail-head">
287
+ <div>
288
+ <span class:list={["chip", SHIP_TONE[row.shipState]]}>{ORDER_SHIP_STATE_LABEL[row.shipState]}</span>
289
+ <h3 id={row.id === defaultRow?.id ? "orders-detail-title" : undefined}>{row.id}</h3>
290
+ <p>{row.customerName} - ${row.total.toFixed(2)} - placed at {new Date(row.placedAt).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })}</p>
291
+ </div>
292
+ {row.blocker && (
293
+ <button class="btn primary" type="button"><svg><use href="#i-alert"></use></svg>Resolve hold</button>
294
+ )}
295
+ </div>
296
+
297
+ {row.blocker ? (
298
+ <div class="orders-next-step">
299
+ <div>
300
+ <span>Next action</span>
301
+ <strong>{row.nextStep} to unlock label creation.</strong>
302
+ <p>{row.blocker} must be resolved before this order can ship.</p>
303
+ </div>
304
+ </div>
305
+ ) : (
306
+ <div class="orders-next-step orders-next-step--clear">
307
+ <div>
308
+ <span>Next action</span>
309
+ <strong>{row.nextStep}</strong>
310
+ <p>No blockers — this order is ready to move.</p>
311
+ </div>
312
+ </div>
313
+ )}
314
+
315
+ <div class="orders-sideout-grid">
316
+ <div>
317
+ <span>Customer</span>
318
+ <strong>{row.customerNote}</strong>
319
+ <small>{row.customerId}</small>
320
+ </div>
321
+ <div>
322
+ <span>Shipment</span>
323
+ <strong>1 package</strong>
324
+ <small>Standard shipping</small>
325
+ </div>
326
+ <div>
327
+ <span>Risk</span>
328
+ <strong>{row.riskLevel[0]!.toUpperCase() + row.riskLevel.slice(1)}</strong>
329
+ <small>Order risk level</small>
330
+ </div>
331
+ </div>
332
+
333
+ <div class="orders-detail-section">
334
+ <div class="orders-detail-section-head">
335
+ <h4>Readiness checks</h4>
336
+ <span class:list={["chip", detail.readinessChecks.some((c) => c.blocking) ? "amber" : "green"]}>
337
+ {detail.readinessChecks.filter((c) => c.blocking).length} action{detail.readinessChecks.filter((c) => c.blocking).length === 1 ? "" : "s"}
338
+ </span>
339
+ </div>
340
+ <div class="orders-check-list">
341
+ {detail.readinessChecks.map((check) => (
342
+ <div class:list={["orders-check-row", { blocking: check.blocking }]}>
343
+ <div>
344
+ <b>{check.label}</b>
345
+ <p>{check.detail}</p>
346
+ </div>
347
+ <strong>{check.value}</strong>
348
+ <button class="btn" type="button">{check.action}</button>
349
+ </div>
350
+ ))}
351
+ </div>
352
+ </div>
353
+
354
+ <details class="orders-subpanel" open>
355
+ <summary>
356
+ <span>Items and totals</span>
357
+ <strong>${detail.total.toFixed(2)}</strong>
358
+ </summary>
359
+ <div class="orders-subpanel-body">
360
+ {detail.lineItems.map((item) => (
361
+ <div class="orders-line-item">
362
+ <div>
363
+ <strong>{item.name}</strong>
364
+ <span>SKU {item.sku} - Qty {item.quantity}</span>
365
+ </div>
366
+ <b>${(item.unitPrice * item.quantity).toFixed(2)}</b>
367
+ </div>
368
+ ))}
369
+ <div class="orders-total-row"><span>Shipping</span><strong>${detail.shipping.toFixed(2)}</strong></div>
370
+ <div class="orders-total-row"><span>Estimated tax</span><strong>${detail.estimatedTax.toFixed(2)}</strong></div>
371
+ <div class="orders-total-row"><span>Taxes and fees</span><strong>${detail.taxesAndFees.toFixed(2)}</strong></div>
372
+ <div class="orders-total-row final"><span>Total</span><strong>${detail.total.toFixed(2)}</strong></div>
373
+ </div>
374
+ </details>
375
+
376
+ <details class="orders-subpanel">
377
+ <summary>
378
+ <span>Activity and evidence</span>
379
+ <strong>{detail.evidenceTimeline.length} events</strong>
380
+ </summary>
381
+ <div class="orders-subpanel-body orders-activity-list">
382
+ {detail.evidenceTimeline.map((item) => (
383
+ <div>
384
+ <time>{new Date(item.at).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })}</time>
385
+ <div><b>{item.title}</b><p>{item.detail}</p></div>
386
+ </div>
387
+ ))}
388
+ </div>
389
+ </details>
390
+
391
+ <div class="orders-detail-section order-assist">
392
+ <div class="orders-detail-section-head">
393
+ <h4><svg><use href="#i-ask"></use></svg>Order Assist</h4>
394
+ <span class:list={["chip", assist.approvalMode === "approval_required" ? "amber" : "blue"]}>
395
+ {assist.approvalMode === "approval_required" ? "Approval required" : "Draft only"}
396
+ </span>
397
+ </div>
398
+ <div class="order-assist-body">
399
+ <p class="order-assist-summary">{assist.summary}</p>
400
+ <div class="order-assist-meta">
401
+ <div><span>Confidence</span><strong>{Math.round(assist.confidence * 100)}%</strong></div>
402
+ <div><span>Owner</span><strong>{assist.owner}</strong></div>
403
+ <div><span>Evidence</span><strong>{assist.evidenceRefs.length} linked</strong></div>
404
+ </div>
405
+ {evidenceRefs.length > 0 && (
406
+ <div class="order-assist-evidence">
407
+ <span class="eyebrow">Dispute-evidence timeline</span>
408
+ {evidenceRefs.map((ref) => (
409
+ <div class="order-assist-evidence-row">
410
+ <time>{new Date(ref.at).toLocaleDateString([], { month: "short", day: "numeric" })}</time>
411
+ <div><b>{ref.label}</b><span class="subtle">{ref.kind.replace(/_/g, " ")}</span></div>
412
+ </div>
413
+ ))}
414
+ </div>
415
+ )}
416
+ <p class="order-assist-footnote">
417
+ {assist.auditTrail.length === 0
418
+ ? "No operator actions taken on this recommendation yet."
419
+ : `${assist.auditTrail.length} audit event(s).`}
420
+ </p>
421
+ </div>
422
+ </div>
423
+ </div>
424
+ );
425
+ })}
426
+ </aside>
427
+ </div>
428
+ </section>
429
+
430
+ <script is:inline nonce={cspNonce}>
431
+ (() => {
432
+ const root = document.querySelector('[data-orders-target-tenant]');
433
+ if (!root) return;
434
+
435
+ const queueButtons = Array.from(root.querySelectorAll("[data-orders-queue-button]"));
436
+ const queueSelect = root.querySelector("[data-orders-queue-select]");
437
+ const paymentSelect = root.querySelector("[data-orders-payment-select]");
438
+ const searchInput = root.querySelector("[data-orders-search]");
439
+ const rows = Array.from(root.querySelectorAll("[data-orders-row]"));
440
+ const emptyState = root.querySelector("[data-orders-empty]");
441
+ const detailRoot = root.querySelector("[data-orders-detail-root]");
442
+ const detailRecords = detailRoot ? Array.from(detailRoot.querySelectorAll("[data-orders-detail-record]")) : [];
443
+
444
+ const setLoading = () => root.setAttribute("data-orders-loading", "");
445
+
446
+ // Queue filter + Refresh + pagination are SERVER-backed: they navigate so the
447
+ // order API re-applies the filter/cursor and returns a fresh page (U5). Any
448
+ // filter change resets pagination to the first page (drops the cursor).
449
+ const navTo = (mutate) => {
450
+ const url = new URL(window.location.href);
451
+ mutate(url.searchParams);
452
+ url.searchParams.delete("cursor");
453
+ setLoading();
454
+ window.location.assign(url.pathname + "?" + url.searchParams.toString());
455
+ };
456
+ const setQueue = (sp, key) => {
457
+ if (key && key !== "all") sp.set("queue", key);
458
+ else sp.delete("queue");
459
+ };
460
+
461
+ // Client refinement over the server-returned rows: payment + free-text search.
462
+ const applyFilters = () => {
463
+ const payment = paymentSelect ? paymentSelect.value : "";
464
+ const term = searchInput ? searchInput.value.trim().toLowerCase() : "";
465
+ let visibleCount = 0;
466
+
467
+ for (const row of rows) {
468
+ const matchesPayment = !payment || row.dataset.payment === payment;
469
+ const matchesSearch = !term || (row.dataset.searchHaystack ?? "").includes(term);
470
+ const visible = matchesPayment && matchesSearch;
471
+ row.hidden = !visible;
472
+ if (visible) visibleCount += 1;
473
+ }
474
+
475
+ if (emptyState) emptyState.hidden = visibleCount > 0 || rows.length === 0;
476
+ };
477
+
478
+ const selectOrder = (orderId) => {
479
+ for (const row of rows) {
480
+ row.classList.toggle("selected", row.dataset.orderId === orderId);
481
+ }
482
+ for (const record of detailRecords) {
483
+ record.hidden = record.dataset.ordersDetailRecord !== orderId;
484
+ }
485
+ if (detailRoot) detailRoot.dataset.adminSelectedId = orderId;
486
+ };
487
+
488
+ queueButtons.forEach((button) => {
489
+ button.addEventListener("click", () => navTo((sp) => setQueue(sp, button.dataset.queueKey ?? "all")));
490
+ });
491
+
492
+ queueSelect?.addEventListener("change", () => navTo((sp) => setQueue(sp, queueSelect.value)));
493
+
494
+ paymentSelect?.addEventListener("change", applyFilters);
495
+ searchInput?.addEventListener("input", applyFilters);
496
+
497
+ // Refresh re-runs the server fetch (fresh live data); Load more advances the cursor.
498
+ root.querySelectorAll("[data-orders-refresh]").forEach((btn) => {
499
+ btn.addEventListener("click", () => {
500
+ setLoading();
501
+ window.location.reload();
502
+ });
503
+ });
504
+ root.querySelectorAll("[data-orders-loadmore]").forEach((link) => {
505
+ link.addEventListener("click", setLoading);
506
+ });
507
+
508
+ root.querySelectorAll("[data-orders-select-order]").forEach((button) => {
509
+ button.addEventListener("click", () => {
510
+ const orderId = button.dataset.ordersSelectOrder;
511
+ if (orderId) selectOrder(orderId);
512
+ });
513
+ });
514
+
515
+ rows.forEach((row) => {
516
+ row.addEventListener("click", (event) => {
517
+ if (event.target instanceof Element && event.target.closest("[data-orders-select-order]")) return;
518
+ const orderId = row.dataset.orderId;
519
+ if (orderId) selectOrder(orderId);
520
+ });
521
+ });
522
+
523
+ applyFilters();
524
+ })();
525
+ </script>
526
+
527
+ <style>
528
+ .orders-tab {
529
+ --orders-sideout-width: minmax(370px, 0.42fr);
530
+ }
531
+
532
+ /* U5: live load states, pagination, and a refresh/navigation affordance. */
533
+ .orders-load-state {
534
+ margin: 0 0 0.75rem;
535
+ padding: 0.75rem 1rem;
536
+ border: 1px solid var(--color-border, #e5e7eb);
537
+ border-radius: var(--radius-md, 8px);
538
+ color: var(--color-muted, #6b7280);
539
+ display: flex;
540
+ align-items: center;
541
+ gap: 0.75rem;
542
+ }
543
+ .orders-load-state.error {
544
+ border-color: #f3c2c2;
545
+ color: #b42318;
546
+ }
547
+ .orders-pagination {
548
+ margin-top: 0.75rem;
549
+ text-align: center;
550
+ }
551
+ [data-orders-loading] {
552
+ opacity: 0.6;
553
+ pointer-events: none;
554
+ cursor: progress;
555
+ }
556
+
557
+ :global(html[data-admin-tab="orders"] .floating-ask) {
558
+ display: none !important;
559
+ }
560
+
561
+ .orders-title-row {
562
+ align-items: flex-start;
563
+ }
564
+
565
+ .orders-primary-actions {
566
+ justify-content: flex-end;
567
+ }
568
+
569
+ .orders-command-strip {
570
+ display: grid;
571
+ grid-template-columns: minmax(0, 1fr) minmax(180px, 220px);
572
+ gap: 12px;
573
+ align-items: stretch;
574
+ }
575
+
576
+ .orders-queue-list {
577
+ display: grid;
578
+ grid-template-columns: repeat(5, minmax(120px, 1fr));
579
+ gap: 8px;
580
+ min-width: 0;
581
+ }
582
+
583
+ .orders-queue-card {
584
+ display: grid;
585
+ gap: 5px;
586
+ min-width: 0;
587
+ padding: 10px;
588
+ border: 1px solid var(--admin-line);
589
+ border-radius: var(--admin-radius);
590
+ background: var(--admin-surface);
591
+ color: var(--admin-ink);
592
+ text-align: left;
593
+ text-decoration: none;
594
+ }
595
+
596
+ .orders-queue-card.active {
597
+ border-color: #efc477;
598
+ background: #fff8ec;
599
+ box-shadow: inset 3px 0 0 var(--admin-amber);
600
+ }
601
+
602
+ .orders-queue-card strong {
603
+ font-size: 24px;
604
+ line-height: 1;
605
+ }
606
+
607
+ .orders-queue-card small,
608
+ .orders-today-summary small,
609
+ .orders-sideout-grid small {
610
+ color: var(--admin-muted);
611
+ font-size: 12px;
612
+ }
613
+
614
+ .orders-today-summary {
615
+ min-width: 0;
616
+ border: 1px solid var(--admin-line);
617
+ border-radius: var(--admin-radius);
618
+ background: var(--admin-surface);
619
+ }
620
+
621
+ .orders-today-summary summary {
622
+ min-height: 100%;
623
+ display: grid;
624
+ gap: 4px;
625
+ padding: 10px 12px;
626
+ cursor: pointer;
627
+ list-style: none;
628
+ }
629
+
630
+ .orders-today-summary summary::-webkit-details-marker,
631
+ .orders-subpanel summary::-webkit-details-marker {
632
+ display: none;
633
+ }
634
+
635
+ .orders-today-summary summary span,
636
+ .orders-list-tools span,
637
+ .orders-next-step span,
638
+ .orders-sideout-grid span,
639
+ .orders-field-grid span {
640
+ color: var(--admin-muted);
641
+ font-size: 11px;
642
+ font-weight: 760;
643
+ text-transform: uppercase;
644
+ }
645
+
646
+ .orders-today-summary summary strong {
647
+ font-size: 20px;
648
+ }
649
+
650
+ .orders-today-summary > div {
651
+ display: grid;
652
+ gap: 7px;
653
+ padding: 0 12px 12px;
654
+ }
655
+
656
+ .orders-today-summary p,
657
+ .orders-next-step p,
658
+ .orders-check-row p,
659
+ .orders-detail-head p,
660
+ .orders-activity-list p {
661
+ margin: 0;
662
+ color: var(--admin-muted);
663
+ font-size: 12px;
664
+ }
665
+
666
+ .orders-today-summary p,
667
+ .orders-total-row {
668
+ display: flex;
669
+ align-items: baseline;
670
+ justify-content: space-between;
671
+ gap: 12px;
672
+ }
673
+
674
+ .orders-workspace {
675
+ display: grid;
676
+ grid-template-columns: minmax(0, 1fr) var(--orders-sideout-width);
677
+ gap: 14px;
678
+ align-items: start;
679
+ min-width: 0;
680
+ }
681
+
682
+ .orders-list-panel,
683
+ .orders-detail-sideout {
684
+ min-width: 0;
685
+ }
686
+
687
+ .orders-list-tools {
688
+ display: grid;
689
+ grid-template-columns: minmax(210px, 1.3fr) repeat(2, minmax(130px, 0.7fr)) auto;
690
+ gap: 8px;
691
+ align-items: end;
692
+ padding: 12px;
693
+ border-bottom: 1px solid var(--admin-line);
694
+ }
695
+
696
+ .orders-list-tools label {
697
+ display: grid;
698
+ gap: 5px;
699
+ min-width: 0;
700
+ }
701
+
702
+ .orders-list-tools input,
703
+ .orders-list-tools select {
704
+ width: 100%;
705
+ min-width: 0;
706
+ min-height: 38px;
707
+ padding: 0 10px;
708
+ border: 1px solid var(--admin-line-strong);
709
+ border-radius: var(--admin-radius-sm);
710
+ background: var(--admin-surface);
711
+ color: var(--admin-ink);
712
+ }
713
+
714
+ .orders-attention-bar {
715
+ display: flex;
716
+ align-items: center;
717
+ justify-content: space-between;
718
+ gap: 10px;
719
+ padding: 10px 12px;
720
+ border-bottom: 1px solid var(--admin-line);
721
+ background: #fff8ec;
722
+ }
723
+
724
+ .orders-attention-bar > div {
725
+ display: flex;
726
+ align-items: center;
727
+ gap: 10px;
728
+ min-width: 0;
729
+ }
730
+
731
+ .orders-attention-bar strong {
732
+ font-size: 13px;
733
+ }
734
+
735
+ .orders-table-wrap {
736
+ border: 0;
737
+ border-radius: 0 0 var(--admin-radius) var(--admin-radius);
738
+ }
739
+
740
+ .orders-table {
741
+ min-width: 690px;
742
+ }
743
+
744
+ .orders-table tr.selected {
745
+ background: #fff8ec;
746
+ box-shadow: inset 3px 0 0 var(--admin-amber);
747
+ }
748
+
749
+ .orders-table tbody tr {
750
+ cursor: pointer;
751
+ }
752
+
753
+ .orders-table td {
754
+ height: 56px;
755
+ }
756
+
757
+ .orders-empty-state {
758
+ margin: 0;
759
+ padding: 24px 14px;
760
+ color: var(--admin-muted);
761
+ font-size: 13px;
762
+ text-align: center;
763
+ }
764
+
765
+ .orders-detail-sideout {
766
+ position: sticky;
767
+ top: 86px;
768
+ display: grid;
769
+ gap: 12px;
770
+ padding: 14px;
771
+ box-shadow: var(--admin-shadow);
772
+ }
773
+
774
+ .orders-detail-head {
775
+ display: flex;
776
+ align-items: flex-start;
777
+ justify-content: space-between;
778
+ gap: 12px;
779
+ padding-bottom: 12px;
780
+ border-bottom: 1px solid var(--admin-line);
781
+ }
782
+
783
+ .orders-detail-head h3 {
784
+ margin: 8px 0 2px;
785
+ font-size: 24px;
786
+ }
787
+
788
+ .orders-next-step {
789
+ padding: 12px;
790
+ border: 1px solid #efc477;
791
+ border-radius: var(--admin-radius);
792
+ background: #fff8ec;
793
+ }
794
+
795
+ .orders-next-step--clear {
796
+ border-color: var(--admin-line);
797
+ background: var(--admin-surface-soft);
798
+ }
799
+
800
+ .orders-next-step strong {
801
+ display: block;
802
+ margin: 5px 0 4px;
803
+ font-size: 15px;
804
+ }
805
+
806
+ .orders-sideout-grid,
807
+ .orders-field-grid {
808
+ display: grid;
809
+ grid-template-columns: repeat(3, minmax(0, 1fr));
810
+ gap: 8px;
811
+ }
812
+
813
+ .orders-sideout-grid > div,
814
+ .orders-field-grid > div {
815
+ min-width: 0;
816
+ padding: 10px;
817
+ border: 1px solid var(--admin-line);
818
+ border-radius: var(--admin-radius-sm);
819
+ background: var(--admin-surface-soft);
820
+ }
821
+
822
+ .orders-sideout-grid strong,
823
+ .orders-field-grid strong {
824
+ display: block;
825
+ margin-top: 4px;
826
+ overflow-wrap: anywhere;
827
+ font-size: 13px;
828
+ }
829
+
830
+ .orders-detail-section {
831
+ border: 1px solid var(--admin-line);
832
+ border-radius: var(--admin-radius);
833
+ background: var(--admin-surface);
834
+ }
835
+
836
+ .orders-detail-section-head {
837
+ display: flex;
838
+ align-items: center;
839
+ justify-content: space-between;
840
+ gap: 10px;
841
+ min-height: 46px;
842
+ padding: 10px 12px;
843
+ border-bottom: 1px solid var(--admin-line);
844
+ }
845
+
846
+ .orders-detail-section-head h4 {
847
+ margin: 0;
848
+ display: flex;
849
+ align-items: center;
850
+ gap: 8px;
851
+ font-size: 14px;
852
+ }
853
+
854
+ .order-assist {
855
+ border-color: #b9d8d4;
856
+ background: #fbfefd;
857
+ }
858
+
859
+ .order-assist-body {
860
+ display: grid;
861
+ gap: 10px;
862
+ padding: 12px;
863
+ }
864
+
865
+ .order-assist-summary {
866
+ margin: 0;
867
+ font-size: 13px;
868
+ }
869
+
870
+ .order-assist-meta {
871
+ display: grid;
872
+ grid-template-columns: repeat(3, minmax(0, 1fr));
873
+ gap: 8px;
874
+ }
875
+
876
+ .order-assist-meta > div {
877
+ min-width: 0;
878
+ padding: 8px 10px;
879
+ border: 1px solid var(--admin-line);
880
+ border-radius: var(--admin-radius-sm);
881
+ background: var(--admin-surface);
882
+ }
883
+
884
+ .order-assist-meta span {
885
+ color: var(--admin-muted);
886
+ font-size: 11px;
887
+ font-weight: 760;
888
+ text-transform: uppercase;
889
+ }
890
+
891
+ .order-assist-meta strong {
892
+ display: block;
893
+ margin-top: 3px;
894
+ font-size: 13px;
895
+ }
896
+
897
+ .order-assist-evidence {
898
+ display: grid;
899
+ gap: 6px;
900
+ }
901
+
902
+ .order-assist-evidence .eyebrow {
903
+ color: var(--admin-muted);
904
+ font-size: 11px;
905
+ font-weight: 760;
906
+ text-transform: uppercase;
907
+ }
908
+
909
+ .order-assist-evidence-row {
910
+ display: grid;
911
+ grid-template-columns: 60px minmax(0, 1fr);
912
+ gap: 8px;
913
+ align-items: baseline;
914
+ }
915
+
916
+ .order-assist-evidence-row time {
917
+ color: var(--admin-muted);
918
+ font-size: 12px;
919
+ }
920
+
921
+ .order-assist-evidence-row b {
922
+ font-size: 12px;
923
+ }
924
+
925
+ .order-assist-footnote {
926
+ margin: 0;
927
+ color: var(--admin-muted);
928
+ font-size: 11px;
929
+ font-style: italic;
930
+ }
931
+
932
+ .orders-check-list {
933
+ display: grid;
934
+ gap: 0;
935
+ padding: 4px 12px;
936
+ }
937
+
938
+ .orders-check-row {
939
+ display: grid;
940
+ grid-template-columns: minmax(0, 1fr) minmax(96px, auto) auto;
941
+ gap: 10px;
942
+ align-items: center;
943
+ min-width: 0;
944
+ padding: 10px 0;
945
+ border-bottom: 1px solid var(--admin-line);
946
+ }
947
+
948
+ .orders-check-row:last-child {
949
+ border-bottom: 0;
950
+ }
951
+
952
+ .orders-check-row.blocking {
953
+ margin: 4px -6px;
954
+ padding: 10px 6px;
955
+ border: 1px solid #efc477;
956
+ border-radius: var(--admin-radius-sm);
957
+ background: #fff8ec;
958
+ }
959
+
960
+ .orders-check-row b,
961
+ .orders-line-item strong,
962
+ .orders-activity-list b {
963
+ font-size: 13px;
964
+ }
965
+
966
+ .orders-check-row > strong {
967
+ font-size: 13px;
968
+ }
969
+
970
+ .orders-subpanel {
971
+ border: 1px solid var(--admin-line);
972
+ border-radius: var(--admin-radius);
973
+ background: var(--admin-surface);
974
+ }
975
+
976
+ .orders-subpanel summary {
977
+ min-height: 44px;
978
+ display: flex;
979
+ align-items: center;
980
+ justify-content: space-between;
981
+ gap: 10px;
982
+ padding: 10px 12px;
983
+ cursor: pointer;
984
+ list-style: none;
985
+ }
986
+
987
+ .orders-subpanel summary span {
988
+ font-weight: 760;
989
+ }
990
+
991
+ .orders-subpanel summary strong {
992
+ font-size: 13px;
993
+ }
994
+
995
+ .orders-subpanel-body {
996
+ display: grid;
997
+ gap: 9px;
998
+ padding: 0 12px 12px;
999
+ border-top: 1px solid var(--admin-line);
1000
+ }
1001
+
1002
+ .orders-line-item {
1003
+ display: flex;
1004
+ justify-content: space-between;
1005
+ gap: 12px;
1006
+ padding-top: 12px;
1007
+ }
1008
+
1009
+ .orders-line-item span {
1010
+ display: block;
1011
+ margin-top: 3px;
1012
+ color: var(--admin-muted);
1013
+ font-size: 12px;
1014
+ }
1015
+
1016
+ .orders-total-row {
1017
+ color: var(--admin-muted);
1018
+ font-size: 13px;
1019
+ }
1020
+
1021
+ .orders-total-row strong {
1022
+ color: var(--admin-ink);
1023
+ }
1024
+
1025
+ .orders-total-row.final {
1026
+ padding-top: 8px;
1027
+ border-top: 1px solid var(--admin-line);
1028
+ color: var(--admin-ink);
1029
+ font-weight: 800;
1030
+ }
1031
+
1032
+ .orders-field-grid {
1033
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1034
+ padding-top: 12px;
1035
+ }
1036
+
1037
+ .orders-activity-list {
1038
+ padding-top: 12px;
1039
+ }
1040
+
1041
+ .orders-activity-list > div {
1042
+ display: grid;
1043
+ grid-template-columns: 64px minmax(0, 1fr);
1044
+ gap: 10px;
1045
+ }
1046
+
1047
+ .orders-activity-list time {
1048
+ color: var(--admin-muted);
1049
+ font-size: 12px;
1050
+ }
1051
+
1052
+ @media (max-width: 1180px) {
1053
+ .orders-command-strip,
1054
+ .orders-workspace {
1055
+ grid-template-columns: 1fr;
1056
+ }
1057
+
1058
+ .orders-queue-list {
1059
+ grid-template-columns: repeat(5, minmax(132px, 1fr));
1060
+ overflow-x: auto;
1061
+ }
1062
+
1063
+ .orders-detail-sideout {
1064
+ position: static;
1065
+ }
1066
+ }
1067
+
1068
+ @media (max-width: 900px) {
1069
+ .orders-list-tools {
1070
+ grid-template-columns: minmax(0, 1fr) minmax(130px, 0.7fr) minmax(130px, 0.7fr);
1071
+ }
1072
+
1073
+ .orders-list-tools .btn {
1074
+ display: none;
1075
+ }
1076
+ }
1077
+
1078
+ @media (max-width: 760px) {
1079
+ .orders-title-row p {
1080
+ display: none;
1081
+ }
1082
+
1083
+ .orders-primary-actions {
1084
+ justify-content: stretch;
1085
+ }
1086
+
1087
+ .orders-primary-actions .btn {
1088
+ flex: 1 1 130px;
1089
+ }
1090
+
1091
+ .orders-primary-actions .btn:not(.primary) {
1092
+ display: none;
1093
+ }
1094
+
1095
+ .orders-command-strip {
1096
+ gap: 8px;
1097
+ }
1098
+
1099
+ .orders-queue-list {
1100
+ display: flex;
1101
+ gap: 8px;
1102
+ padding-bottom: 2px;
1103
+ overflow-x: auto;
1104
+ }
1105
+
1106
+ .orders-queue-card {
1107
+ min-width: 142px;
1108
+ padding: 9px;
1109
+ }
1110
+
1111
+ .orders-queue-card strong {
1112
+ font-size: 20px;
1113
+ }
1114
+
1115
+ .orders-today-summary {
1116
+ display: none;
1117
+ }
1118
+
1119
+ .orders-list-tools {
1120
+ grid-template-columns: 1fr;
1121
+ padding: 10px;
1122
+ }
1123
+
1124
+ .orders-list-tools label:nth-of-type(3) {
1125
+ display: none;
1126
+ }
1127
+
1128
+ .orders-attention-bar {
1129
+ align-items: stretch;
1130
+ flex-direction: column;
1131
+ }
1132
+
1133
+ .orders-attention-bar > div {
1134
+ align-items: flex-start;
1135
+ flex-direction: column;
1136
+ gap: 6px;
1137
+ }
1138
+
1139
+ .orders-table {
1140
+ min-width: 0;
1141
+ }
1142
+
1143
+ .orders-table thead {
1144
+ display: none;
1145
+ }
1146
+
1147
+ .orders-table,
1148
+ .orders-table tbody,
1149
+ .orders-table tr,
1150
+ .orders-table td {
1151
+ display: block;
1152
+ width: 100%;
1153
+ }
1154
+
1155
+ .orders-table tr {
1156
+ padding: 10px 12px;
1157
+ border-bottom: 1px solid var(--admin-line);
1158
+ }
1159
+
1160
+ .orders-table td {
1161
+ min-height: 0;
1162
+ height: auto;
1163
+ display: flex;
1164
+ align-items: baseline;
1165
+ justify-content: space-between;
1166
+ gap: 14px;
1167
+ padding: 5px 0;
1168
+ border-bottom: 0;
1169
+ text-align: right;
1170
+ }
1171
+
1172
+ .orders-table td::before {
1173
+ content: attr(data-label);
1174
+ color: var(--admin-muted);
1175
+ font-size: 11px;
1176
+ font-weight: 760;
1177
+ text-align: left;
1178
+ text-transform: uppercase;
1179
+ }
1180
+
1181
+ .orders-table td:first-child,
1182
+ .orders-table td:nth-child(2) {
1183
+ align-items: flex-start;
1184
+ }
1185
+
1186
+ .orders-table td:first-child {
1187
+ padding-top: 0;
1188
+ }
1189
+
1190
+ .orders-table .subtle {
1191
+ text-align: right;
1192
+ }
1193
+
1194
+ .orders-table .action .btn {
1195
+ width: 100%;
1196
+ }
1197
+
1198
+ .orders-detail-head,
1199
+ .orders-check-row,
1200
+ .orders-line-item {
1201
+ align-items: stretch;
1202
+ flex-direction: column;
1203
+ }
1204
+
1205
+ .orders-detail-head {
1206
+ display: grid;
1207
+ }
1208
+
1209
+ .orders-sideout-grid,
1210
+ .orders-field-grid,
1211
+ .orders-check-row {
1212
+ grid-template-columns: 1fr;
1213
+ }
1214
+
1215
+ .orders-detail-sideout {
1216
+ padding: 12px;
1217
+ }
1218
+
1219
+ .orders-activity-list > div {
1220
+ grid-template-columns: 1fr;
1221
+ }
1222
+ }
1223
+ </style>