@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.
- package/LICENSE +58 -0
- package/README.md +42 -0
- package/apps/storefront/astro.config.mjs +66 -0
- package/apps/storefront/dev-plugins/dev-loop-state.mjs +147 -0
- package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +238 -0
- package/apps/storefront/env.d.ts +162 -0
- package/apps/storefront/migrations/0000_baseline.sql +54 -0
- package/apps/storefront/migrations/0001_products_fts.sql +9 -0
- package/apps/storefront/migrations/README.md +54 -0
- package/apps/storefront/migrations/meta/0000_snapshot.json +347 -0
- package/apps/storefront/migrations/meta/0001_snapshot.json +347 -0
- package/apps/storefront/migrations/meta/_journal.json +20 -0
- package/apps/storefront/package.json +42 -0
- package/apps/storefront/public/brand/northwind-logo-dark.svg +4 -0
- package/apps/storefront/public/brand/northwind-logo-light.svg +4 -0
- package/apps/storefront/public/brand/northwind-og.svg +13 -0
- package/apps/storefront/public/favicon.svg +5 -0
- package/apps/storefront/public/js/dashboard-team.js +147 -0
- package/apps/storefront/public/js/dashboard-vendor-search.js +115 -0
- package/apps/storefront/public/shared/commerce-chrome.css +369 -0
- package/apps/storefront/src/components/Badge.astro +34 -0
- package/apps/storefront/src/components/Breadcrumbs.astro +45 -0
- package/apps/storefront/src/components/CollectionCard.astro +56 -0
- package/apps/storefront/src/components/EmptyState.astro +55 -0
- package/apps/storefront/src/components/Img.astro +68 -0
- package/apps/storefront/src/components/ProductCard.astro +228 -0
- package/apps/storefront/src/components/Section.astro +39 -0
- package/apps/storefront/src/components/Seo.astro +70 -0
- package/apps/storefront/src/components/WidgetFrame.astro +34 -0
- package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +531 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +597 -0
- package/apps/storefront/src/components/admin/AdminFoxyCartSettingsPanel.astro +227 -0
- package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +465 -0
- package/apps/storefront/src/components/admin/AdminProductsTab.astro +901 -0
- package/apps/storefront/src/components/admin/AdminReportingTab.astro +435 -0
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +782 -0
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +1223 -0
- package/apps/storefront/src/components/auth/AuthBadge.astro +44 -0
- package/apps/storefront/src/components/chrome/AnnouncementBar.astro +108 -0
- package/apps/storefront/src/components/chrome/MegaMenu.astro +26 -0
- package/apps/storefront/src/components/chrome/NavDropdown.astro +201 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +201 -0
- package/apps/storefront/src/components/chrome/SiteHeader.astro +234 -0
- package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
- package/apps/storefront/src/components/commerce/ArticleCard.astro +242 -0
- package/apps/storefront/src/components/commerce/BrandGrid.astro +175 -0
- package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
- package/apps/storefront/src/components/commerce/FoxySidecartStyles.astro +61 -0
- package/apps/storefront/src/components/commerce/PriceDisplay.astro +91 -0
- package/apps/storefront/src/components/commerce/ProductCarousel.astro +197 -0
- package/apps/storefront/src/components/commerce/ProductGrid.astro +75 -0
- package/apps/storefront/src/components/commerce/RatingStars.astro +58 -0
- package/apps/storefront/src/components/commerce/ReviewList.astro +54 -0
- package/apps/storefront/src/components/commerce/ReviewSummary.astro +44 -0
- package/apps/storefront/src/components/commerce/TagFilterCloud.astro +149 -0
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +37 -0
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +99 -0
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +45 -0
- package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
- package/apps/storefront/src/components/compliance/PactActNotice.astro +35 -0
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +80 -0
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +38 -0
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +35 -0
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +53 -0
- package/apps/storefront/src/components/home/Hero.astro +164 -0
- package/apps/storefront/src/components/islands/AgeGate.tsx +109 -0
- package/apps/storefront/src/components/islands/ImageGallery.tsx +237 -0
- package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +73 -0
- package/apps/storefront/src/components/islands/MobileNav.tsx +239 -0
- package/apps/storefront/src/components/islands/QuickView.tsx +138 -0
- package/apps/storefront/src/components/islands/RecentlyViewed.tsx +122 -0
- package/apps/storefront/src/components/islands/SavedList.tsx +99 -0
- package/apps/storefront/src/components/islands/SearchTypeahead.tsx +248 -0
- package/apps/storefront/src/components/islands/VariantSelector.tsx +375 -0
- package/apps/storefront/src/components/marketing/CardGrid.astro +72 -0
- package/apps/storefront/src/components/marketing/CodeSample.astro +20 -0
- package/apps/storefront/src/components/marketing/CtaBand.astro +24 -0
- package/apps/storefront/src/components/marketing/Faq.astro +37 -0
- package/apps/storefront/src/components/marketing/Integrations.astro +63 -0
- package/apps/storefront/src/components/marketing/MarketingBlocks.astro +53 -0
- package/apps/storefront/src/components/marketing/MarketingCtas.astro +48 -0
- package/apps/storefront/src/components/marketing/MarketingHero.astro +72 -0
- package/apps/storefront/src/components/marketing/ProofStrip.astro +37 -0
- package/apps/storefront/src/components/marketing/SplitCompare.astro +53 -0
- package/apps/storefront/src/components/marketing/Steps.astro +36 -0
- package/apps/storefront/src/components/marketing/Testimonials.astro +38 -0
- package/apps/storefront/src/components/marketing/TrustBar.astro +22 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +56 -0
- package/apps/storefront/src/components/membership/TierCards.astro +164 -0
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +148 -0
- package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
- package/apps/storefront/src/components/plp/FacetSidebar.astro +199 -0
- package/apps/storefront/src/components/plp/Pagination.astro +99 -0
- package/apps/storefront/src/components/plp/SortSelect.astro +64 -0
- package/apps/storefront/src/components/style-guide/StyleGuideNav.astro +95 -0
- package/apps/storefront/src/components/subscription/HowItWorksSteps.astro +67 -0
- package/apps/storefront/src/components/subscription/ManageSubscriptionEntry.astro +67 -0
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +101 -0
- package/apps/storefront/src/config/adminTargetTenant.ts +28 -0
- package/apps/storefront/src/config/devTenantSeed.ts +78 -0
- package/apps/storefront/src/config/devTenants.ts +16 -0
- package/apps/storefront/src/config/kvTenantResolver.ts +87 -0
- package/apps/storefront/src/config/resolver.ts +102 -0
- package/apps/storefront/src/config/tenants.ts +87 -0
- package/apps/storefront/src/layouts/Layout.astro +368 -0
- package/apps/storefront/src/lib/admin/README.md +122 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +21 -0
- package/apps/storefront/src/lib/admin/ai/askEnvelope.ts +48 -0
- package/apps/storefront/src/lib/admin/ai/audit.ts +14 -0
- package/apps/storefront/src/lib/admin/ai/index.ts +6 -0
- package/apps/storefront/src/lib/admin/ai/operationalRuns.ts +155 -0
- package/apps/storefront/src/lib/admin/ai/recommendation.ts +20 -0
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +172 -0
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +65 -0
- package/apps/storefront/src/lib/admin/envelope.ts +10 -0
- package/apps/storefront/src/lib/admin/evidence/evidenceReference.ts +32 -0
- package/apps/storefront/src/lib/admin/memoizeByEnvelope.ts +27 -0
- package/apps/storefront/src/lib/admin/orderClient.ts +146 -0
- package/apps/storefront/src/lib/admin/seed.ts +54 -0
- package/apps/storefront/src/lib/admin/services/mock/aiWorkflows.ts +59 -0
- package/apps/storefront/src/lib/admin/services/mock/customers.ts +86 -0
- package/apps/storefront/src/lib/admin/services/mock/evidence.ts +99 -0
- package/apps/storefront/src/lib/admin/services/mock/foxyCartSettings.ts +78 -0
- package/apps/storefront/src/lib/admin/services/mock/fulfillment.ts +62 -0
- package/apps/storefront/src/lib/admin/services/mock/index.ts +12 -0
- package/apps/storefront/src/lib/admin/services/mock/orders.ts +184 -0
- package/apps/storefront/src/lib/admin/services/mock/products.ts +105 -0
- package/apps/storefront/src/lib/admin/services/mock/reporting.ts +51 -0
- package/apps/storefront/src/lib/admin/services/mock/settings.ts +70 -0
- package/apps/storefront/src/lib/admin/services/mock/tenants.ts +29 -0
- package/apps/storefront/src/lib/admin/services/orders.ts +300 -0
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +67 -0
- package/apps/storefront/src/lib/admin/viewmodels/customers.ts +56 -0
- package/apps/storefront/src/lib/admin/viewmodels/fulfillment.ts +49 -0
- package/apps/storefront/src/lib/admin/viewmodels/orders.ts +92 -0
- package/apps/storefront/src/lib/admin/viewmodels/products.ts +74 -0
- package/apps/storefront/src/lib/admin/viewmodels/reporting.ts +34 -0
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +64 -0
- package/apps/storefront/src/lib/adminTenantEnvelope.ts +90 -0
- package/apps/storefront/src/lib/analytics/index.ts +47 -0
- package/apps/storefront/src/lib/auth/brokerAssertion.ts +136 -0
- package/apps/storefront/src/lib/auth/devAuth.ts +90 -0
- package/apps/storefront/src/lib/auth/gatePage.ts +81 -0
- package/apps/storefront/src/lib/auth/identityToken.ts +341 -0
- package/apps/storefront/src/lib/auth/loginGate.ts +119 -0
- package/apps/storefront/src/lib/auth/route.ts +146 -0
- package/apps/storefront/src/lib/auth/session.ts +203 -0
- package/apps/storefront/src/lib/auth/totAccessClient.ts +503 -0
- package/apps/storefront/src/lib/basePath.ts +41 -0
- package/apps/storefront/src/lib/cfImage.ts +55 -0
- package/apps/storefront/src/lib/chrome/model.ts +244 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +34 -0
- package/apps/storefront/src/lib/colors.ts +97 -0
- package/apps/storefront/src/lib/commerce/foxyCartSettings.ts +253 -0
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +91 -0
- package/apps/storefront/src/lib/content-edit/client.ts +230 -0
- package/apps/storefront/src/lib/content-edit/index.ts +14 -0
- package/apps/storefront/src/lib/content-edit/route.ts +25 -0
- package/apps/storefront/src/lib/content-edit/types.ts +19 -0
- package/apps/storefront/src/lib/cspStaticHashes.ts +24 -0
- package/apps/storefront/src/lib/d1/catalog.ts +366 -0
- package/apps/storefront/src/lib/dashboard/staffAdmission.ts +43 -0
- package/apps/storefront/src/lib/dashboard/staffVendorAccess.ts +449 -0
- package/apps/storefront/src/lib/dashboard/tenantSelection.ts +136 -0
- package/apps/storefront/src/lib/dashboard/vendorQuery.ts +61 -0
- package/apps/storefront/src/lib/demoGate.ts +113 -0
- package/apps/storefront/src/lib/dev/activityIngestToken.ts +78 -0
- package/apps/storefront/src/lib/dev/activityStore.ts +72 -0
- package/apps/storefront/src/lib/dev/ai-widget-client.js +308 -0
- package/apps/storefront/src/lib/dev/apiBase.ts +14 -0
- package/apps/storefront/src/lib/dev/cliSignInCode.ts +155 -0
- package/apps/storefront/src/lib/dev/devDraft.ts +133 -0
- package/apps/storefront/src/lib/dev/hostedCockpit.ts +169 -0
- package/apps/storefront/src/lib/dev/livePreview.ts +77 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +120 -0
- package/apps/storefront/src/lib/dev/storeStatus.ts +142 -0
- package/apps/storefront/src/lib/diag/redact.ts +20 -0
- package/apps/storefront/src/lib/edgeCache.ts +128 -0
- package/apps/storefront/src/lib/email/magicLinkInviteEmail.ts +62 -0
- package/apps/storefront/src/lib/env.ts +60 -0
- package/apps/storefront/src/lib/fixtures/sampleProducts.ts +130 -0
- package/apps/storefront/src/lib/format.ts +38 -0
- package/apps/storefront/src/lib/foxyCommerce.ts +49 -0
- package/apps/storefront/src/lib/imageAttrs.ts +55 -0
- package/apps/storefront/src/lib/jsonld.ts +166 -0
- package/apps/storefront/src/lib/maintenance.ts +71 -0
- package/apps/storefront/src/lib/membership/model.ts +97 -0
- package/apps/storefront/src/lib/memoryKv.ts +32 -0
- package/apps/storefront/src/lib/messaging/messagesClient.ts +143 -0
- package/apps/storefront/src/lib/placeholder.ts +99 -0
- package/apps/storefront/src/lib/previewReload.ts +125 -0
- package/apps/storefront/src/lib/priceBuckets.ts +33 -0
- package/apps/storefront/src/lib/rawChrome.ts +505 -0
- package/apps/storefront/src/lib/rawMarketingHtml.ts +136 -0
- package/apps/storefront/src/lib/search/index.ts +74 -0
- package/apps/storefront/src/lib/sections.ts +129 -0
- package/apps/storefront/src/lib/securityHeaders.ts +31 -0
- package/apps/storefront/src/lib/storyblok/content-model.ts +289 -0
- package/apps/storefront/src/lib/storyblok/provider.ts +607 -0
- package/apps/storefront/src/lib/subscription/model.ts +68 -0
- package/apps/storefront/src/lib/tenantRouting.ts +91 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +177 -0
- package/apps/storefront/src/lib/tot/d1Client.ts +62 -0
- package/apps/storefront/src/lib/tot/query.ts +249 -0
- package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
- package/apps/storefront/src/lib/url.ts +142 -0
- package/apps/storefront/src/lib/wishlist.ts +63 -0
- package/apps/storefront/src/middleware/index.ts +455 -0
- package/apps/storefront/src/pages/404.astro +61 -0
- package/apps/storefront/src/pages/[...slug].astro +90 -0
- package/apps/storefront/src/pages/admin.astro +1675 -0
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +53 -0
- package/apps/storefront/src/pages/api/admin/orders.ts +71 -0
- package/apps/storefront/src/pages/api/auth/logout.ts +21 -0
- package/apps/storefront/src/pages/api/auth/magic-exchange.ts +107 -0
- package/apps/storefront/src/pages/api/auth/send.ts +85 -0
- package/apps/storefront/src/pages/api/auth/verify.ts +135 -0
- package/apps/storefront/src/pages/api/cache-purge.ts +44 -0
- package/apps/storefront/src/pages/api/checkout-style.css.ts +43 -0
- package/apps/storefront/src/pages/api/cli/policy.ts +70 -0
- package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +164 -0
- package/apps/storefront/src/pages/api/csp-report.ts +25 -0
- package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +124 -0
- package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +120 -0
- package/apps/storefront/src/pages/api/dev/activity.ts +124 -0
- package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +73 -0
- package/apps/storefront/src/pages/api/dev/diff.ts +68 -0
- package/apps/storefront/src/pages/api/dev/file.ts +57 -0
- package/apps/storefront/src/pages/api/dev/locate.ts +125 -0
- package/apps/storefront/src/pages/api/dev/publish.ts +128 -0
- package/apps/storefront/src/pages/api/dev/tree.ts +70 -0
- package/apps/storefront/src/pages/api/dev/write.ts +116 -0
- package/apps/storefront/src/pages/api/newsletter.ts +104 -0
- package/apps/storefront/src/pages/api/request-access.ts +172 -0
- package/apps/storefront/src/pages/auth/login.astro +149 -0
- package/apps/storefront/src/pages/auth/magic.astro +105 -0
- package/apps/storefront/src/pages/capabilities.astro +292 -0
- package/apps/storefront/src/pages/collections/[handle].astro +177 -0
- package/apps/storefront/src/pages/collections/index.astro +59 -0
- package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +100 -0
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +154 -0
- package/apps/storefront/src/pages/dashboard/index.astro +160 -0
- package/apps/storefront/src/pages/index.astro +290 -0
- package/apps/storefront/src/pages/llms.txt.ts +92 -0
- package/apps/storefront/src/pages/products/[handle].astro +402 -0
- package/apps/storefront/src/pages/robots.txt.ts +24 -0
- package/apps/storefront/src/pages/saved.astro +35 -0
- package/apps/storefront/src/pages/search.astro +150 -0
- package/apps/storefront/src/pages/sitemap.xml.ts +78 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +1280 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +229 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +144 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +116 -0
- package/apps/storefront/src/pages/style-guide/index.astro +98 -0
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
- package/apps/storefront/src/styles/global.css +292 -0
- package/apps/storefront/src/themes/reference.ts +68 -0
- package/apps/storefront/src/themes/schema.ts +111 -0
- package/apps/storefront/src/themes/tenants/index.ts +51 -0
- package/apps/storefront/tsconfig.json +16 -0
- package/apps/storefront/vitest.config.ts +22 -0
- package/package.json +26 -0
- package/packages/public-runtime/LICENSE +58 -0
- package/packages/public-runtime/package.json +22 -0
- package/packages/public-runtime/src/binary-path.ts +68 -0
- package/packages/public-runtime/src/capabilities.ts +89 -0
- package/packages/public-runtime/src/catalog-api.ts +81 -0
- package/packages/public-runtime/src/catalog-d1.ts +192 -0
- package/packages/public-runtime/src/checkout-style-publish.ts +200 -0
- package/packages/public-runtime/src/checkout-style.ts +375 -0
- package/packages/public-runtime/src/csp.ts +109 -0
- package/packages/public-runtime/src/customization-preview.ts +197 -0
- package/packages/public-runtime/src/customization-reconcile.ts +63 -0
- package/packages/public-runtime/src/customization-runtime.ts +157 -0
- package/packages/public-runtime/src/customization-scripts.ts +64 -0
- package/packages/public-runtime/src/customization-versioning.ts +95 -0
- package/packages/public-runtime/src/foxy-orders.ts +136 -0
- package/packages/public-runtime/src/foxy.ts +196 -0
- package/packages/public-runtime/src/hot-standby-readiness.ts +140 -0
- package/packages/public-runtime/src/index.ts +34 -0
- package/packages/public-runtime/src/product.ts +159 -0
- package/packages/public-runtime/src/search.ts +45 -0
- package/packages/public-runtime/src/tenant-assets.ts +444 -0
- package/packages/public-runtime/src/tenant.ts +384 -0
- package/packages/public-runtime/tsconfig.json +8 -0
- package/packages/public-runtime/vitest.config.ts +8 -0
- package/pnpm-workspace.yaml +7 -0
- package/scripts/build/copy-tenant-assets.mjs +69 -0
- package/scripts/dev/ai-edit.mjs +183 -0
- package/scripts/dev/checkout-watch.mjs +116 -0
- package/scripts/dev/file-browser.mjs +159 -0
- package/scripts/dev/file-writer.mjs +141 -0
- package/scripts/dev/git-status.mjs +171 -0
- package/scripts/dev/locate-handler.mjs +163 -0
- package/scripts/dev/locate.mjs +112 -0
- package/scripts/dev/port-check.mjs +48 -0
- package/scripts/dev/safe-path.mjs +109 -0
- package/scripts/dev/shot.mjs +46 -0
- package/scripts/dev/tenant-source-root.mjs +76 -0
- package/scripts/dev/unified-diff.mjs +81 -0
- package/scripts/tot-dev.mjs +376 -0
- package/tenants/README.md +14 -0
- package/tenants/home/content/chrome.json +7 -0
- package/tenants/home/content/home.html +58 -0
- package/tenants/home/content/home.json +11 -0
- package/tenants/home/public/js/storefront.js +210 -0
- package/tenants/home/public/logo-wordmark.png +0 -0
- package/tenants/home/public/pages/storefront.css +212 -0
- package/tenants/home/theme.json +6 -0
- package/tsconfig.base.json +20 -0
|
@@ -0,0 +1,1675 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Tenant admin interface launch surface.
|
|
4
|
+
*
|
|
5
|
+
* This route intentionally does not use the public storefront Layout: it is a
|
|
6
|
+
* white-labeled operations surface, not marketing chrome. Middleware still
|
|
7
|
+
* resolves the tenant, injects CSP/security headers, and supports shared preview
|
|
8
|
+
* path routing, so this works as:
|
|
9
|
+
*
|
|
10
|
+
* /admin
|
|
11
|
+
* /<tenant-domain>/admin
|
|
12
|
+
*
|
|
13
|
+
* SHELL CONTRACT — every AdminXTab.astro component mounted below must preserve:
|
|
14
|
+
* - A `<section id="{tab}" data-admin-panel data-agent-scope="{tab}">` root,
|
|
15
|
+
* where {tab} is one of: orders, products, customers, fulfillment,
|
|
16
|
+
* reporting, ai-workflows, settings. The shell's hash router shows/hides
|
|
17
|
+
* panels by matching `html[data-admin-tab]` to this id, and the AI Ask
|
|
18
|
+
* envelope reads `data-agent-scope` to label its active context.
|
|
19
|
+
* - No tab-switching chrome of its own — `#{tab}` deep links and the shell's
|
|
20
|
+
* nav (desktop sidebar + `.mobile-tabbar` under 760px) are the only ways
|
|
21
|
+
* to activate a tab. Both are wired through the shared
|
|
22
|
+
* `[data-admin-tab-link]` click handler at the bottom of this file; adding
|
|
23
|
+
* a new entry point for tab switching means adding that attribute, not new
|
|
24
|
+
* JS.
|
|
25
|
+
* - Tables/wide content wrapped in `.table-wrap` (`overflow-x: auto`) so a
|
|
26
|
+
* tab can never force page-level horizontal scroll, checked down to 360px.
|
|
27
|
+
* - White-labeled, dense, merchant-first copy — see
|
|
28
|
+
* docs/handoffs/admin-app-implementation-baseline.md for the full product
|
|
29
|
+
* frame this shell exists to serve.
|
|
30
|
+
*/
|
|
31
|
+
export const prerender = false;
|
|
32
|
+
|
|
33
|
+
import AdminCustomersTab from "../components/admin/AdminCustomersTab.astro";
|
|
34
|
+
import AdminFulfillmentTab from "../components/admin/AdminFulfillmentTab.astro";
|
|
35
|
+
import AdminAIWorkflowsTab from "../components/admin/AdminAIWorkflowsTab.astro";
|
|
36
|
+
import OrdersWorkspace from "../components/admin/orders/OrdersWorkspace.astro";
|
|
37
|
+
import AdminProductsTab from "../components/admin/AdminProductsTab.astro";
|
|
38
|
+
import AdminReportingTab from "../components/admin/AdminReportingTab.astro";
|
|
39
|
+
import AdminSettingsTab from "../components/admin/AdminSettingsTab.astro";
|
|
40
|
+
import { readViewerSession } from "@/lib/auth/route";
|
|
41
|
+
import { resolveAdminTenantMode } from "@/lib/adminTenantEnvelope";
|
|
42
|
+
|
|
43
|
+
const { tenant, basePath, cspNonce } = Astro.locals;
|
|
44
|
+
const adminPath = `${basePath || ""}/admin`;
|
|
45
|
+
const tenantLabel = tenant.displayName || tenant.appDomain || tenant.tenant_id;
|
|
46
|
+
const viewerSession = await readViewerSession(Astro);
|
|
47
|
+
const viewerEmail = (Astro.locals.viewer?.email ?? viewerSession?.email ?? "").toLowerCase();
|
|
48
|
+
const viewerDomain = viewerEmail.split("@").at(-1) ?? "";
|
|
49
|
+
const canViewInternalGuide = viewerDomain === "tokenoftrust.com";
|
|
50
|
+
|
|
51
|
+
// Internal admin-app tenant mode (baseline doc §Admin App As Tenant,
|
|
52
|
+
// tenant-envelope-contract): every mounted tab receives an explicit
|
|
53
|
+
// { adminAppTenant, targetTenant } envelope. Only a real ToT-staff session
|
|
54
|
+
// may move targetTenant away from the routed tenant, and only to a tenant
|
|
55
|
+
// they already have real access to — see lib/adminTenantEnvelope.ts for the
|
|
56
|
+
// authorization rule (deny-by-default, refused not hidden-only).
|
|
57
|
+
const adminTenantMode = resolveAdminTenantMode({
|
|
58
|
+
session: viewerSession,
|
|
59
|
+
adminAppTenant: tenant.appDomain,
|
|
60
|
+
requestedTarget: Astro.url.searchParams.get("asTenant"),
|
|
61
|
+
nowSeconds: Math.floor(Date.now() / 1000),
|
|
62
|
+
});
|
|
63
|
+
const { envelope: tenantEnvelope, isStaff, pickerTenants, deniedTarget } = adminTenantMode;
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
<!doctype html>
|
|
67
|
+
<html
|
|
68
|
+
lang="en"
|
|
69
|
+
data-internal-admin-guide={canViewInternalGuide ? "true" : "false"}
|
|
70
|
+
data-admin-app-tenant={tenantEnvelope.adminAppTenant}
|
|
71
|
+
data-admin-target-tenant={tenantEnvelope.targetTenant}
|
|
72
|
+
data-admin-operator={tenantEnvelope.operator ?? ""}
|
|
73
|
+
data-admin-is-staff={isStaff ? "true" : "false"}
|
|
74
|
+
>
|
|
75
|
+
<head>
|
|
76
|
+
<meta charset="utf-8" />
|
|
77
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
78
|
+
<meta name="robots" content="noindex, nofollow" />
|
|
79
|
+
<title>{tenantLabel} Admin</title>
|
|
80
|
+
<script is:inline nonce={cspNonce}>
|
|
81
|
+
(() => {
|
|
82
|
+
const adminTabs = ["orders", "products", "customers", "fulfillment", "reporting", "ai-workflows", "settings"];
|
|
83
|
+
const canViewInternalGuide = document.documentElement.dataset.internalAdminGuide === "true";
|
|
84
|
+
const defaultTab = canViewInternalGuide ? "orders" : "settings";
|
|
85
|
+
const hash = window.location.hash.slice(1);
|
|
86
|
+
const activeTab = adminTabs.includes(hash) ? hash : defaultTab;
|
|
87
|
+
const mode = canViewInternalGuide && hash && !adminTabs.includes(hash) ? "guide" : "work-area";
|
|
88
|
+
|
|
89
|
+
document.documentElement.classList.add("admin-tabs-ready");
|
|
90
|
+
document.documentElement.dataset.adminTab = activeTab;
|
|
91
|
+
document.documentElement.dataset.adminMode = mode;
|
|
92
|
+
})();
|
|
93
|
+
</script>
|
|
94
|
+
<style is:global nonce={cspNonce}>
|
|
95
|
+
:root {
|
|
96
|
+
color-scheme: light;
|
|
97
|
+
--admin-bg: #f5f7f6;
|
|
98
|
+
--admin-surface: #ffffff;
|
|
99
|
+
--admin-surface-soft: #f9fbfa;
|
|
100
|
+
--admin-surface-tint: #eef6f4;
|
|
101
|
+
--admin-ink: #16211f;
|
|
102
|
+
--admin-muted: #64716d;
|
|
103
|
+
--admin-muted-2: #8c9894;
|
|
104
|
+
--admin-line: #dce5e2;
|
|
105
|
+
--admin-line-strong: #bdcbc7;
|
|
106
|
+
--admin-teal: #007f79;
|
|
107
|
+
--admin-teal-dark: #005d58;
|
|
108
|
+
--admin-teal-soft: #dff4f1;
|
|
109
|
+
--admin-green: #137a55;
|
|
110
|
+
--admin-green-soft: #e3f5eb;
|
|
111
|
+
--admin-blue: #2458c7;
|
|
112
|
+
--admin-blue-soft: #e7efff;
|
|
113
|
+
--admin-amber: #a45d00;
|
|
114
|
+
--admin-amber-soft: #fff0d5;
|
|
115
|
+
--admin-red: #b42318;
|
|
116
|
+
--admin-red-soft: #fde7e4;
|
|
117
|
+
--admin-violet: #6941c6;
|
|
118
|
+
--admin-violet-soft: #efe9ff;
|
|
119
|
+
--admin-shadow: 0 18px 50px rgba(23, 33, 31, 0.08);
|
|
120
|
+
--admin-radius: 8px;
|
|
121
|
+
--admin-radius-sm: 6px;
|
|
122
|
+
--admin-scroll-offset: 104px;
|
|
123
|
+
--admin-font:
|
|
124
|
+
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
125
|
+
"Segoe UI", sans-serif;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
* {
|
|
129
|
+
box-sizing: border-box;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
html {
|
|
133
|
+
max-width: 100%;
|
|
134
|
+
height: 100%;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
body {
|
|
139
|
+
margin: 0;
|
|
140
|
+
height: 100%;
|
|
141
|
+
min-height: 100vh;
|
|
142
|
+
background: var(--admin-bg);
|
|
143
|
+
color: var(--admin-ink);
|
|
144
|
+
font: 14px/1.5 var(--admin-font);
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
button,
|
|
149
|
+
input,
|
|
150
|
+
select {
|
|
151
|
+
font: inherit;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
button {
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
svg {
|
|
159
|
+
width: 18px;
|
|
160
|
+
height: 18px;
|
|
161
|
+
stroke: currentColor;
|
|
162
|
+
stroke-width: 1.9;
|
|
163
|
+
stroke-linecap: round;
|
|
164
|
+
stroke-linejoin: round;
|
|
165
|
+
fill: none;
|
|
166
|
+
flex: 0 0 auto;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.shell {
|
|
170
|
+
display: grid;
|
|
171
|
+
grid-template-columns: 248px minmax(0, 1fr);
|
|
172
|
+
height: 100vh;
|
|
173
|
+
min-height: 0;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.sidebar {
|
|
178
|
+
position: sticky;
|
|
179
|
+
top: 0;
|
|
180
|
+
height: 100vh;
|
|
181
|
+
min-height: 0;
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 22px;
|
|
185
|
+
padding: 20px 16px;
|
|
186
|
+
overflow-y: auto;
|
|
187
|
+
overscroll-behavior: contain;
|
|
188
|
+
background: #101b18;
|
|
189
|
+
color: #eef7f5;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.brand {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 12px;
|
|
196
|
+
min-height: 42px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.brand-mark {
|
|
200
|
+
width: 36px;
|
|
201
|
+
height: 36px;
|
|
202
|
+
display: grid;
|
|
203
|
+
place-items: center;
|
|
204
|
+
border-radius: var(--admin-radius);
|
|
205
|
+
background: #00a991;
|
|
206
|
+
color: #06231e;
|
|
207
|
+
font-weight: 800;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.brand b,
|
|
211
|
+
.store strong {
|
|
212
|
+
display: block;
|
|
213
|
+
font-size: 14px;
|
|
214
|
+
line-height: 1.1;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.brand span,
|
|
218
|
+
.store span {
|
|
219
|
+
display: block;
|
|
220
|
+
margin-top: 3px;
|
|
221
|
+
color: rgba(238, 247, 245, 0.7);
|
|
222
|
+
font-size: 12px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.store {
|
|
226
|
+
display: grid;
|
|
227
|
+
gap: 4px;
|
|
228
|
+
padding: 12px;
|
|
229
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
230
|
+
border-radius: var(--admin-radius);
|
|
231
|
+
background: rgba(255, 255, 255, 0.06);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.tenant-picker {
|
|
235
|
+
display: grid;
|
|
236
|
+
gap: 4px;
|
|
237
|
+
padding: 10px 12px;
|
|
238
|
+
border: 1px solid rgba(0, 169, 145, 0.4);
|
|
239
|
+
border-radius: var(--admin-radius);
|
|
240
|
+
background: rgba(0, 169, 145, 0.1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.tenant-picker label {
|
|
244
|
+
color: rgba(238, 247, 245, 0.7);
|
|
245
|
+
font-size: 11px;
|
|
246
|
+
font-weight: 760;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.tenant-picker select {
|
|
251
|
+
min-height: 34px;
|
|
252
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
253
|
+
border-radius: var(--admin-radius-sm);
|
|
254
|
+
background: #101b18;
|
|
255
|
+
color: #eef7f5;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.tenant-picker-denied {
|
|
259
|
+
padding: 10px 12px;
|
|
260
|
+
border: 1px solid var(--admin-amber);
|
|
261
|
+
border-radius: var(--admin-radius);
|
|
262
|
+
background: rgba(164, 93, 0, 0.16);
|
|
263
|
+
color: #eef7f5;
|
|
264
|
+
font-size: 12px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.tenant-picker-denied code {
|
|
268
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.nav {
|
|
272
|
+
display: grid;
|
|
273
|
+
gap: 4px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.nav a {
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
gap: 10px;
|
|
280
|
+
min-height: 40px;
|
|
281
|
+
padding: 0 10px;
|
|
282
|
+
border-radius: var(--admin-radius-sm);
|
|
283
|
+
color: rgba(238, 247, 245, 0.74);
|
|
284
|
+
text-decoration: none;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.nav a.active {
|
|
288
|
+
background: rgba(0, 169, 145, 0.18);
|
|
289
|
+
color: #ffffff;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.nav-group {
|
|
293
|
+
display: grid;
|
|
294
|
+
gap: 3px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.nav-subnav {
|
|
298
|
+
display: grid;
|
|
299
|
+
gap: 2px;
|
|
300
|
+
margin: 0 0 3px 28px;
|
|
301
|
+
padding-left: 10px;
|
|
302
|
+
border-left: 1px solid rgba(255, 255, 255, 0.14);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.nav-subnav a {
|
|
306
|
+
min-height: 32px;
|
|
307
|
+
padding: 0 8px;
|
|
308
|
+
gap: 8px;
|
|
309
|
+
font-size: 12px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.nav-subnav svg {
|
|
313
|
+
width: 16px;
|
|
314
|
+
height: 16px;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.health {
|
|
318
|
+
margin-top: auto;
|
|
319
|
+
display: grid;
|
|
320
|
+
gap: 9px;
|
|
321
|
+
padding: 12px;
|
|
322
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
323
|
+
border-radius: var(--admin-radius);
|
|
324
|
+
background: rgba(255, 255, 255, 0.05);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.health-row {
|
|
328
|
+
display: flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: space-between;
|
|
331
|
+
gap: 10px;
|
|
332
|
+
font-size: 12px;
|
|
333
|
+
color: rgba(238, 247, 245, 0.72);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.health-row strong {
|
|
337
|
+
color: #ffffff;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.main {
|
|
341
|
+
min-width: 0;
|
|
342
|
+
min-height: 0;
|
|
343
|
+
height: 100vh;
|
|
344
|
+
overflow-x: hidden;
|
|
345
|
+
overflow-y: auto;
|
|
346
|
+
overscroll-behavior: contain;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.content,
|
|
350
|
+
.band,
|
|
351
|
+
.panel,
|
|
352
|
+
.panel-body,
|
|
353
|
+
.table-wrap {
|
|
354
|
+
min-width: 0;
|
|
355
|
+
max-width: 100%;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.topbar {
|
|
359
|
+
position: sticky;
|
|
360
|
+
top: 0;
|
|
361
|
+
z-index: 30;
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: space-between;
|
|
365
|
+
gap: 18px;
|
|
366
|
+
min-height: 68px;
|
|
367
|
+
padding: 14px 24px;
|
|
368
|
+
border-bottom: 1px solid var(--admin-line);
|
|
369
|
+
background: rgba(245, 247, 246, 0.94);
|
|
370
|
+
backdrop-filter: blur(16px);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.page-title h1 {
|
|
374
|
+
margin: 0;
|
|
375
|
+
font-size: 22px;
|
|
376
|
+
font-weight: 760;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.page-title p {
|
|
380
|
+
margin: 2px 0 0;
|
|
381
|
+
color: var(--admin-muted);
|
|
382
|
+
font-size: 13px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.page-title,
|
|
386
|
+
.page-title p,
|
|
387
|
+
.note,
|
|
388
|
+
code {
|
|
389
|
+
min-width: 0;
|
|
390
|
+
overflow-wrap: anywhere;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.actions {
|
|
394
|
+
display: flex;
|
|
395
|
+
flex-wrap: wrap;
|
|
396
|
+
gap: 8px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.btn,
|
|
400
|
+
.icon-btn {
|
|
401
|
+
min-height: 38px;
|
|
402
|
+
display: inline-flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
justify-content: center;
|
|
405
|
+
gap: 8px;
|
|
406
|
+
border: 1px solid var(--admin-line-strong);
|
|
407
|
+
border-radius: var(--admin-radius-sm);
|
|
408
|
+
background: var(--admin-surface);
|
|
409
|
+
color: var(--admin-ink);
|
|
410
|
+
text-decoration: none;
|
|
411
|
+
white-space: nowrap;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.btn {
|
|
415
|
+
padding: 0 13px;
|
|
416
|
+
font-weight: 680;
|
|
417
|
+
max-width: 100%;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.icon-btn {
|
|
421
|
+
width: 38px;
|
|
422
|
+
padding: 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.btn.primary {
|
|
426
|
+
border-color: var(--admin-teal-dark);
|
|
427
|
+
background: var(--admin-teal);
|
|
428
|
+
color: #ffffff;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.floating-ask {
|
|
432
|
+
position: fixed;
|
|
433
|
+
right: 24px;
|
|
434
|
+
bottom: 24px;
|
|
435
|
+
z-index: 80;
|
|
436
|
+
min-height: 58px;
|
|
437
|
+
max-width: min(280px, calc(100vw - 32px));
|
|
438
|
+
display: inline-flex;
|
|
439
|
+
align-items: center;
|
|
440
|
+
gap: 10px;
|
|
441
|
+
padding: 9px 15px 9px 10px;
|
|
442
|
+
border: 1px solid rgba(0, 93, 88, 0.24);
|
|
443
|
+
border-radius: 999px;
|
|
444
|
+
background: var(--admin-teal);
|
|
445
|
+
color: #ffffff;
|
|
446
|
+
box-shadow: 0 16px 38px rgba(22, 33, 31, 0.22);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.floating-ask-icon {
|
|
450
|
+
width: 40px;
|
|
451
|
+
height: 40px;
|
|
452
|
+
display: grid;
|
|
453
|
+
place-items: center;
|
|
454
|
+
border-radius: 50%;
|
|
455
|
+
background: rgba(255, 255, 255, 0.16);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.floating-ask-label {
|
|
459
|
+
display: grid;
|
|
460
|
+
gap: 1px;
|
|
461
|
+
min-width: 0;
|
|
462
|
+
text-align: left;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.floating-ask-label strong {
|
|
466
|
+
font-size: 14px;
|
|
467
|
+
line-height: 1.1;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.floating-ask-label small {
|
|
471
|
+
max-width: 160px;
|
|
472
|
+
overflow: hidden;
|
|
473
|
+
color: rgba(255, 255, 255, 0.78);
|
|
474
|
+
font-size: 11px;
|
|
475
|
+
font-weight: 680;
|
|
476
|
+
line-height: 1.2;
|
|
477
|
+
text-overflow: ellipsis;
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.content {
|
|
482
|
+
display: grid;
|
|
483
|
+
grid-template-columns: minmax(0, 1fr);
|
|
484
|
+
gap: 18px;
|
|
485
|
+
padding: 20px 24px 40px;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.band {
|
|
489
|
+
display: grid;
|
|
490
|
+
grid-template-columns: minmax(0, 1fr);
|
|
491
|
+
gap: 14px;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
[data-admin-panel],
|
|
495
|
+
[data-admin-guide-section] {
|
|
496
|
+
scroll-margin-top: var(--admin-scroll-offset);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
html.admin-tabs-ready [data-admin-panel] {
|
|
500
|
+
display: none;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="orders"] #orders,
|
|
504
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="products"] #products,
|
|
505
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="customers"] #customers,
|
|
506
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="fulfillment"] #fulfillment,
|
|
507
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="reporting"] #reporting,
|
|
508
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="ai-workflows"] #ai-workflows,
|
|
509
|
+
html.admin-tabs-ready[data-admin-mode="work-area"][data-admin-tab="settings"] #settings {
|
|
510
|
+
display: grid;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
html.admin-tabs-ready[data-admin-mode="work-area"] [data-admin-guide-section],
|
|
514
|
+
html.admin-tabs-ready[data-admin-mode="work-area"] [data-admin-tab-help] {
|
|
515
|
+
display: none;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
html[data-internal-admin-guide="false"] [data-admin-internal-only] {
|
|
519
|
+
display: none !important;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.section-title {
|
|
523
|
+
display: flex;
|
|
524
|
+
align-items: center;
|
|
525
|
+
justify-content: space-between;
|
|
526
|
+
gap: 12px;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.section-title h2 {
|
|
530
|
+
margin: 0;
|
|
531
|
+
font-size: 16px;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.section-title p {
|
|
535
|
+
margin: 4px 0 0;
|
|
536
|
+
color: var(--admin-muted);
|
|
537
|
+
font-size: 13px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.grid {
|
|
541
|
+
display: grid;
|
|
542
|
+
grid-template-columns: minmax(0, 1fr);
|
|
543
|
+
gap: 12px;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.grid.cols-2 {
|
|
547
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.grid.cols-3 {
|
|
551
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.grid.cols-4 {
|
|
555
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.panel {
|
|
559
|
+
border: 1px solid var(--admin-line);
|
|
560
|
+
border-radius: var(--admin-radius);
|
|
561
|
+
background: var(--admin-surface);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.panel-head {
|
|
565
|
+
display: flex;
|
|
566
|
+
align-items: center;
|
|
567
|
+
justify-content: space-between;
|
|
568
|
+
gap: 12px;
|
|
569
|
+
min-height: 50px;
|
|
570
|
+
padding: 13px 14px;
|
|
571
|
+
border-bottom: 1px solid var(--admin-line);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.panel-head h3 {
|
|
575
|
+
margin: 0;
|
|
576
|
+
display: flex;
|
|
577
|
+
align-items: center;
|
|
578
|
+
gap: 8px;
|
|
579
|
+
font-size: 15px;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.panel-body {
|
|
583
|
+
display: grid;
|
|
584
|
+
grid-template-columns: minmax(0, 1fr);
|
|
585
|
+
gap: 12px;
|
|
586
|
+
padding: 14px;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.metric {
|
|
590
|
+
min-height: 84px;
|
|
591
|
+
padding: 14px;
|
|
592
|
+
border: 1px solid var(--admin-line);
|
|
593
|
+
border-radius: var(--admin-radius);
|
|
594
|
+
background: var(--admin-surface);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.metric span,
|
|
598
|
+
.field label {
|
|
599
|
+
display: block;
|
|
600
|
+
color: var(--admin-muted);
|
|
601
|
+
font-size: 11px;
|
|
602
|
+
font-weight: 760;
|
|
603
|
+
text-transform: uppercase;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.metric strong {
|
|
607
|
+
display: block;
|
|
608
|
+
margin-top: 9px;
|
|
609
|
+
font-size: 24px;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.metric small {
|
|
613
|
+
display: block;
|
|
614
|
+
margin-top: 3px;
|
|
615
|
+
color: var(--admin-muted);
|
|
616
|
+
font-size: 12px;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.chip {
|
|
620
|
+
display: inline-flex;
|
|
621
|
+
align-items: center;
|
|
622
|
+
gap: 5px;
|
|
623
|
+
min-height: 23px;
|
|
624
|
+
padding: 0 8px;
|
|
625
|
+
border-radius: 999px;
|
|
626
|
+
background: var(--admin-surface-tint);
|
|
627
|
+
color: #3f4d49;
|
|
628
|
+
font-size: 12px;
|
|
629
|
+
font-weight: 740;
|
|
630
|
+
white-space: nowrap;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.chip.green {
|
|
634
|
+
background: var(--admin-green-soft);
|
|
635
|
+
color: var(--admin-green);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.chip.teal {
|
|
639
|
+
background: var(--admin-teal-soft);
|
|
640
|
+
/* Slightly darker than --admin-teal (#007f79): the shared token only
|
|
641
|
+
reaches ~4.26:1 against --admin-teal-soft, just under WCAG AA's
|
|
642
|
+
4.5:1 for this chip's 12px text (hardening-qa a11y pass). Scoped to
|
|
643
|
+
this rule only — --admin-teal itself stays unchanged since it is
|
|
644
|
+
also used as a background (e.g. .btn.primary) where the pairing
|
|
645
|
+
and contrast requirement are different. */
|
|
646
|
+
color: #007973;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.chip.blue {
|
|
650
|
+
background: var(--admin-blue-soft);
|
|
651
|
+
color: var(--admin-blue);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.chip.amber {
|
|
655
|
+
background: var(--admin-amber-soft);
|
|
656
|
+
color: var(--admin-amber);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.chip.red {
|
|
660
|
+
background: var(--admin-red-soft);
|
|
661
|
+
color: var(--admin-red);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.chip.violet {
|
|
665
|
+
background: var(--admin-violet-soft);
|
|
666
|
+
color: var(--admin-violet);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.swatches {
|
|
670
|
+
display: grid;
|
|
671
|
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
672
|
+
gap: 10px;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.swatch {
|
|
676
|
+
overflow: hidden;
|
|
677
|
+
border: 1px solid var(--admin-line);
|
|
678
|
+
border-radius: var(--admin-radius-sm);
|
|
679
|
+
background: var(--admin-surface);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.swatch-color {
|
|
683
|
+
min-height: 58px;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.swatch-meta {
|
|
687
|
+
padding: 10px;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.swatch-meta b {
|
|
691
|
+
display: block;
|
|
692
|
+
font-size: 13px;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.swatch-meta code,
|
|
696
|
+
.note code {
|
|
697
|
+
color: var(--admin-muted);
|
|
698
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
699
|
+
font-size: 12px;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.field {
|
|
703
|
+
min-width: 0;
|
|
704
|
+
padding: 11px;
|
|
705
|
+
border: 1px solid var(--admin-line);
|
|
706
|
+
border-radius: var(--admin-radius-sm);
|
|
707
|
+
background: #fcfdfc;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.field strong {
|
|
711
|
+
display: block;
|
|
712
|
+
margin-top: 6px;
|
|
713
|
+
font-size: 13px;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.field p {
|
|
717
|
+
margin: 5px 0 0;
|
|
718
|
+
color: var(--admin-muted);
|
|
719
|
+
font-size: 12px;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.ship-gate {
|
|
723
|
+
display: flex;
|
|
724
|
+
align-items: center;
|
|
725
|
+
justify-content: space-between;
|
|
726
|
+
gap: 14px;
|
|
727
|
+
padding: 14px;
|
|
728
|
+
border: 1px solid #a6d8c0;
|
|
729
|
+
border-radius: var(--admin-radius);
|
|
730
|
+
background: #eefaf3;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.ship-gate.amber {
|
|
734
|
+
border-color: #f1c883;
|
|
735
|
+
background: #fff6e7;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.ship-gate-main,
|
|
739
|
+
.risk-alert-main {
|
|
740
|
+
display: flex;
|
|
741
|
+
align-items: center;
|
|
742
|
+
gap: 12px;
|
|
743
|
+
min-width: 0;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.seal {
|
|
747
|
+
width: 42px;
|
|
748
|
+
height: 42px;
|
|
749
|
+
display: grid;
|
|
750
|
+
place-items: center;
|
|
751
|
+
border-radius: 50%;
|
|
752
|
+
background: var(--admin-green);
|
|
753
|
+
color: #ffffff;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.ship-gate.amber .seal {
|
|
757
|
+
background: var(--admin-amber);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.ship-gate b,
|
|
761
|
+
.risk-alert b {
|
|
762
|
+
display: block;
|
|
763
|
+
font-size: 15px;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.ship-gate span,
|
|
767
|
+
.risk-alert span {
|
|
768
|
+
display: block;
|
|
769
|
+
margin-top: 3px;
|
|
770
|
+
color: var(--admin-muted);
|
|
771
|
+
font-size: 12px;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
.risk-alert {
|
|
775
|
+
display: flex;
|
|
776
|
+
align-items: center;
|
|
777
|
+
justify-content: space-between;
|
|
778
|
+
gap: 14px;
|
|
779
|
+
padding: 12px 14px;
|
|
780
|
+
border: 1px solid #f1c883;
|
|
781
|
+
border-radius: var(--admin-radius);
|
|
782
|
+
background: #fff6e7;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.risk-alert svg {
|
|
786
|
+
color: var(--admin-amber);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.order-assist-grid {
|
|
790
|
+
display: grid;
|
|
791
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
792
|
+
gap: 10px;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.order-assist-card {
|
|
796
|
+
display: grid;
|
|
797
|
+
gap: 7px;
|
|
798
|
+
min-width: 0;
|
|
799
|
+
padding: 12px;
|
|
800
|
+
border: 1px solid var(--admin-line);
|
|
801
|
+
border-radius: var(--admin-radius-sm);
|
|
802
|
+
background: var(--admin-surface);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.order-assist-card strong {
|
|
806
|
+
font-size: 14px;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.order-assist-card p {
|
|
810
|
+
margin: 0;
|
|
811
|
+
color: var(--admin-muted);
|
|
812
|
+
font-size: 12px;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.tabs {
|
|
816
|
+
display: flex;
|
|
817
|
+
gap: 6px;
|
|
818
|
+
max-width: 100%;
|
|
819
|
+
overflow-x: auto;
|
|
820
|
+
border-bottom: 1px solid var(--admin-line);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.mobile-tabbar {
|
|
824
|
+
display: none;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.tab {
|
|
828
|
+
flex: 0 0 auto;
|
|
829
|
+
min-height: 38px;
|
|
830
|
+
display: inline-flex;
|
|
831
|
+
align-items: center;
|
|
832
|
+
padding: 0 12px;
|
|
833
|
+
border: 0;
|
|
834
|
+
border-bottom: 2px solid transparent;
|
|
835
|
+
background: transparent;
|
|
836
|
+
color: var(--admin-muted);
|
|
837
|
+
font-weight: 760;
|
|
838
|
+
text-decoration: none;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.tab.active {
|
|
842
|
+
border-color: var(--admin-teal);
|
|
843
|
+
color: var(--admin-ink);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.table-wrap {
|
|
847
|
+
overflow-x: auto;
|
|
848
|
+
border: 1px solid var(--admin-line);
|
|
849
|
+
border-radius: var(--admin-radius);
|
|
850
|
+
background: var(--admin-surface);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
table {
|
|
854
|
+
width: 100%;
|
|
855
|
+
min-width: 860px;
|
|
856
|
+
border-collapse: collapse;
|
|
857
|
+
font-size: 13px;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
th {
|
|
861
|
+
height: 38px;
|
|
862
|
+
padding: 0 12px;
|
|
863
|
+
border-bottom: 1px solid var(--admin-line);
|
|
864
|
+
background: var(--admin-surface-soft);
|
|
865
|
+
color: var(--admin-muted);
|
|
866
|
+
font-size: 11px;
|
|
867
|
+
font-weight: 760;
|
|
868
|
+
text-align: left;
|
|
869
|
+
text-transform: uppercase;
|
|
870
|
+
white-space: nowrap;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
td {
|
|
874
|
+
height: 46px;
|
|
875
|
+
padding: 7px 12px;
|
|
876
|
+
border-bottom: 1px solid var(--admin-line);
|
|
877
|
+
vertical-align: middle;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
tr:last-child td {
|
|
881
|
+
border-bottom: 0;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
.subtle {
|
|
885
|
+
display: block;
|
|
886
|
+
margin-top: 2px;
|
|
887
|
+
color: var(--admin-muted);
|
|
888
|
+
font-size: 12px;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
.money,
|
|
892
|
+
.action {
|
|
893
|
+
text-align: right;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.check-row {
|
|
897
|
+
display: grid;
|
|
898
|
+
grid-template-columns: minmax(120px, 0.36fr) minmax(0, 1fr) auto;
|
|
899
|
+
gap: 10px;
|
|
900
|
+
align-items: center;
|
|
901
|
+
padding: 10px 0;
|
|
902
|
+
border-bottom: 1px solid var(--admin-line);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.check-row:last-child {
|
|
906
|
+
border-bottom: 0;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.check-row.blocking {
|
|
910
|
+
margin: 0 -8px;
|
|
911
|
+
padding: 10px 8px;
|
|
912
|
+
border: 1px solid #f1c883;
|
|
913
|
+
border-radius: var(--admin-radius-sm);
|
|
914
|
+
background: #fff8ec;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.check-row b {
|
|
918
|
+
font-size: 13px;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.check-row p {
|
|
922
|
+
margin: 2px 0 0;
|
|
923
|
+
color: var(--admin-muted);
|
|
924
|
+
font-size: 12px;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.type-sample h3 {
|
|
928
|
+
margin: 0 0 8px;
|
|
929
|
+
font-size: 25px;
|
|
930
|
+
line-height: 1.12;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.type-sample p {
|
|
934
|
+
margin: 0;
|
|
935
|
+
color: var(--admin-muted);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.note {
|
|
939
|
+
margin: 0;
|
|
940
|
+
color: var(--admin-muted);
|
|
941
|
+
font-size: 13px;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
@media (max-width: 1120px) {
|
|
945
|
+
.shell {
|
|
946
|
+
grid-template-columns: 76px minmax(0, 1fr);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.brand b,
|
|
950
|
+
.brand span,
|
|
951
|
+
.store,
|
|
952
|
+
.nav span,
|
|
953
|
+
.nav-subnav,
|
|
954
|
+
.health {
|
|
955
|
+
display: none;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.sidebar {
|
|
959
|
+
align-items: center;
|
|
960
|
+
padding-inline: 12px;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.nav a {
|
|
964
|
+
justify-content: center;
|
|
965
|
+
padding: 0;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.grid.cols-4,
|
|
969
|
+
.grid.cols-3,
|
|
970
|
+
.order-assist-grid {
|
|
971
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
@media (max-width: 760px) {
|
|
976
|
+
html,
|
|
977
|
+
body {
|
|
978
|
+
height: auto;
|
|
979
|
+
min-height: 100%;
|
|
980
|
+
overflow-x: hidden;
|
|
981
|
+
overflow-y: auto;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.shell {
|
|
985
|
+
grid-template-columns: 1fr;
|
|
986
|
+
height: auto;
|
|
987
|
+
min-height: 100vh;
|
|
988
|
+
overflow: visible;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.main {
|
|
992
|
+
height: auto;
|
|
993
|
+
min-height: 0;
|
|
994
|
+
overflow: visible;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.sidebar {
|
|
998
|
+
position: static;
|
|
999
|
+
height: auto;
|
|
1000
|
+
overflow: visible;
|
|
1001
|
+
flex-direction: row;
|
|
1002
|
+
align-items: center;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.nav {
|
|
1006
|
+
display: none;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.mobile-tabbar {
|
|
1010
|
+
position: sticky;
|
|
1011
|
+
top: 0;
|
|
1012
|
+
z-index: 29;
|
|
1013
|
+
display: flex;
|
|
1014
|
+
padding-inline: 16px;
|
|
1015
|
+
background: rgba(245, 247, 246, 0.94);
|
|
1016
|
+
backdrop-filter: blur(16px);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.topbar,
|
|
1020
|
+
.ship-gate,
|
|
1021
|
+
.risk-alert,
|
|
1022
|
+
.section-title,
|
|
1023
|
+
.panel-head {
|
|
1024
|
+
align-items: flex-start;
|
|
1025
|
+
flex-direction: column;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.content,
|
|
1029
|
+
.topbar {
|
|
1030
|
+
padding-inline: 16px;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
.floating-ask {
|
|
1034
|
+
right: 16px;
|
|
1035
|
+
bottom: 16px;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
[data-admin-panel],
|
|
1039
|
+
[data-admin-guide-section] {
|
|
1040
|
+
scroll-margin-top: 16px;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.actions {
|
|
1044
|
+
width: 100%;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
.actions .btn {
|
|
1048
|
+
flex: 1 1 150px;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.grid.cols-4,
|
|
1052
|
+
.grid.cols-3,
|
|
1053
|
+
.grid.cols-2,
|
|
1054
|
+
.order-assist-grid {
|
|
1055
|
+
grid-template-columns: 1fr;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
.check-row {
|
|
1059
|
+
grid-template-columns: 1fr;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
</style>
|
|
1063
|
+
</head>
|
|
1064
|
+
<body>
|
|
1065
|
+
<svg aria-hidden="true" style="position:absolute;width:0;height:0;overflow:hidden">
|
|
1066
|
+
<symbol id="i-orders" viewBox="0 0 24 24"><path d="M7 4h10a2 2 0 0 1 2 2v14l-3-2-2 2-2-2-2 2-3 2V6a2 2 0 0 1 2-2Z"></path><path d="M9 9h6M9 13h6"></path></symbol>
|
|
1067
|
+
<symbol id="i-box" viewBox="0 0 24 24"><path d="m3 7 9-4 9 4-9 4-9-4Z"></path><path d="M3 7v10l9 4 9-4V7"></path><path d="M12 11v10"></path></symbol>
|
|
1068
|
+
<symbol id="i-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H7a4 4 0 0 0-4 4v2"></path><circle cx="9.5" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.9"></path><path d="M16 3.1a4 4 0 0 1 0 7.8"></path></symbol>
|
|
1069
|
+
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 3 20 6v6c0 5-3.4 8.4-8 9-4.6-.6-8-4-8-9V6l8-3Z"></path><path d="m8.8 12 2.2 2.2L15.8 9"></path></symbol>
|
|
1070
|
+
<symbol id="i-chart" viewBox="0 0 24 24"><path d="M4 19V5"></path><path d="M4 19h16"></path><path d="M8 16v-5"></path><path d="M12 16V8"></path><path d="M16 16v-3"></path></symbol>
|
|
1071
|
+
<symbol id="i-workflows" viewBox="0 0 24 24"><path d="M6 5h6a4 4 0 0 1 4 4v1"></path><path d="m19 7-3 3-3-3"></path><path d="M18 19h-6a4 4 0 0 1-4-4v-1"></path><path d="m5 17 3-3 3 3"></path><circle cx="6" cy="5" r="2"></circle><circle cx="18" cy="19" r="2"></circle></symbol>
|
|
1072
|
+
<symbol id="i-ask" viewBox="0 0 24 24"><path d="M21 12a8 8 0 0 1-8 8H7l-4 3 1.4-5A8 8 0 1 1 21 12Z"></path><path d="M9.5 9.2A2.7 2.7 0 0 1 12 7.6c1.6 0 2.8 1 2.8 2.4 0 1.7-1.6 2.2-2.4 3-.4.4-.5.7-.5 1.4"></path><path d="M12 17h.01"></path></symbol>
|
|
1073
|
+
<symbol id="i-gear" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2 3-.2-.1a1.8 1.8 0 0 0-2-.2 1.8 1.8 0 0 0-1 1.6v.2h-5v-.2a1.8 1.8 0 0 0-1-1.6 1.8 1.8 0 0 0-2 .2l-.2.1-2-3 .1-.1A1.7 1.7 0 0 0 4.8 15a1.8 1.8 0 0 0-1.5-1H3v-4h.3a1.8 1.8 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.9l-.1-.1 2-3 .2.1a1.8 1.8 0 0 0 2 .2 1.8 1.8 0 0 0 1-1.6V2.5h5v.2a1.8 1.8 0 0 0 1 1.6 1.8 1.8 0 0 0 2-.2l.2-.1 2 3-.1.1a1.7 1.7 0 0 0-.3 1.9 1.8 1.8 0 0 0 1.5 1h.3v4h-.3a1.8 1.8 0 0 0-1.5 1Z"></path></symbol>
|
|
1074
|
+
<symbol id="i-check" viewBox="0 0 24 24"><path d="m5 12 4.2 4.2L19 6.8"></path></symbol>
|
|
1075
|
+
<symbol id="i-alert" viewBox="0 0 24 24"><path d="M12 3 22 20H2L12 3Z"></path><path d="M12 9v5"></path><path d="M12 17h.01"></path></symbol>
|
|
1076
|
+
<symbol id="i-card" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"></rect><path d="M3 10h18"></path><path d="M7 15h4"></path></symbol>
|
|
1077
|
+
<symbol id="i-truck" viewBox="0 0 24 24"><path d="M3 7h11v9H3z"></path><path d="M14 10h4l3 3v3h-7z"></path><circle cx="7" cy="18" r="2"></circle><circle cx="17" cy="18" r="2"></circle></symbol>
|
|
1078
|
+
<symbol id="i-file" viewBox="0 0 24 24"><path d="M7 3h7l5 5v13H7z"></path><path d="M14 3v5h5"></path><path d="M10 13h6M10 17h4"></path></symbol>
|
|
1079
|
+
<symbol id="i-refresh" viewBox="0 0 24 24"><path d="M20 12a8 8 0 0 1-13.6 5.7L4 15"></path><path d="M4 20v-5h5"></path><path d="M4 12A8 8 0 0 1 17.6 6.3L20 9"></path><path d="M20 4v5h-5"></path></symbol>
|
|
1080
|
+
<symbol id="i-export" viewBox="0 0 24 24"><path d="M12 3v12"></path><path d="m7 8 5-5 5 5"></path><path d="M5 15v4h14v-4"></path></symbol>
|
|
1081
|
+
</svg>
|
|
1082
|
+
|
|
1083
|
+
<div class="shell">
|
|
1084
|
+
<aside class="sidebar" aria-label="Admin navigation">
|
|
1085
|
+
<div class="brand">
|
|
1086
|
+
<div class="brand-mark">T</div>
|
|
1087
|
+
<div>
|
|
1088
|
+
<b>Token of Trust</b>
|
|
1089
|
+
<span>Storefront Admin</span>
|
|
1090
|
+
</div>
|
|
1091
|
+
</div>
|
|
1092
|
+
|
|
1093
|
+
<div class="store">
|
|
1094
|
+
<strong>{tenantLabel}</strong>
|
|
1095
|
+
<span>{tenant.appDomain}</span>
|
|
1096
|
+
</div>
|
|
1097
|
+
|
|
1098
|
+
{isStaff && pickerTenants.length > 0 && (
|
|
1099
|
+
<form class="tenant-picker" method="get" data-admin-tenant-picker>
|
|
1100
|
+
<label for="admin-tenant-picker-select">Operating as (internal)</label>
|
|
1101
|
+
<select
|
|
1102
|
+
id="admin-tenant-picker-select"
|
|
1103
|
+
name="asTenant"
|
|
1104
|
+
data-admin-tenant-picker-select
|
|
1105
|
+
>
|
|
1106
|
+
<option value={tenant.appDomain} selected={tenantEnvelope.targetTenant === tenant.appDomain}>
|
|
1107
|
+
{tenant.appDomain} (this app)
|
|
1108
|
+
</option>
|
|
1109
|
+
{pickerTenants
|
|
1110
|
+
.filter((t) => t.resource !== tenant.appDomain)
|
|
1111
|
+
.map((t) => (
|
|
1112
|
+
<option value={t.resource} selected={tenantEnvelope.targetTenant === t.resource}>
|
|
1113
|
+
{t.resource}
|
|
1114
|
+
</option>
|
|
1115
|
+
))}
|
|
1116
|
+
</select>
|
|
1117
|
+
</form>
|
|
1118
|
+
)}
|
|
1119
|
+
|
|
1120
|
+
{deniedTarget && (
|
|
1121
|
+
<div class="tenant-picker-denied" role="alert">
|
|
1122
|
+
Access to <code>{deniedTarget}</code> is not authorized for your account.
|
|
1123
|
+
</div>
|
|
1124
|
+
)}
|
|
1125
|
+
|
|
1126
|
+
<nav class="nav">
|
|
1127
|
+
<a href="#orders" data-admin-tab-link><svg><use href="#i-file"></use></svg><span>Orders</span></a>
|
|
1128
|
+
<a href="#products" data-admin-tab-link><svg><use href="#i-box"></use></svg><span>Products</span></a>
|
|
1129
|
+
<a href="#customers" data-admin-tab-link><svg><use href="#i-users"></use></svg><span>Customers</span></a>
|
|
1130
|
+
<a href="#fulfillment" data-admin-tab-link><svg><use href="#i-truck"></use></svg><span>Fulfillment</span></a>
|
|
1131
|
+
<a href="#reporting" data-admin-tab-link><svg><use href="#i-chart"></use></svg><span>Reporting</span></a>
|
|
1132
|
+
<a href="#ai-workflows" data-admin-tab-link><svg><use href="#i-workflows"></use></svg><span>AI Workflows</span></a>
|
|
1133
|
+
<a href="#settings" data-admin-tab-link><svg><use href="#i-gear"></use></svg><span>Settings</span></a>
|
|
1134
|
+
</nav>
|
|
1135
|
+
|
|
1136
|
+
<div class="health" aria-label="System status">
|
|
1137
|
+
<div class="health-row"><span>Order sync</span><strong>Live</strong></div>
|
|
1138
|
+
<div class="health-row"><span>Payments</span><strong>OK</strong></div>
|
|
1139
|
+
<div class="health-row"><span>Risk checks</span><strong>OK</strong></div>
|
|
1140
|
+
</div>
|
|
1141
|
+
</aside>
|
|
1142
|
+
|
|
1143
|
+
<main class="main">
|
|
1144
|
+
<header class="topbar">
|
|
1145
|
+
<div class="page-title">
|
|
1146
|
+
<h1>Storefront Admin</h1>
|
|
1147
|
+
<p>{tenantLabel} · operations workspace · <code>{adminPath}</code></p>
|
|
1148
|
+
</div>
|
|
1149
|
+
</header>
|
|
1150
|
+
|
|
1151
|
+
<div class="tabs mobile-tabbar" role="tablist" aria-label="Admin work areas">
|
|
1152
|
+
<a class="tab" href="#orders" role="tab" aria-controls="orders" aria-selected="false" data-admin-tab-link>Orders</a>
|
|
1153
|
+
<a class="tab" href="#products" role="tab" aria-controls="products" aria-selected="false" data-admin-tab-link>Products</a>
|
|
1154
|
+
<a class="tab" href="#customers" role="tab" aria-controls="customers" aria-selected="false" data-admin-tab-link>Customers</a>
|
|
1155
|
+
<a class="tab" href="#fulfillment" role="tab" aria-controls="fulfillment" aria-selected="false" data-admin-tab-link>Fulfillment</a>
|
|
1156
|
+
<a class="tab" href="#reporting" role="tab" aria-controls="reporting" aria-selected="false" data-admin-tab-link>Reporting</a>
|
|
1157
|
+
<a class="tab" href="#ai-workflows" role="tab" aria-controls="ai-workflows" aria-selected="false" data-admin-tab-link>AI Workflows</a>
|
|
1158
|
+
<a class="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false" data-admin-tab-link>Settings</a>
|
|
1159
|
+
</div>
|
|
1160
|
+
|
|
1161
|
+
<div class="content">
|
|
1162
|
+
{canViewInternalGuide && (
|
|
1163
|
+
<section class="band" aria-labelledby="foundation-title" data-admin-guide-section>
|
|
1164
|
+
<div class="section-title">
|
|
1165
|
+
<div>
|
|
1166
|
+
<h2 id="foundation-title">Foundation</h2>
|
|
1167
|
+
<p>Quiet, dense, merchant-first operations. Compliance and risk are visible, but only loud when they block shipping or sale completion.</p>
|
|
1168
|
+
</div>
|
|
1169
|
+
<span class="chip violet">Preview · noindex</span>
|
|
1170
|
+
</div>
|
|
1171
|
+
|
|
1172
|
+
<div class="grid cols-4">
|
|
1173
|
+
<div class="metric"><span>Primary task</span><strong>Ship</strong><small>Orders lead with readiness and fulfillment</small></div>
|
|
1174
|
+
<div class="metric"><span>Density</span><strong>Table</strong><small>Collapsed viewer supports rapid scanning</small></div>
|
|
1175
|
+
<div class="metric"><span>Risk posture</span><strong>Quiet</strong><small>Warn only when merchant action is needed</small></div>
|
|
1176
|
+
<div class="metric"><span>Branding</span><strong>White label</strong><small>No vendor checkout/provider naming</small></div>
|
|
1177
|
+
</div>
|
|
1178
|
+
</section>
|
|
1179
|
+
)}
|
|
1180
|
+
|
|
1181
|
+
{canViewInternalGuide && (
|
|
1182
|
+
<section id="work-areas" class="band" aria-labelledby="work-areas-title" data-admin-internal-only>
|
|
1183
|
+
<div class="section-title">
|
|
1184
|
+
<div>
|
|
1185
|
+
<h2 id="work-areas-title">Same-Tree Work Area Hooks</h2>
|
|
1186
|
+
<p>Each admin tab has a stable anchor, component file, and layout boundary so agents can work in one tree without stepping on the shared shell.</p>
|
|
1187
|
+
</div>
|
|
1188
|
+
<span class="chip teal">Worktree-free scaffold</span>
|
|
1189
|
+
</div>
|
|
1190
|
+
|
|
1191
|
+
<div class="panel">
|
|
1192
|
+
<div class="panel-head">
|
|
1193
|
+
<h3><svg><use href="#i-box"></use></svg>Admin Tab Map</h3>
|
|
1194
|
+
<span class="chip">Shared route: apps/storefront/src/pages/admin.astro</span>
|
|
1195
|
+
</div>
|
|
1196
|
+
<div class="panel-body grid">
|
|
1197
|
+
<div class="tabs admin-tabs" role="tablist" aria-label="Admin work areas">
|
|
1198
|
+
<a id="admin-tab-orders" class="tab active" href="#orders" role="tab" aria-controls="orders" aria-selected="true" data-admin-tab-link>Orders</a>
|
|
1199
|
+
<a id="admin-tab-products" class="tab" href="#products" role="tab" aria-controls="products" aria-selected="false" data-admin-tab-link>Products</a>
|
|
1200
|
+
<a id="admin-tab-customers" class="tab" href="#customers" role="tab" aria-controls="customers" aria-selected="false" data-admin-tab-link>Customers</a>
|
|
1201
|
+
<a id="admin-tab-fulfillment" class="tab" href="#fulfillment" role="tab" aria-controls="fulfillment" aria-selected="false" data-admin-tab-link>Fulfillment</a>
|
|
1202
|
+
<a id="admin-tab-reporting" class="tab" href="#reporting" role="tab" aria-controls="reporting" aria-selected="false" data-admin-tab-link>Reporting</a>
|
|
1203
|
+
<a id="admin-tab-ai-workflows" class="tab" href="#ai-workflows" role="tab" aria-controls="ai-workflows" aria-selected="false" data-admin-tab-link>AI Workflows</a>
|
|
1204
|
+
<a id="admin-tab-settings" class="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false" data-admin-tab-link>Settings</a>
|
|
1205
|
+
</div>
|
|
1206
|
+
|
|
1207
|
+
<div class="grid cols-3" data-admin-tab-help>
|
|
1208
|
+
<div class="field">
|
|
1209
|
+
<label>Shared shell</label>
|
|
1210
|
+
<strong>Admin chrome, tokens, icons, component primitives</strong>
|
|
1211
|
+
<p>Touch the route only for shared patterns or new top-level hooks.</p>
|
|
1212
|
+
</div>
|
|
1213
|
+
<div class="field">
|
|
1214
|
+
<label>Tab ownership</label>
|
|
1215
|
+
<strong>One component per work area</strong>
|
|
1216
|
+
<p>Products, Customers, Fulfillment, Reporting, AI Workflows, and Settings each have their own file under components/admin.</p>
|
|
1217
|
+
</div>
|
|
1218
|
+
<div class="field">
|
|
1219
|
+
<label>Escalation</label>
|
|
1220
|
+
<strong>Use worktrees for shared contracts or same-tab collisions</strong>
|
|
1221
|
+
<p>This scaffold is reasonable for UX tab work. Route, data model, and auth changes still deserve coordination.</p>
|
|
1222
|
+
</div>
|
|
1223
|
+
</div>
|
|
1224
|
+
</div>
|
|
1225
|
+
</div>
|
|
1226
|
+
</section>
|
|
1227
|
+
)}
|
|
1228
|
+
|
|
1229
|
+
{canViewInternalGuide && (
|
|
1230
|
+
<section id="settings-theme" class="band" aria-labelledby="tokens-title" data-admin-guide-section>
|
|
1231
|
+
<div class="section-title">
|
|
1232
|
+
<div>
|
|
1233
|
+
<h2 id="tokens-title">Theme Tokens</h2>
|
|
1234
|
+
<p>These variables define the administrative interface theme independently of the public storefront theme.</p>
|
|
1235
|
+
</div>
|
|
1236
|
+
</div>
|
|
1237
|
+
|
|
1238
|
+
<div class="panel">
|
|
1239
|
+
<div class="panel-head">
|
|
1240
|
+
<h3><svg><use href="#i-card"></use></svg>Color</h3>
|
|
1241
|
+
<span class="chip teal">Admin palette</span>
|
|
1242
|
+
</div>
|
|
1243
|
+
<div class="panel-body">
|
|
1244
|
+
<div class="swatches">
|
|
1245
|
+
<div class="swatch"><div class="swatch-color" style="background:#f5f7f6"></div><div class="swatch-meta"><b>Background</b><code>--admin-bg</code></div></div>
|
|
1246
|
+
<div class="swatch"><div class="swatch-color" style="background:#ffffff"></div><div class="swatch-meta"><b>Surface</b><code>--admin-surface</code></div></div>
|
|
1247
|
+
<div class="swatch"><div class="swatch-color" style="background:#16211f"></div><div class="swatch-meta"><b>Ink</b><code>--admin-ink</code></div></div>
|
|
1248
|
+
<div class="swatch"><div class="swatch-color" style="background:#007f79"></div><div class="swatch-meta"><b>Action</b><code>--admin-teal</code></div></div>
|
|
1249
|
+
<div class="swatch"><div class="swatch-color" style="background:#137a55"></div><div class="swatch-meta"><b>Ready</b><code>--admin-green</code></div></div>
|
|
1250
|
+
<div class="swatch"><div class="swatch-color" style="background:#a45d00"></div><div class="swatch-meta"><b>Review</b><code>--admin-amber</code></div></div>
|
|
1251
|
+
<div class="swatch"><div class="swatch-color" style="background:#b42318"></div><div class="swatch-meta"><b>Blocked</b><code>--admin-red</code></div></div>
|
|
1252
|
+
<div class="swatch"><div class="swatch-color" style="background:#2458c7"></div><div class="swatch-meta"><b>Info</b><code>--admin-blue</code></div></div>
|
|
1253
|
+
</div>
|
|
1254
|
+
</div>
|
|
1255
|
+
</div>
|
|
1256
|
+
|
|
1257
|
+
<div class="grid cols-2">
|
|
1258
|
+
<div class="panel type-sample">
|
|
1259
|
+
<div class="panel-head"><h3>Typography</h3><span class="chip">0 letter spacing</span></div>
|
|
1260
|
+
<div class="panel-body">
|
|
1261
|
+
<h3>Order #2248206305</h3>
|
|
1262
|
+
<p>Use system UI for fast operational scanning. Reserve 22-27px headings for page/detail titles, 15-16px for section heads, and 12-13px for dense metadata.</p>
|
|
1263
|
+
</div>
|
|
1264
|
+
</div>
|
|
1265
|
+
<div class="panel">
|
|
1266
|
+
<div class="panel-head"><h3>Shape and Space</h3><span class="chip">8px max cards</span></div>
|
|
1267
|
+
<div class="panel-body grid cols-2">
|
|
1268
|
+
<div class="field"><label>Radius</label><strong>8px / 6px</strong><p>Cards, panels, form controls</p></div>
|
|
1269
|
+
<div class="field"><label>Grid gap</label><strong>12-14px</strong><p>Dense, predictable rhythm</p></div>
|
|
1270
|
+
<div class="field"><label>Hit target</label><strong>38-44px</strong><p>Buttons and controls</p></div>
|
|
1271
|
+
<div class="field"><label>Shadow</label><strong>Subtle</strong><p>Only for active detail surfaces</p></div>
|
|
1272
|
+
</div>
|
|
1273
|
+
</div>
|
|
1274
|
+
</div>
|
|
1275
|
+
</section>
|
|
1276
|
+
|
|
1277
|
+
<section id="settings-components" class="band" aria-labelledby="components-title" data-admin-guide-section>
|
|
1278
|
+
<div class="section-title">
|
|
1279
|
+
<div>
|
|
1280
|
+
<h2 id="components-title">Core Components</h2>
|
|
1281
|
+
<p>Reusable elements for orders, fulfillment, customers, compliance, and risk workflows.</p>
|
|
1282
|
+
</div>
|
|
1283
|
+
</div>
|
|
1284
|
+
|
|
1285
|
+
<div class="grid cols-2">
|
|
1286
|
+
<div class="panel">
|
|
1287
|
+
<div class="panel-head"><h3><svg><use href="#i-check"></use></svg>Buttons and Chips</h3></div>
|
|
1288
|
+
<div class="panel-body grid">
|
|
1289
|
+
<div class="actions">
|
|
1290
|
+
<button class="btn primary" type="button"><svg><use href="#i-truck"></use></svg>Fulfill</button>
|
|
1291
|
+
<button class="btn" type="button"><svg><use href="#i-export"></use></svg>Export</button>
|
|
1292
|
+
<button class="icon-btn" type="button" aria-label="Refresh"><svg><use href="#i-refresh"></use></svg></button>
|
|
1293
|
+
</div>
|
|
1294
|
+
<div class="actions">
|
|
1295
|
+
<span class="chip green">Paid</span>
|
|
1296
|
+
<span class="chip teal">Ready</span>
|
|
1297
|
+
<span class="chip blue">Excise</span>
|
|
1298
|
+
<span class="chip amber">Review</span>
|
|
1299
|
+
<span class="chip red">Blocked</span>
|
|
1300
|
+
<span class="chip violet">Test store</span>
|
|
1301
|
+
</div>
|
|
1302
|
+
</div>
|
|
1303
|
+
</div>
|
|
1304
|
+
|
|
1305
|
+
<div class="panel">
|
|
1306
|
+
<div class="panel-head"><h3><svg><use href="#i-file"></use></svg>Tabs</h3></div>
|
|
1307
|
+
<div class="panel-body">
|
|
1308
|
+
<div class="tabs" role="tablist" aria-label="Static tab sample">
|
|
1309
|
+
<button class="tab active" type="button">Order</button>
|
|
1310
|
+
<button class="tab" type="button">Customer</button>
|
|
1311
|
+
<button class="tab" type="button">Compliance</button>
|
|
1312
|
+
<button class="tab" type="button">Order Risk</button>
|
|
1313
|
+
<button class="tab" type="button">Activity</button>
|
|
1314
|
+
</div>
|
|
1315
|
+
<p class="note" style="margin-top:12px">Compliance and Order Risk are secondary tabs. Top alerts jump directly to them only when action is required.</p>
|
|
1316
|
+
</div>
|
|
1317
|
+
</div>
|
|
1318
|
+
</div>
|
|
1319
|
+
</section>
|
|
1320
|
+
)}
|
|
1321
|
+
|
|
1322
|
+
<OrdersWorkspace envelope={tenantEnvelope} />
|
|
1323
|
+
<AdminProductsTab envelope={tenantEnvelope} />
|
|
1324
|
+
<AdminCustomersTab envelope={tenantEnvelope} />
|
|
1325
|
+
<AdminFulfillmentTab envelope={tenantEnvelope} />
|
|
1326
|
+
<AdminReportingTab envelope={tenantEnvelope} />
|
|
1327
|
+
<AdminAIWorkflowsTab envelope={tenantEnvelope} />
|
|
1328
|
+
<AdminSettingsTab envelope={tenantEnvelope} />
|
|
1329
|
+
|
|
1330
|
+
{canViewInternalGuide && (
|
|
1331
|
+
<section id="risk" class="band" aria-labelledby="risk-title" data-admin-guide-section>
|
|
1332
|
+
<div class="section-title">
|
|
1333
|
+
<div>
|
|
1334
|
+
<h2 id="risk-title">Order Risk</h2>
|
|
1335
|
+
<p>Risk is quiet at low levels and becomes top-level only when it blocks fulfillment.</p>
|
|
1336
|
+
</div>
|
|
1337
|
+
</div>
|
|
1338
|
+
|
|
1339
|
+
<div class="risk-alert">
|
|
1340
|
+
<div class="risk-alert-main">
|
|
1341
|
+
<svg><use href="#i-alert"></use></svg>
|
|
1342
|
+
<div>
|
|
1343
|
+
<b>Order Risk: Review required</b>
|
|
1344
|
+
<span>Risk checks need review before this order can ship.</span>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
<button class="btn" type="button">View risk</button>
|
|
1348
|
+
</div>
|
|
1349
|
+
|
|
1350
|
+
<div class="panel">
|
|
1351
|
+
<div class="panel-head"><h3><svg><use href="#i-alert"></use></svg>Order Risk Tab</h3><span class="chip amber">Review required</span></div>
|
|
1352
|
+
<div class="panel-body grid">
|
|
1353
|
+
<div class="grid cols-2">
|
|
1354
|
+
<div class="field"><label>Risk status</label><strong>Review required</strong><p>Risk checks need review before this order can ship.</p></div>
|
|
1355
|
+
<div class="field"><label>Risk score</label><strong>Needs review</strong><p>Review risk</p></div>
|
|
1356
|
+
</div>
|
|
1357
|
+
<div class="check-row blocking">
|
|
1358
|
+
<div><b>Gateway fraud risk</b><p>Payment is authorized but not fully captured.</p></div>
|
|
1359
|
+
<strong>Pending capture</strong>
|
|
1360
|
+
<span class="chip amber">Action</span>
|
|
1361
|
+
</div>
|
|
1362
|
+
<div class="check-row blocking">
|
|
1363
|
+
<div><b>Manual review</b><p>Resolve the hold before creating a shipping label.</p></div>
|
|
1364
|
+
<strong>Required</strong>
|
|
1365
|
+
<span class="chip amber">Action</span>
|
|
1366
|
+
</div>
|
|
1367
|
+
<div class="check-row">
|
|
1368
|
+
<div><b>Customer history</b><p>Returning customers show order count, member-since date, and lifetime spend.</p></div>
|
|
1369
|
+
<strong>3 orders</strong>
|
|
1370
|
+
<span class="chip teal">OK</span>
|
|
1371
|
+
</div>
|
|
1372
|
+
</div>
|
|
1373
|
+
</div>
|
|
1374
|
+
</section>
|
|
1375
|
+
)}
|
|
1376
|
+
|
|
1377
|
+
{canViewInternalGuide && (
|
|
1378
|
+
<section id="compliance" class="band" aria-labelledby="compliance-title" data-admin-guide-section>
|
|
1379
|
+
<div class="section-title">
|
|
1380
|
+
<div>
|
|
1381
|
+
<h2 id="compliance-title">Compliance</h2>
|
|
1382
|
+
<p>Relevant regulated-sale details stay available without becoming the hero of the order page.</p>
|
|
1383
|
+
</div>
|
|
1384
|
+
</div>
|
|
1385
|
+
|
|
1386
|
+
<div class="panel">
|
|
1387
|
+
<div class="panel-head"><h3><svg><use href="#i-shield"></use></svg>Compliance Tab</h3><span class="chip green">Ready to Ship</span></div>
|
|
1388
|
+
<div class="panel-body grid">
|
|
1389
|
+
<div class="check-row">
|
|
1390
|
+
<div><b>Identity and age</b><p>21+ assurance completed before checkout.</p></div>
|
|
1391
|
+
<strong>Passed</strong>
|
|
1392
|
+
<span class="chip green">OK</span>
|
|
1393
|
+
</div>
|
|
1394
|
+
<div class="check-row">
|
|
1395
|
+
<div><b>Excise tax</b><p>Minnesota jurisdiction via tot_excise_tax.</p></div>
|
|
1396
|
+
<strong>$142.50 collected</strong>
|
|
1397
|
+
<span class="chip blue">OK</span>
|
|
1398
|
+
</div>
|
|
1399
|
+
<div class="check-row">
|
|
1400
|
+
<div><b>PACT workflow</b><p>Shipping and reporting placeholders are ready for fulfillment.</p></div>
|
|
1401
|
+
<strong>Ready</strong>
|
|
1402
|
+
<span class="chip teal">OK</span>
|
|
1403
|
+
</div>
|
|
1404
|
+
<div class="check-row">
|
|
1405
|
+
<div><b>Evidence</b><p>Audit evidence available if needed.</p></div>
|
|
1406
|
+
<strong>Logged</strong>
|
|
1407
|
+
<span class="chip teal">OK</span>
|
|
1408
|
+
</div>
|
|
1409
|
+
</div>
|
|
1410
|
+
</div>
|
|
1411
|
+
</section>
|
|
1412
|
+
)}
|
|
1413
|
+
|
|
1414
|
+
{canViewInternalGuide && (
|
|
1415
|
+
<section class="band" aria-labelledby="copy-title" data-admin-guide-section>
|
|
1416
|
+
<div class="section-title">
|
|
1417
|
+
<div>
|
|
1418
|
+
<h2 id="copy-title">Language Rules</h2>
|
|
1419
|
+
<p>Terminology for the white-labeled admin experience.</p>
|
|
1420
|
+
</div>
|
|
1421
|
+
</div>
|
|
1422
|
+
<div class="grid cols-3">
|
|
1423
|
+
<div class="field"><label>Green state</label><strong>Ready to Ship</strong><p>Use for paid, checked, shippable orders.</p></div>
|
|
1424
|
+
<div class="field"><label>Blocking state</label><strong>Hold: Review Required</strong><p>Use when the merchant must act before shipment.</p></div>
|
|
1425
|
+
<div class="field"><label>Provider labels</label><strong>Do not show</strong><p>Use Storefront, checkout, order sync, and customer record.</p></div>
|
|
1426
|
+
</div>
|
|
1427
|
+
<p class="note">Implementation note: provider-specific identifiers can remain in server logs and engineering docs, but customer- and merchant-facing admin screens stay white-labeled.</p>
|
|
1428
|
+
</section>
|
|
1429
|
+
)}
|
|
1430
|
+
</div>
|
|
1431
|
+
</main>
|
|
1432
|
+
</div>
|
|
1433
|
+
|
|
1434
|
+
<button class="floating-ask" type="button" data-admin-ask aria-label="Ask about Orders" title="Ask about Orders">
|
|
1435
|
+
<span class="floating-ask-icon" aria-hidden="true"><svg><use href="#i-ask"></use></svg></span>
|
|
1436
|
+
<span class="floating-ask-label">
|
|
1437
|
+
<strong>Ask</strong>
|
|
1438
|
+
<small data-admin-ask-context>Orders</small>
|
|
1439
|
+
</span>
|
|
1440
|
+
</button>
|
|
1441
|
+
|
|
1442
|
+
<script is:inline nonce={cspNonce}>
|
|
1443
|
+
(() => {
|
|
1444
|
+
const tabIds = ["orders", "products", "customers", "fulfillment", "reporting", "ai-workflows", "settings"];
|
|
1445
|
+
const tabSet = new Set(tabIds);
|
|
1446
|
+
const tabHashPattern = new RegExp(`^#(${tabIds.join("|")})$`);
|
|
1447
|
+
const canViewInternalGuide = document.documentElement.dataset.internalAdminGuide === "true";
|
|
1448
|
+
const defaultTab = canViewInternalGuide ? "orders" : "settings";
|
|
1449
|
+
const askContexts = {
|
|
1450
|
+
orders: "Orders",
|
|
1451
|
+
products: "Products",
|
|
1452
|
+
customers: "Customers",
|
|
1453
|
+
fulfillment: "Fulfillment",
|
|
1454
|
+
reporting: "Reporting",
|
|
1455
|
+
"ai-workflows": "AI Workflows",
|
|
1456
|
+
settings: "Settings",
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
const panelIdFromHash = () => {
|
|
1460
|
+
const hash = window.location.hash.slice(1);
|
|
1461
|
+
if (!hash) return defaultTab;
|
|
1462
|
+
if (tabSet.has(hash)) return hash;
|
|
1463
|
+
if (!canViewInternalGuide) return defaultTab;
|
|
1464
|
+
|
|
1465
|
+
const target = hash ? document.getElementById(hash) : null;
|
|
1466
|
+
const panel = target?.closest?.("[data-admin-panel]");
|
|
1467
|
+
if (panel?.id && tabSet.has(panel.id)) return panel.id;
|
|
1468
|
+
|
|
1469
|
+
return null;
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
const setActiveTab = (id) => {
|
|
1473
|
+
if (!tabSet.has(id)) return;
|
|
1474
|
+
|
|
1475
|
+
document.documentElement.dataset.adminMode = "work-area";
|
|
1476
|
+
document.documentElement.dataset.adminTab = id;
|
|
1477
|
+
setAskContext(id);
|
|
1478
|
+
|
|
1479
|
+
document.querySelectorAll("[data-admin-panel]").forEach((panel) => {
|
|
1480
|
+
const active = panel.id === id;
|
|
1481
|
+
panel.hidden = !active;
|
|
1482
|
+
panel.setAttribute("aria-hidden", active ? "false" : "true");
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
document.querySelectorAll("[data-admin-tab-link]").forEach((link) => {
|
|
1486
|
+
const active = link.hash === `#${id}`;
|
|
1487
|
+
link.classList.toggle("active", active);
|
|
1488
|
+
|
|
1489
|
+
if (link.getAttribute("role") === "tab") {
|
|
1490
|
+
link.setAttribute("aria-selected", active ? "true" : "false");
|
|
1491
|
+
link.tabIndex = active ? 0 : -1;
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
const setGuideMode = () => {
|
|
1497
|
+
if (!canViewInternalGuide) {
|
|
1498
|
+
setActiveTab(defaultTab);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
document.documentElement.dataset.adminMode = "guide";
|
|
1503
|
+
setAskContext(defaultTab);
|
|
1504
|
+
|
|
1505
|
+
document.querySelectorAll("[data-admin-panel]").forEach((panel) => {
|
|
1506
|
+
panel.hidden = true;
|
|
1507
|
+
panel.setAttribute("aria-hidden", "true");
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
document.querySelectorAll("[data-admin-tab-link]").forEach((link) => {
|
|
1511
|
+
link.classList.remove("active");
|
|
1512
|
+
|
|
1513
|
+
if (link.getAttribute("role") === "tab") {
|
|
1514
|
+
link.setAttribute("aria-selected", "false");
|
|
1515
|
+
link.tabIndex = 0;
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
// Ask context envelope (baseline §Horizontal AI Concepts / Ask,
|
|
1521
|
+
// ai-evidence-substrate item a — mirrors lib/admin/ai/askEnvelope.ts's
|
|
1522
|
+
// buildAskEnvelope; duplicated here in plain JS because is:inline
|
|
1523
|
+
// scripts cannot import modules). No chat backend yet: this is
|
|
1524
|
+
// dev-inspectable only (window.__toTAdminAsk + a CustomEvent), never
|
|
1525
|
+
// sent over the network.
|
|
1526
|
+
const buildAskEnvelopePlain = (id) => {
|
|
1527
|
+
const root = document.documentElement.dataset;
|
|
1528
|
+
const isStaff = root.adminIsStaff === "true";
|
|
1529
|
+
const panel = document.getElementById(id);
|
|
1530
|
+
const selectedRecordId = panel?.querySelector("[data-admin-selected-id]")?.dataset.adminSelectedId || null;
|
|
1531
|
+
|
|
1532
|
+
return {
|
|
1533
|
+
tenant: {
|
|
1534
|
+
adminAppTenant: root.adminAppTenant ?? "",
|
|
1535
|
+
targetTenant: root.adminTargetTenant ?? "",
|
|
1536
|
+
operator: root.adminOperator || null,
|
|
1537
|
+
},
|
|
1538
|
+
activeTab: id,
|
|
1539
|
+
selectedRecordId,
|
|
1540
|
+
role: isStaff ? "internal_staff" : "merchant_operator",
|
|
1541
|
+
permissions: isStaff ? ["internal_admin", "cross_tenant_read"] : ["merchant_admin"],
|
|
1542
|
+
filters: {},
|
|
1543
|
+
evidenceContext: [],
|
|
1544
|
+
};
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
const setAskContext = (id) => {
|
|
1548
|
+
const label = askContexts[id] ?? "Storefront";
|
|
1549
|
+
const askButton = document.querySelector("[data-admin-ask]");
|
|
1550
|
+
const askContext = document.querySelector("[data-admin-ask-context]");
|
|
1551
|
+
|
|
1552
|
+
if (askContext) askContext.textContent = label;
|
|
1553
|
+
if (askButton) {
|
|
1554
|
+
askButton.setAttribute("aria-label", `Ask about ${label}`);
|
|
1555
|
+
askButton.setAttribute("title", `Ask about ${label}`);
|
|
1556
|
+
askButton.dataset.askScope = id;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
window.__toTAdminAsk = buildAskEnvelopePlain(id);
|
|
1560
|
+
};
|
|
1561
|
+
|
|
1562
|
+
const scrollWorkspaceToTop = () => {
|
|
1563
|
+
const scroller = document.querySelector(".main");
|
|
1564
|
+
const reset = () => {
|
|
1565
|
+
if (scroller) {
|
|
1566
|
+
scroller.scrollTo({ top: 0, left: 0 });
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
window.scrollTo({ top: 0, left: 0 });
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
reset();
|
|
1574
|
+
requestAnimationFrame(reset);
|
|
1575
|
+
setTimeout(reset, 0);
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
const scrollToHashTarget = (panelId) => {
|
|
1579
|
+
const hash = window.location.hash.slice(1);
|
|
1580
|
+
const target = hash ? document.getElementById(hash) : null;
|
|
1581
|
+
|
|
1582
|
+
if (!target || tabSet.has(hash) || target.id === panelId) {
|
|
1583
|
+
scrollWorkspaceToTop();
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
target?.scrollIntoView({ block: "start" });
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
const activateFromHash = ({ scroll = false } = {}) => {
|
|
1591
|
+
const panelId = panelIdFromHash();
|
|
1592
|
+
|
|
1593
|
+
if (panelId) {
|
|
1594
|
+
setActiveTab(panelId);
|
|
1595
|
+
const hash = window.location.hash.slice(1);
|
|
1596
|
+
if (!canViewInternalGuide && (!hash || !tabSet.has(hash))) {
|
|
1597
|
+
history.replaceState(null, "", "#settings");
|
|
1598
|
+
}
|
|
1599
|
+
if (scroll) requestAnimationFrame(() => scrollToHashTarget(panelId));
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
setGuideMode();
|
|
1604
|
+
if (scroll) {
|
|
1605
|
+
const hash = window.location.hash.slice(1);
|
|
1606
|
+
requestAnimationFrame(() => document.getElementById(hash)?.scrollIntoView({ block: "start" }));
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
document.querySelector("[data-admin-tenant-picker-select]")?.addEventListener("change", (event) => {
|
|
1611
|
+
const params = new URLSearchParams(window.location.search);
|
|
1612
|
+
params.set("asTenant", event.target.value);
|
|
1613
|
+
window.location.assign(`${window.location.pathname}?${params}${window.location.hash}`);
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
// Emit the Ask envelope on open. No chat backend yet — dev-inspectable
|
|
1617
|
+
// only via window.__toTAdminAsk (already kept current by setAskContext)
|
|
1618
|
+
// and this "tot:admin-ask" CustomEvent.
|
|
1619
|
+
document.querySelector("[data-admin-ask]")?.addEventListener("click", () => {
|
|
1620
|
+
const activeTab = document.documentElement.dataset.adminTab ?? defaultTab;
|
|
1621
|
+
const envelope = buildAskEnvelopePlain(activeTab);
|
|
1622
|
+
window.__toTAdminAsk = envelope;
|
|
1623
|
+
document.dispatchEvent(new CustomEvent("tot:admin-ask", { detail: envelope }));
|
|
1624
|
+
if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") {
|
|
1625
|
+
console.debug("[admin:ask]", envelope);
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
document.addEventListener("click", (event) => {
|
|
1630
|
+
const target = event.target instanceof Element ? event.target : event.target?.parentElement;
|
|
1631
|
+
const link = target?.closest("a[href^='#']");
|
|
1632
|
+
if (!link || !tabHashPattern.test(link.hash)) return;
|
|
1633
|
+
|
|
1634
|
+
event.preventDefault();
|
|
1635
|
+
const id = link.hash.slice(1);
|
|
1636
|
+
setActiveTab(id);
|
|
1637
|
+
history.pushState(null, "", link.hash);
|
|
1638
|
+
scrollWorkspaceToTop();
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
document.addEventListener("keydown", (event) => {
|
|
1642
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
1643
|
+
const currentTab = target?.closest("[role='tab'][data-admin-tab-link]");
|
|
1644
|
+
if (!currentTab) return;
|
|
1645
|
+
|
|
1646
|
+
// Filter to visible tabs only: the mobile tab bar and the internal
|
|
1647
|
+
// Admin Tab Map can both be present in the DOM at once (one hidden
|
|
1648
|
+
// via CSS depending on viewport/viewer), and this selector matches
|
|
1649
|
+
// role="tab" elements in either — without this filter, arrow/home/
|
|
1650
|
+
// end navigation could walk into a display:none tablist whose
|
|
1651
|
+
// .focus() is a silent no-op.
|
|
1652
|
+
const tabs = Array.from(document.querySelectorAll("[role='tab'][data-admin-tab-link]")).filter(
|
|
1653
|
+
(el) => el.offsetParent !== null,
|
|
1654
|
+
);
|
|
1655
|
+
const currentIndex = tabs.indexOf(currentTab);
|
|
1656
|
+
let nextIndex = currentIndex;
|
|
1657
|
+
|
|
1658
|
+
if (event.key === "ArrowRight") nextIndex = (currentIndex + 1) % tabs.length;
|
|
1659
|
+
if (event.key === "ArrowLeft") nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
1660
|
+
if (event.key === "Home") nextIndex = 0;
|
|
1661
|
+
if (event.key === "End") nextIndex = tabs.length - 1;
|
|
1662
|
+
|
|
1663
|
+
if (nextIndex === currentIndex) return;
|
|
1664
|
+
|
|
1665
|
+
event.preventDefault();
|
|
1666
|
+
tabs[nextIndex].focus();
|
|
1667
|
+
tabs[nextIndex].click();
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
window.addEventListener("hashchange", () => activateFromHash({ scroll: true }));
|
|
1671
|
+
activateFromHash({ scroll: Boolean(window.location.hash) });
|
|
1672
|
+
})();
|
|
1673
|
+
</script>
|
|
1674
|
+
</body>
|
|
1675
|
+
</html>
|