8 Commits

Author SHA1 Message Date
08a7035dcd add legal footer 2025-02-28 13:27:59 +00:00
46575b08f7 build(docker): update Dockerfile for better setup order (#39)
Reviewed-on: #39
Co-authored-by: Jan Klattenhoff <jan@kjan.email>
Co-committed-by: Jan Klattenhoff <jan@kjan.email>
2025-02-28 13:21:50 +00:00
bd06199f87 stuff again (#38)
Co-authored-by: Jan Klattenhoff <jan@kjan.email>
Reviewed-on: #38
2025-02-28 13:12:25 +00:00
c220c2aef8 stuff (#37)
Co-authored-by: Jan Klattenhoff <jan@kjan.email>
Reviewed-on: #37
2025-02-28 11:53:10 +00:00
dacd7f5cd2 stuff (#36)
Reviewed-on: #36
2025-02-28 10:30:49 +00:00
49a989fd39 fix wrong link (#35)
Reviewed-on: #35
2025-02-27 18:19:57 +00:00
9c6e991e58 change open end to 03:00 (#34)
Reviewed-on: #34
2025-02-27 17:52:54 +00:00
f15a97fd5a add missing event data in order mail (#33)
Reviewed-on: #33
2025-02-27 17:27:40 +00:00
21 changed files with 149 additions and 49 deletions

View File

@ -1,11 +1,6 @@
FROM composer AS composer
FROM php:apache
COPY .. /var/www/html/
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY .docker/hosts/abiball.conf /etc/apache2/sites-enabled
RUN chown -R www-data:www-data /var/www/html
RUN rm /etc/apache2/sites-enabled/000-default.conf
RUN apt-get update
@ -17,7 +12,14 @@ RUN a2enmod rewrite
RUN pecl install apcu && docker-php-ext-enable apcu
WORKDIR /var/www/html
COPY .. /var/www/html/
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY .docker/hosts/abiball.conf /etc/apache2/sites-enabled
RUN chown -R www-data:www-data /var/www/html
RUN composer install --optimize-autoloader --no-suggest --no-progress
RUN php bin/console tailwind:build
RUN php bin/console asset-map:compile
RUN echo "APP_ENV=prod" > .env.local
RUN echo "APP_ENV=prod" > .env.local

View File

@ -12,6 +12,7 @@ export default class extends Controller {
"ticketType",
"foodType",
"note",
"donation"
];
stripe;
@ -191,6 +192,7 @@ export default class extends Controller {
lastname: this.lastnameTarget.value.trim(),
email: this.emailTarget.value.trim(),
phone: this.phoneTarget.value.trim(),
donation: Number(this.donationTarget.value) ?? null
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -4,19 +4,16 @@ namespace App\Controller\Admin;
use App\Entity\Customer;
use App\Entity\Payment;
use App\Enum\TicketData;
use Doctrine\Common\Collections\Collection;
use App\Form\TicketType;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
use EasyCorp\Bundle\EasyAdminBundle\Field\CollectionField;
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TelephoneField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use App\Form\TicketType;
class CustomerCrudController extends AbstractCrudController
{

View File

@ -9,6 +9,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField;
class PaymentCrudController extends AbstractCrudController
{
@ -23,6 +24,8 @@ class PaymentCrudController extends AbstractCrudController
->setCrudController(CustomerCrudController::class)
->formatValue(fn($value, $payment) => $payment->getCustomer()->getEmail());
yield BooleanField::new('completed', 'Bezahlt')->renderAsSwitch(false);
yield NumberField::new('donation', 'Spende')
->formatValue(fn (?float $donation) => ($donation??'0').'€');
}
public function configureActions(Actions $actions): Actions

View File

@ -31,7 +31,7 @@ class TicketCrudController extends AbstractCrudController
yield AssociationField::new('customer', 'Käufer')
->setCrudController(CustomerCrudController::class)
->formatValue(fn(Customer $customer) => $customer->getEmail())
->formatValue(fn(Customer $customer) => $customer->getFirstname(). ' ' . $customer->getLastname())
->hideOnForm();
yield BooleanField::new('checkedIn', '')

View File

@ -9,6 +9,7 @@ class PersonalData
public string $lastname,
public string $email,
public string $phone,
public ?float $donation,
) {
}
}

View File

@ -24,6 +24,9 @@ class Payment
#[ORM\JoinColumn(nullable: false)]
private ?Customer $customer = null;
#[ORM\Column(nullable: true)]
private ?float $donation = null;
public function getId(): ?int
{
return $this->id;
@ -71,4 +74,16 @@ class Payment
return $total + TicketData::TICKET_DATA[$ticket->getType()]['price'];
}, 0);
}
public function getDonation(): ?float
{
return $this->donation;
}
public function setDonation(?float $donation): static
{
$this->donation = $donation;
return $this;
}
}

View File

@ -7,6 +7,7 @@ use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ContactForm extends AbstractType
{
@ -16,4 +17,11 @@ class ContactForm extends AbstractType
->add('message', TextareaType::class)
->add('phone', TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
}

View File

@ -24,6 +24,7 @@ final readonly class ContactService
->htmlTemplate('email/contact.html.twig')
->context(['data' => $data])
->subject('Kontakt aufnahme')
->replyTo($data->email)
->from($this->senderEmail)
->to($this->contactEmail);

View File

@ -30,7 +30,7 @@ class TicketService
public function handleTicketData(TicketFormData $data): Session
{
$session = $this->createSession($this->getLineItems($data->tickets), $data->personal->email);
$session = $this->createSession($this->getLineItems($data->tickets, $data->personal->donation), $data->personal->email);
$this->saveTicketData($data, $session->id);
@ -57,16 +57,30 @@ class TicketService
$payment = (new Payment())
->setSessionId($sessionId)
->setCompleted(false)
->setCustomer($this->createEntityFromData($data));
->setCustomer($this->createEntityFromData($data))
->setDonation($data->personal->donation);
$this->em->persist($payment);
$this->em->flush();
}
private function getLineItems(array $tickets): array
private function getLineItems(array $tickets, float $donation = null): array
{
$lineItems = [];
if ($donation) {
$lineItems[] = [
'price_data' => [
'currency' => 'eur',
'product_data' => [
'name' => 'Spende',
],
'unit_amount' => $donation * 100,
],
'quantity' => 1,
];
}
foreach ($tickets as $ticket) {
$ticketData = TicketEnum::TICKET_DATA[$ticket->ticketType];
$lineItems[] = [

View File

@ -21,7 +21,8 @@ class Ticket extends AbstractExtension
return [
new TwigFilter('food', $this->getFoodName(...)),
new TwigFilter('ticket', $this->getTicket(...)),
new TwigFilter('qr', $this->generateUrl(...)),
new TwigFilter('qr', $this->generateQr(...)),
new TwigFilter('url', $this->url(...)),
];
}
@ -35,12 +36,17 @@ class Ticket extends AbstractExtension
return TicketData::TICKET_DATA[$id] ?? ['name' => 'N/A', 'price' => 'N/A'];
}
public function generateUrl(\App\Entity\Ticket $ticket): string
public function generateQr(\App\Entity\Ticket $ticket): string
{
return (new QRCode)->render($this->urlGenerator->generate('admin', [
return (new QRCode)->render($this->url($ticket));
}
public function url(\App\Entity\Ticket $ticket): string
{
return $this->urlGenerator->generate('admin', [
'crudAction' => 'detail',
'crudControllerFqcn' => TicketCrudController::class,
'entityId' => $ticket->getCustomer()?->getId()
], UrlGeneratorInterface::ABSOLUTE_URL));
], UrlGeneratorInterface::ABSOLUTE_URL);
}
}

View File

@ -16,7 +16,7 @@
<td>{{ (ticket.type | ticket)['price'] }}€</td>
<td>{{ ticket.note }}</td>
<td>
<a href="{{ path('admin', { entity: 'App\Entity\Ticket', action: 'detail', id: ticket.id }) }}"
<a href="{{ ticket|url }}"
class="btn btn-info btn-sm">
View
</a>

View File

@ -6,8 +6,11 @@
<div class="min-h-screen bg-gradient-to-br from-yellow-50 via-white to-orange-50 relative overflow-hidden">
<header class="w-full bg-white/90 backdrop-blur-md shadow-lg fixed top-0 z-50 border-b border-gray-100">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex justify-between items-center">
<a href="{{ path('app_home') }}">
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity" />
<a href="{{ path('app_home') }}" class="flex items-center space-x-4">
<img src="{{ asset('images/logos/new_logo.png') }}" alt="Waldorfschule Bremen Osterholz"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity"/>
<img src="{{ asset('images/logos/second_logo.png') }}" alt="Secondary Logo"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity sm:block hidden"/>
</a>
<a href="{{ path('app_home') }}" class="bg-gradient-to-r from-red-500 to-orange-500 hover:from-red-600 hover:to-orange-600 text-white px-4 sm:px-6 md:px-8 py-2 sm:py-2.5 rounded-full text-xs sm:text-sm font-medium shadow-md hover:shadow-lg transition-all duration-300">
Zurück zur Startseite

View File

@ -12,16 +12,7 @@
<tr>
<td>
<font color="#1f2937" size="5" face="Arial, Helvetica, sans-serif">Contact Details</font>
<table cellpadding="15" cellspacing="0" border="1" width="100%" bgcolor="#f9fafb" bordercolor="#e5e7eb" style="border-collapse: collapse; margin-top: 20px;">
<tr>
<td>
<font color="#6b7280" size="2" face="Arial, Helvetica, sans-serif">Email</font><br>
<font color="#1f2937" size="3" face="Arial, Helvetica, sans-serif">{{ data.email }}</font>
</td>
</tr>
</table>
{% if data.phone %}
<table cellpadding="15" cellspacing="0" border="1" width="100%" bgcolor="#f9fafb" bordercolor="#e5e7eb" style="border-collapse: collapse; margin-top: 20px;">
<tr>
<td>
@ -30,7 +21,7 @@
</td>
</tr>
</table>
{% endif %}
<table cellpadding="15" cellspacing="0" border="1" width="100%" bgcolor="#f9fafb" bordercolor="#e5e7eb" style="border-collapse: collapse; margin-top: 20px;">
<tr>
<td>

View File

@ -69,9 +69,17 @@
</p>
<div class="details-box" style="background-color: #f8f9fa; border-radius: 8px; padding: 25px 20px; margin: 0 0 25px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
<h2 style="color: #372064; font-size: 24px; margin: 0 0 20px 0; font-weight: 500;">Veranstaltungsdetails</h2>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Datum: [Datum]</p>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Uhrzeit: [Uhrzeit]</p>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Adresse: [Adresse]</p>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Datum: 28. Juni 2025</p>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Uhrzeit: 17:30 - 03:00</p>
<p style="color: #666666; margin: 10px 0; font-size: 16px;">Adresse: Graubündener Str. 4, 28325 Bremen</p>
</div>
<div style="display: inline-flex; align-items: center; border: 1px solid gray; border-radius: 4px; padding: 3px 6px">
<a href="https://fws-bremen-abiball.de{{ path('app_calendar') }}"
target="_blank"
style="display: inline-flex; align-items: center; padding: 12px 24px; background-color: #e5e7eb; border-radius: 4px; text-decoration: none; transition: background-color 0.3s ease;">
<twig:ux:icon name="heroicons:calendar" style="width: 20px; height: 20px; margin-right: 8px; color: #4b5563;" />
<span style="font-size: 14px; font-weight: 500; color: #4b5563;">Zum Kalender hinzufügen</span>
</a>
</div>
</div>
</td>

View File

@ -3,11 +3,16 @@
{% block title %}Home{% endblock %}
{% block body %}
<div class="min-h-screen bg-gradient-to-br from-yellow-50 via-white to-orange-50 relative overflow-hidden">
<div class="min-h-screen relative overflow-hidden" style="background-image: url('{{ asset('images/backgrounds/abiball_bg.jpeg') }}'); background-size: cover; background-position: center; background-attachment: fixed;">
<div class="min-h-screen bg-black/30 backdrop-blur-[2px] relative overflow-hidden">
<header class="w-full bg-white/90 backdrop-blur-md shadow-lg fixed top-0 z-50 border-b border-gray-100">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex justify-between items-center">
<img src="{{ asset('images/logo.png') }}" alt="Logo"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity"/>
<div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex justify-between items-center">
<a href="{{ path('app_home') }}" class="flex items-center space-x-4">
<img src="{{ asset('images/logos/new_logo.png') }}" alt="Waldorfschule Bremen Osterholz"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity"/>
<img src="{{ asset('images/logos/second_logo.png') }}" alt="Secondary Logo"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity sm:block hidden"/>
</a>
<a href="{{ path('app_contact') }}"
class="bg-gradient-to-r from-red-500 to-orange-500 hover:from-red-600 hover:to-orange-600 text-white px-4 sm:px-6 md:px-8 py-2 sm:py-2.5 rounded-full text-xs sm:text-sm font-medium shadow-md hover:shadow-lg transition-all duration-300">
Kontaktaufnahme
@ -15,11 +20,11 @@
</div>
</header>
<main class="container mx-auto px-4 sm:px-6 lg:px-8 pt-24 sm:pt-28 md:pt-32 pb-16 sm:pb-20 flex flex-col items-center justify-center relative z-10">
<h1 class="text-3xl sm:text-4xl md:text-5xl font-bold text-gray-800 text-center mb-6 sm:mb-8 tracking-tight leading-tight">
<main class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pt-24 sm:pt-28 md:pt-32 pb-16 sm:pb-20 flex flex-col items-center justify-center relative z-10">
<h1 class="text-3xl sm:text-4xl md:text-5xl font-bold text-white text-center mb-6 sm:mb-8 tracking-tight leading-tight">
Willkommen zum <span class="bg-clip-text text-transparent bg-gradient-to-r from-red-500 to-orange-500">Abiball 2025</span>
</h1>
<div class="w-full max-w-4xl">
<div class="w-full max-w-6xl">
<div class="bg-white/80 backdrop-blur-md shadow-xl rounded-2xl sm:rounded-3xl p-6 sm:p-8 md:p-10 mb-6 sm:mb-8 transform transition-all duration-300 border border-gray-100">
<h1 class="text-3xl">Willkommen zum Abiball 2025! 🎉🎓</h1>
<p class="text-base sm:text-lg text-gray-700 leading-relaxed whitespace-pre-line text-wrap">
@ -31,7 +36,7 @@
</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 w-full gap-3 mb-5">
<div class="grid grid-cols-1 sm:grid-cols-3 w-full gap-3 mb-5">
<div class="bg-white/80 backdrop-blur-md shadow-xl rounded-2xl sm:rounded-3xl p-5 mb-3 sm:mb-12 transform transition-all duration-300 border border-gray-100">
<h2 class="text-xl text-center font-semibold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-red-500 to-orange-500">Datum & Uhrzeit</h2>
<div class="space-y-3">
@ -41,7 +46,7 @@
</div>
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:clock" class="w-5 h-5 text-orange-600" />
<span class="text-gray-700">17:30 - Open End</span>
<span class="text-gray-700">17:30 - 03:00</span>
</div>
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:map-pin" class="w-5 h-5 text-orange-600" />
@ -75,6 +80,39 @@
</div>
</div>
</div>
<div class="bg-white/80 backdrop-blur-md shadow-xl rounded-2xl sm:rounded-3xl p-5 mb-3 sm:mb-12 transform transition-all duration-300 border border-gray-100">
<h2 class="text-xl text-center font-semibold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-red-500 to-orange-500">Ticket Preise</h2>
<div class="space-y-3">
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:ticket" class="w-5 h-5 text-orange-600" />
<div class="text-gray-700">
<span class="font-medium">All-Inclusive Ticket</span>
<span class="ml-2">50€</span>
</div>
</div>
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:cake" class="w-5 h-5 text-orange-600" />
<div class="text-gray-700">
<span class="font-medium">After-Show Ticket</span>
<span class="ml-2">20€</span>
</div>
</div>
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:musical-note" class="w-5 h-5 text-orange-600" />
<div class="text-gray-700">
<span class="font-medium">Kind (6-12 Jahre)</span>
<span class="ml-2">15€</span>
</div>
</div>
<div class="flex items-center space-x-3">
<twig:ux:icon name="heroicons:musical-note" class="w-5 h-5 text-orange-600" />
<div class="text-gray-700">
<span class="font-medium">Kind (0-6 Jahre)</span>
<span class="ml-2">0€</span>
</div>
</div>
</div>
</div>
</div>
<div class="text-center space-y-6">
@ -86,4 +124,5 @@
</div>
</main>
</div>
</div>
{% endblock %}

View File

@ -6,8 +6,11 @@
<div class="min-h-screen bg-white relative overflow-hidden">
<header class="w-full bg-white/90 backdrop-blur-md shadow-lg fixed top-0 z-50 border-b border-gray-100">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex justify-between items-center">
<a href="{{ path('app_home') }}" class="flex items-center space-x-2 group">
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="w-32 sm:w-36 md:w-40 h-auto group-hover:opacity-90 transition-opacity" />
<a href="{{ path('app_home') }}" class="flex items-center space-x-4">
<img src="{{ asset('images/logos/new_logo.png') }}" alt="Waldorfschule Bremen Osterholz"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity"/>
<img src="{{ asset('images/logos/second_logo.png') }}" alt="Secondary Logo"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity sm:block hidden"/>
</a>
<a href="{{ path('app_home') }}" class="bg-gradient-to-r from-red-500 to-orange-500 hover:from-red-600 hover:to-orange-600 text-white px-6 py-2.5 rounded-full text-sm font-medium shadow-md hover:shadow-lg transition-all duration-300 flex items-center space-x-2">
<twig:ux:icon name="line-md:home" class="w-4 h-4" />
@ -49,6 +52,10 @@
<twig:ux:icon name="ic:baseline-phone" class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
<input name="phone" data-form-target="phone" class="w-full pl-10 pr-4 py-3 rounded-xl border border-gray-200 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all" type="tel" placeholder="Telefon (optional)">
</div>
<div class="relative">
<twig:ux:icon name="tabler:coin" class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
<input name="donation" data-form-target="donation" class="w-full pl-10 pr-4 py-3 rounded-xl border border-gray-200 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all" type="number" placeholder="Spende (optional)">
</div>
</div>
</section>

View File

@ -8,8 +8,11 @@ Danke
<div class="min-h-screen bg-gradient-to-br from-yellow-50 via-white to-orange-50 relative overflow-hidden">
<header class="w-full bg-white/90 backdrop-blur-md shadow-lg fixed top-0 z-50 border-b border-gray-100">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex justify-between items-center">
<a href="{{ path('app_home') }}" class="flex items-center space-x-2 group">
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="w-32 sm:w-36 md:w-40 h-auto group-hover:opacity-90 transition-opacity" />
<a href="{{ path('app_home') }}" class="flex items-center space-x-4">
<img src="{{ asset('images/logos/new_logo.png') }}" alt="Waldorfschule Bremen Osterholz"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity"/>
<img src="{{ asset('images/logos/second_logo.png') }}" alt="Secondary Logo"
class="w-32 sm:w-36 md:w-40 h-auto hover:opacity-90 transition-opacity sm:block hidden"/>
</a>
<a href="{{ path('app_home') }}" class="bg-gradient-to-r from-red-500 to-orange-500 hover:from-red-600 hover:to-orange-600 text-white px-6 py-2.5 rounded-full text-sm font-medium shadow-md hover:shadow-lg transition-all duration-300 flex items-center space-x-2">
<twig:ux:icon name="line-md:home" class="w-4 h-4" />
@ -69,7 +72,7 @@ Danke
</div>
<div class="flex items-center gap-3">
<twig:ux:icon name="heroicons:musical-note" class="w-6 h-6 text-orange-600 flex-shrink-0" />
<span class="text-gray-600 font-medium whitespace-nowrap">ab 22:00</span>
<span class="text-gray-600 font-medium whitespace-nowrap">22:00-03:00</span>
<span class="text-gray-700">Feier & Tanz im Saal</span>
</div>
</div>