Preview token propagation:
- JavaScript in storefront base.html appends _preview query param to
all internal links when in preview mode, so clicking nav items
(Services, Contact, etc.) preserves the preview bypass
Scraped content enrichment:
- POC builder now appends first 5 scraped paragraphs to about/services/
projects pages, so the POC shows actual content from the prospect's
site instead of just generic template text
- Extracts tagline from second scraped heading
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Build POC button on site detail now passes site_id to the POC
builder, which populates the existing site's store with CMS content
instead of trying to create a new site (which failed with duplicate
slug error).
- poc_builder_service.build_poc() accepts optional site_id param
- If site_id given: uses existing site, skips hosted_site_service.create()
- If not given: creates new site (standalone POC build)
- API schema: added site_id to BuildPocRequest
- Frontend: passes this.site.id in the build request
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When deleting a hosted site, the associated Store is now soft-deleted
(sets deleted_at). This frees the subdomain for reuse via the partial
unique index (WHERE deleted_at IS NULL).
Previously the Store was orphaned, blocking subdomain reuse.
Closes docs/proposals/hosting-cascade-delete.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Slugify now handles both domains and business names gracefully:
- Domain: strip protocol/www/TLD → batirenovation-strasbourg
- Business name: take first 3 meaningful words, skip filler
(le, la, du, des, the, and) → boulangerie-coin
- Cap at 30 chars
Clients without a domain get clean slugs from their business name
instead of the full title truncated mid-word.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Site detail template: x-show → x-if to prevent Alpine evaluating
expressions when site is null during async loading
- Slugify: prefer domain_name over business_name for subdomain
generation (batirenovation-strasbourg vs bati-rnovation-strasbourg-
peinture-ravalement-dans). Cap at 30 chars. Strip protocol/TLD.
- POC builder passes domain_name for clean slugs
- Remove .lu/.fr/.com TLD from slugs automatically
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Schema: add merchant_id/prospect_id with model_validator requiring
at least one. Remove from-prospect endpoint (unified into POST /sites)
- Service: rewrite create() — if merchant_id use it directly, if
prospect_id auto-create merchant from prospect data. Remove system
merchant hack entirely. Extract _create_merchant_from_prospect helper.
- Simplify accept_proposal() — merchant already exists at creation,
only creates subscription and marks prospect converted
- Tests: update all create calls with merchant_id, replace from-prospect
tests with prospect_id + validation tests
Closes docs/proposals/hosting-site-creation-fix.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>