Theme App Extension (extensions/datetime-widget/) hand-scaffolded from
Shopify's documented structure, since `shopify app generate extension`
needs the same interactive Partner login `shopify app init`/`dev` do
(unavailable in this session) — user confirmed this approach.
- blocks/app-embed.liquid: site-wide toggle that loads the widget's JS/CSS
once (target: body)
- blocks/datetime-picker.liquid: the actual app block merchants add to a
cart/product page section, with per-method show/hide toggles and an
optional location override, all theme-editor-configurable
- src/datetime-widget.ts: vanilla TS (no framework, ~2kb gzipped) — renders
method -> date -> slot, calls the app-proxy availability endpoint, and on
selection writes to /cart/update.js cart attributes using a *method-
specific* attribute name ("Pickup date" vs "Delivery date" vs "Shipping
date", from locales/en.default.json) rather than one generic label — this
is the direct fix for the "estimated delivery date on a pickup order"
complaint PRODUCT_STRATEGY.md §3.1 calls out. Only collects a selection;
never enforces anything itself (Phase 4's Validation Function does that).
- locales/en.default.json + en.default.schema.json: i18n from day one
- app/routes/apps.scheduling.availability.tsx: the public app-proxy
endpoint the widget calls. Path is `apps.scheduling.availability` (not
the plan's suggested `api.availability`) because shopify.app.toml's
[app_proxy].url already includes the /apps/scheduling prefix, and
Shopify forwards a shop-facing /apps/scheduling/availability request to
{url}/availability against that full url — so the Remix route path has
to mirror the proxy path exactly for the forwarding to land correctly.
Resolves (location, method) -> DB rows -> getAvailability(), scoped by
shopDomain throughout. No consumption wired up (Booking doesn't exist
until Phase 4), so this correctly shows full capacity everywhere for now.
Theme App Extensions have no CLI build step, so `npm run build:widget`
(esbuild, added as a devDependency) bundles src/ into assets/ and is wired
as a predev/predeploy hook so `shopify app dev`/`deploy` never ship a stale
bundle. CI now also runs both `npm run build` and `npm run build:widget`.
Verified: lint, typecheck, unit tests, both builds pass; a live script
exercising the exact DB-query + getAvailability path the availability route
uses (bypassing HTTP, since real app-proxy signature verification needs a
live tunnel) returned correct results against the Postgres container —
correct EDT offset, correct capacity, and exactly the weekday-filtered set
of open dates for the seeded bakery template.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
65 lines
2.2 KiB
Plaintext
65 lines
2.2 KiB
Plaintext
<div
|
|
class="dd-widget"
|
|
data-dd-widget
|
|
data-heading="{{ block.settings.heading | escape }}"
|
|
data-show-shipping="{{ block.settings.show_shipping }}"
|
|
data-show-local-delivery="{{ block.settings.show_local_delivery }}"
|
|
data-show-pickup="{{ block.settings.show_pickup }}"
|
|
data-label-shipping="{{ 'widget.method_shipping' | t | escape }}"
|
|
data-label-local-delivery="{{ 'widget.method_local_delivery' | t | escape }}"
|
|
data-label-pickup="{{ 'widget.method_pickup' | t | escape }}"
|
|
data-attr-label-shipping="{{ 'widget.date_label_shipping' | t | escape }}"
|
|
data-attr-label-local-delivery="{{ 'widget.date_label_local_delivery' | t | escape }}"
|
|
data-attr-label-pickup="{{ 'widget.date_label_pickup' | t | escape }}"
|
|
data-label-choose-date="{{ 'widget.choose_date' | t | escape }}"
|
|
data-label-choose-time="{{ 'widget.choose_time' | t | escape }}"
|
|
data-label-no-dates="{{ 'widget.no_dates' | t | escape }}"
|
|
data-label-confirmed="{{ 'widget.confirmed' | t | escape }}"
|
|
data-label-change="{{ 'widget.change' | t | escape }}"
|
|
data-label-loading="{{ 'widget.loading' | t | escape }}"
|
|
data-label-error="{{ 'widget.error' | t | escape }}"
|
|
{% if block.settings.location_id != blank %}data-location-id="{{ block.settings.location_id | escape }}"{% endif %}
|
|
{{ block.shopify_attributes }}
|
|
>
|
|
<noscript>{{ 'widget.enable_js' | t | escape }}</noscript>
|
|
</div>
|
|
|
|
{% schema %}
|
|
{
|
|
"name": "t:datetime_picker.name",
|
|
"target": "section",
|
|
"settings": [
|
|
{
|
|
"type": "text",
|
|
"id": "heading",
|
|
"label": "t:datetime_picker.heading_label",
|
|
"default": "Choose your delivery date"
|
|
},
|
|
{
|
|
"type": "checkbox",
|
|
"id": "show_shipping",
|
|
"label": "t:datetime_picker.show_shipping_label",
|
|
"default": true
|
|
},
|
|
{
|
|
"type": "checkbox",
|
|
"id": "show_local_delivery",
|
|
"label": "t:datetime_picker.show_local_delivery_label",
|
|
"default": true
|
|
},
|
|
{
|
|
"type": "checkbox",
|
|
"id": "show_pickup",
|
|
"label": "t:datetime_picker.show_pickup_label",
|
|
"default": true
|
|
},
|
|
{
|
|
"type": "text",
|
|
"id": "location_id",
|
|
"label": "t:datetime_picker.location_id_label",
|
|
"info": "t:datetime_picker.location_id_info"
|
|
}
|
|
]
|
|
}
|
|
{% endschema %}
|