@tokenoftrust/storefront-runner 2.0.2 → 2.0.4

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 (254) hide show
  1. package/apps/storefront/.astro/content.d.ts +159 -0
  2. package/apps/storefront/.astro/integrations/_astrojs_cloudflare/cloudflare.d.ts +1 -0
  3. package/apps/storefront/.astro/types.d.ts +3 -0
  4. package/apps/storefront/node_modules/.bin/acorn +43 -0
  5. package/apps/storefront/node_modules/.bin/astro +43 -0
  6. package/apps/storefront/node_modules/.bin/astro-check +43 -0
  7. package/apps/storefront/node_modules/.bin/browserslist +43 -0
  8. package/apps/storefront/node_modules/.bin/cf-wrangler +43 -0
  9. package/apps/storefront/node_modules/.bin/depcruise +43 -0
  10. package/apps/storefront/node_modules/.bin/depcruise-baseline +43 -0
  11. package/apps/storefront/node_modules/.bin/depcruise-fmt +43 -0
  12. package/apps/storefront/node_modules/.bin/depcruise-wrap-stream-in-html +43 -0
  13. package/apps/storefront/node_modules/.bin/dependency-cruise +43 -0
  14. package/apps/storefront/node_modules/.bin/dependency-cruiser +43 -0
  15. package/apps/storefront/node_modules/.bin/drizzle-kit +43 -0
  16. package/apps/storefront/node_modules/.bin/esbuild +43 -0
  17. package/apps/storefront/node_modules/.bin/jiti +43 -0
  18. package/apps/storefront/node_modules/.bin/playwright +43 -0
  19. package/apps/storefront/node_modules/.bin/playwright-core +43 -0
  20. package/apps/storefront/node_modules/.bin/prettier +43 -0
  21. package/apps/storefront/node_modules/.bin/rollup +43 -0
  22. package/apps/storefront/node_modules/.bin/tsc +43 -0
  23. package/apps/storefront/node_modules/.bin/tsserver +43 -0
  24. package/apps/storefront/node_modules/.bin/tsx +43 -0
  25. package/apps/storefront/node_modules/.bin/vite +43 -0
  26. package/apps/storefront/node_modules/.bin/vitest +43 -0
  27. package/apps/storefront/node_modules/.bin/workerd +43 -0
  28. package/apps/storefront/node_modules/.bin/wrangler +43 -0
  29. package/apps/storefront/node_modules/.bin/wrangler2 +43 -0
  30. package/apps/storefront/node_modules/.bin/yaml +43 -0
  31. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js +58 -0
  32. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js.map +1 -0
  33. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js +741 -0
  34. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js.map +1 -0
  35. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js +2549 -0
  36. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js.map +1 -0
  37. package/apps/storefront/node_modules/.vite/deps/_metadata.json +150 -0
  38. package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js +434 -0
  39. package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js.map +1 -0
  40. package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  41. package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  42. package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
  43. package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  44. package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  45. package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  46. package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  47. package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  48. package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js +1535 -0
  49. package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js.map +1 -0
  50. package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js +48 -0
  51. package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js.map +1 -0
  52. package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js +42 -0
  53. package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js.map +1 -0
  54. package/apps/storefront/node_modules/.vite/deps/package.json +3 -0
  55. package/apps/storefront/node_modules/.vite/deps/preact.js +310 -0
  56. package/apps/storefront/node_modules/.vite/deps/preact.js.map +1 -0
  57. package/apps/storefront/node_modules/.vite/deps/preact_compat.js +420 -0
  58. package/apps/storefront/node_modules/.vite/deps/preact_compat.js.map +1 -0
  59. package/apps/storefront/node_modules/.vite/deps/preact_compat_jsx-runtime.js +4 -0
  60. package/apps/storefront/node_modules/.vite/deps/preact_debug.js +236 -0
  61. package/apps/storefront/node_modules/.vite/deps/preact_debug.js.map +1 -0
  62. package/apps/storefront/node_modules/.vite/deps/preact_devtools.js +12 -0
  63. package/apps/storefront/node_modules/.vite/deps/preact_devtools.js.map +1 -0
  64. package/apps/storefront/node_modules/.vite/deps/preact_hooks.js +192 -0
  65. package/apps/storefront/node_modules/.vite/deps/preact_hooks.js.map +1 -0
  66. package/apps/storefront/node_modules/.vite/deps/preact_jsx-dev-runtime.js +3 -0
  67. package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js +92 -0
  68. package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js.map +1 -0
  69. package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js +59 -0
  70. package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js.map +1 -0
  71. package/apps/storefront/node_modules/.vite/deps/rolldown-runtime-BPOCksWG.js +24 -0
  72. package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js +189 -0
  73. package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js.map +1 -0
  74. package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js +13 -0
  75. package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js.map +1 -0
  76. package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js +458 -0
  77. package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js.map +1 -0
  78. package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js +1016 -0
  79. package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js.map +1 -0
  80. package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js +32 -0
  81. package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js.map +1 -0
  82. package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js +113 -0
  83. package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js.map +1 -0
  84. package/apps/storefront/node_modules/.vite/deps_ssr/_metadata.json +8 -0
  85. package/apps/storefront/node_modules/.vite/deps_ssr/package.json +3 -0
  86. package/apps/storefront/src/layouts/Layout.astro +1 -1
  87. package/apps/storefront/src/lib/apps/registryAccess.ts +45 -0
  88. package/apps/storefront/src/lib/auth/adminEntry.ts +4 -4
  89. package/apps/storefront/src/lib/shared/capability.ts +68 -0
  90. package/apps/storefront/src/lib/shared/developerInvites.ts +1250 -0
  91. package/apps/storefront/src/lib/shared/invitePreflight.ts +123 -0
  92. package/apps/storefront/src/lib/webhooks/deliveryEngine.ts +38 -4
  93. package/apps/storefront/src/lib/webhooks/emit.ts +1 -1
  94. package/apps/storefront/src/lib/webhooks/getDispatcher.ts +1 -1
  95. package/apps/storefront/src/pages/api/auth/magic-exchange.ts +1 -1
  96. package/apps/storefront/src/pages/index.astro +1 -1
  97. package/apps/storefront/src/pages/products/[handle].astro +1 -1
  98. package/package.json +1 -1
  99. package/packages/public-runtime/node_modules/.bin/tsc +43 -0
  100. package/packages/public-runtime/node_modules/.bin/tsserver +43 -0
  101. package/packages/public-runtime/node_modules/.bin/vitest +43 -0
  102. package/pnpm-workspace.yaml +3 -0
  103. package/scripts/lib/tenant-dirs.mjs +80 -0
  104. package/scripts/tenant/lint-embeds.mjs +180 -0
  105. package/scripts/tenant/materialize-tenant-content.mjs +533 -0
  106. package/scripts/tenant/validate.mjs +837 -0
  107. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +0 -68
  108. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +0 -111
  109. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +0 -2
  110. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +0 -247
  111. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +0 -73
  112. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +0 -79
  113. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +0 -10982
  114. package/apps/storefront/scripts/import-v84-through-ws2.ts +0 -38
  115. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +0 -2
  116. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +0 -4
  117. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +0 -185
  118. package/apps/storefront/scripts/prove-clone-isolation.ts +0 -506
  119. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +0 -148
  120. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +0 -35
  121. package/apps/storefront/scripts/replay-v84-clean-clone.ts +0 -214
  122. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +0 -18
  123. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +0 -18
  124. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +0 -18
  125. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +0 -17
  126. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +0 -15
  127. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +0 -21
  128. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +0 -42
  129. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +0 -145
  130. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +0 -18
  131. package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +0 -531
  132. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +0 -878
  133. package/apps/storefront/src/components/admin/AdminBlogTab.astro +0 -1435
  134. package/apps/storefront/src/components/admin/AdminCheckoutSettingsPanel.astro +0 -226
  135. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +0 -593
  136. package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +0 -465
  137. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +0 -105
  138. package/apps/storefront/src/components/admin/AdminProductsTab.astro +0 -901
  139. package/apps/storefront/src/components/admin/AdminPublishTab.astro +0 -2983
  140. package/apps/storefront/src/components/admin/AdminReportingTab.astro +0 -435
  141. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +0 -782
  142. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +0 -611
  143. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +0 -1223
  144. package/apps/storefront/src/lib/apps/adminService.ts +0 -116
  145. package/apps/storefront/src/lib/apps/adminSession.ts +0 -214
  146. package/apps/storefront/src/lib/apps/apiAuth.ts +0 -91
  147. package/apps/storefront/src/lib/apps/apiRoute.ts +0 -35
  148. package/apps/storefront/src/lib/apps/catalogMapper.ts +0 -39
  149. package/apps/storefront/src/lib/apps/consentConfigStore.ts +0 -228
  150. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +0 -447
  151. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +0 -367
  152. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +0 -315
  153. package/apps/storefront/src/lib/apps/healthAggregate.ts +0 -66
  154. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +0 -614
  155. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +0 -125
  156. package/apps/storefront/src/lib/apps/integrationReadModel.ts +0 -563
  157. package/apps/storefront/src/lib/apps/integrationsTab.ts +0 -547
  158. package/apps/storefront/src/lib/apps/integrationsView.ts +0 -106
  159. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +0 -599
  160. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +0 -170
  161. package/apps/storefront/src/lib/apps/tokenIssuer.ts +0 -121
  162. package/apps/storefront/src/lib/apps/tokenVerifier.ts +0 -148
  163. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +0 -16
  164. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +0 -105
  165. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +0 -177
  166. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +0 -251
  167. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +0 -241
  168. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +0 -211
  169. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +0 -409
  170. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +0 -368
  171. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +0 -188
  172. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +0 -291
  173. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +0 -95
  174. package/apps/storefront/src/lib/content-edit/applyDiff.ts +0 -91
  175. package/apps/storefront/src/lib/content-edit/client.ts +0 -286
  176. package/apps/storefront/src/lib/content-edit/index.ts +0 -14
  177. package/apps/storefront/src/lib/content-edit/route.ts +0 -25
  178. package/apps/storefront/src/lib/content-edit/types.ts +0 -19
  179. package/apps/storefront/src/lib/dev/cockpitStore.ts +0 -57
  180. package/apps/storefront/src/lib/publish/adminPublishTab.ts +0 -4625
  181. package/apps/storefront/src/lib/publish/apex-readiness.ts +0 -360
  182. package/apps/storefront/src/lib/publish/buildStream.ts +0 -269
  183. package/apps/storefront/src/lib/publish/collapsePersistence.ts +0 -72
  184. package/apps/storefront/src/lib/publish/dispatchHealth.ts +0 -267
  185. package/apps/storefront/src/lib/publish/domainState.ts +0 -360
  186. package/apps/storefront/src/lib/publish/lifecycleState.ts +0 -369
  187. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +0 -29
  188. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +0 -78
  189. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +0 -62
  190. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +0 -124
  191. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +0 -75
  192. package/apps/storefront/src/lib/publish/panelPolling.ts +0 -78
  193. package/apps/storefront/src/lib/publish/publish-guard.ts +0 -88
  194. package/apps/storefront/src/lib/publish/publishFormat.ts +0 -165
  195. package/apps/storefront/src/lib/publish/publishHero.ts +0 -177
  196. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +0 -66
  197. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +0 -141
  198. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +0 -41
  199. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +0 -47
  200. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +0 -42
  201. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +0 -59
  202. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +0 -440
  203. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +0 -161
  204. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +0 -325
  205. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +0 -241
  206. package/apps/storefront/src/lib/publish/shipWorkspace.ts +0 -439
  207. package/apps/storefront/src/lib/publish/sseRoute.ts +0 -119
  208. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +0 -215
  209. package/apps/storefront/src/lib/publish/streamLogConsole.ts +0 -142
  210. package/apps/storefront/src/lib/publish/versionNumber.ts +0 -52
  211. package/apps/storefront/src/pages/admin/ops-timeline.astro +0 -420
  212. package/apps/storefront/src/pages/admin/review-release.astro +0 -265
  213. package/apps/storefront/src/pages/admin/select.astro +0 -96
  214. package/apps/storefront/src/pages/admin.astro +0 -570
  215. package/apps/storefront/src/pages/api/apps/admin/credentials/rotate.ts +0 -86
  216. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +0 -174
  217. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +0 -96
  218. package/apps/storefront/src/pages/api/apps/admin/health.ts +0 -44
  219. package/apps/storefront/src/pages/api/apps/admin/install.ts +0 -125
  220. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +0 -118
  221. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +0 -89
  222. package/apps/storefront/src/pages/api/apps/admin/list.ts +0 -26
  223. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +0 -85
  224. package/apps/storefront/src/pages/api/apps/admin/resume.ts +0 -79
  225. package/apps/storefront/src/pages/api/apps/admin/suspend.ts +0 -80
  226. package/apps/storefront/src/pages/api/apps/admin/uninstall.ts +0 -98
  227. package/apps/storefront/src/pages/api/apps/admin/update.ts +0 -126
  228. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries/[deliveryId]/replay.ts +0 -70
  229. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries.ts +0 -51
  230. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +0 -255
  231. package/apps/storefront/src/pages/api/apps/oauth/token.ts +0 -87
  232. package/apps/storefront/src/pages/api/apps/v1/attribution.ts +0 -162
  233. package/apps/storefront/src/pages/api/apps/v1/catalog/products/[handle].ts +0 -39
  234. package/apps/storefront/src/pages/api/apps/v1/catalog/products.ts +0 -49
  235. package/apps/storefront/src/pages/api/apps/v1/health.ts +0 -32
  236. package/apps/storefront/src/pages/api/apps/v1/inventory.ts +0 -59
  237. package/apps/storefront/src/pages/api/apps/v1/orders/[id].ts +0 -48
  238. package/apps/storefront/src/pages/api/apps/v1/orders.ts +0 -73
  239. package/apps/storefront/src/pages/api/apps/v1/reports.ts +0 -21
  240. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries/[deliveryId]/replay.ts +0 -74
  241. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries.ts +0 -47
  242. package/apps/storefront/src/pages/api/cache-purge.ts +0 -55
  243. package/apps/storefront/src/pages/api/compliance/tamper.ts +0 -88
  244. package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +0 -164
  245. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +0 -154
  246. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +0 -120
  247. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +0 -108
  248. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +0 -70
  249. package/apps/storefront/src/pages/api/test/dev-session.ts +0 -133
  250. package/apps/storefront/src/pages/cockpit.astro +0 -1548
  251. package/apps/storefront/src/pages/dashboard/[appDomain]/apps/index.astro +0 -119
  252. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +0 -105
  253. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +0 -177
  254. package/apps/storefront/src/pages/dashboard/index.astro +0 -160
@@ -1,901 +0,0 @@
1
- ---
2
- /**
3
- * Products tab (baseline §Products — catalog readiness and sellability),
4
- * wired to the live products view model (lib/admin/services/mock, from
5
- * data-view-contracts) rather than static prototype data.
6
- */
7
- import { getProductsViewModel } from "@/lib/admin/services/mock";
8
- import type { ProductRow } from "@/lib/admin/viewmodels/products";
9
- import type { AdminTenantEnvelope } from "@/lib/admin/envelope";
10
-
11
- interface Props {
12
- envelope: AdminTenantEnvelope;
13
- }
14
-
15
- const { envelope } = Astro.props;
16
- const cspNonce = Astro.locals.cspNonce;
17
-
18
- const vm = getProductsViewModel(envelope);
19
-
20
- const STATUS_TONE: Record<ProductRow["status"], string> = {
21
- active: "green",
22
- draft: "amber",
23
- archived: "",
24
- };
25
-
26
- function savedViewKeysFor(row: ProductRow): string[] {
27
- const keys = ["all"];
28
- if (!row.sellable) keys.push("not_sellable");
29
- if (row.aiChannelReady) keys.push("ai_channel_ready");
30
- return keys;
31
- }
32
-
33
- const totalVariants = vm.rows.reduce((sum, row) => sum + (vm.detailsById[row.id]?.variants.length ?? 0), 0);
34
- const needsAttentionCount = vm.rows.filter((r) => r.missingExciseCount > 0).length;
35
- const aiReadyCount = vm.rows.filter((r) => r.aiChannelReady).length;
36
- const avgReadiness = vm.rows.length
37
- ? Math.round(vm.rows.reduce((sum, r) => sum + r.readinessScore, 0) / vm.rows.length)
38
- : 0;
39
- const totalExciseGaps = vm.rows.reduce((sum, r) => sum + r.missingExciseCount, 0);
40
-
41
- const defaultView = vm.savedViews.find((v) => v.key === "not_sellable") && needsAttentionCount > 0 ? "not_sellable" : "all";
42
- const defaultRow = vm.rows.find((r) => !r.sellable) ?? vm.rows[0] ?? null;
43
-
44
- ---
45
-
46
- <section id="products" class="band products-tab" aria-labelledby="admin-tab-products products-title" data-admin-panel data-agent-scope="products" role="tabpanel" data-products-target-tenant={vm.targetTenant}>
47
- <div class="section-title">
48
- <div>
49
- <h2 id="products-title">Products</h2>
50
- <p>Catalog, variants, pricing, inventory, media, and sellability.</p>
51
- </div>
52
- <div class="actions">
53
- <button class="btn" type="button"><svg><use href="#i-export"></use></svg>Export</button>
54
- <button class="btn" type="button"><svg><use href="#i-refresh"></use></svg>Import</button>
55
- <button class="btn primary" type="button"><svg><use href="#i-box"></use></svg>Add product</button>
56
- </div>
57
- </div>
58
-
59
- <div class="grid cols-4">
60
- <div class="metric"><span>Active products</span><strong>{vm.rows.length}</strong><small>In this catalog</small></div>
61
- <div class="metric"><span>Variants</span><strong>{totalVariants}</strong><small>Across all products</small></div>
62
- <div class="metric"><span>Needs attention</span><strong>{needsAttentionCount}</strong><small>Missing excise metadata</small></div>
63
- <div class="metric"><span>AI channel ready</span><strong>{aiReadyCount}</strong><small>Of {vm.rows.length} products</small></div>
64
- </div>
65
-
66
- <div class="panel product-intelligence-panel">
67
- <div class="panel-head product-panel-head">
68
- <h3><svg><use href="#i-ask"></use></svg>Catalog Intelligence</h3>
69
- <span class:list={["chip", totalExciseGaps > 0 ? "amber" : "green"]}>{totalExciseGaps} excise gap{totalExciseGaps === 1 ? "" : "s"}</span>
70
- </div>
71
- <div class="panel-body grid">
72
- <div class="product-signal-grid">
73
- <div class="product-signal">
74
- <span class:list={["chip", avgReadiness >= 90 ? "teal" : "amber"]}>Readiness score</span>
75
- <strong>{avgReadiness}%</strong>
76
- <p>Catalog quality, sellability, and excise metadata across all products.</p>
77
- </div>
78
- <div class="product-signal">
79
- <span class:list={["chip", totalExciseGaps > 0 ? "amber" : "green"]}>Excise metadata gaps</span>
80
- <strong>{totalExciseGaps} field{totalExciseGaps === 1 ? "" : "s"}</strong>
81
- <p>Missing tax class, wholesale basis, or volume/weight needed for regulated sale readiness.</p>
82
- </div>
83
- <div class="product-signal">
84
- <span class="chip violet">AI channel readiness</span>
85
- <strong>{aiReadyCount} of {vm.rows.length}</strong>
86
- <p>Products with complete, agentic-commerce-ready listing data.</p>
87
- </div>
88
- </div>
89
- <div class="actions">
90
- <button class="btn primary" type="button">Review excise gaps</button>
91
- <button class="btn" type="button">Draft catalog fixes</button>
92
- <button class="btn" type="button">Schedule readiness audit</button>
93
- </div>
94
- </div>
95
- </div>
96
-
97
- <div class="product-workspace">
98
- <div class="panel product-list-panel">
99
- <div class="panel-head product-panel-head">
100
- <h3><svg><use href="#i-box"></use></svg>Catalog</h3>
101
- <span class="chip teal">{vm.rows.length} products</span>
102
- </div>
103
- <div class="panel-body grid">
104
- <div class="tabs" role="tablist" aria-label="Product saved views" data-products-view-list>
105
- {vm.savedViews.map((view) => (
106
- <button
107
- type="button"
108
- class:list={["tab", { active: view.key === defaultView }]}
109
- data-products-view-button
110
- data-view-key={view.key}
111
- >
112
- {view.label}
113
- </button>
114
- ))}
115
- </div>
116
-
117
- <div class="product-toolbar" aria-label="Product controls">
118
- <label class="product-search">
119
- <span>Search</span>
120
- <input type="search" placeholder="Search products, SKUs, category" aria-label="Search products" data-products-search />
121
- </label>
122
- <div class="product-filters" aria-label="Product filters">
123
- <label>
124
- <span>Status</span>
125
- <select aria-label="Filter by status" data-products-status-select>
126
- <option value="">Any status</option>
127
- <option value="active">Active</option>
128
- <option value="draft">Draft</option>
129
- <option value="archived">Archived</option>
130
- </select>
131
- </label>
132
- <label>
133
- <span>Sellability</span>
134
- <select aria-label="Filter by sellability" data-products-sellability-select>
135
- <option value="">Any</option>
136
- <option value="sellable">Sellable</option>
137
- <option value="blocked">Not sellable</option>
138
- </select>
139
- </label>
140
- </div>
141
- </div>
142
-
143
- <div class="table-wrap products-table-wrap">
144
- <table class="products-table">
145
- <thead>
146
- <tr>
147
- <th>Product</th>
148
- <th>Type</th>
149
- <th>Status</th>
150
- <th>Readiness</th>
151
- <th>Sellability</th>
152
- <th>AI channel</th>
153
- <th class="action">Action</th>
154
- </tr>
155
- </thead>
156
- <tbody data-products-tbody>
157
- {vm.rows.map((row) => (
158
- <tr
159
- class:list={[{ selected: row.id === defaultRow?.id }]}
160
- data-products-row
161
- data-product-id={row.id}
162
- data-view-keys={savedViewKeysFor(row).join(" ")}
163
- data-status={row.status}
164
- data-sellability={row.sellable ? "sellable" : "blocked"}
165
- data-search-haystack={`${row.name} ${row.id} ${row.category} ${row.productType}`.toLowerCase()}
166
- >
167
- <td>
168
- <div class="product-title-cell">
169
- <div>
170
- <strong>{row.name}</strong>
171
- <span class="subtle">{row.category}</span>
172
- </div>
173
- </div>
174
- </td>
175
- <td><strong>{row.productType}</strong></td>
176
- <td><span class:list={["chip", STATUS_TONE[row.status]]}>{row.status[0]!.toUpperCase() + row.status.slice(1)}</span></td>
177
- <td><strong>{row.readinessScore}%</strong>{row.missingExciseCount > 0 && <span class="subtle">{row.missingExciseCount} gap{row.missingExciseCount === 1 ? "" : "s"}</span>}</td>
178
- <td><span class:list={["chip", row.sellable ? "green" : "amber"]}>{row.sellable ? "Sellable" : "Excise gaps"}</span></td>
179
- <td><span class:list={["chip", row.aiChannelReady ? "teal" : ""]}>{row.aiChannelReady ? "Ready" : "Not ready"}</span></td>
180
- <td class="action"><button type="button" class="btn" data-products-select-row={row.id}>Open</button></td>
181
- </tr>
182
- ))}
183
- </tbody>
184
- </table>
185
- <p class="products-empty-state" data-products-empty hidden>No products match the current filters.</p>
186
- </div>
187
- </div>
188
- </div>
189
-
190
- <aside class="panel product-detail-panel" aria-labelledby="product-detail-title" data-products-detail-root>
191
- {vm.rows.map((row) => {
192
- const detail = vm.detailsById[row.id]!;
193
- return (
194
- <div
195
- class="product-detail-record"
196
- data-products-detail-record={row.id}
197
- hidden={row.id !== defaultRow?.id}
198
- >
199
- <div class="panel-head product-panel-head">
200
- <h3 id={row.id === defaultRow?.id ? "product-detail-title" : undefined}><svg><use href="#i-file"></use></svg>{row.name}</h3>
201
- <span class:list={["chip", row.sellable ? "green" : "amber"]}>{row.sellable ? "Sellable" : "Excise gaps"}</span>
202
- </div>
203
- <div class="panel-body grid">
204
- <div class="product-detail-meta">
205
- <div>
206
- <span class="eyebrow">Product type</span>
207
- <strong>{row.productType}</strong>
208
- </div>
209
- <div>
210
- <span class="eyebrow">Category</span>
211
- <strong>{row.category}</strong>
212
- </div>
213
- <div>
214
- <span class="eyebrow">Readiness score</span>
215
- <strong>{row.readinessScore}%</strong>
216
- </div>
217
- </div>
218
-
219
- {!row.sellable && detail.complianceNotes.length > 0 && (
220
- <div class="check-row blocking">
221
- <div><b>Not sellable</b><p>{detail.complianceNotes[0]}</p></div>
222
- <strong>{row.missingExciseCount} missing</strong>
223
- <span class="chip amber">Action</span>
224
- </div>
225
- )}
226
-
227
- <details class="detail-section" open>
228
- <summary class="detail-section-head">
229
- <h4>Variants</h4>
230
- <span class="chip">{detail.variants.length}</span>
231
- </summary>
232
- <div class="mini-table" role="table" aria-label="Variants">
233
- {detail.variants.map((variant) => (
234
- <div class="mini-row" role="row">
235
- <div role="cell"><strong>{variant.name}</strong><span class="subtle">{variant.sku}</span></div>
236
- <div role="cell"><span class="eyebrow">Stock</span><strong>{variant.inventory}</strong></div>
237
- <div role="cell"><span class="eyebrow">Price</span><strong>${variant.price.toFixed(2)}</strong></div>
238
- </div>
239
- ))}
240
- </div>
241
- </details>
242
-
243
- <details class="detail-section">
244
- <summary class="detail-section-head">
245
- <h4>Media</h4>
246
- <span class="chip">{detail.media.length}</span>
247
- </summary>
248
- <div class="mini-table" role="table" aria-label="Media">
249
- {detail.media.map((asset) => (
250
- <div class="mini-row" role="row">
251
- <div role="cell"><strong>{asset.kind[0]!.toUpperCase() + asset.kind.slice(1)}</strong><span class="subtle">{asset.alt}</span></div>
252
- <div role="cell"><span class="subtle">{asset.url}</span></div>
253
- </div>
254
- ))}
255
- </div>
256
- </details>
257
-
258
- <details class="detail-section" open={!row.sellable}>
259
- <summary class="detail-section-head">
260
- <h4>Excise metadata readiness</h4>
261
- <span class:list={["chip", row.missingExciseCount > 0 ? "amber" : "green"]}>{row.missingExciseCount} missing</span>
262
- </summary>
263
- <div>
264
- {detail.exciseChecks.map((check) => (
265
- <div class:list={["check-row", { blocking: !check.present }]}>
266
- <div><b>{check.label}</b><p>{check.present ? `Set to ${check.value}.` : "Not yet set — required for regulated sale readiness."}</p></div>
267
- <strong>{check.present ? check.value : "Missing"}</strong>
268
- <span class:list={["chip", check.present ? "green" : "amber"]}>{check.present ? "OK" : "Fix"}</span>
269
- </div>
270
- ))}
271
- </div>
272
- </details>
273
-
274
- <details class="detail-section">
275
- <summary class="detail-section-head">
276
- <h4>AI channel readiness</h4>
277
- <span class:list={["chip", row.aiChannelReady ? "teal" : "amber"]}>{row.aiChannelReady ? "Ready" : "Not ready"}</span>
278
- </summary>
279
- <div class="check-row">
280
- <div><b>Agentic commerce surfaces</b><p>{row.aiChannelReady ? "Listing data is complete enough for AI shopping agents to represent this product." : "Complete excise metadata and sellability first — AI channel readiness depends on a clean listing."}</p></div>
281
- <strong>{row.aiChannelReady ? "Ready" : "Blocked"}</strong>
282
- <span class:list={["chip", row.aiChannelReady ? "teal" : "amber"]}>{row.aiChannelReady ? "OK" : "Action"}</span>
283
- </div>
284
- </details>
285
-
286
- <details class="detail-section">
287
- <summary class="detail-section-head">
288
- <h4>Activity</h4>
289
- <span class="chip">{detail.activity.length}</span>
290
- </summary>
291
- <div class="mini-table" role="table" aria-label="Activity">
292
- {detail.activity.map((event) => (
293
- <div class="mini-row" role="row">
294
- <div role="cell"><strong>{event.title}</strong><span class="subtle">{event.detail}</span></div>
295
- <div role="cell"><span class="subtle">{new Date(event.at).toLocaleString([], { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" })}</span></div>
296
- </div>
297
- ))}
298
- </div>
299
- </details>
300
- </div>
301
- </div>
302
- );
303
- })}
304
- </aside>
305
- </div>
306
- </section>
307
-
308
- <script is:inline nonce={cspNonce}>
309
- (() => {
310
- const root = document.querySelector('[data-products-target-tenant]');
311
- if (!root) return;
312
-
313
- const viewButtons = Array.from(root.querySelectorAll("[data-products-view-button]"));
314
- const statusSelect = root.querySelector("[data-products-status-select]");
315
- const sellabilitySelect = root.querySelector("[data-products-sellability-select]");
316
- const searchInput = root.querySelector("[data-products-search]");
317
- const rows = Array.from(root.querySelectorAll("[data-products-row]"));
318
- const emptyState = root.querySelector("[data-products-empty]");
319
- const detailRoot = root.querySelector("[data-products-detail-root]");
320
- const detailRecords = detailRoot ? Array.from(detailRoot.querySelectorAll("[data-products-detail-record]")) : [];
321
-
322
- let activeView = (viewButtons.find((b) => b.classList.contains("active")) || viewButtons[0])?.dataset.viewKey ?? "all";
323
-
324
- const applyFilters = () => {
325
- const status = statusSelect ? statusSelect.value : "";
326
- const sellability = sellabilitySelect ? sellabilitySelect.value : "";
327
- const term = searchInput ? searchInput.value.trim().toLowerCase() : "";
328
- let visibleCount = 0;
329
-
330
- for (const row of rows) {
331
- const viewKeys = (row.dataset.viewKeys ?? "").split(" ");
332
- const matchesView = viewKeys.includes(activeView);
333
- const matchesStatus = !status || row.dataset.status === status;
334
- const matchesSellability = !sellability || row.dataset.sellability === sellability;
335
- const matchesSearch = !term || (row.dataset.searchHaystack ?? "").includes(term);
336
- const visible = matchesView && matchesStatus && matchesSellability && matchesSearch;
337
- row.hidden = !visible;
338
- if (visible) visibleCount += 1;
339
- }
340
-
341
- if (emptyState) emptyState.hidden = visibleCount > 0;
342
- };
343
-
344
- const selectProduct = (productId) => {
345
- for (const row of rows) {
346
- row.classList.toggle("selected", row.dataset.productId === productId);
347
- }
348
- for (const record of detailRecords) {
349
- record.hidden = record.dataset.productsDetailRecord !== productId;
350
- }
351
- };
352
-
353
- viewButtons.forEach((button) => {
354
- button.addEventListener("click", () => {
355
- activeView = button.dataset.viewKey ?? "all";
356
- viewButtons.forEach((b) => b.classList.toggle("active", b === button));
357
- applyFilters();
358
- });
359
- });
360
-
361
- statusSelect?.addEventListener("change", applyFilters);
362
- sellabilitySelect?.addEventListener("change", applyFilters);
363
- searchInput?.addEventListener("input", applyFilters);
364
-
365
- root.querySelectorAll("[data-products-select-row]").forEach((button) => {
366
- button.addEventListener("click", () => {
367
- const productId = button.dataset.productsSelectRow;
368
- if (productId) selectProduct(productId);
369
- });
370
- });
371
-
372
- rows.forEach((row) => {
373
- row.addEventListener("click", (event) => {
374
- if (event.target instanceof Element && event.target.closest("[data-products-select-row]")) return;
375
- const productId = row.dataset.productId;
376
- if (productId) selectProduct(productId);
377
- });
378
- });
379
-
380
- applyFilters();
381
- })();
382
- </script>
383
-
384
- <style>
385
- .products-tab {
386
- --product-split: minmax(0, 1.18fr) minmax(360px, 0.82fr);
387
- }
388
-
389
- .products-tab svg {
390
- width: 18px;
391
- height: 18px;
392
- stroke: currentColor;
393
- stroke-width: 1.9;
394
- stroke-linecap: round;
395
- stroke-linejoin: round;
396
- fill: none;
397
- flex: 0 0 auto;
398
- }
399
-
400
- .products-tab button,
401
- .products-tab input,
402
- .products-tab select {
403
- font: inherit;
404
- }
405
-
406
- .products-tab button {
407
- cursor: pointer;
408
- }
409
-
410
- .products-tab .section-title {
411
- display: flex;
412
- align-items: center;
413
- justify-content: space-between;
414
- gap: 12px;
415
- min-width: 0;
416
- }
417
-
418
- .products-tab .section-title h2 {
419
- margin: 0;
420
- font-size: 16px;
421
- }
422
-
423
- .products-tab .section-title p {
424
- margin: 4px 0 0;
425
- color: var(--admin-muted);
426
- font-size: 13px;
427
- }
428
-
429
- .products-tab .actions {
430
- display: flex;
431
- flex-wrap: wrap;
432
- gap: 8px;
433
- min-width: 0;
434
- }
435
-
436
- .products-tab .btn {
437
- min-height: 38px;
438
- max-width: 100%;
439
- display: inline-flex;
440
- align-items: center;
441
- justify-content: center;
442
- gap: 8px;
443
- padding: 0 13px;
444
- border: 1px solid var(--admin-line-strong);
445
- border-radius: var(--admin-radius-sm);
446
- background: var(--admin-surface);
447
- color: var(--admin-ink);
448
- font-weight: 680;
449
- text-decoration: none;
450
- white-space: nowrap;
451
- }
452
-
453
- .products-tab .btn.primary {
454
- border-color: var(--admin-teal-dark);
455
- background: var(--admin-teal);
456
- color: #ffffff;
457
- }
458
-
459
- .products-tab .grid {
460
- display: grid;
461
- grid-template-columns: minmax(0, 1fr);
462
- gap: 12px;
463
- min-width: 0;
464
- }
465
-
466
- .products-tab .grid.cols-4 {
467
- grid-template-columns: repeat(4, minmax(0, 1fr));
468
- }
469
-
470
- .products-tab .panel {
471
- min-width: 0;
472
- border: 1px solid var(--admin-line);
473
- border-radius: var(--admin-radius);
474
- background: var(--admin-surface);
475
- }
476
-
477
- .products-tab .panel-head {
478
- display: flex;
479
- align-items: center;
480
- justify-content: space-between;
481
- gap: 12px;
482
- min-height: 50px;
483
- padding: 13px 14px;
484
- border-bottom: 1px solid var(--admin-line);
485
- }
486
-
487
- .products-tab .panel-head h3 {
488
- margin: 0;
489
- display: flex;
490
- align-items: center;
491
- gap: 8px;
492
- font-size: 15px;
493
- }
494
-
495
- .products-tab .panel-body {
496
- display: grid;
497
- grid-template-columns: minmax(0, 1fr);
498
- gap: 12px;
499
- padding: 14px;
500
- }
501
-
502
- .product-intelligence-panel {
503
- border-color: #b9d8d4;
504
- background: #fbfefd;
505
- }
506
-
507
- .product-signal-grid {
508
- display: grid;
509
- grid-template-columns: repeat(3, minmax(0, 1fr));
510
- gap: 10px;
511
- }
512
-
513
- .product-signal {
514
- display: grid;
515
- gap: 7px;
516
- min-width: 0;
517
- padding: 12px;
518
- border: 1px solid var(--admin-line);
519
- border-radius: var(--admin-radius-sm);
520
- background: var(--admin-surface);
521
- }
522
-
523
- .product-signal strong {
524
- font-size: 20px;
525
- }
526
-
527
- .product-signal p {
528
- margin: 0;
529
- color: var(--admin-muted);
530
- font-size: 12px;
531
- }
532
-
533
- .products-tab .metric {
534
- min-height: 84px;
535
- padding: 14px;
536
- border: 1px solid var(--admin-line);
537
- border-radius: var(--admin-radius);
538
- background: var(--admin-surface);
539
- }
540
-
541
- .products-tab .metric span,
542
- .products-tab .field label {
543
- display: block;
544
- color: var(--admin-muted);
545
- font-size: 11px;
546
- font-weight: 760;
547
- text-transform: uppercase;
548
- }
549
-
550
- .products-tab .metric strong {
551
- display: block;
552
- margin-top: 9px;
553
- font-size: 24px;
554
- }
555
-
556
- .products-tab .metric small {
557
- display: block;
558
- margin-top: 3px;
559
- color: var(--admin-muted);
560
- font-size: 12px;
561
- }
562
-
563
- .products-tab .tabs {
564
- display: flex;
565
- gap: 6px;
566
- max-width: 100%;
567
- overflow-x: auto;
568
- border-bottom: 1px solid var(--admin-line);
569
- }
570
-
571
- .products-tab .tab {
572
- flex: 0 0 auto;
573
- min-height: 38px;
574
- display: inline-flex;
575
- align-items: center;
576
- padding: 0 12px;
577
- border: 0;
578
- border-bottom: 2px solid transparent;
579
- background: transparent;
580
- color: var(--admin-muted);
581
- font-weight: 760;
582
- text-decoration: none;
583
- }
584
-
585
- .products-tab .tab.active {
586
- border-color: var(--admin-teal);
587
- color: var(--admin-ink);
588
- }
589
-
590
- .products-tab .table-wrap {
591
- min-width: 0;
592
- max-width: 100%;
593
- overflow-x: auto;
594
- border: 1px solid var(--admin-line);
595
- border-radius: var(--admin-radius);
596
- background: var(--admin-surface);
597
- }
598
-
599
- .products-tab table {
600
- width: 100%;
601
- min-width: 860px;
602
- border-collapse: collapse;
603
- font-size: 13px;
604
- }
605
-
606
- .products-tab th {
607
- height: 38px;
608
- padding: 0 12px;
609
- border-bottom: 1px solid var(--admin-line);
610
- background: var(--admin-surface-soft);
611
- color: var(--admin-muted);
612
- font-size: 11px;
613
- font-weight: 760;
614
- text-align: left;
615
- text-transform: uppercase;
616
- white-space: nowrap;
617
- }
618
-
619
- .products-tab td {
620
- height: 46px;
621
- padding: 7px 12px;
622
- border-bottom: 1px solid var(--admin-line);
623
- vertical-align: middle;
624
- }
625
-
626
- .products-tab tbody tr {
627
- cursor: pointer;
628
- }
629
-
630
- .products-tab tr.selected {
631
- background: #eef6f4;
632
- }
633
-
634
- .products-tab tr:last-child td {
635
- border-bottom: 0;
636
- }
637
-
638
- .products-tab .subtle {
639
- display: block;
640
- margin-top: 2px;
641
- color: var(--admin-muted);
642
- font-size: 12px;
643
- }
644
-
645
- .products-tab .action {
646
- text-align: right;
647
- }
648
-
649
- .products-empty-state {
650
- margin: 0;
651
- padding: 24px 14px;
652
- color: var(--admin-muted);
653
- font-size: 13px;
654
- text-align: center;
655
- }
656
-
657
- .products-tab .check-row {
658
- display: grid;
659
- grid-template-columns: minmax(120px, 0.36fr) minmax(0, 1fr) auto;
660
- gap: 10px;
661
- align-items: center;
662
- padding: 10px 0;
663
- border-bottom: 1px solid var(--admin-line);
664
- }
665
-
666
- .products-tab .check-row:last-child {
667
- border-bottom: 0;
668
- }
669
-
670
- .products-tab .check-row.blocking {
671
- margin: 4px -6px;
672
- padding: 10px 6px;
673
- border: 1px solid #efc477;
674
- border-radius: var(--admin-radius-sm);
675
- background: #fff8ec;
676
- }
677
-
678
- .products-tab .check-row b {
679
- font-size: 13px;
680
- }
681
-
682
- .products-tab .check-row p {
683
- margin: 2px 0 0;
684
- color: var(--admin-muted);
685
- font-size: 12px;
686
- }
687
-
688
- .product-workspace {
689
- display: grid;
690
- grid-template-columns: var(--product-split);
691
- gap: 14px;
692
- align-items: start;
693
- min-width: 0;
694
- }
695
-
696
- .product-list-panel,
697
- .product-detail-panel {
698
- min-width: 0;
699
- }
700
-
701
- .product-detail-panel {
702
- position: sticky;
703
- top: 86px;
704
- box-shadow: var(--admin-shadow);
705
- }
706
-
707
- .product-panel-head {
708
- min-width: 0;
709
- }
710
-
711
- .product-panel-head h3 {
712
- min-width: 0;
713
- overflow-wrap: anywhere;
714
- }
715
-
716
- .product-toolbar {
717
- display: grid;
718
- gap: 8px;
719
- align-items: end;
720
- }
721
-
722
- .product-search {
723
- min-width: 0;
724
- }
725
-
726
- .product-filters {
727
- display: grid;
728
- grid-template-columns: repeat(2, minmax(128px, 1fr));
729
- gap: 8px;
730
- min-width: 0;
731
- }
732
-
733
- .product-toolbar label,
734
- .product-filters label {
735
- display: grid;
736
- gap: 5px;
737
- min-width: 0;
738
- }
739
-
740
- .product-toolbar span,
741
- .product-filters span,
742
- .eyebrow {
743
- color: var(--admin-muted);
744
- font-size: 11px;
745
- font-weight: 760;
746
- text-transform: uppercase;
747
- }
748
-
749
- .product-toolbar input,
750
- .product-toolbar select,
751
- .product-filters select {
752
- min-width: 0;
753
- width: 100%;
754
- min-height: 38px;
755
- padding: 0 10px;
756
- overflow: hidden;
757
- border: 1px solid var(--admin-line-strong);
758
- border-radius: var(--admin-radius-sm);
759
- background: var(--admin-surface);
760
- color: var(--admin-ink);
761
- text-overflow: ellipsis;
762
- }
763
-
764
- .products-table {
765
- min-width: 900px;
766
- }
767
-
768
- .products-table td {
769
- height: 58px;
770
- }
771
-
772
- .product-title-cell {
773
- display: flex;
774
- align-items: center;
775
- gap: 8px;
776
- min-width: 0;
777
- }
778
-
779
- .product-detail-meta {
780
- display: grid;
781
- grid-template-columns: repeat(3, minmax(0, 1fr));
782
- gap: 10px;
783
- }
784
-
785
- .product-detail-meta > div {
786
- min-width: 0;
787
- padding-bottom: 10px;
788
- border-bottom: 1px solid var(--admin-line);
789
- }
790
-
791
- .product-detail-meta strong,
792
- .mini-row strong {
793
- display: block;
794
- margin-top: 3px;
795
- overflow-wrap: anywhere;
796
- }
797
-
798
- .detail-section {
799
- display: grid;
800
- gap: 10px;
801
- padding-top: 2px;
802
- }
803
-
804
- .detail-section + .detail-section {
805
- padding-top: 12px;
806
- border-top: 1px solid var(--admin-line);
807
- }
808
-
809
- .detail-section-head {
810
- display: flex;
811
- align-items: center;
812
- justify-content: space-between;
813
- gap: 10px;
814
- cursor: pointer;
815
- list-style: none;
816
- }
817
-
818
- .detail-section-head::-webkit-details-marker {
819
- display: none;
820
- }
821
-
822
- .detail-section-head h4 {
823
- margin: 0;
824
- font-size: 13px;
825
- }
826
-
827
- .mini-table {
828
- display: grid;
829
- margin-top: 10px;
830
- border: 1px solid var(--admin-line);
831
- border-radius: var(--admin-radius);
832
- overflow: hidden;
833
- }
834
-
835
- .mini-row {
836
- display: grid;
837
- grid-template-columns: minmax(130px, 1.25fr) 90px 90px;
838
- gap: 10px;
839
- align-items: center;
840
- min-height: 58px;
841
- padding: 9px 10px;
842
- border-bottom: 1px solid var(--admin-line);
843
- }
844
-
845
- .mini-row:last-child {
846
- border-bottom: 0;
847
- }
848
-
849
- @media (max-width: 1180px) {
850
- .product-workspace {
851
- grid-template-columns: minmax(0, 1fr);
852
- }
853
-
854
- .product-detail-panel {
855
- position: static;
856
- }
857
- }
858
-
859
- @media (max-width: 920px) {
860
- .products-tab .section-title,
861
- .products-tab .panel-head {
862
- align-items: flex-start;
863
- flex-direction: column;
864
- }
865
-
866
- .products-tab .grid.cols-4,
867
- .product-signal-grid {
868
- grid-template-columns: repeat(2, minmax(0, 1fr));
869
- }
870
-
871
- .product-filters {
872
- grid-template-columns: repeat(2, minmax(0, 1fr));
873
- }
874
- }
875
-
876
- @media (max-width: 640px) {
877
- .products-tab .grid.cols-4,
878
- .product-signal-grid {
879
- grid-template-columns: 1fr;
880
- }
881
-
882
- .products-tab .actions {
883
- width: 100%;
884
- }
885
-
886
- .products-tab .actions .btn {
887
- flex: 1 1 150px;
888
- }
889
-
890
- .products-tab .check-row {
891
- grid-template-columns: 1fr;
892
- }
893
-
894
- .product-toolbar,
895
- .product-filters,
896
- .product-detail-meta,
897
- .mini-row {
898
- grid-template-columns: 1fr;
899
- }
900
- }
901
- </style>