diff --git a/CLAUDE.md b/CLAUDE.md index b7b7687..e169088 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -478,7 +478,18 @@ Routes (registered before `shop_slug` catch-all): - `/s/{shop_id}/tag/{slug}` — public tag detail page (works without JS) Tag input on product edit: comma-separated `tags` field on -`product_edit.j2`; server slugifies, dedupes, upserts. +`product_edit.j2`; server slugifies, dedupes, upserts. **Phase 2.7**: +that field is now the no-JS fallback. With JS, `static/js/product_tags.js` +turns it into a chip editor and POSTs each add/remove to the +`product_tags` route — `/p/{product_id}/tags` (registered before the +`product_slug` catch-all), `@shop_editor_required` + `@trial_active_required`, +`action=add|remove`. AJAX (`X-Requested-With`) → JSON, no full page +reload; plain POST → 302 back to product edit. The JS keeps the hidden +`tags` field in lock-step with the chips so a later full "Save Settings" +is a no-op, never a stale revert. Capability-driven split helper: +`views/__init__.py:is_ajax(request)` (single source of truth; +`shop.py:_is_ajax` delegates to it). Removable chip component: +`.tag-chip-removable` family in `common.css` + `/styleguide#tagchips`. Home rendering: `views/shop.py:_build_home_layout_context()` builds `home_chips` (list of `Tag`), `home_lanes` (list of diff --git a/docs/architecture.md b/docs/architecture.md index 4b7bd98..8b84646 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -221,6 +221,7 @@ mps_page_session (raw rows) | 6-sentence SERP excerpt (MPS-24 Phase 2.6) | `Product.excerpt_sentences(n=6, max_chars=1500)` via shared `_strip_markdown()` helper | `shop_tag.j2` row description | Always on | | Facet nav on shop home (MPS-24 Phase 2.6b) | `_facet_nav.j2` macros + `home.j2` / `shop.j2` wrappers | Desktop sidebar + mobile `
` accordion when `shop.home_layout >= 1` | Opt-in via home_layout | | Mobile SERP rows under each lane (MPS-24 Phase 2.6b) | `home.j2` / `shop.j2` `.tag-lane-rows` markup + CSS visibility swap | Horizontal tiles ≥800px; vertical SERP rows with 6-sentence excerpts <800px | On for layout 2 | +| Per-product SPA tag chips (MPS-24 Phase 2.7) | `product_tags` view (`/p/{id}/tags`) + `static/js/product_tags.js` + `views/__init__.py:is_ajax()` | Chip add/remove on product edit, AJAX persist, no full page reload; no-JS keeps the comma `tags` field on the main form | Always on (JS-enhanced; no-JS fallback) | ## Ticket Index @@ -250,7 +251,7 @@ mps_page_session (raw rows) | [MPS-21](tickets/mps-21.md) | Make-an-Offer Mode | Complete | | [MPS-22](tickets/mps-22.md) | Kill-Switch Feature Flags — Karaoke + Torrent Off by Default | Complete | | [MPS-23](tickets/mps-23.md) | Consolidated Transactional Sender Identity + Shop Contact Email | Open | -| [MPS-24](tickets/mps-24.md) | Shop home page overhaul + product categorization (tags + chips + lanes + auto-suggest) | In progress (Phases 1 + 2 landed) | +| [MPS-24](tickets/mps-24.md) | Shop home page overhaul + product categorization (tags + chips + lanes + auto-suggest + per-product SPA tag chips) | In progress (Phases 1 + 2 + 2.6 + 2.7 landed) | ## Related Docs diff --git a/docs/design-system.md b/docs/design-system.md index 5ff4d3e..40cb5b8 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -284,6 +284,11 @@ All components are documented with live examples at `/styleguide`. The styleguid | `[data-tag-slugs]` | `.serp-item` | Space-separated tag slugs the item carries; consumed by `tag_filter.js` | | `.tag-suggest-list` / `.tag-suggest-item` | `shop_tags.j2` (Phase 2) | Suggested-cluster well rendered when operator clicks "Suggest categories from titles + descriptions"; per-cluster grid with label, sample titles, Apply / Dismiss actions | | `.tag-suggest-actions` | `shop_tags.j2` | Apply / Dismiss button row, `grid-auto-flow: column` | +| `.product-tag-chips` / `.tag-chip-list` | `product_edit.j2` (Phase 2.7) | Per-product SPA tag editor container; `js-only`, revealed by `product_tags.js`. Chips wrap as inline-level pills (the `