@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,300 @@
1
+ /**
2
+ * REAL orders view model (card 8585 / U3) — replaces the deterministic mock as
3
+ * the Orders tab's data source. Fetches the tenant's orders from tot-foxycart via
4
+ * U2's order client and maps the frozen DTO (`@tot/public-runtime` foxy-orders)
5
+ * into the EXISTING `OrdersViewModel` shape, so `OrdersWorkspace.astro` renders
6
+ * unchanged. Read-only (scope-locks) — no write paths.
7
+ *
8
+ * The mapping is a pure function (`mapOrdersToViewModel`) for testability; the
9
+ * orchestration (`getOrdersViewModel`) lists orders, eagerly fetches each order's
10
+ * detail (OrdersWorkspace renders `detailsById[row.id]` for every row), and maps.
11
+ * On ANY upstream failure it returns a valid EMPTY view model so the tab renders
12
+ * (rich loading/empty/error UX is U5). NOTE: the live "real qa data" path is
13
+ * gated on ToT-core exposing `GET /api/exciseTax/orders` (see U1/U2 notes); until
14
+ * then this yields the empty model on qa.
15
+ *
16
+ * Identity/age fields (riskLevel, readiness "identity_and_age", evidence) are
17
+ * derived provisionally from commerce facts here; U4 (the tot20 verification
18
+ * join) fills them authoritatively via the DTO's null `verification` /
19
+ * `verificationDetail` slots — no shape change needed.
20
+ */
21
+ import type {
22
+ FoxyOrderDetail,
23
+ FoxyOrderListFilters,
24
+ FoxyOrderPage,
25
+ FoxyOrderSummary,
26
+ FoxyOrderVerification,
27
+ } from "@tot/public-runtime";
28
+ import type {
29
+ OrderDetail,
30
+ OrderEvidenceEvent,
31
+ OrderQueueSummary,
32
+ OrderReadinessCheck,
33
+ OrderRow,
34
+ OrderShipState,
35
+ OrdersViewModel,
36
+ } from "../viewmodels/orders";
37
+ import type { FoxyOrderClient } from "../orderClient";
38
+
39
+ /** Next-step label, mirroring the mock's queue semantics. */
40
+ function deriveNextStep(fulfillment: OrderShipState, blocker: string | null): string {
41
+ if (fulfillment === "hold_review_required") {
42
+ return blocker === "Payment capture" ? "Capture payment" : "Review risk";
43
+ }
44
+ if (fulfillment === "completed") return "View record";
45
+ return "Create label";
46
+ }
47
+
48
+ /** Provisional risk until U4 fills `verification`. Prefer a real riskLevel when present. */
49
+ function deriveRisk(
50
+ verification: FoxyOrderVerification | null,
51
+ blocker: string | null,
52
+ ): OrderRow["riskLevel"] {
53
+ const r = verification?.riskLevel;
54
+ if (r === "low" || r === "medium" || r === "high") return r;
55
+ if (blocker === "Age verification hold") return "high";
56
+ return blocker ? "medium" : "low";
57
+ }
58
+
59
+ /** Map a DTO summary to the view model's OrderRow. */
60
+ export function mapSummaryToRow(s: FoxyOrderSummary): OrderRow {
61
+ return {
62
+ id: s.id,
63
+ placedAt: s.placedAt,
64
+ customerId: s.customer.id ?? "",
65
+ customerName: s.customer.name,
66
+ customerNote: "", // not carried by the commerce DTO
67
+ itemsSummary: s.itemsSummary,
68
+ productId: "", // not carried by the commerce order summary DTO (mock links by product index)
69
+ total: s.total,
70
+ payment: s.payment,
71
+ shipState: s.fulfillment,
72
+ blocker: s.blocker,
73
+ nextStep: deriveNextStep(s.fulfillment, s.blocker),
74
+ riskLevel: deriveRisk(s.verification, s.blocker),
75
+ };
76
+ }
77
+
78
+ /** Commerce-derived readiness checks (U4 enriches the identity dimension). */
79
+ function deriveReadinessChecks(d: FoxyOrderDetail): OrderReadinessCheck[] {
80
+ const ageHold = d.blocker === "Age verification hold";
81
+ return [
82
+ {
83
+ key: "payment_capture",
84
+ label: "Payment capture",
85
+ value: d.payment === "captured" ? "Captured" : "Authorized",
86
+ detail: "Capture the authorization before creating the shipping label.",
87
+ tone: d.payment === "captured" ? "green" : "amber",
88
+ action: "Capture payment",
89
+ blocking: d.payment !== "captured",
90
+ },
91
+ {
92
+ key: "tax_and_fees",
93
+ label: "Tax and fees",
94
+ value: "Collected",
95
+ detail: "Required amounts are included in the order total.",
96
+ tone: "blue",
97
+ action: "View total",
98
+ blocking: false,
99
+ },
100
+ {
101
+ key: "identity_and_age",
102
+ label: "Identity and age",
103
+ value: ageHold ? "Needs review" : "Passed",
104
+ detail: "Checkout assurance completed before order placement.",
105
+ tone: ageHold ? "amber" : "green",
106
+ action: "View evidence",
107
+ blocking: ageHold,
108
+ },
109
+ {
110
+ key: "address",
111
+ label: "Address",
112
+ value: d.shippingAddress ? "Serviceable" : "Unknown",
113
+ detail: "Destination is deliverable after any hold is resolved.",
114
+ tone: d.shippingAddress ? "green" : "amber",
115
+ action: "View address",
116
+ blocking: false,
117
+ },
118
+ ];
119
+ }
120
+
121
+ /** Base commerce evidence events (U4 replaces with the real verification timeline). */
122
+ function deriveEvidenceTimeline(d: FoxyOrderDetail): OrderEvidenceEvent[] {
123
+ const events: OrderEvidenceEvent[] = [
124
+ { title: "Order imported", detail: "Storefront order became available in the admin queue.", at: d.placedAt },
125
+ { title: "Payment authorized", detail: "The customer completed checkout and payment was authorized.", at: d.placedAt },
126
+ ];
127
+ if (d.blocker) {
128
+ events.push({ title: "Hold added", detail: `${d.blocker} must be resolved before shipment.`, at: d.placedAt });
129
+ }
130
+ return events;
131
+ }
132
+
133
+ /** Map a DTO detail (its summary already mapped to `row`) to the view model's OrderDetail. */
134
+ export function mapDetailToDetail(d: FoxyOrderDetail, row: OrderRow): OrderDetail {
135
+ return {
136
+ ...row,
137
+ lineItems: d.lineItems.map((li) => ({
138
+ sku: li.sku,
139
+ name: li.name,
140
+ quantity: li.quantity,
141
+ unitPrice: li.unitPrice,
142
+ })),
143
+ shipping: d.totals.shipping,
144
+ estimatedTax: d.totals.estimatedTax,
145
+ taxesAndFees: d.totals.taxesAndFees,
146
+ readinessChecks: deriveReadinessChecks(d),
147
+ evidenceTimeline: deriveEvidenceTimeline(d),
148
+ };
149
+ }
150
+
151
+ function buildQueues(rows: OrderRow[]): OrderQueueSummary[] {
152
+ const needsAttention = rows.filter((r) => r.shipState === "hold_review_required").length;
153
+ const readyToShip = rows.filter((r) => r.shipState === "ready_to_ship").length;
154
+ const completed = rows.filter((r) => r.shipState === "completed").length;
155
+ return [
156
+ { key: "needs_attention", label: "Needs attention", count: needsAttention, detail: "Hold before shipment" },
157
+ { key: "ready_to_ship", label: "Ready to ship", count: readyToShip, detail: "Labels available" },
158
+ { key: "unfulfilled", label: "Unfulfilled", count: readyToShip + needsAttention, detail: "Oldest open order first" },
159
+ { key: "all", label: "All orders", count: rows.length, detail: "Newest first" },
160
+ { key: "completed", label: "Completed", count: completed, detail: "Delivered" },
161
+ ];
162
+ }
163
+
164
+ /**
165
+ * PURE map of DTO orders → the OrdersViewModel. `details` supplies the per-order
166
+ * detail (line items / totals); a row with no matching detail falls back to a
167
+ * minimal detail built from the row so `detailsById[row.id]` is always present.
168
+ */
169
+ export function mapOrdersToViewModel(
170
+ targetTenant: string,
171
+ summaries: FoxyOrderSummary[],
172
+ details: Record<string, FoxyOrderDetail>,
173
+ ): OrdersViewModel {
174
+ const rows = summaries.map(mapSummaryToRow);
175
+
176
+ const detailsById: Record<string, OrderDetail> = {};
177
+ for (const row of rows) {
178
+ const d = details[row.id];
179
+ detailsById[row.id] = d
180
+ ? mapDetailToDetail(d, row)
181
+ : {
182
+ ...row,
183
+ lineItems: [],
184
+ shipping: 0,
185
+ estimatedTax: 0,
186
+ taxesAndFees: 0,
187
+ readinessChecks: [],
188
+ evidenceTimeline: [],
189
+ };
190
+ }
191
+
192
+ const paidOrders = rows.filter((r) => r.payment === "captured");
193
+ const grossSales = paidOrders.reduce((sum, r) => sum + r.total, 0);
194
+
195
+ return {
196
+ targetTenant,
197
+ queues: buildQueues(rows),
198
+ rows,
199
+ detailsById,
200
+ today: {
201
+ grossSales,
202
+ paidOrders: paidOrders.length,
203
+ averageOrder: paidOrders.length ? Math.round((grossSales / paidOrders.length) * 100) / 100 : 0,
204
+ refunds: 0,
205
+ oldestOpenAt: rows.length ? rows[rows.length - 1]!.placedAt : null,
206
+ },
207
+ };
208
+ }
209
+
210
+ /** A valid, empty view model — rendered when nothing is available or a fetch fails. */
211
+ export function emptyOrdersViewModel(targetTenant: string): OrdersViewModel {
212
+ return mapOrdersToViewModel(targetTenant, [], {});
213
+ }
214
+
215
+ export interface GetOrdersViewModelOptions {
216
+ targetTenant: string;
217
+ client: FoxyOrderClient;
218
+ /** List filters (queue/status/pagination); passed through to the order client. */
219
+ filters?: Parameters<FoxyOrderClient["listOrders"]>[0];
220
+ }
221
+
222
+ /**
223
+ * Fetch + map the real orders view model. Lists orders, eagerly fetches each
224
+ * order's detail (bounded by the list page size), and maps. Any upstream error
225
+ * (incl. the not-yet-available ToT-core endpoint) degrades to the empty model so
226
+ * the Orders tab always renders.
227
+ */
228
+ /**
229
+ * Load outcome for the Orders tab (U5) — lets the UI pick the right state:
230
+ * `loaded` (rows), `empty` (fetch OK, 0 orders), `error` (upstream failed), and
231
+ * `not_configured` (produced by the caller when no bearer/client is available —
232
+ * the service itself never returns it). `page` carries the pagination cursor.
233
+ */
234
+ export type OrdersLoadStatus = "loaded" | "empty" | "error" | "not_configured";
235
+
236
+ export interface OrdersLoadResult {
237
+ status: OrdersLoadStatus;
238
+ vm: OrdersViewModel;
239
+ page: FoxyOrderPage | null;
240
+ }
241
+
242
+ /**
243
+ * Parse server-backed list filters from a request's query string (U5). `queue`
244
+ * of `"all"` (or absent) means no server filter. Non-numeric `limit` is ignored.
245
+ * `payment`/search are NOT here — they are client-side refinements (the DTO list
246
+ * API has no payment filter, per contract §9).
247
+ */
248
+ export function parseOrderFilters(params: URLSearchParams): FoxyOrderListFilters {
249
+ const f: FoxyOrderListFilters = {};
250
+ const queue = params.get("queue");
251
+ if (queue && queue !== "all") f.queue = queue;
252
+ const status = params.get("status");
253
+ if (status) f.status = status;
254
+ const from = params.get("from");
255
+ if (from) f.from = from;
256
+ const to = params.get("to");
257
+ if (to) f.to = to;
258
+ const limit = params.get("limit");
259
+ if (limit && Number.isFinite(Number(limit))) f.limit = Number(limit);
260
+ const cursor = params.get("cursor");
261
+ if (cursor) f.cursor = cursor;
262
+ return f;
263
+ }
264
+
265
+ /**
266
+ * Fetch + map the orders view model AND report the load outcome (U5). Lists,
267
+ * eagerly fetches per-order detail, maps. Distinguishes empty from error so the
268
+ * tab can degrade gracefully. Never throws.
269
+ */
270
+ export async function getOrdersViewModelResult(
271
+ opts: GetOrdersViewModelOptions,
272
+ ): Promise<OrdersLoadResult> {
273
+ try {
274
+ const { orders, page } = await opts.client.listOrders(opts.filters);
275
+ const details: Record<string, FoxyOrderDetail> = {};
276
+ await Promise.all(
277
+ orders.map(async (o) => {
278
+ try {
279
+ details[o.id] = await opts.client.getOrder(o.id);
280
+ } catch {
281
+ /* detail unavailable — row still renders from its summary */
282
+ }
283
+ }),
284
+ );
285
+ return {
286
+ status: orders.length ? "loaded" : "empty",
287
+ vm: mapOrdersToViewModel(opts.targetTenant, orders, details),
288
+ page,
289
+ };
290
+ } catch {
291
+ return { status: "error", vm: emptyOrdersViewModel(opts.targetTenant), page: null };
292
+ }
293
+ }
294
+
295
+ /** Back-compat convenience: just the view model (see getOrdersViewModelResult). */
296
+ export async function getOrdersViewModel(
297
+ opts: GetOrdersViewModelOptions,
298
+ ): Promise<OrdersViewModel> {
299
+ return (await getOrdersViewModelResult(opts)).vm;
300
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * AI Workflows view model (baseline §AI Workflows) — the 8 named recurring
3
+ * jobs as a control-plane record. No execution: this unit only defines the
4
+ * read-oriented record shape (ai-workflows-scaffold/operational build the tab
5
+ * + execution on top of it). `ApprovalMode`/`AuditEvent`/`AiRecommendation`
6
+ * now come from the ai-evidence-substrate (`lib/admin/ai`), which formalizes
7
+ * the approval/evidence models this view model originally stubbed.
8
+ */
9
+
10
+ import type { ApprovalMode } from "../ai/approvalMode";
11
+ import type { AiRecommendation } from "../ai/recommendation";
12
+ import type { AuditEvent } from "../ai/audit";
13
+
14
+ export type { ApprovalMode };
15
+
16
+ export type WorkflowStatus = "scheduled" | "running" | "needs_review" | "paused" | "planned";
17
+
18
+ export const AI_WORKFLOW_NAMES = [
19
+ "Performance Regression Guard",
20
+ "Stockout Forecaster",
21
+ "Price Sensitivity Planner",
22
+ "Chargeback Defense Watcher",
23
+ "AI Channel Readiness Audit",
24
+ "Fulfillment Exception Analyst",
25
+ "Compliance Drift Monitor",
26
+ "Growth Opportunity Digest",
27
+ ] as const;
28
+
29
+ export type AiWorkflowName = (typeof AI_WORKFLOW_NAMES)[number];
30
+
31
+ /**
32
+ * The 3 workflows that run end-to-end against mock source data. They are
33
+ * recommendation flows a human must act on — never an auto-action — so their
34
+ * `approvalMode` is always `draft_only` or `approval_required`, never
35
+ * `guarded_automation` (see `ai/approvalMode.ts` + `ai/operationalRuns.ts`).
36
+ * Defined here (the leaf view-model module) so both the mock service and the
37
+ * operational runner can import it without a dependency cycle.
38
+ */
39
+ export const OPERATIONAL_WORKFLOW_KEYS = [
40
+ "stockout-forecaster",
41
+ "chargeback-defense-watcher",
42
+ "ai-channel-readiness-audit",
43
+ ] as const;
44
+
45
+ export type OperationalWorkflowKey = (typeof OPERATIONAL_WORKFLOW_KEYS)[number];
46
+
47
+ export interface WorkflowRecord {
48
+ key: string;
49
+ name: AiWorkflowName;
50
+ cadence: string;
51
+ triggerType: string;
52
+ owner: string;
53
+ status: WorkflowStatus;
54
+ approvalMode: ApprovalMode;
55
+ lastRunAt: string | null;
56
+ nextRunAt: string | null;
57
+ sourceData: string[];
58
+ evidenceLinks: string[];
59
+ /** Recommendation-shaped: carries its own evidence/confidence/owner/approvalMode/auditTrail (baseline: "Evidence, confidence, owner, approval mode, and audit history attached to recommendations"). */
60
+ suggestedAction: AiRecommendation | null;
61
+ auditTrail: AuditEvent[];
62
+ }
63
+
64
+ export interface AiWorkflowsViewModel {
65
+ targetTenant: string;
66
+ workflows: WorkflowRecord[];
67
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Customers view model (baseline §Customers) — directory + detail, and the
3
+ * `Valuable But Blocked` first-class reach-out/unblock queue.
4
+ */
5
+
6
+ export type VerificationState = "verified" | "pending" | "blocked" | "not_required";
7
+
8
+ export type CustomerSegment = "new" | "returning" | "vip";
9
+
10
+ export interface CustomerRow {
11
+ id: string;
12
+ name: string;
13
+ email: string;
14
+ orderCount: number;
15
+ lifetimeValue: number;
16
+ lastOrderAt: string | null;
17
+ segment: CustomerSegment;
18
+ supportFlags: string[];
19
+ verificationState: VerificationState;
20
+ }
21
+
22
+ export type BlockedReason = "verification" | "destination" | "address" | "support";
23
+
24
+ export interface CustomerBlockedQueueRow {
25
+ customerId: string;
26
+ name: string;
27
+ recoverableValue: number;
28
+ blocker: BlockedReason;
29
+ blockerDetail: string;
30
+ action: string;
31
+ }
32
+
33
+ export interface CustomerOrderRef {
34
+ orderId: string;
35
+ placedAt: string;
36
+ total: number;
37
+ }
38
+
39
+ export interface CustomerActivityEvent {
40
+ title: string;
41
+ detail: string;
42
+ at: string;
43
+ }
44
+
45
+ export interface CustomerDetail extends CustomerRow {
46
+ orders: CustomerOrderRef[];
47
+ activity: CustomerActivityEvent[];
48
+ notes: string[];
49
+ }
50
+
51
+ export interface CustomersViewModel {
52
+ targetTenant: string;
53
+ directory: CustomerRow[];
54
+ detailsById: Record<string, CustomerDetail>;
55
+ valuableButBlocked: CustomerBlockedQueueRow[];
56
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Fulfillment view model (baseline §Fulfillment) — operational shipment
3
+ * queues, label locks, SLA age, and carrier/destination readiness. AI here is
4
+ * intentionally narrow (`Fulfillment Assist` drafts only — see aiWorkflows.ts
5
+ * / ai-evidence-substrate for the approval-gated substrate this later wires
6
+ * into).
7
+ */
8
+
9
+ export type FulfillmentQueueKey =
10
+ | "ready_to_ship"
11
+ | "holds"
12
+ | "picked"
13
+ | "packed"
14
+ | "label_needed"
15
+ | "shipped"
16
+ | "exceptions";
17
+
18
+ export interface FulfillmentQueueSummary {
19
+ key: FulfillmentQueueKey;
20
+ label: string;
21
+ count: number;
22
+ }
23
+
24
+ export interface ShipmentRow {
25
+ orderId: string;
26
+ queue: FulfillmentQueueKey;
27
+ carrier: string;
28
+ destination: string;
29
+ slaAgeHours: number;
30
+ labelLocked: boolean;
31
+ labelLockReason: string | null;
32
+ carrierReady: boolean;
33
+ destinationReady: boolean;
34
+ }
35
+
36
+ export type FulfillmentAssistKind = "blocker_explanation" | "customer_update_draft" | "evidence_prep";
37
+
38
+ export interface FulfillmentAssistDraft {
39
+ orderId: string;
40
+ kind: FulfillmentAssistKind;
41
+ summary: string;
42
+ }
43
+
44
+ export interface FulfillmentViewModel {
45
+ targetTenant: string;
46
+ queues: FulfillmentQueueSummary[];
47
+ shipments: ShipmentRow[];
48
+ assistDrafts: FulfillmentAssistDraft[];
49
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Orders view model (baseline §Orders) — exception-first queue, ready-to-ship
3
+ * queue, dense table, selected-order detail, and the evidence timeline behind
4
+ * `Order Assist`. Every field here is read-oriented; write paths do not exist
5
+ * yet (data-view-contracts is deliberately read-only, per the baseline's
6
+ * suggested buildout sequence step 3).
7
+ */
8
+
9
+ export type OrderPaymentState = "authorized" | "captured" | "refunded";
10
+
11
+ export type OrderShipState = "ready_to_ship" | "hold_review_required" | "unfulfilled" | "completed";
12
+
13
+ /** The exact merchant-facing language the shell contract requires. */
14
+ export const ORDER_SHIP_STATE_LABEL: Record<OrderShipState, string> = {
15
+ ready_to_ship: "Ready to Ship",
16
+ hold_review_required: "Hold: Review Required",
17
+ unfulfilled: "Unfulfilled",
18
+ completed: "Completed",
19
+ };
20
+
21
+ export interface OrderReadinessCheck {
22
+ key: string;
23
+ label: string;
24
+ value: string;
25
+ detail: string;
26
+ tone: "green" | "amber" | "blue" | "red";
27
+ action: string;
28
+ blocking: boolean;
29
+ }
30
+
31
+ export interface OrderEvidenceEvent {
32
+ title: string;
33
+ detail: string;
34
+ at: string;
35
+ }
36
+
37
+ export interface OrderLineItem {
38
+ sku: string;
39
+ name: string;
40
+ quantity: number;
41
+ unitPrice: number;
42
+ }
43
+
44
+ export interface OrderRow {
45
+ id: string;
46
+ placedAt: string;
47
+ customerId: string;
48
+ customerName: string;
49
+ customerNote: string;
50
+ itemsSummary: string;
51
+ productId: string;
52
+ total: number;
53
+ payment: OrderPaymentState;
54
+ shipState: OrderShipState;
55
+ blocker: string | null;
56
+ nextStep: string;
57
+ riskLevel: "low" | "medium" | "high";
58
+ }
59
+
60
+ export interface OrderDetail extends OrderRow {
61
+ readinessChecks: OrderReadinessCheck[];
62
+ evidenceTimeline: OrderEvidenceEvent[];
63
+ lineItems: OrderLineItem[];
64
+ shipping: number;
65
+ estimatedTax: number;
66
+ taxesAndFees: number;
67
+ }
68
+
69
+ export type OrderQueueKey = "needs_attention" | "ready_to_ship" | "unfulfilled" | "all" | "completed";
70
+
71
+ export interface OrderQueueSummary {
72
+ key: OrderQueueKey;
73
+ label: string;
74
+ count: number;
75
+ detail: string;
76
+ }
77
+
78
+ export interface OrdersTodaySummary {
79
+ grossSales: number;
80
+ paidOrders: number;
81
+ averageOrder: number;
82
+ refunds: number;
83
+ oldestOpenAt: string | null;
84
+ }
85
+
86
+ export interface OrdersViewModel {
87
+ targetTenant: string;
88
+ queues: OrderQueueSummary[];
89
+ rows: OrderRow[];
90
+ detailsById: Record<string, OrderDetail>;
91
+ today: OrdersTodaySummary;
92
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Products view model (baseline §Products) — catalog readiness and
3
+ * sellability, including the explicit excise-metadata readiness checks called
4
+ * out in the baseline doc (tax class, exempt flag, nicotine flag, wholesale
5
+ * basis, retail price, pack quantity, volume/weight, category).
6
+ */
7
+
8
+ export type ExciseCheckKey =
9
+ | "tax_class"
10
+ | "exempt_flag"
11
+ | "nicotine_flag"
12
+ | "wholesale_basis"
13
+ | "retail_price"
14
+ | "pack_quantity"
15
+ | "volume_or_weight"
16
+ | "category";
17
+
18
+ export interface ExciseReadinessCheck {
19
+ key: ExciseCheckKey;
20
+ label: string;
21
+ present: boolean;
22
+ value: string | null;
23
+ }
24
+
25
+ export interface ProductVariant {
26
+ sku: string;
27
+ name: string;
28
+ inventory: number;
29
+ price: number;
30
+ }
31
+
32
+ export interface ProductMediaAsset {
33
+ url: string;
34
+ alt: string;
35
+ kind: "image" | "video";
36
+ }
37
+
38
+ export interface ProductActivityEvent {
39
+ title: string;
40
+ detail: string;
41
+ at: string;
42
+ }
43
+
44
+ export interface ProductRow {
45
+ id: string;
46
+ name: string;
47
+ productType: string;
48
+ category: string;
49
+ status: "active" | "draft" | "archived";
50
+ readinessScore: number;
51
+ sellable: boolean;
52
+ aiChannelReady: boolean;
53
+ missingExciseCount: number;
54
+ }
55
+
56
+ export interface ProductDetail extends ProductRow {
57
+ variants: ProductVariant[];
58
+ media: ProductMediaAsset[];
59
+ exciseChecks: ExciseReadinessCheck[];
60
+ activity: ProductActivityEvent[];
61
+ complianceNotes: string[];
62
+ }
63
+
64
+ export interface ProductSavedView {
65
+ key: string;
66
+ label: string;
67
+ }
68
+
69
+ export interface ProductsViewModel {
70
+ targetTenant: string;
71
+ savedViews: ProductSavedView[];
72
+ rows: ProductRow[];
73
+ detailsById: Record<string, ProductDetail>;
74
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Reporting view model (baseline §Reporting) — read-only category summaries
3
+ * that drill back into the owning tab's working rows, plus the `Analysis
4
+ * Brief`. Reporting depends on the other domains for trustworthy data, so
5
+ * this stays read-only per the suggested buildout sequence.
6
+ */
7
+
8
+ export type ReportingCategory = "sales" | "products" | "customers" | "fulfillment" | "compliance" | "tax" | "risk";
9
+
10
+ export interface ReportingSummary {
11
+ category: ReportingCategory;
12
+ label: string;
13
+ headline: string;
14
+ value: string;
15
+ trend: "up" | "down" | "flat";
16
+ /** Deep link back to the owning tab's working surface, e.g. "#orders?holdOnly=1". */
17
+ drilldownHref: string;
18
+ }
19
+
20
+ export interface AnalysisBriefItem {
21
+ title: string;
22
+ detail: string;
23
+ drilldownHref: string;
24
+ evidenceRef: string;
25
+ }
26
+
27
+ export interface ReportingViewModel {
28
+ targetTenant: string;
29
+ summaries: ReportingSummary[];
30
+ analysisBrief: {
31
+ drivers: AnalysisBriefItem[];
32
+ watchItems: AnalysisBriefItem[];
33
+ };
34
+ }