metatroncubeswdev 4a1d4e323f
Some checks failed
CI / Lint, Unit & Integration Tests (push) Has been cancelled
fix: add missing shopify.web.toml — root cause of dev preview never loading
shopify.web.toml never existed at the repo root. Without it, `shopify app
dev` runs the theme/UI-extension/Function dev servers fine but never
starts the actual Remix app, and has no local dev-server port to build a
tunnel URL from for the embedded admin app — so Admin always showed the
generic "Find this app in the pages where you work" fallback, regardless
of which app record was linked, cache state, or CLI version. Root-caused
via `shopify app dev --verbose`, which showed the CLI's own reverse proxy
had a route for /extensions but none for /.

Also updates shopify.app.toml's client_id to a freshly linked app (the
previous one, and two accidental duplicates created while chasing this
bug via `dev --reset`/`config link`, were deleted from the Dev Dashboard),
resets application_url/redirect_urls to placeholders now that dev's
auto-update actually works, restores checkout-datetime's network_access
capability (briefly disabled to test an unrelated theory — confirmed not
the real cause), and widens the dev-log gitignore pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 02:15:50 -04:00

171 lines
9.6 KiB
Markdown

# Delivery Date & Time
Shopify app: scheduling for Shipping / Local Delivery / Store Pickup, with
date-time slots, capacity intelligence, and **all-plan checkout enforcement**
via Cart/Checkout Validation Functions.
Read **[PRODUCT_STRATEGY.md](./PRODUCT_STRATEGY.md)** (why) and
**[IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md)** (how, phased build
order) before making changes. [CLAUDE.md](./CLAUDE.md) holds the
non-negotiables for AI-assisted work in this repo.
## Getting started
```sh
npm install
cp .env.example .env # fill in Shopify app credentials after linking
docker compose up -d # local Postgres (5433) + Redis (6380)
npx prisma migrate dev
npm run dev # shopify app dev — requires `shopify auth login` first
```
Dev uses Postgres, same as prod, since the schema relies on Prisma enums and
(from Phase 5 on) array fields that SQLite can't express. `npm run worker`
runs the BullMQ worker (jobs/worker.ts) once Phase 4 makes it do anything.
**`shopify.web.toml` at the repo root is required** — it tells `shopify app
dev` how to run this project's Remix app (`[commands] dev = "npm exec remix
vite:dev"`) and lets the CLI wire that local dev server into its reverse
proxy/tunnel. Without it, `dev` still runs the extensions fine but never
starts the actual admin app, and the embedded app will show Shopify's
generic "Find this app in the pages where you work" fallback with no
useful error — `application_url`/`redirect_urls` in `shopify.app.toml` are
placeholders that `dev` overwrites live on Shopify's servers each session
once this file exists; don't hand-edit them.
## Commands
| Command | Purpose |
|---|---|
| `npm run dev` | `shopify app dev` — local dev against a dev store |
| `npm test` | Vitest unit tests |
| `npm run test:e2e` | Playwright E2E |
| `npm run lint` / `npm run typecheck` | ESLint / `tsc --noEmit` |
| `npx prisma migrate dev` | DB migrations |
| `npm run deploy` | `shopify app deploy` — deploy extensions/functions |
| `npm run worker` | BullMQ worker (hold-expiry, notifications) |
| `npm run test:integration` | Redis/Postgres-backed tests (slot-hold concurrency, booking flow) — needs `docker compose up -d` |
| `npm run test:functions` | Real WASM build + `function-runner` tests for both Shopify Functions, against fixtures |
| `npm run typegen:functions` | Regenerate `extensions/*/generated/api.ts` from each Function's `schema.graphql` + `.graphql` query (also runs automatically before `npm run typecheck`) |
## Before public launch
The 3 mandatory GDPR compliance webhooks (`customers/data_request`,
`customers/redact`, `shop/redact`) are commented out in `shopify.app.toml`
— Shopify refuses to push them until the org requests and is granted
**Protected customer data access** in the Partner Dashboard (Apps → this
app → API access → Protected customer data), which is a manual
questionnaire/approval step. The handlers are fully implemented against the
real schema (`app/services/gdpr.server.ts`, used by
`app/routes/webhooks.customers.*.tsx` and `webhooks.shop.redact.tsx`,
covered by `tests/integration/gdpr.test.ts`): `customers/data_request`
compiles and logs the customer's Booking history for the merchant to hand
off (Shopify's webhook has no response payload — a self-serve export is a
Phase 9+ notification-system enhancement); `customers/redact` anonymizes
`customerEmail`/`customerPhone` on matching Bookings while keeping the
booking rows for the shop's own revenue/utilization history;
`shop/redact` deletes every shopDomain-scoped row (Booking first, then
Location — whose cascade removes SlotTemplate/SlotOverride/BlackoutDate/
Zone/Rate — then Shop and Session; `GeocodeCache` is deliberately excluded,
it's a shared address-keyed cache with no shopDomain). Once Protected
customer data access is granted, uncomment the three
`[[webhooks.subscriptions]]` blocks near the bottom of the webhooks
section. **Required before any public launch or Built-for-Shopify
submission** — don't ship without it.
## Status
Phase 0 (scaffold & CI) through Phase 7 (POS + Checkout UI extensions) are
complete — that's the full v1 launch scope per §8 of
`PRODUCT_STRATEGY.md`/§6 of `IMPLEMENTATION_PLAN.md`. Phase 8 (billing +
Built-for-Shopify hardening) is in progress; Phases 9-10 (v1.x fast-follow,
v2) are explicitly separate post-launch milestones in the plan, not part of
v1.
**Billing (Phase 8):** real Shopify Billing API integration
(`app/shopify.server.ts`'s `billing` config, built from
`app/lib/billing-plans.ts`'s plan/price table — Free/Starter/Growth/Pro per
`PRODUCT_STRATEGY.md` §6). `app/routes/app.billing.tsx` lets a merchant
switch plans (`billing.request`) or downgrade to Free (`billing.cancel`);
`webhooks.app_subscriptions.update.tsx` is the durable sync path that keeps
`Shop.tier` correct even when a merchant cancels from Shopify's own billing
page instead of this app. Feature gates enforced **server-side** in both
loader and action (not just hidden in the UI): Delivery zones/rates need
Growth+ (`app.zones._index.tsx`, `app.rates._index.tsx`), the dispatch
dashboard needs Starter+ (`app.dashboard.tsx`, `app.dashboard.export.tsx`),
and location count is capped per tier (Free=1, Starter=3, Growth/Pro=
unlimited — `app.locations.new.tsx`, `app.locations._index.tsx`'s
seed-template action). Gated features currently only cover what's actually
built in Phases 0-7 — several Growth/Pro features listed in
`PRODUCT_STRATEGY.md` §6 (waitlists, reschedule portal, SMS, etc.) are
Phase 9/10 and not yet implemented, so aren't gated on anything yet.
**Onboarding & help (Phase 8):** `app/routes/app._index.tsx` shows a "Get
set up" checklist (add a location, configure weekly slots, enable the
storefront widget, set up delivery zones if Growth+) that auto-detects
completion from your actual data, except the storefront-widget step —
theme customization isn't visible to this app's database, so it's a
manual "Mark as done" acknowledgment stored in `Shop.settings.onboarding`.
`app/routes/app.help.tsx` (linked from the nav as "Help") is a short
in-app reference explaining locations/slots, server-side enforcement,
zones/rates, the dashboard, POS/checkout, and data handling.
**Not yet verified live:** the onboarding checklist and Help page pass
typecheck/lint/build but haven't been exercised in an actual embedded
admin session — worth a quick look once you're running `shopify app dev`
against a dev store.
**Every scheduling surface calls the same two service functions**
(`app/services/availability-request.server.ts`,
`app/services/hold-request.server.ts`) — the storefront widget (app-proxy
auth), POS (`extensions/pos-datetime`, session-token auth), and Plus
checkout (`extensions/checkout-datetime`, session-token auth) each have
their own thin route wrapper but share the exact same resolution logic and
Redis-backed capacity pool, so a slot booked from any one of them is
unavailable on the other two. `extensions/checkout-datetime`'s Thank You
block and `extensions/datetime-widget`'s new `order-confirmation.liquid`
block both show the confirmed slot after checkout — the Liquid block is
what actually satisfies "all plans," since Checkout UI Extensions'
thank-you/order-status targets are Plus-only; there's no
`purchase.order-status.block.render` target in this API version (verified
against `@shopify/ui-extensions`' own type definitions — an early guess
based on the target name pattern was wrong).
**Unverified without a live device/store to test against** (noted in-code
where relevant): `pos-datetime` and `checkout-datetime` both assume
`process.env.APP_URL` is substituted at build time to the app's backend
origin, and neither extension's actual runtime behavior has been exercised
outside of typechecking against `@shopify/ui-extensions`' bundled types
(which did catch several wrong API-shape guesses during development).
Phase 5's Google Maps / geocoding features (pickup-location map in the
widget, radius-zone eligibility, address auto-geocoding on Save Location)
are only live if `GOOGLE_MAPS_API_KEY` is set — either as an env var for
server-side geocoding, or as the "Google Maps API key" block setting in the
theme editor for the storefront map. Without a key, everything else in
Phase 5 (postal-code zones, distance-band rates, delivery-density
thresholds) still works — those don't need Maps at all.
The storefront widget's TypeScript source lives in `widget-src/datetime-widget/`,
**not** inside `extensions/datetime-widget/` — a Theme App Extension's
directory may only contain `assets`, `blocks`, `snippets`, and `locales`
(the CLI hard-rejects anything else, e.g. a `src/` folder, with "Only
assets, blocks, snippets, locales directories are allowed"). Editing the
widget? Run `npm run build:widget` to bundle it into
`extensions/datetime-widget/assets/datetime-widget.js` — it also runs
automatically before `npm run dev` / `npm run deploy`.
**Functions are JavaScript, not Rust** (`extensions/validation-slot/`,
`extensions/delivery-customization/`) — no Rust toolchain was available in
the environment that built Phase 4, and `IMPLEMENTATION_PLAN.md` §1
explicitly allows JS as a fallback. Both were generated with
`shopify app generate extension` (once a real Partner login was available)
and their business logic (`src/evaluate.js` in each) is verified two ways:
plain Vitest unit tests at the repo root (`npm test`) and real
`function-runner` fixture tests that compile actual WASM
(`npm run test:functions`, also in CI). `extensions/*/generated/` and
`extensions/*/dist/` aren't committed (matching the CLI's own
`.gitignore` for these extensions) — `npm run typegen:functions`
regenerates the types from the committed `schema.graphql`, and building
runs automatically as part of `npm run dev` / `test:functions`.