fix(hosting): site detail null guard + cleaner preview URLs
- 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>
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
{{ loading_state('Loading site...') }}
|
||||
{{ error_state('Error loading site') }}
|
||||
|
||||
<div x-show="!loading && !error && site" class="space-y-6">
|
||||
<template x-if="!loading && !error && site">
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between my-6 gap-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
@@ -187,6 +188,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Send Proposal Modal -->
|
||||
{% call modal('proposalModal', 'Send Proposal', show_var='showProposalModal', size='md', show_footer=false) %}
|
||||
|
||||
Reference in New Issue
Block a user