@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,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* <AdminFoxyCartSettingsPanel> — the "Cart & Checkout appearance (FoxyCart)"
|
|
4
|
+
* settings section. Mirrors the tot-foxycart settings surface (brand colors,
|
|
5
|
+
* per-template-type URLs, store/feature config); each visual field is
|
|
6
|
+
* prepopulated from the tenant's theme (the style-guide) and can be overridden
|
|
7
|
+
* or reset back to that theme default.
|
|
8
|
+
*
|
|
9
|
+
* Presentational at the current admin altitude: the resolve/override/reset MODEL
|
|
10
|
+
* is real (lib/commerce/foxyCartSettings), but SAVE is the tot-foxycart
|
|
11
|
+
* `/theme/foxy` PUT/refresh + Foxy color PATCH seam — surfaced, not faked.
|
|
12
|
+
*/
|
|
13
|
+
import { getFoxyCartSettingsViewModel } from "@/lib/admin/services/mock";
|
|
14
|
+
import { resolveAdminTargetTenant } from "@/config/adminTargetTenant";
|
|
15
|
+
import type { FoxyFieldGroup, FoxySettingsField } from "@/lib/commerce/foxyCartSettings";
|
|
16
|
+
|
|
17
|
+
const { envelope } = Astro.props;
|
|
18
|
+
// Resolve the TARGET tenant's config through the resolver seam (config layer),
|
|
19
|
+
// then build the view model from it — lib never reaches into the static map.
|
|
20
|
+
const targetTenant = await resolveAdminTargetTenant(envelope.targetTenant);
|
|
21
|
+
const vm = getFoxyCartSettingsViewModel(envelope, targetTenant);
|
|
22
|
+
|
|
23
|
+
const GROUP_META: Record<FoxyFieldGroup, { title: string; blurb: string }> = {
|
|
24
|
+
colors: {
|
|
25
|
+
title: "Brand colors",
|
|
26
|
+
blurb: "Foxy template colors, prepopulated from this tenant's theme tokens.",
|
|
27
|
+
},
|
|
28
|
+
templates: {
|
|
29
|
+
title: "Templates",
|
|
30
|
+
blurb: "Per-surface hosted template URLs (checkout, cart, sidecart, receipt, email).",
|
|
31
|
+
},
|
|
32
|
+
store: {
|
|
33
|
+
title: "Store & features",
|
|
34
|
+
blurb: "Store identity and checkout feature toggles. No theme default.",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const GROUP_ORDER: FoxyFieldGroup[] = ["colors", "templates", "store"];
|
|
38
|
+
const byGroup = (g: FoxyFieldGroup) => vm.fields.filter((f) => f.group === g);
|
|
39
|
+
|
|
40
|
+
const isColor = (f: FoxySettingsField) => f.kind === "color";
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<div class="panel" id="settings-cart-appearance">
|
|
44
|
+
<div class="panel-head settings-panel-head">
|
|
45
|
+
<h3><svg><use href="#i-gear"></use></svg>Cart & Checkout Appearance</h3>
|
|
46
|
+
<span class="chip blue">FoxyCart</span>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{!vm.hasStore ? (
|
|
50
|
+
<div class="panel-body">
|
|
51
|
+
<p class="note">This tenant has no FoxyCart store configured, so there is nothing to theme here.</p>
|
|
52
|
+
</div>
|
|
53
|
+
) : (
|
|
54
|
+
<div class="panel-body grid">
|
|
55
|
+
<div class="fx-head">
|
|
56
|
+
<p class="note">
|
|
57
|
+
Values are <b>prepopulated from the theme</b> (the style guide). Override any field, or
|
|
58
|
+
reset it back to the theme default. {vm.overrideCount > 0 ? `${vm.overrideCount} field(s) currently overridden.` : "No overrides yet."}
|
|
59
|
+
</p>
|
|
60
|
+
<button class="btn" type="button" disabled={vm.overrideCount === 0}>
|
|
61
|
+
<svg><use href="#i-refresh"></use></svg>Reset all to theme
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{GROUP_ORDER.map((g) => (
|
|
66
|
+
<section class="fx-group">
|
|
67
|
+
<div class="fx-group-head">
|
|
68
|
+
<h4>{GROUP_META[g].title}</h4>
|
|
69
|
+
<p>{GROUP_META[g].blurb}</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="fx-fields">
|
|
72
|
+
{byGroup(g).map((f) => (
|
|
73
|
+
<div class="fx-field">
|
|
74
|
+
<div class="fx-field-label">
|
|
75
|
+
<label>{f.label}</label>
|
|
76
|
+
<span class:list={["chip", f.source === "override" ? "amber" : "green"]}>
|
|
77
|
+
{f.source === "override" ? "Overridden" : "Theme default"}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="fx-field-control">
|
|
82
|
+
{isColor(f) ? (
|
|
83
|
+
<span class="fx-swatch" style={`background:${f.value}`} aria-hidden="true"></span>
|
|
84
|
+
) : null}
|
|
85
|
+
|
|
86
|
+
{f.kind === "boolean" ? (
|
|
87
|
+
<span class:list={["chip", f.value ? "teal" : ""]}>{f.value ? "On" : "Off"}</span>
|
|
88
|
+
) : f.kind === "select" ? (
|
|
89
|
+
<select disabled>
|
|
90
|
+
{(f.options ?? []).map((o) => (
|
|
91
|
+
<option selected={o === f.value}>{o}</option>
|
|
92
|
+
))}
|
|
93
|
+
</select>
|
|
94
|
+
) : (
|
|
95
|
+
<input
|
|
96
|
+
type="text"
|
|
97
|
+
value={String(f.value)}
|
|
98
|
+
readonly
|
|
99
|
+
class:list={["fx-input", { url: f.kind === "url" }]}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
{f.resettable && f.source === "override" ? (
|
|
104
|
+
<button class="btn ghost fx-reset" type="button" title={`Reset to theme default: ${f.default}`}>
|
|
105
|
+
Reset
|
|
106
|
+
</button>
|
|
107
|
+
) : f.resettable ? (
|
|
108
|
+
<span class="fx-default-hint">= theme</span>
|
|
109
|
+
) : (
|
|
110
|
+
<span class="fx-default-hint">no theme default</span>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{f.help ? <p class="fx-help">{f.help}</p> : null}
|
|
115
|
+
</div>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
</section>
|
|
119
|
+
))}
|
|
120
|
+
|
|
121
|
+
<p class="note fx-seam">
|
|
122
|
+
<svg><use href="#i-alert"></use></svg>
|
|
123
|
+
Saving writes to tot-foxycart: colors via Foxy <code>template_config.colors</code>, template
|
|
124
|
+
URLs via <code>PUT /theme/foxy</code> then <code>POST /theme/foxy/refresh</code>. That API hop
|
|
125
|
+
is not wired in this build.
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<style>
|
|
132
|
+
.fx-head {
|
|
133
|
+
display: flex;
|
|
134
|
+
gap: 12px;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
flex-wrap: wrap;
|
|
138
|
+
}
|
|
139
|
+
.fx-group {
|
|
140
|
+
border-top: 1px solid var(--admin-line);
|
|
141
|
+
padding-top: 14px;
|
|
142
|
+
margin-top: 4px;
|
|
143
|
+
}
|
|
144
|
+
.fx-group-head h4 {
|
|
145
|
+
margin: 0;
|
|
146
|
+
font-size: 13px;
|
|
147
|
+
}
|
|
148
|
+
.fx-group-head p {
|
|
149
|
+
margin: 2px 0 12px;
|
|
150
|
+
color: var(--admin-muted);
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
}
|
|
153
|
+
.fx-fields {
|
|
154
|
+
display: grid;
|
|
155
|
+
gap: 14px;
|
|
156
|
+
}
|
|
157
|
+
.fx-field {
|
|
158
|
+
display: grid;
|
|
159
|
+
gap: 6px;
|
|
160
|
+
padding-bottom: 12px;
|
|
161
|
+
border-bottom: 1px solid var(--admin-line);
|
|
162
|
+
}
|
|
163
|
+
.fx-field:last-child {
|
|
164
|
+
border-bottom: 0;
|
|
165
|
+
padding-bottom: 0;
|
|
166
|
+
}
|
|
167
|
+
.fx-field-label {
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
gap: 10px;
|
|
171
|
+
}
|
|
172
|
+
.fx-field-label label {
|
|
173
|
+
font-size: 13px;
|
|
174
|
+
font-weight: 640;
|
|
175
|
+
color: var(--admin-ink);
|
|
176
|
+
}
|
|
177
|
+
.fx-field-control {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 8px;
|
|
181
|
+
flex-wrap: wrap;
|
|
182
|
+
}
|
|
183
|
+
.fx-swatch {
|
|
184
|
+
width: 26px;
|
|
185
|
+
height: 26px;
|
|
186
|
+
border-radius: var(--admin-radius-sm);
|
|
187
|
+
border: 1px solid var(--admin-line-strong);
|
|
188
|
+
flex: none;
|
|
189
|
+
}
|
|
190
|
+
.fx-input {
|
|
191
|
+
min-height: 34px;
|
|
192
|
+
padding: 0 10px;
|
|
193
|
+
border: 1px solid var(--admin-line-strong);
|
|
194
|
+
border-radius: var(--admin-radius-sm);
|
|
195
|
+
background: var(--admin-surface);
|
|
196
|
+
color: var(--admin-ink);
|
|
197
|
+
min-width: 220px;
|
|
198
|
+
}
|
|
199
|
+
.fx-input.url {
|
|
200
|
+
min-width: 340px;
|
|
201
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
}
|
|
204
|
+
.fx-reset {
|
|
205
|
+
font-size: 12px;
|
|
206
|
+
}
|
|
207
|
+
.fx-default-hint {
|
|
208
|
+
color: var(--admin-muted);
|
|
209
|
+
font-size: 11px;
|
|
210
|
+
}
|
|
211
|
+
.fx-help {
|
|
212
|
+
margin: 0;
|
|
213
|
+
color: var(--admin-muted);
|
|
214
|
+
font-size: 12px;
|
|
215
|
+
}
|
|
216
|
+
.fx-seam {
|
|
217
|
+
display: flex;
|
|
218
|
+
gap: 8px;
|
|
219
|
+
align-items: flex-start;
|
|
220
|
+
border-top: 1px solid var(--admin-line);
|
|
221
|
+
padding-top: 12px;
|
|
222
|
+
}
|
|
223
|
+
.fx-seam code {
|
|
224
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
225
|
+
font-size: 11px;
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getFulfillmentViewModel, getSettingsViewModel } from "@/lib/admin/services/mock";
|
|
3
|
+
|
|
4
|
+
const { envelope } = Astro.props;
|
|
5
|
+
const fulfillment = getFulfillmentViewModel(envelope);
|
|
6
|
+
const settings = getSettingsViewModel(envelope);
|
|
7
|
+
|
|
8
|
+
const QUEUE_TONE: Record<string, string> = {
|
|
9
|
+
ready_to_ship: "green",
|
|
10
|
+
holds: "amber",
|
|
11
|
+
picked: "blue",
|
|
12
|
+
packed: "blue",
|
|
13
|
+
label_needed: "amber",
|
|
14
|
+
shipped: "teal",
|
|
15
|
+
exceptions: "red",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const readyToShipCount = fulfillment.queues.find((q) => q.key === "ready_to_ship")?.count ?? 0;
|
|
19
|
+
const holdsCount = fulfillment.queues.find((q) => q.key === "holds")?.count ?? 0;
|
|
20
|
+
const exceptionsCount = fulfillment.queues.find((q) => q.key === "exceptions")?.count ?? 0;
|
|
21
|
+
const labelsLockedCount = fulfillment.shipments.filter((s) => s.labelLocked).length;
|
|
22
|
+
const oldestSlaHours = fulfillment.shipments.reduce((max, s) => Math.max(max, s.slaAgeHours), 0);
|
|
23
|
+
|
|
24
|
+
const selectedShipment = fulfillment.shipments.find((s) => s.labelLocked) ?? fulfillment.shipments[0] ?? null;
|
|
25
|
+
const selectedAssistDraft = selectedShipment
|
|
26
|
+
? fulfillment.assistDrafts.find((d) => d.orderId === selectedShipment.orderId) ?? null
|
|
27
|
+
: null;
|
|
28
|
+
|
|
29
|
+
function queueLabel(key: string): string {
|
|
30
|
+
return fulfillment.queues.find((q) => q.key === key)?.label ?? key;
|
|
31
|
+
}
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
<!-- agent-hook:fulfillment-tab -->
|
|
35
|
+
<section id="fulfillment" class="band fulfillment-tab" aria-labelledby="admin-tab-fulfillment fulfillment-title" data-admin-panel data-agent-scope="fulfillment" role="tabpanel">
|
|
36
|
+
<div class="section-title">
|
|
37
|
+
<div>
|
|
38
|
+
<h2 id="fulfillment-title">Fulfillment</h2>
|
|
39
|
+
<p>Ship paid orders, clear exceptions, print labels, and keep fulfillment moving.</p>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="actions">
|
|
42
|
+
<button class="btn" type="button"><svg><use href="#i-export"></use></svg>Export queue</button>
|
|
43
|
+
<button class="btn primary" type="button"><svg><use href="#i-truck"></use></svg>Create batch</button>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{holdsCount + exceptionsCount > 0 && (
|
|
48
|
+
<div class="ship-gate amber fulfillment-gate" aria-labelledby="fulfillment-gate-title">
|
|
49
|
+
<div class="ship-gate-main">
|
|
50
|
+
<span class="seal"><svg><use href="#i-alert"></use></svg></span>
|
|
51
|
+
<div>
|
|
52
|
+
<b id="fulfillment-gate-title">Hold: Review Required</b>
|
|
53
|
+
<span>{holdsCount + exceptionsCount} shipment{holdsCount + exceptionsCount === 1 ? "" : "s"} need merchant action. Ready-to-ship orders stay visible and fast to label.</span>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="actions">
|
|
57
|
+
<a class="btn" href="#fulfillment-blockers">Open blockers</a>
|
|
58
|
+
<a class="btn primary" href="#fulfillment-ready">Ship ready orders</a>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
<div class="grid cols-4">
|
|
64
|
+
<div class="metric">
|
|
65
|
+
<span>Ready to ship</span>
|
|
66
|
+
<strong>{readyToShipCount}</strong>
|
|
67
|
+
<small>Paid, allocated, and checked</small>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="metric">
|
|
70
|
+
<span>Labels locked</span>
|
|
71
|
+
<strong>{labelsLockedCount}</strong>
|
|
72
|
+
<small>Blocked until the stated reason clears</small>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="metric">
|
|
75
|
+
<span>Holds</span>
|
|
76
|
+
<strong>{holdsCount}</strong>
|
|
77
|
+
<small>Action required before label creation</small>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="metric">
|
|
80
|
+
<span>Oldest SLA age</span>
|
|
81
|
+
<strong>{oldestSlaHours}h</strong>
|
|
82
|
+
<small>Since payment was captured</small>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="panel fulfillment-assist-panel">
|
|
87
|
+
<div class="panel-head fulfillment-panel-head">
|
|
88
|
+
<h3><svg><use href="#i-ask"></use></svg>Fulfillment Assist</h3>
|
|
89
|
+
<span class="chip blue">Operator support, not autopilot — draft-only, evidence-linked</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="panel-body grid">
|
|
92
|
+
{fulfillment.assistDrafts.length === 0 ? (
|
|
93
|
+
<p class="note">No exceptions or label locks right now — nothing for Assist to explain.</p>
|
|
94
|
+
) : (
|
|
95
|
+
<div class="assist-signal-grid">
|
|
96
|
+
{fulfillment.assistDrafts.map((draft) => (
|
|
97
|
+
<div class="assist-signal">
|
|
98
|
+
<span class="chip amber">{draft.orderId}</span>
|
|
99
|
+
<p>{draft.summary}</p>
|
|
100
|
+
</div>
|
|
101
|
+
))}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
<p class="note">Assist never automates a shipping decision — every output here is a draft or explanation an operator reviews before acting.</p>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="fulfillment-workspace">
|
|
109
|
+
<div class="panel fulfillment-queue-panel">
|
|
110
|
+
<div class="panel-head fulfillment-panel-head">
|
|
111
|
+
<h3><svg><use href="#i-truck"></use></svg>Fulfillment Queue</h3>
|
|
112
|
+
<span class="chip teal">{fulfillment.shipments.length} shipments</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="panel-body grid">
|
|
115
|
+
<div class="tabs" role="tablist" aria-label="Fulfillment queues">
|
|
116
|
+
{fulfillment.queues.map((queue, index) => (
|
|
117
|
+
<button class:list={["tab", { active: index === 0 }]} type="button">{queue.label} ({queue.count})</button>
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="table-wrap fulfillment-table-wrap" id="fulfillment-ready">
|
|
122
|
+
<table class="fulfillment-table">
|
|
123
|
+
<thead>
|
|
124
|
+
<tr>
|
|
125
|
+
<th>Order</th>
|
|
126
|
+
<th>Destination</th>
|
|
127
|
+
<th>Queue</th>
|
|
128
|
+
<th>SLA age</th>
|
|
129
|
+
<th>Carrier</th>
|
|
130
|
+
<th>Label</th>
|
|
131
|
+
<th class="action">Action</th>
|
|
132
|
+
</tr>
|
|
133
|
+
</thead>
|
|
134
|
+
<tbody>
|
|
135
|
+
{fulfillment.shipments.map((shipment) => (
|
|
136
|
+
<tr class:list={[{ selected: shipment.orderId === selectedShipment?.orderId }]}>
|
|
137
|
+
<td><strong>{shipment.orderId}</strong></td>
|
|
138
|
+
<td><strong>{shipment.destination}</strong><span class="subtle">{shipment.destinationReady ? "Serviceable" : "Needs review"}</span></td>
|
|
139
|
+
<td><span class:list={["chip", QUEUE_TONE[shipment.queue] ?? ""]}>{queueLabel(shipment.queue)}</span></td>
|
|
140
|
+
<td><strong>{shipment.slaAgeHours}h</strong></td>
|
|
141
|
+
<td><span class:list={["chip", shipment.carrierReady ? "green" : "amber"]}>{shipment.carrier}</span></td>
|
|
142
|
+
<td><span class:list={["chip", shipment.labelLocked ? "amber" : "green"]}>{shipment.labelLocked ? "Locked" : "Unlocked"}</span></td>
|
|
143
|
+
<td class="action"><a class="btn" href={shipment.labelLocked ? "#fulfillment-blockers" : "#fulfillment-detail"}>{shipment.labelLocked ? "Resolve" : "Create label"}</a></td>
|
|
144
|
+
</tr>
|
|
145
|
+
))}
|
|
146
|
+
</tbody>
|
|
147
|
+
</table>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<aside class="panel fulfillment-detail-panel" id="fulfillment-detail" aria-labelledby="fulfillment-detail-title">
|
|
153
|
+
{selectedShipment ? (
|
|
154
|
+
<>
|
|
155
|
+
<div class="panel-head fulfillment-panel-head">
|
|
156
|
+
<h3 id="fulfillment-detail-title"><svg><use href="#i-file"></use></svg>{selectedShipment.orderId}</h3>
|
|
157
|
+
<span class:list={["chip", selectedShipment.labelLocked ? "amber" : "green"]}>
|
|
158
|
+
{selectedShipment.labelLocked ? "Shipping Label Locked" : "Shipping Label Unlocked"}
|
|
159
|
+
</span>
|
|
160
|
+
</div>
|
|
161
|
+
<div class="panel-body grid">
|
|
162
|
+
<div class="detail-summary">
|
|
163
|
+
<div>
|
|
164
|
+
<span class="eyebrow">Destination</span>
|
|
165
|
+
<strong>{selectedShipment.destination}</strong>
|
|
166
|
+
<span class="subtle">{selectedShipment.destinationReady ? "Serviceable" : "Needs review"}</span>
|
|
167
|
+
</div>
|
|
168
|
+
<div>
|
|
169
|
+
<span class="eyebrow">Carrier</span>
|
|
170
|
+
<strong>{selectedShipment.carrier}</strong>
|
|
171
|
+
<span class="subtle">{selectedShipment.carrierReady ? "Eligible" : "Not eligible yet"}</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div>
|
|
174
|
+
<span class="eyebrow">SLA age</span>
|
|
175
|
+
<strong>{selectedShipment.slaAgeHours}h</strong>
|
|
176
|
+
<span class="subtle">Since payment captured</span>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class:list={["label-card", selectedShipment.labelLocked ? "locked" : "unlocked"]}>
|
|
181
|
+
<div>
|
|
182
|
+
<span class="eyebrow">Shipping label</span>
|
|
183
|
+
<strong>{selectedShipment.labelLocked ? "Locked until the blocker clears" : "Ready to create"}</strong>
|
|
184
|
+
<p>
|
|
185
|
+
{selectedShipment.labelLocked
|
|
186
|
+
? selectedShipment.labelLockReason ?? "Resolve the outstanding blocker to unlock this label."
|
|
187
|
+
: "Carrier and destination checks are clear — create the label whenever you're ready."}
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
<button class="btn primary" type="button" disabled={selectedShipment.labelLocked}>
|
|
191
|
+
{selectedShipment.labelLocked ? "Label locked" : "Create label"}
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{selectedShipment.labelLocked && (
|
|
196
|
+
<div class="action-strip" id="fulfillment-blockers">
|
|
197
|
+
<a class="btn primary" href="#fulfillment-readiness">Resolve blocker</a>
|
|
198
|
+
<button class="btn" type="button">Contact customer</button>
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
<div class="detail-section" id="fulfillment-readiness">
|
|
203
|
+
<div class="detail-section-head">
|
|
204
|
+
<h4>Readiness Checks</h4>
|
|
205
|
+
<span class:list={["chip", selectedShipment.carrierReady && selectedShipment.destinationReady ? "green" : "amber"]}>
|
|
206
|
+
{[!selectedShipment.carrierReady && "carrier", !selectedShipment.destinationReady && "destination"].filter(Boolean).length || "0"} blocker(s)
|
|
207
|
+
</span>
|
|
208
|
+
</div>
|
|
209
|
+
<div class:list={["check-row", { blocking: !selectedShipment.carrierReady }]}>
|
|
210
|
+
<div><b>Carrier eligibility</b><p>Carrier options unlock once the shipment's carrier check passes.</p></div>
|
|
211
|
+
<strong>{selectedShipment.carrier}</strong>
|
|
212
|
+
<span class:list={["chip", selectedShipment.carrierReady ? "green" : "amber"]}>{selectedShipment.carrierReady ? "OK" : "Waiting"}</span>
|
|
213
|
+
</div>
|
|
214
|
+
<div class:list={["check-row", { blocking: !selectedShipment.destinationReady }]}>
|
|
215
|
+
<div><b>Address and jurisdiction</b><p>Destination is serviceable once any jurisdiction or address concerns clear.</p></div>
|
|
216
|
+
<strong>{selectedShipment.destination}</strong>
|
|
217
|
+
<span class:list={["chip", selectedShipment.destinationReady ? "green" : "amber"]}>{selectedShipment.destinationReady ? "Ready" : "Review"}</span>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
{selectedAssistDraft && (
|
|
222
|
+
<div class="detail-section">
|
|
223
|
+
<div class="detail-section-head">
|
|
224
|
+
<h4>Fulfillment Assist</h4>
|
|
225
|
+
<span class="chip blue">Draft</span>
|
|
226
|
+
</div>
|
|
227
|
+
<p class="note">{selectedAssistDraft.summary}</p>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
</>
|
|
232
|
+
) : (
|
|
233
|
+
<div class="panel-body">
|
|
234
|
+
<p class="note">No shipments yet for this tenant.</p>
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
237
|
+
</aside>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div class="panel fulfillment-policy-panel">
|
|
241
|
+
<div class="panel-head fulfillment-panel-head">
|
|
242
|
+
<h3><svg><use href="#i-shield"></use></svg>Shipping Policy Context</h3>
|
|
243
|
+
<span class:list={["chip", settings.shippingRestrictions.length > 0 ? "amber" : "green"]}>
|
|
244
|
+
{settings.shippingRestrictions.length > 0 ? `${settings.shippingRestrictions.length} region(s) restricted` : "No active restrictions"}
|
|
245
|
+
</span>
|
|
246
|
+
</div>
|
|
247
|
+
<div class="panel-body grid">
|
|
248
|
+
<p class="note">Per-shipment destination readiness (above) reflects that shipment's own carrier/jurisdiction check. This panel surfaces the tenant's current shipping-restriction policy from Settings for context — see the Settings tab to manage it.</p>
|
|
249
|
+
{settings.shippingRestrictions.length > 0 && (
|
|
250
|
+
<div class="readiness-list" aria-label="Restricted regions">
|
|
251
|
+
{settings.shippingRestrictions.map((restriction) => (
|
|
252
|
+
<div class="check-row">
|
|
253
|
+
<div><b>{restriction.region}</b><p>{restriction.reason}</p></div>
|
|
254
|
+
<span class="chip amber">Restricted</span>
|
|
255
|
+
</div>
|
|
256
|
+
))}
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</section>
|
|
262
|
+
|
|
263
|
+
<style>
|
|
264
|
+
.fulfillment-tab {
|
|
265
|
+
--fulfillment-split: minmax(0, 1.2fr) minmax(360px, 0.8fr);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.fulfillment-gate {
|
|
269
|
+
scroll-margin-top: 92px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.fulfillment-assist-panel {
|
|
273
|
+
border-color: #b9c9ec;
|
|
274
|
+
background: #fbfcff;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.assist-signal-grid {
|
|
278
|
+
display: grid;
|
|
279
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
280
|
+
gap: 10px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.assist-signal {
|
|
284
|
+
display: grid;
|
|
285
|
+
gap: 7px;
|
|
286
|
+
min-width: 0;
|
|
287
|
+
padding: 12px;
|
|
288
|
+
border: 1px solid var(--admin-line);
|
|
289
|
+
border-radius: var(--admin-radius-sm);
|
|
290
|
+
background: var(--admin-surface);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.assist-signal p {
|
|
294
|
+
margin: 0;
|
|
295
|
+
color: var(--admin-muted);
|
|
296
|
+
font-size: 12px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.fulfillment-workspace {
|
|
300
|
+
display: grid;
|
|
301
|
+
grid-template-columns: var(--fulfillment-split);
|
|
302
|
+
gap: 14px;
|
|
303
|
+
align-items: start;
|
|
304
|
+
min-width: 0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.fulfillment-queue-panel,
|
|
308
|
+
.fulfillment-detail-panel {
|
|
309
|
+
min-width: 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.fulfillment-detail-panel {
|
|
313
|
+
position: sticky;
|
|
314
|
+
top: 86px;
|
|
315
|
+
box-shadow: var(--admin-shadow);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.fulfillment-panel-head {
|
|
319
|
+
min-width: 0;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.fulfillment-panel-head h3 {
|
|
323
|
+
min-width: 0;
|
|
324
|
+
overflow-wrap: anywhere;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.fulfillment-table {
|
|
328
|
+
min-width: 900px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.fulfillment-table td {
|
|
332
|
+
height: 58px;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.fulfillment-table tr.selected {
|
|
336
|
+
background: #fff8ec;
|
|
337
|
+
box-shadow: inset 3px 0 0 var(--admin-amber);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.detail-summary {
|
|
341
|
+
display: grid;
|
|
342
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
343
|
+
gap: 10px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.detail-summary > div {
|
|
347
|
+
min-width: 0;
|
|
348
|
+
padding-bottom: 10px;
|
|
349
|
+
border-bottom: 1px solid var(--admin-line);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.detail-summary strong {
|
|
353
|
+
display: block;
|
|
354
|
+
margin-top: 3px;
|
|
355
|
+
overflow-wrap: anywhere;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.eyebrow {
|
|
359
|
+
color: var(--admin-muted);
|
|
360
|
+
font-size: 11px;
|
|
361
|
+
font-weight: 760;
|
|
362
|
+
text-transform: uppercase;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.label-card {
|
|
366
|
+
display: flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
justify-content: space-between;
|
|
369
|
+
gap: 12px;
|
|
370
|
+
padding: 12px;
|
|
371
|
+
border: 1px solid var(--admin-line);
|
|
372
|
+
border-radius: var(--admin-radius);
|
|
373
|
+
background: var(--admin-surface-soft);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.label-card.locked {
|
|
377
|
+
border-color: #f1c883;
|
|
378
|
+
background: #fff8ec;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.label-card.unlocked {
|
|
382
|
+
border-color: #a6d8c0;
|
|
383
|
+
background: #eefaf3;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.label-card p {
|
|
387
|
+
margin: 4px 0 0;
|
|
388
|
+
color: var(--admin-muted);
|
|
389
|
+
font-size: 12px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.label-card .btn[disabled] {
|
|
393
|
+
border-color: #d9b978;
|
|
394
|
+
background: #f5e7c8;
|
|
395
|
+
color: var(--admin-amber);
|
|
396
|
+
cursor: not-allowed;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.action-strip {
|
|
400
|
+
display: flex;
|
|
401
|
+
flex-wrap: wrap;
|
|
402
|
+
gap: 8px;
|
|
403
|
+
scroll-margin-top: 92px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.detail-section {
|
|
407
|
+
display: grid;
|
|
408
|
+
gap: 10px;
|
|
409
|
+
padding-top: 2px;
|
|
410
|
+
scroll-margin-top: 92px;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.detail-section + .detail-section {
|
|
414
|
+
padding-top: 12px;
|
|
415
|
+
border-top: 1px solid var(--admin-line);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.detail-section-head {
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: center;
|
|
421
|
+
justify-content: space-between;
|
|
422
|
+
gap: 10px;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.detail-section-head h4 {
|
|
426
|
+
margin: 0;
|
|
427
|
+
font-size: 13px;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.fulfillment-policy-panel .readiness-list {
|
|
431
|
+
display: grid;
|
|
432
|
+
gap: 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@media (max-width: 1280px) {
|
|
436
|
+
.fulfillment-workspace {
|
|
437
|
+
grid-template-columns: minmax(0, 1fr);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.fulfillment-detail-panel {
|
|
441
|
+
position: static;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
@media (max-width: 920px) {
|
|
446
|
+
.detail-summary {
|
|
447
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.label-card {
|
|
451
|
+
align-items: flex-start;
|
|
452
|
+
flex-direction: column;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
@media (max-width: 640px) {
|
|
457
|
+
.detail-summary {
|
|
458
|
+
grid-template-columns: 1fr;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.label-card .btn {
|
|
462
|
+
width: 100%;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
</style>
|