{# Registration Section - Tailwind Version #}
{# Defaults definiert in event-single.tailwind.html.twig (styles block) #}
<section id="{% if lang == 'en' or isEnglish %}registration{% else %}anmeldung{% endif %}" class="bg-registration registration-accent py-12">
<div class="max-w-4xl mx-auto px-4">
{# Batch ist Source of Truth fuer Preis und Steuerverhalten (event.priceInEuroCent ist Legacy). #}
{% set displayPrice = null %}
{% set isTaxIncluded = false %}
{% set isFreeForKiCampus = false %}
{% set isFreeForCommunity = false %}
{% if batch and batch.priceInEuroCent %}
{% set displayPrice = batch.priceInEuroCent %}
{% set isTaxIncluded = batch.taxBehavior == 'inclusive' %}
{% set isFreeForKiCampus = batch.isFreeForKiCampus %}
{% set isFreeForCommunity = batch.isFreeForCommunity %}
{% elseif event is defined and event and event.priceInEuroCent %}
{% set displayPrice = event.priceInEuroCent %}
{% set isTaxIncluded = event.isTaxIncluded %}
{% set isFreeForKiCampus = event.isFreeForKiCampus %}
{% set isFreeForCommunity = event.isFreeForCommunity %}
{% endif %}
{# --- Registrierungs-Header --- #}
{% if event is defined %}
{% if batch.registrationHeadline %}
{# Custom Header: ersetzt Standard-Details-Box und Free-Access-Boxen #}
<div class="text-center mb-8">
<h2 class="text-white text-3xl lg:text-4xl font-bold mb-3">{{ batch.registrationHeadline }}</h2>
{% if batch.registrationSubtitle %}
<p class="text-white/80 text-lg">{{ batch.registrationSubtitle|raw }}</p>
{% endif %}
</div>
{% else %}
{# Event Details Box #}
<div class="bg-white/10 backdrop-blur rounded-xl p-4 mb-6">
<div class="flex flex-wrap justify-between gap-4 text-white">
{# When #}
<div class="flex items-center gap-2">
<span class="text-xl">📅</span>
<div>
<strong>{% if isEnglish %}When:{% else %}Wann:{% endif %}</strong>
{% if isMultiBatchEvent and batch and batch.startDate %}
{{ batch.startDate|date('d.m.Y H:i') }}{% if batch.endDate %} - {{ batch.endDate|date('H:i') }} Uhr{% endif %}
{% elseif event.startDate %}
{{ event.startDate|date('d.m.Y H:i') }}{% if event.endDate %} - {{ event.endDate|date('H:i') }} Uhr{% endif %}
{% endif %}
</div>
</div>
{# Where #}
<div class="flex items-center gap-2">
<span class="text-xl">📍</span>
<div>
<strong>{% if isEnglish %}Where:{% else %}Wo:{% endif %}</strong>
{% if event.location %}{{ event.location }}{% elseif event.isOnline %}Online{% else %}STARTPLATZ{% endif %}
</div>
</div>
{# Fee #}
{% if displayPrice is not null %}
<div class="flex items-center gap-2">
<span class="text-xl">💰</span>
<div>
<strong>{% if isEnglish %}Fee:{% else %}Gebuehr:{% endif %}</strong>
{% if displayPrice == 0 %}
{% if isEnglish %}free{% else %}kostenlos{% endif %}
{% else %}
{{ (displayPrice/100)|number_format(2, ',', '.') }} EUR
{% if isTaxIncluded %}{% if isEnglish %}incl. VAT{% else %}inkl. MwSt.{% endif %}{% else %}{% if isEnglish %}excl. VAT{% else %}zzgl. MwSt.{% endif %}{% endif %}
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
{# KI Campus Free Access Box #}
{% if displayPrice and isFreeForKiCampus %}
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-4">
<div class="flex items-start gap-3">
<span class="text-green-500 text-xl">✓</span>
<div>
<strong class="text-green-700">
{% if isEnglish %}Free for KI Campus members{% else %}Kostenlos fuer KI Campus Mitglieder{% endif %}
</strong>
<p class="text-sm text-gray-600 mt-1">
{% if isEnglish %}
Join now for just EUR49/month and get access to this and many other free AI courses and events!
{% else %}
Werde Mitglied fuer nur 49EUR/Monat und erhalte Zugang zu diesem und vielen weiteren kostenlosen KI-Kursen und Events!
{% endif %}
</p>
<a target="_blank" href="https://ki-campus.onepage.me/" class="inline-block mt-2 px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors text-sm font-semibold">
{% if isEnglish %}Become a KI Campus member now{% else %}Jetzt KI Campus Mitglied werden{% endif %}
</a>
</div>
</div>
</div>
{% endif %}
{# Community Free Access Box #}
{% if displayPrice and isFreeForCommunity %}
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-4">
<div class="flex items-start gap-3">
<span class="text-green-500 text-xl">✓</span>
<div>
<strong class="text-green-700">
{% if isEnglish %}Free for Community members{% else %}Kostenlos fuer Community Mitglieder{% endif %}
</strong>
<p class="text-sm text-gray-600 mt-1">
{% if isEnglish %}
As a Community member, you get free access to this event!
{% else %}
Als Community Mitglied erhaeltst Du kostenlosen Zugang zu diesem Event!
{% endif %}
</p>
</div>
</div>
</div>
{% endif %}
{% endif %}{# end registrationHeadline if/else #}
{# Payment pending: started + paid event → show hint and re-show form below #}
{% set isPaidAndStarted = application.applicationStatus is defined and application.applicationStatus == 'started' and batch.priceInEuroCent > 0 %}
{# Application Status #}
{% if application.applicationStatus is defined and application.applicationStatus is not empty %}
{% if batch.registrationMode == 'lead-capture' and batch.registrationRedirectUrl and application.applicationStatus == 'applied' %}
{# Lead-Capture: show redirect link instead of generic status #}
<div class="bg-white rounded-2xl p-6 shadow-xl text-center">
<p class="text-gray-700 mb-4">
{% if isEnglish %}
Thank you for your inquiry! Book your consultation now:
{% else %}
Danke fuer Deine Anfrage! Buche jetzt Dein Gespraech:
{% endif %}
</p>
<a href="{{ batch.registrationRedirectUrl }}" target="_blank"
class="inline-block px-8 py-4 bg-gray-900 text-white font-bold text-lg rounded-xl hover:bg-gray-800 transition-colors no-underline">
{{ batch.registrationButtonText ?? (isEnglish ? 'Book now' : 'Jetzt buchen') }}
</a>
</div>
{% elseif isPaidAndStarted %}
<div class="bg-amber-50 border border-amber-200 rounded-xl p-4 mb-4">
<strong class="text-amber-700">
{% if isEnglish %}Payment pending{% else %}Zahlung ausstehend{% endif %}
</strong>
<p class="text-amber-600 mt-1">
{% if isEnglish %}
Your registration has been started. Please complete the payment to confirm your spot.
{% else %}
Deine Anmeldung wurde gestartet. Bitte schliesse die Zahlung ab, um Deinen Platz zu sichern.
{% endif %}
</p>
</div>
{% else %}
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 mb-4">
<strong class="text-blue-700">{{ phrases.event_phrase_application_status|default('Anmeldestatus:') }}</strong>
<p class="text-blue-600 mt-1">
{% if application.applicationStatus == 'started' %}
{{ phrases.event_phrase_status_started|default('Du bist vorgemerkt. Sobald Du Deine E-Mail Adresse bestaetigt hast, wird die Anmeldung gueltig.') }}
{% elseif application.applicationStatus == 'applied' %}
{% if event is defined and event and event.isFormalLanguage %}
{{ phrases.event_phrase_status_applied_formal|default('Sie sind angemeldet. Wir freuen uns auf Sie!') }}
{% else %}
{{ phrases.event_phrase_status_applied|default('Du bist angemeldet. Wir freuen uns auf Dich!') }}
{% endif %}
{% else %}
{{ phrases.event_phrase_status_other|default('Du bist angemeldet mit dem Status ==') }} {{ application.applicationStatus }}
{% endif %}
</p>
</div>
{% endif %}
{% endif %}
{% endif %}
{# Ticket Availability Counter #}
{% if settings.showParticipantCounter|default(false) %}
{% set maxParticipants = batch.capacity|default(0) %}
{% set countedStatuses = settings.countStatus|default(['applied', 'approved']) %}
{% set registered = 0 %}
{% if 'started' in countedStatuses and batch.startedCount is not null %}
{% set registered = registered + batch.startedCount %}
{% endif %}
{% if 'applied' in countedStatuses and batch.appliedCount is not null %}
{% set registered = registered + batch.appliedCount %}
{% endif %}
{% if 'approved' in countedStatuses and batch.approvedCount is not null %}
{% set registered = registered + batch.approvedCount %}
{% endif %}
{% if 'rejected' in countedStatuses and batch.rejectedCount is not null %}
{% set registered = registered + batch.rejectedCount %}
{% endif %}
{% set available = maxParticipants - registered %}
<div class="bg-white/10 backdrop-blur rounded-xl p-3 mb-6 text-center text-white">
{% if isEnglish %}
<strong>{{ registered }} of {{ maxParticipants }} seats taken</strong> |
<strong class="text-yellow-300">{{ available }} left</strong> - don't miss your spot
{% else %}
<strong>{{ registered }} von {{ maxParticipants }} Plaetzen belegt</strong> |
<strong class="text-yellow-300">{{ available }} uebrig</strong> - sichere dir deinen Platz
{% endif %}
</div>
{% endif %}
{# Registration Form — also shown when payment is pending (started + paid) #}
{% if application.applicationStatus is defined and (application.applicationStatus is empty or isPaidAndStarted) %}
{# Waiting List or Full Notice #}
{% set effectiveCapacity = batch.capacity|default(0) %}
{% if effectiveCapacity > 0 and batch.appliedCount >= effectiveCapacity %}
{% if settings.hasWaitingList %}
<h2 class="text-2xl font-bold text-white text-center mb-6">
{{ phrases.event_phrase_all_seats_booked_waiting_list|default('Alle Plaetze sind gebucht - Bitte registriere Dich fuer die Warteliste') }}
</h2>
{% else %}
<h2 class="text-2xl font-bold text-white text-center mb-6">
{{ phrases.event_phrase_all_seats_booked_no_registration|default('Alle Plaetze sind gebucht - Eine Anmeldung ist leider nicht moeglich') }}
</h2>
{% endif %}
{% endif %}
{# Show form if not full OR has waiting list #}
{% if (effectiveCapacity == 0 or batch.appliedCount < effectiveCapacity) or (effectiveCapacity > 0 and batch.appliedCount >= effectiveCapacity and settings.hasWaitingList) %}
{# Form Container #}
<div class="bg-white rounded-2xl p-6 shadow-xl">
{{ form_start(form) }}
{{ form_errors(form) }}
{# Name Fields (Side by Side) #}
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
{{ form_widget(form.firstName, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all',
'placeholder': phrases.phrase_form_firstName_placeholder ~ ' (*)'
}}) }}
</div>
<div>
{{ form_widget(form.lastName, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all',
'placeholder': phrases.phrase_form_lastName_placeholder ~ ' (*)'
}}) }}
</div>
</div>
{# Startup Name (Optional) #}
{% if batch.askForTeam %}
<div class="mb-4">
{{ form_widget(form.startupName, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all',
'placeholder': 'Firma'
}, 'required': false}) }}
</div>
{% endif %}
{# Email #}
<div class="mb-4">
{{ form_widget(form.email, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all',
'placeholder': 'E-mail (*)'
}}) }}
</div>
{# Phone #}
{% if batch.hasIncludePhone %}
<div class="mb-4">
{{ form_widget(form.phone, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all',
'placeholder': 'Phone Number'
}, 'required': false}) }}
<p class="text-sm text-gray-500 mt-1">
{{ phrases.request_phone_number_for_updates|default('Bitte gib Deine Telefonnummer an, damit wir Dich im Fall von Aenderungen erreichen koennen.') }}
</p>
</div>
{% endif %}
{# LinkedIn #}
{% if batch.hasIncludeLinkedIn %}
<div class="mb-4">
{{ form_widget(form.linkedin, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all'
}, 'required': false}) }}
</div>
{% endif %}
{# Extra Fields (Dynamic) #}
{% if attribute(settings, 'extraFields') is defined %}
{% set extraFields = attribute(settings, 'extraFields') %}
{% for extraField in extraFields %}
{% set formField = attribute(form, extraField.field) %}
{% if extraField.type == 'checkbox' %}
<div class="mb-4">
<label class="flex items-start gap-3 cursor-pointer">
{{ form_widget(formField, {'attr': {
'class': 'mt-1 w-5 h-5 accent-[color:var(--registration-accent,#9632FF)] border-gray-300 rounded'
}, 'required': extraField.required}) }}
<span class="text-gray-600 text-sm">{{ extraField.label }}</span>
</label>
</div>
{% else %}
<div class="mb-4">
{{ form_widget(formField, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all'
}, 'required': extraField.required}) }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{# Applicant Types #}
{% if settings.applicantTypes is defined and settings.applicantTypes %}
<div class="mb-4">
{{ form_widget(form.applicantType, {'attr': {
'class': 'w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[color:var(--registration-accent,#9632FF)] focus:border-transparent outline-none transition-all'
}, 'required': false}) }}
</div>
{% endif %}
{# Newsletter Permission #}
{% if batch.hasIncludeNewsletterPermission %}
<div class="mb-4">
<label class="flex items-start gap-3 cursor-pointer">
{{ form_widget(form.hasNewsletterPermissionGiven, {'attr': {
'class': 'mt-1 w-5 h-5 accent-[color:var(--registration-accent,#9632FF)] border-gray-300 rounded'
}, 'required': false}) }}
<span class="text-gray-600 text-sm">
{% if application.lang == 'DE' %}
Ja, ich moechte gerne ueber weitere Veranstaltungen informiert werden.
{% else %}
Yes, I would like to be informed about further events.
{% endif %}
</span>
</label>
</div>
{% endif %}
{# Volunteer Consent #}
{% if settings.hasIncludeVolunteerConsent is defined and settings.hasIncludeVolunteerConsent %}
<div class="mb-4">
<label class="flex items-start gap-3 cursor-pointer">
{{ form_widget(form.hasVolunteerAgreed, {'attr': {
'class': 'mt-1 w-5 h-5 accent-[color:var(--registration-accent,#9632FF)] border-gray-300 rounded'
}, 'required': false}) }}
<span class="text-gray-600 text-sm">
{% if application.lang == 'DE' %}
Ja, ich wuerde mich gerne als Volunteer, Mentor oder Unterstuetzer einbringen
{% else %}
Yes, I would like to contribute as a volunteer, mentor, or supporter
{% endif %}
</span>
</label>
</div>
{% endif %}
{# Terms Consent #}
{% if batch.hasIncludeTermsConsent %}
<div class="mb-4 p-3 bg-gray-50 rounded-lg">
<p class="text-sm text-gray-500">
{% if application.lang == 'DE' %}
Bei unseren Veranstaltungen koennen Aufnahmen fuer Social Media gemacht werden.
<br>Mit Deiner Anmeldung stimmst Du zu, dass Bildaufnahmen fuer Social Media verwendet werden duerfen.
{% else %}
At our events, recordings for social media can be made.
<br>By registering, you agree that image recordings may be used for social media.
{% endif %}
</p>
</div>
{% endif %}
{# AI Hub Trial Membership Note #}
{% if batch.productId > 322 and batch.productId < 330 %}
<div class="mb-4 p-3 bg-gray-50 rounded-lg">
<p class="text-sm text-gray-500">
(*) die Probemitgliedschaft im AI Hub verlaengert sich nicht automatisch und wird 30 Tage nach Ende der ausgewaehlten AI-Summer-School-Ausgabe automatisch und ohne zusaetzliche Kosten gekuendigt.
</p>
</div>
{% endif %}
{# Price Information #}
{% if batch.priceInEuroCent %}
<div class="mb-4 p-3 bg-gray-50 rounded-lg">
{% if application.lang == 'DE' %}
<strong>Der Standardpreis betraegt {{ application.priceInEuroCent / 100|number_format(0, ',', '.') }}EUR</strong> (zzgl. MwSt.)
{% if application.discountPercent %}
<br>
Du erhaelst einen Discount wegen <i>{{ application.discountReason }}</i> in Hoehe von {{ application.discountPercent }}%
und daher betraegt der Preis fuer Dich {{ application.realPriceinEuroCent / 100|number_format(0, ',', '.') }}EUR (zzgl. MwSt.)
{% endif %}
<br>
{% if application.realPriceinEuroCent %}
<small class="text-gray-500">Mit Klick auf "Jetzt anmelden" wirst Du angemeldet und der Zahlungsvorgang wird gestartet</small>
{% endif %}
{% else %}
<strong>The standard price is {{ application.priceInEuroCent / 100|number_format(0, ',', '.') }}EUR</strong>
{% if application.discountPercent %}
<br>
You will receive a discount due to <i>{{ application.discountReason }}</i> in the amount of {{ application.discountPercent }}%
and therefore the price for you is {{ application.realPriceinEuroCent / 100|number_format(0, ',', '.') }}EUR
{% endif %}
<br>
{% if application.realPriceinEuroCent %}
<small class="text-gray-500">By clicking "Register Now" you will be registered and the payment process will be started</small>
{% endif %}
{% endif %}
</div>
{% endif %}
{# Submit Button #}
{% set label = batch.registrationButtonText ?? phrases.phrase_form_button_call_to_act %}
<div class="text-center mt-6">
{{ form_widget(form.finish, {'attr': {
'class': 'px-8 py-4 bg-gray-900 text-white font-bold text-lg rounded-xl hover:bg-gray-800 transition-colors cursor-pointer'
}, 'label': label}) }}
</div>
{# CSRF Token #}
{% if not embed|default(false) %}
{{ form_row(form._token) }}
{% endif %}
{# B-128: Meta CAPI dedup event ID #}
<input type="hidden" name="_meta_event_id" id="metaEventId" value="">
{{ form_end(form, {'render_rest': false}) }}
{# Meta Pixel CompleteRegistration Event - fires on lead-capture form submit (B-128: with CAPI dedup eventID) #}
<script>
(function() {
var form = document.querySelector('form[name="{{ form.vars.name }}"]');
if (!form) return;
var isSubmitting = false;
form.addEventListener('submit', function(e) {
if (isSubmitting) return true;
if (typeof fbq !== 'undefined') {
e.preventDefault();
e.stopPropagation();
var submitted = false;
function submitForm() {
if (submitted) return;
submitted = true;
isSubmitting = true;
form.submit();
}
var metaEventId = (crypto && crypto.randomUUID) ? crypto.randomUUID() : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
var eventIdField = document.getElementById('metaEventId');
if (eventIdField) eventIdField.value = metaEventId;
fbq('track', 'CompleteRegistration', {
content_name: '{{ event.title|e('js') }}'
}, { eventID: metaEventId });
setTimeout(function() {
submitForm();
}, 1000);
}
});
})();
</script>
{# Reduced Tickets Info #}
{% if batch.priceInEuroCent and attribute(phrases, 'phrase_reduced_tickets') is defined %}
<div class="mt-4">
<p class="text-sm text-gray-500">{{ phrases.phrase_reduced_tickets }}</p>
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
</div>
{# Multi-Batch: Other Dates Section #}
{% if isMultiBatchEvent and futureBatches|length > 1 %}
<div class="max-w-4xl mx-auto px-4 mt-12" id="{{ isEnglish ? 'OtherDates' : 'WeitereTermine' }}">
<h3 class="text-2xl font-bold text-white text-center mb-6">
{{ phrases.event_phrase_other_dates|default('Weitere Termine') }}
</h3>
<div class="space-y-3">
{% for futureBatch in futureBatches %}
{% if futureBatch.id != batch.id %}
<a href="{{ path('event_show_single_by_date', {'slug': event.slug, 'date': futureBatch.startDate|date('Y-m-d')}) }}"
class="block bg-white rounded-xl p-4 hover:shadow-lg transition-shadow">
<div class="flex flex-wrap items-center justify-between gap-4">
<div>
<h5 class="text-lg font-semibold text-gray-900">
{% if lang == 'DE' %}
{{ futureBatch.startDate|date('d. F Y', 'Europe/Berlin')|replace({
'January': 'Januar', 'February': 'Februar', 'March': 'Maerz',
'April': 'April', 'May': 'Mai', 'June': 'Juni',
'July': 'Juli', 'August': 'August', 'September': 'September',
'October': 'Oktober', 'November': 'November', 'December': 'Dezember'
}) }}
{% else %}
{{ futureBatch.startDate|date('F d, Y') }}
{% endif %}
</h5>
<p class="text-gray-600">
{{ futureBatch.startDate|date('H:i') }} -
{% if futureBatch.endDate %}
{{ futureBatch.endDate|date('H:i') }} Uhr
{% else %}
{{ futureBatch.startDate|date_modify('+2 hours')|date('H:i') }} Uhr
{% endif %}
</p>
{% if futureBatch.name and futureBatch.name != batch.name %}
<p class="text-sm text-gray-400">{{ futureBatch.name }}</p>
{% endif %}
</div>
<div class="flex items-center gap-2">
{% set isBatchOpen = futureBatch.start and futureBatch.end and date(futureBatch.start) <= date() and date(futureBatch.end) >= date() %}
{% set futureBatchCapacity = futureBatch.capacity|default(0) %}
{% if futureBatchCapacity > 0 and futureBatch.appliedCount >= futureBatchCapacity %}
<span class="px-3 py-1 bg-red-100 text-red-700 rounded-full text-sm font-medium">Ausgebucht</span>
{% elseif not isBatchOpen %}
<span class="px-3 py-1 bg-gray-100 text-gray-600 rounded-full text-sm font-medium">Anmeldung geschlossen</span>
{% else %}
<span class="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm font-medium">
{% if futureBatchCapacity > 0 %}
{{ futureBatchCapacity - futureBatch.appliedCount }} Plaetze frei
{% else %}
Plaetze verfuegbar
{% endif %}
</span>
<span class="text-[color:var(--registration-accent,#9632FF)] font-medium">Zu diesem Termin →</span>
{% endif %}
</div>
</div>
</a>
{% endif %}
{% endfor %}
</div>
{% if selectedDate %}
<div class="mt-4 text-center">
<a href="{{ path('event_show_single', {'slug': event.slug}) }}" class="text-white hover:text-gray-200 underline">
← Alle Termine anzeigen
</a>
</div>
{% endif %}
</div>
{% endif %}
</section>