Skip to content

Commit e600711

Browse files
authored
Email template updates (#824)
1 parent c3056c5 commit e600711

File tree

44 files changed

+6480
-2889
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+6480
-2889
lines changed

backend/app/DomainObjects/Status/OrderStatus.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
namespace HiEvents\DomainObjects\Status;
44

5+
use HiEvents\DomainObjects\Enums\BaseEnum;
6+
57
enum OrderStatus
68
{
9+
use BaseEnum;
10+
711
case RESERVED;
812
case CANCELLED;
913
case COMPLETED;

backend/app/Mail/Attendee/AttendeeTicketMail.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function content(): Content
5959
with: [
6060
'renderedBody' => $this->renderedTemplate->body,
6161
'renderedCta' => $this->renderedTemplate->cta,
62+
'eventSettings' => $this->eventSettings,
6263
]
6364
);
6465
}

backend/app/Mail/Order/OrderSummary.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public function content(): Content
5454
with: [
5555
'renderedBody' => $this->renderedTemplate->body,
5656
'renderedCta' => $this->renderedTemplate->cta,
57+
'eventSettings' => $this->eventSettings,
5758
]
5859
);
5960
}

backend/app/Services/Domain/Email/EmailTemplateService.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ public function __construct(
1818
) {
1919
}
2020

21-
/**
22-
* Get a template for the given type and scope
23-
*/
24-
public function getTemplate(
21+
public function getTemplateByType(
2522
EmailTemplateType $type,
2623
int $accountId,
2724
?int $eventId = null,
@@ -165,7 +162,7 @@ private function getDefaultTemplates(): array
165162
'body' => <<<'LIQUID'
166163
<strong>Your Order is Confirmed! 🎉</strong><br>
167164
168-
{% if order.is_pending %}
165+
{% if order.is_awaiting_offline_payment %}
169166
<strong>ℹ️ Payment Pending:</strong> Your order is pending payment. Tickets have been issued but will not be valid until payment is received.<br>
170167
<strong>Payment Instructions</strong><br>
171168
Please follow the instructions below to complete your payment:<br>
@@ -180,7 +177,7 @@ private function getDefaultTemplates(): array
180177
<strong>Event Details</strong><br>
181178
<strong>Event Name:</strong> {{ event.title }}<br>
182179
<strong>Date & Time:</strong> {{ event.date }} at {{ event.time }}<br>
183-
{% if event.location %}<strong>Location:</strong> {{ event.location }}<br>{% endif %}
180+
{% if event.full_address %}<strong>Location:</strong> {{ event.full_address }}<br>{% endif %}
184181
<br>
185182
186183
{% if settings.post_checkout_message %}
@@ -203,7 +200,7 @@ private function getDefaultTemplates(): array
203200
'body' => <<<'LIQUID'
204201
<strong>You're going to {{ event.title }}! 🎉</strong><br>
205202
206-
{% if order.is_pending %}
203+
{% if order.is_awaiting_offline_payment %}
207204
<strong>ℹ️ Payment Pending:</strong> Your order is pending payment. Tickets have been issued but will not be valid until payment is received.<br>
208205
{% endif %}
209206
@@ -215,7 +212,7 @@ private function getDefaultTemplates(): array
215212
<strong>Event:</strong> {{ event.title }}<br>
216213
<strong>Date:</strong> {{ event.date }}<br>
217214
<strong>Time:</strong> {{ event.time }}<br>
218-
{% if event.location %}<strong>Location:</strong> {{ event.location }}<br>{% endif %}
215+
{% if event.full_address %}<strong>Location:</strong> {{ event.full_address }}<br>{% endif %}
219216
<br>
220217
221218
<strong>Your Ticket</strong><br>

backend/app/Services/Domain/Email/EmailTokenContextBuilder.php

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use HiEvents\Helper\DateHelper;
1616
use HiEvents\Helper\IdHelper;
1717
use HiEvents\Helper\Url;
18+
use HiEvents\Locale;
1819

1920
class EmailTokenContextBuilder
2021
{
@@ -25,16 +26,21 @@ public function buildOrderConfirmationContext(
2526
EventSettingDomainObject $eventSettings
2627
): array
2728
{
28-
$eventDate = new Carbon(DateHelper::convertFromUTC($event->getStartDate(), $event->getTimezone()));
29+
$eventStartDate = new Carbon(DateHelper::convertFromUTC($event->getStartDate(), $event->getTimezone()));
30+
$eventEndDate = $event->getEndDate() ? new Carbon(DateHelper::convertFromUTC($event->getEndDate(), $event->getTimezone())) : null;
2931

3032
return [
3133
// Event object
3234
'event' => [
3335
'title' => $event->getTitle(),
34-
'date' => $eventDate->format('F j, Y'),
35-
'time' => $eventDate->format('g:i A'),
36-
'location' => $eventSettings->getLocationDetails() ? AddressHelper::formatAddress($eventSettings->getLocationDetails()) : '',
36+
'date' => $eventStartDate->format('F j, Y'),
37+
'time' => $eventStartDate->format('g:i A'),
38+
'end_date' => $eventEndDate?->format('F j, Y') ?? '',
39+
'end_time' => $eventEndDate?->format('g:i A') ?? '',
40+
'full_address' => $eventSettings->getLocationDetails() ? AddressHelper::formatAddress($eventSettings->getLocationDetails()) : '',
41+
'location_details' => $eventSettings->getLocationDetails(),
3742
'description' => $event->getDescription() ?? '',
43+
'timezone' => $event->getTimezone(),
3844
],
3945

4046
// Order object
@@ -47,10 +53,13 @@ public function buildOrderConfirmationContext(
4753
'number' => $order->getPublicId(),
4854
'total' => Currency::format($order->getTotalGross(), $event->getCurrency()),
4955
'date' => (new Carbon($order->getCreatedAt()))->format('F j, Y'),
56+
'currency' => $order->getCurrency(), // added
57+
'locale' => $order->getLocale(), // added
5058
'first_name' => $order->getFirstName() ?? '',
5159
'last_name' => $order->getLastName() ?? '',
5260
'email' => $order->getEmail() ?? '',
53-
'is_pending' => $order->isOrderAwaitingOfflinePayment(),
61+
'is_awaiting_offline_payment' => $order->isOrderAwaitingOfflinePayment(),
62+
'is_offline_payment' => $order->getPaymentProvider() === PaymentProviders::OFFLINE->value,
5463
],
5564

5665
// Organizer object
@@ -65,9 +74,6 @@ public function buildOrderConfirmationContext(
6574
'offline_payment_instructions' => $eventSettings->getOfflinePaymentInstructions() ?? '',
6675
'post_checkout_message' => $eventSettings->getPostCheckoutMessage() ?? '',
6776
],
68-
69-
// Top-level flags (for backward compatibility and convenience)
70-
'is_offline_payment' => $order->getPaymentProvider() === PaymentProviders::OFFLINE->value,
7177
];
7278
}
7379

@@ -113,8 +119,20 @@ public function buildPreviewContext(string $templateType): array
113119
'title' => __('Summer Music Festival 2024'),
114120
'date' => 'April 25, 2029',
115121
'time' => '7:00 PM',
116-
'location' => __('Madison Square Garden, New York'),
122+
'end_date' => 'April 26, 2029',
123+
'end_time' => '11:00 PM',
124+
'full_address' => __('3 Arena, North Wall Quay, Dublin 1, Ireland'),
117125
'description' => __('Join us for an unforgettable evening of live music featuring top artists from around the world.'),
126+
'timezone' => 'UTC',
127+
'location_details' => [
128+
'venue_name' => '3 Arena',
129+
'address_line_1' => 'North Wall Quay',
130+
'address_line_2' => '',
131+
'city' => 'Dublin',
132+
'state_or_region' => 'Dublin 1',
133+
'zip_or_postal_code' => 'D01 T0X4',
134+
'country' => 'IE',
135+
]
118136
],
119137
'order' => [
120138
'url' => 'https://example.com/order/ABC123',
@@ -124,7 +142,10 @@ public function buildPreviewContext(string $templateType): array
124142
'first_name' => 'John',
125143
'last_name' => 'Smith',
126144
'email' => '[email protected]',
127-
'is_pending' => false,
145+
'is_awaiting_offline_payment' => false,
146+
'is_offline_payment' => false,
147+
'locale' => Locale::EN->value,
148+
'currency' => 'USD'
128149
],
129150
'organizer' => [
130151
'name' => 'ACME Events Inc.',
@@ -135,7 +156,6 @@ public function buildPreviewContext(string $templateType): array
135156
'offline_payment_instructions' => __('Please transfer the total amount to the following bank account within 5 business days.'),
136157
'post_checkout_message' => __('Thank you for your purchase! We look forward to seeing you at the event.'),
137158
],
138-
'is_offline_payment' => false,
139159
];
140160

141161
if ($templateType === 'attendee_ticket') {

backend/app/Services/Domain/Email/MailBuilderService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private function renderAttendeeTicketTemplate(
7777
EventSettingDomainObject $eventSettings,
7878
OrganizerDomainObject $organizer
7979
): ?RenderedEmailTemplateDTO {
80-
$template = $this->emailTemplateService->getTemplate(
80+
$template = $this->emailTemplateService->getTemplateByType(
8181
type: EmailTemplateType::ATTENDEE_TICKET,
8282
accountId: $event->getAccountId(),
8383
eventId: $event->getId(),
@@ -105,7 +105,7 @@ private function renderOrderSummaryTemplate(
105105
EventSettingDomainObject $eventSettings,
106106
OrganizerDomainObject $organizer
107107
): ?RenderedEmailTemplateDTO {
108-
$template = $this->emailTemplateService->getTemplate(
108+
$template = $this->emailTemplateService->getTemplateByType(
109109
type: EmailTemplateType::ORDER_CONFIRMATION,
110110
accountId: $event->getAccountId(),
111111
eventId: $event->getId(),

backend/app/Services/Domain/Invoice/InvoiceCreateService.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function createInvoiceForOrder(int $orderId): InvoiceDomainObject
6161
]);
6262
}
6363

64-
public function getLatestInvoiceNumber(int $eventId, EventSettingDomainObject $eventSettings): string
64+
private function getLatestInvoiceNumber(int $eventId, EventSettingDomainObject $eventSettings): string
6565
{
6666
$latestInvoice = $this->invoiceRepository->findLatestInvoiceForEvent($eventId);
6767

@@ -76,5 +76,4 @@ public function getLatestInvoiceNumber(int $eventId, EventSettingDomainObject $e
7676

7777
return $prefix . $nextInvoiceNumber;
7878
}
79-
8079
}

backend/app/Services/Infrastructure/Email/LiquidTemplateRenderer.php

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,63 @@ public function getAvailableTokens(EmailTemplateType $type): array
5858
],
5959
[
6060
'token' => '{{ event.date }}',
61-
'description' => __('The event date'),
61+
'description' => __('The event start date'),
6262
'example' => 'January 15, 2024',
6363
],
6464
[
6565
'token' => '{{ event.time }}',
66-
'description' => __('The event time'),
66+
'description' => __('The event start time'),
6767
'example' => '7:00 PM',
6868
],
6969
[
70-
'token' => '{{ event.location }}',
71-
'description' => __('The event location'),
72-
'example' => 'Madison Square Garden',
70+
'token' => '{{ event.end_date }}',
71+
'description' => __('The event end date'),
72+
'example' => 'January 16, 2024',
73+
],
74+
[
75+
'token' => '{{ event.end_time }}',
76+
'description' => __('The event end time'),
77+
'example' => '11:00 PM',
78+
],
79+
[
80+
'token' => '{{ event.full_address }}',
81+
'description' => __('The full event address'),
82+
'example' => '3 Arena, North Wall Quay, Dublin 1, D01 T0X4, Ireland',
83+
],
84+
[
85+
'token' => '{{ event.location_details.venue_name }}',
86+
'description' => __('The event venue name'),
87+
'example' => '3 Arena',
88+
],
89+
[
90+
'token' => '{{ event.location_details.address_line_1 }}',
91+
'description' => __('The venue address line 1'),
92+
'example' => 'North Wall Quay',
93+
],
94+
[
95+
'token' => '{{ event.location_details.address_line_2 }}',
96+
'description' => __('The venue address line 2'),
97+
'example' => '',
98+
],
99+
[
100+
'token' => '{{ event.location_details.city }}',
101+
'description' => __('The venue city'),
102+
'example' => 'Dublin',
103+
],
104+
[
105+
'token' => '{{ event.location_details.state_or_region }}',
106+
'description' => __('The venue state or region'),
107+
'example' => 'Dublin 1',
108+
],
109+
[
110+
'token' => '{{ event.location_details.zip_or_postal_code }}',
111+
'description' => __('The venue ZIP or postal code'),
112+
'example' => 'D01 T0X4',
113+
],
114+
[
115+
'token' => '{{ event.location_details.country }}',
116+
'description' => __('The venue country code'),
117+
'example' => 'IE',
73118
],
74119
[
75120
'token' => '{{ event.description }}',
@@ -139,16 +184,6 @@ public function getAvailableTokens(EmailTemplateType $type): array
139184
'description' => __('The email of the person who placed the order'),
140185
'example' => '[email protected]',
141186
],
142-
[
143-
'token' => '{% if order.is_pending %}',
144-
'description' => __('Conditional: Check if order is pending payment'),
145-
'example' => '{% if order.is_pending %}Payment pending{% endif %}',
146-
],
147-
[
148-
'token' => '{% if is_offline_payment %}',
149-
'description' => __('Conditional: Check if payment is offline'),
150-
'example' => '{% if is_offline_payment %}Offline payment{% endif %}',
151-
],
152187
];
153188

154189
$attendeeTokens = [

backend/resources/views/emails/custom-template.blade.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@
77
{{ $renderedCta['label'] }}
88
</x-mail::button>
99
@endif
10-
</x-mail::message>
10+
11+
{!! $eventSettings->getGetEmailFooterHtml() !!}
12+
13+
</x-mail::message>

backend/tests/Unit/Services/Domain/Email/EmailTemplateServiceTest.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ class EmailTemplateServiceTest extends TestCase
2121
protected function setUp(): void
2222
{
2323
parent::setUp();
24-
24+
2525
$this->mockRepository = Mockery::mock(EmailTemplateRepositoryInterface::class);
2626
$this->mockLiquidRenderer = Mockery::mock(LiquidTemplateRenderer::class);
2727
$this->mockTokenBuilder = Mockery::mock(EmailTokenContextBuilder::class);
28-
28+
2929
$this->emailTemplateService = new EmailTemplateService(
3030
$this->mockRepository,
3131
$this->mockLiquidRenderer,
@@ -55,7 +55,7 @@ public function test_gets_event_level_template_when_exists(): void
5555
->once()
5656
->andReturn($eventTemplate);
5757

58-
$result = $this->emailTemplateService->getTemplate(
58+
$result = $this->emailTemplateService->getTemplateByType(
5959
EmailTemplateType::ORDER_CONFIRMATION,
6060
1, // accountId
6161
1, // eventId
@@ -87,7 +87,7 @@ public function test_falls_back_to_organizer_template_when_no_event_template():
8787
->once()
8888
->andReturn($organizerTemplate);
8989

90-
$result = $this->emailTemplateService->getTemplate(
90+
$result = $this->emailTemplateService->getTemplateByType(
9191
EmailTemplateType::ORDER_CONFIRMATION,
9292
1, // accountId
9393
1, // eventId
@@ -110,7 +110,7 @@ public function test_returns_null_when_no_templates_exist(): void
110110
->once()
111111
->andReturn(null);
112112

113-
$result = $this->emailTemplateService->getTemplate(
113+
$result = $this->emailTemplateService->getTemplateByType(
114114
EmailTemplateType::ORDER_CONFIRMATION,
115115
1, // accountId
116116
1, // eventId
@@ -142,7 +142,7 @@ public function test_gets_organizer_level_template_when_no_event_id(): void
142142
->once()
143143
->andReturn($organizerTemplate);
144144

145-
$result = $this->emailTemplateService->getTemplate(
145+
$result = $this->emailTemplateService->getTemplateByType(
146146
EmailTemplateType::ATTENDEE_TICKET,
147147
1, // accountId
148148
null, // eventId
@@ -174,7 +174,7 @@ public function test_prefers_active_templates_over_inactive(): void
174174
->once()
175175
->andReturn($activeTemplate);
176176

177-
$result = $this->emailTemplateService->getTemplate(
177+
$result = $this->emailTemplateService->getTemplateByType(
178178
EmailTemplateType::ORDER_CONFIRMATION,
179179
1, // accountId
180180
1, // eventId
@@ -207,7 +207,7 @@ public function test_handles_different_template_types(): void
207207
->once()
208208
->andReturn($attendeeTicketTemplate);
209209

210-
$result = $this->emailTemplateService->getTemplate(
210+
$result = $this->emailTemplateService->getTemplateByType(
211211
EmailTemplateType::ATTENDEE_TICKET,
212212
1, // accountId
213213
null, // eventId
@@ -238,4 +238,4 @@ private function createMockTemplate(
238238
'getEngine' => 'liquid',
239239
]);
240240
}
241-
}
241+
}

0 commit comments

Comments
 (0)