From d3804375943b87bdca60878b83f44aa533f7b199 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Thu, 2 Apr 2026 22:49:32 +0200 Subject: [PATCH] fix(hosting): site detail null guard + cleaner preview URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- .../hosting/services/hosted_site_service.py | 16 +++++++++++++--- .../hosting/services/poc_builder_service.py | 1 + .../templates/hosting/admin/site-detail.html | 4 +++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/modules/hosting/services/hosted_site_service.py b/app/modules/hosting/services/hosted_site_service.py index 10450bc4..722e9aeb 100644 --- a/app/modules/hosting/services/hosted_site_service.py +++ b/app/modules/hosting/services/hosted_site_service.py @@ -35,11 +35,19 @@ ALLOWED_TRANSITIONS: dict[HostedSiteStatus, list[HostedSiteStatus]] = { def _slugify(name: str) -> str: - """Generate a URL-safe slug from a business name.""" + """Generate a short URL-safe slug from a business name or domain.""" slug = name.lower().strip() + # Remove protocol/www if domain was passed + for prefix in ["https://", "http://", "www."]: + if slug.startswith(prefix): + slug = slug[len(prefix):] + slug = slug.rstrip("/") + # Remove TLD if it looks like a domain + if "." in slug and " " not in slug: + slug = slug.rsplit(".", 1)[0] slug = re.sub(r"[^a-z0-9\s-]", "", slug) slug = re.sub(r"[\s-]+", "-", slug) - return slug.strip("-")[:50] + return slug.strip("-")[:30] class HostedSiteService: @@ -101,7 +109,9 @@ class HostedSiteService: business_name = data["business_name"] merchant_id = data.get("merchant_id") prospect_id = data.get("prospect_id") - slug = _slugify(business_name) + # Prefer domain_name for slug (shorter, cleaner), fall back to business_name + slug_source = data.get("domain_name") or business_name + slug = _slugify(slug_source) # Find hosting platform platform = db.query(Platform).filter(Platform.code == "hosting").first() diff --git a/app/modules/hosting/services/poc_builder_service.py b/app/modules/hosting/services/poc_builder_service.py index 65d60609..589ace0b 100644 --- a/app/modules/hosting/services/poc_builder_service.py +++ b/app/modules/hosting/services/poc_builder_service.py @@ -60,6 +60,7 @@ class PocBuilderService: # 4. Create HostedSite + Store site_data = { "business_name": context["business_name"], + "domain_name": prospect.domain_name, # used for clean subdomain slug "prospect_id": prospect_id, "contact_email": context.get("email"), "contact_phone": context.get("phone"), diff --git a/app/modules/hosting/templates/hosting/admin/site-detail.html b/app/modules/hosting/templates/hosting/admin/site-detail.html index 9b65ecd3..9a727bb7 100644 --- a/app/modules/hosting/templates/hosting/admin/site-detail.html +++ b/app/modules/hosting/templates/hosting/admin/site-detail.html @@ -12,7 +12,8 @@ {{ loading_state('Loading site...') }} {{ error_state('Error loading site') }} -
+ {% call modal('proposalModal', 'Send Proposal', show_var='showProposalModal', size='md', show_footer=false) %}