Files
e-cosplay/templates/pages/prestation.twig
Serreau Jovann 80e4eaa907 ```
 feat(PagesController): Ajoute la vérification de disponibilité du slug.

Ajoute une route pour vérifier la disponibilité d'un slug d'EPage.
Utilise EpageService pour vérifier si le slug est disponible.
Retourne une réponse JSON indiquant la disponibilité et le slug.
```
2025-12-26 13:31:23 +01:00

364 lines
24 KiB
Twig

{% extends 'base.twig' %}
{# --- METADATA & SCHEMA --- #}
{% block title %}{{ 'page.presentation.title'|trans }}{% endblock %}
{% block meta_description %}{{ 'page.presentation.description'|trans }}{% endblock %}
{% block canonical_url %}
<link rel="canonical" href="{{ url('app_pages') }}" />
{% endblock %}
{% block breadcrumb_schema %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "{{ 'breadcrumb.home'|trans }}",
"item": "{{ app.request.schemeAndHttpHost }}"
},
{
"@type": "ListItem",
"position": 2,
"name": "{{ 'page_presentation.breadcrumb'|trans }}",
"item": "{{ app.request.schemeAndHttpHost }}{{ app.request.pathInfo }}"
}
]
}
</script>
{% endblock %}
{# --- BODY --- #}
{% block body %}
<div class="py-16 bg-[#f0f0f0] min-h-screen italic font-bold">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{# Titre Neubrutaliste #}
<div class="text-center mb-20">
<h1 class="inline-block text-5xl md:text-7xl font-black bg-white border-8 border-gray-900 p-6 shadow-[12px_12px_0px_rgba(0,0,0,1)] uppercase tracking-tighter">
<span class="text-transparent bg-clip-text bg-gradient-to-r from-yellow-500 to-red-600">EPage</span>
<span class="text-gray-900">// {{ 'page.presentation.header'|trans }}</span>
</h1>
</div>
{# Conteneur des Fonctionnalités #}
<div class="bg-white border-8 border-gray-900 p-8 md:p-14 shadow-[16px_16px_0px_rgba(0,0,0,1)] mb-16">
<h2 class="text-4xl font-black text-gray-900 mb-10 uppercase underline decoration-yellow-500 decoration-8 underline-offset-8">
{{ 'page.presentation.subtitle'|trans }}
</h2>
<p class="text-2xl text-gray-800 mb-8 leading-tight font-black uppercase tracking-tight">
{{ 'page.presentation.intro_paragraph_1'|trans }}
<span class="bg-yellow-400 px-2 text-black">EPage</span>
{{ 'page.presentation.intro_paragraph_1_2'|trans }}
</p>
{# Alerte Rôle EPage #}
<div class="bg-gray-900 text-white p-6 border-l-[16px] border-red-500 mb-12 shadow-[8px_8px_0px_rgba(239,68,68,0.3)]">
<p class="text-lg md:text-xl font-bold leading-relaxed">
{{ 'page.presentation.intro_paragraph_2'|trans({
'page_span': '<span class="text-yellow-400 uppercase font-black">EPage</span>',
'strong_tag': '<span class="text-white">',
'strong_end_tag': '</span>',
'warning_tag': '<span class="block mt-4 text-red-400 uppercase tracking-widest text-sm underline font-black">'
})|raw }}
</p>
</div>
{# GRILLE DES FONCTIONNALITÉS #}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 text-gray-900">
{# Bloc 1: SEO #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-red-500 border-4 border-gray-900 text-white mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.seo.title'|trans }}</h3>
</div>
<p class="font-bold text-gray-700 leading-snug">{{ 'page.presentation.feature.seo.description'|trans({
'page_span': '<span class="text-red-600 font-black">EPage</span>'
})|raw }}</p>
</div>
{# Bloc 2: Centralisation #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-yellow-400 border-4 border-gray-900 text-black mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3"/><path d="M9 17H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"/><line x1="8" x2="16" y1="12" y2="12"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.nexus.title'|trans }}</h3>
</div>
<p class="font-bold text-gray-700 leading-snug">{{ 'page.presentation.feature.nexus.description'|trans({
'strong_tag': '<span class="text-black underline decoration-yellow-400 decoration-4">',
'strong_end_tag': '</span>'
})|raw }}</p>
</div>
{# Bloc 3: Événements #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-red-500 border-4 border-gray-900 text-white mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/><path d="M8 18h.01"/><path d="M12 18h.01"/><path d="M16 18h.01"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.convention.title'|trans }}</h3>
</div>
<p class="font-bold text-gray-700 leading-snug">{{ 'page.presentation.feature.convention.description'|trans }}</p>
</div>
{# Bloc 4: Intégration #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-yellow-400 border-4 border-gray-900 text-black mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M11 14h2a2 2 0 0 0 2-2V7.5l-3.24-3.24a1 1 0 0 0-1.42 0L8 7.5V12a2 2 0 0 0 2 2h2"/><path d="M10 10l.02.02"/><path d="M14 10l.02.02"/><path d="M20 16c0 4.42-3.58 8-8 8s-8-3.58-8-8 8-16 8-16 8 11.58 8 16z"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.approval.title'|trans }}</h3>
</div>
<p class="font-bold text-gray-700 leading-snug">{{ 'page.presentation.feature.approval.description'|trans({
'strong_tag': '<span class="text-black underline decoration-red-500 decoration-4">',
'strong_end_tag': '</span>'
})|raw }}</p>
</div>
{# Bloc 5: Sécurité #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-red-500 border-4 border-gray-900 text-white mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/><path d="m9 12 2 2 4-4"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.security.title'|trans }}</h3>
</div>
<div class="font-bold text-gray-700 leading-tight space-y-2">
<p>{{ 'page.presentation.feature.security.description'|trans({'page_span': 'EPage', 'strong_tag': '<b>', 'strong_end_tag': '</b>'})|raw }}</p>
<p class="text-xs uppercase bg-gray-100 p-1">{{ 'page.presentation.feature.security.encrypted_data'|trans({'strong_tag': '', 'strong_end_tag': ''}) }}</p>
</div>
</div>
{# Bloc 6: Contact #}
<div class="group p-8 border-4 border-gray-900 bg-white shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200">
<div class="flex items-center mb-4">
<div class="p-3 bg-yellow-400 border-4 border-gray-900 text-black mr-4 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M22 10V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h10"/><path d="m22 10-7.23 6.11a3 3 0 0 1-3.48 0L2 10"/><path d="m16 22 2 2 4-4"/></svg>
</div>
<h3 class="font-black text-xl uppercase leading-none">{{ 'page.presentation.feature.contact.title'|trans }}</h3>
</div>
<p class="font-bold text-gray-700 leading-snug">{{ 'page.presentation.feature.contact.description'|trans }}</p>
<a href="{{ path('app_rgpd') }}" class="mt-4 block text-[10px] uppercase font-black text-red-600 hover:underline italic">{{ 'page.presentation.feature.contact.policy_text'|trans }}</a>
</div>
</div>
</div>
{# BLOC : OFFRE SANS ENGAGEMENT #}
<div class="mt-12 p-10 bg-white border-8 border-red-500 shadow-[12px_12px_0px_rgba(239,68,68,1)] text-center relative overflow-hidden">
<div class="absolute top-0 left-0 bg-red-500 text-white px-6 py-2 uppercase font-black tracking-widest text-xs rotate-[-2deg] -translate-x-2 translate-y-2 border-4 border-gray-900">
PRO MISSION
</div>
<h4 class="text-4xl font-black text-gray-900 mb-6 uppercase tracking-tighter italic">
{{ 'page.presentation.flexibility.title'|trans }}
</h4>
<div class="text-2xl font-bold text-gray-800 leading-tight">
{{ 'page.presentation.flexibility.no_commitment'|trans({
'page_span': '<span class="text-red-600 uppercase font-black">EPage</span>',
'strong_tag': '<span class="underline decoration-yellow-400 decoration-4 px-1">',
'strong_end_tag': '</span>'
})|raw }}
</div>
</div>
{# Appel à l'action étapes #}
<div class="text-center mt-24">
<h3 class="text-5xl font-black text-gray-900 uppercase italic tracking-tighter bg-yellow-400 inline-block px-4 border-4 border-gray-900 shadow-[6px_6px_0px_rgba(0,0,0,1)]">
{{ 'page.presentation.call_to_action'|trans }}
</h3>
</div>
{# GRILLE INFOS TECHNIQUES #}
<div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-8">
{# Bloc 1: Domaine #}
<div class="p-8 bg-white border-4 border-gray-900 shadow-[8px_8px_0px_rgba(234,179,8,1)]">
<h4 class="text-2xl font-black text-yellow-600 mb-4 uppercase tracking-tighter italic border-b-4 border-gray-100 pb-2">
{{ 'page.presentation.domain_info.title'|trans }}
</h4>
<div class="space-y-4 font-bold text-gray-800 text-sm md:text-base uppercase tracking-tight">
<p>{{ 'page.presentation.domain_info.no_domain_question'|trans }}</p>
<code class="block bg-gray-900 text-yellow-400 p-3 border-2 border-gray-900 font-mono text-xs break-all">
votre_pseudo.e-cosplay.com
</code>
<p>{{ 'page.presentation.domain_info.has_domain_question'|trans }}</p>
<p class="text-xs text-gray-500">{{ 'page.presentation.domain_info.cname_guide'|trans({'page_span': 'EPage'})|raw }}</p>
</div>
</div>
{# Bloc 2: Copyright #}
<div class="p-8 bg-white border-4 border-gray-900 shadow-[8px_8px_0px_rgba(37,99,235,1)]">
<h4 class="text-2xl font-black text-blue-600 mb-4 uppercase tracking-tighter italic border-b-4 border-gray-100 pb-2">
{{ 'page.presentation.copyright_info.title'|trans }}
</h4>
<p class="font-bold text-gray-800 leading-snug uppercase text-sm">
{{ 'page.presentation.copyright_info.responsibility'|trans({
'page_span': 'EPage',
'strong_tag': '<span class="text-blue-600">',
'strong_end_tag': '</span>',
'red_strong': '<span class="text-red-600">'
})|raw }}
</p>
</div>
{# Bloc 3: Add-ons #}
<div class="p-8 bg-white border-4 border-gray-900 shadow-[8px_8px_0px_rgba(220,38,38,1)]">
<h4 class="text-2xl font-black text-red-600 mb-4 uppercase tracking-tighter italic border-b-4 border-gray-100 pb-2">
{{ 'page.presentation.additional_features.title'|trans }}
</h4>
<p class="font-bold text-gray-800 leading-snug uppercase text-sm mb-4">
{{ 'page.presentation.additional_features.request_support'|trans({
'page_span': 'EPage',
'strong_tag': '<span class="text-red-600">',
'strong_end_tag': '</span>'
})|raw }}
</p>
<p class="text-[10px] text-gray-400 uppercase font-black tracking-widest border-t-2 border-gray-100 pt-4">
PARTNER_REFERRAL: SARL SITECONSEIL
</p>
</div>
</div>
{# BLOC TARIFICATION #}
<div id="pricing-section" class="mt-24 text-center" style="display: none">
<h3 class="text-5xl font-black text-gray-900 mb-10 uppercase tracking-tighter">{{ 'page.presentation.pricing.choose_period'|trans }}</h3>
<div class="max-w-2xl mx-auto bg-white border-8 border-gray-900 p-8 md:p-12 shadow-[20px_20px_0px_rgba(0,0,0,1)] relative">
<div class="absolute -top-6 left-1/2 -translate-x-1/2 bg-gray-900 text-white px-10 py-2 uppercase font-black text-xl italic skew-x-[-10deg]">
Access Terminal
</div>
<h4 class="text-4xl font-black text-transparent bg-clip-text bg-gradient-to-r from-yellow-500 to-red-600 mb-10 uppercase italic">
EPage Connect
</h4>
{# SÉLECTEUR DE DURÉE #}
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3 mb-10">
{% for period in ['1M', '2M', '3M', '6M', '12M'] %}
<label class="relative cursor-pointer group">
<input type="radio" name="duration" value="{{ period }}" class="peer hidden" {% if period == '1M' %}checked{% endif %} onchange="updatePrice(this.value)">
<span class="flex items-center justify-center py-3 border-4 border-gray-900 font-black uppercase text-xs transition-all peer-checked:bg-red-500 peer-checked:text-white peer-checked:translate-x-1 peer-checked:translate-y-1 peer-checked:shadow-none shadow-[4px_4px_0px_rgba(0,0,0,1)] group-hover:bg-gray-100">
{{ ('page.presentation.pricing.period_' ~ period|lower)|trans }}
</span>
</label>
{% endfor %}
</div>
{# PRIX DYNAMIQUE #}
<div class="bg-gray-900 text-white p-8 border-4 border-gray-900 shadow-[8px_8px_0px_rgba(234,179,8,1)] mb-8">
<div class="text-7xl font-black tracking-tighter mb-2 italic">
<span id="current-price">5€</span>
</div>
<p class="text-yellow-400 font-black uppercase tracking-widest text-xs">
{{ 'page.presentation.pricing.tax_info'|trans }}
</p>
</div>
<p id="price-per-month" class="text-sm font-black text-gray-500 uppercase tracking-widest mb-10 h-10"></p>
<button onclick="redirectToCheckout()" class="w-full py-6 bg-red-600 text-white border-4 border-gray-900 font-black text-2xl uppercase tracking-widest shadow-[10px_10px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-2 hover:translate-y-2 transition-all active:scale-95">
{{ 'page.presentation.pricing.cta_button'|trans }}
</button>
<p class="text-[9px] text-gray-400 uppercase font-bold mt-6 tracking-widest">
{{ 'page.presentation.pricing.disclaimer'|trans }}
</p>
</div>
</div>
{# BLOC PARTENARIAT #}
<div class="mt-32 p-10 bg-white border-8 border-cyan-500 shadow-[12px_12px_0px_rgba(6,182,212,1)] flex flex-col md:flex-row items-center gap-10">
<div class="flex-1 text-left">
<h3 class="text-3xl font-black text-cyan-600 uppercase italic tracking-tighter mb-4">
{{ 'page.presentation.siteconseil_partner.title'|trans }}
</h3>
<p class="text-gray-800 font-bold uppercase text-sm leading-tight">
{{ 'page.presentation.siteconseil_partner.intro'|trans({
'page_span': '<span class="text-cyan-600 font-black underline">EPage</span>',
'strong_tag': '<b>',
'strong_end_tag': '</b>'
})|raw }}
</p>
</div>
<div class="bg-gray-900 text-white p-6 border-4 border-gray-900 shadow-[8px_8px_0px_rgba(6,182,212,0.5)] max-w-sm">
<p class="text-xs font-black text-cyan-400 uppercase mb-4 tracking-widest">{{ 'page.presentation.siteconseil_partner.recommended'|trans }}</p>
<p class="text-sm font-bold mb-6">
{{ 'page.presentation.siteconseil_partner.cms_info'|trans({
'partner_strong': '<span class="text-cyan-400">SARL SITECONSEIL</span>',
'cms_strong': '<span class="text-yellow-400 font-black">Esy-Flex</span>'
})|raw }}
</p>
<a href="https://www.siteconseil.fr" target="_blank" class="block text-center py-3 bg-cyan-500 text-gray-900 font-black uppercase text-xs border-2 border-white hover:bg-white transition-colors">
{{ 'page.presentation.siteconseil_partner.discover_button'|trans }} _
</a>
</div>
</div>
</div>
{# --- JAVASCRIPT --- #}
<script>
const pricing = {
'1M': {price: 5, label: '5€ / mois'},
'2M': {price: 8, label: '4€ / mois'},
'3M': {price: 12, label: '4€ / mois'},
'6M': {price: 25, label: '4.16€ / mois'},
'12M': {price: 40, label: '3.33€ / mois'}
};
const translations = {
'monthly_renewal': '{{ 'page.presentation.js.monthly_renewal'|trans }}',
'annual_billing': '{{ 'page.presentation.js.annual_billing'|trans }}',
'periodic_billing': '{{ 'page.presentation.js.periodic_billing'|trans }}',
};
function updatePrice(duration) {
const priceElement = document.getElementById('current-price');
const perMonthElement = document.getElementById('price-per-month');
const data = pricing[duration];
if (data) {
priceElement.textContent = data.price + '€';
if (duration === '1M') {
perMonthElement.textContent = translations.monthly_renewal;
} else if (duration === '12M') {
const translatedText = translations.annual_billing.replace(
'%average_price%', `<span class="text-red-600 font-black underline decoration-4 underline-offset-4">${data.label}</span>`
);
perMonthElement.innerHTML = translatedText;
} else {
const months = duration.replace('M', '');
const translatedText = translations.periodic_billing
.replace('%duration%', months)
.replace('%average_price%', `<span class="text-red-600 font-black underline decoration-4 underline-offset-4">${data.label}</span>`);
perMonthElement.innerHTML = translatedText;
}
}
}
function initializePrice() {
updatePrice('1M');
}
function redirectToCheckout() {
const selectedDurationInput = document.querySelector('input[name="duration"]:checked');
const selectedDuration = selectedDurationInput ? selectedDurationInput.value : '1M';
window.location.href = `/pages/onboaring?abo=${selectedDuration}`;
}
if (typeof window.Turbo !== 'undefined') {
document.addEventListener('turbo:load', initializePrice);
} else {
document.addEventListener('DOMContentLoaded', initializePrice);
}
</script>
</div>
{% endblock %}