Add booking system with calendar and email notifications (v0.3.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m5s
All checks were successful
Create Release Package / build-release (push) Successful in 1m5s
- Booking Custom Post Type with full management features - Room and guest relationship tracking - Check-in/check-out date management with validation - Booking status workflow (pending, confirmed, checked_in, checked_out, cancelled) - Automatic price calculation using existing Calculator - Availability system with real-time conflict detection - AJAX endpoint for instant availability validation - Calendar admin page with monthly view and room/building filters - Color-coded booking status display with legend - Email notifications for new bookings, confirmations, and cancellations - HTML email templates with placeholder-based system - Auto-generated booking references (BNB-YYYY-NNNNN) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
587
src/Booking/EmailNotifier.php
Normal file
587
src/Booking/EmailNotifier.php
Normal file
@@ -0,0 +1,587 @@
|
||||
<?php
|
||||
/**
|
||||
* Email notifier for bookings.
|
||||
*
|
||||
* Handles sending email notifications for booking events.
|
||||
*
|
||||
* @package Magdev\WpBnb\Booking
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace Magdev\WpBnb\Booking;
|
||||
|
||||
use Magdev\WpBnb\PostTypes\Booking;
|
||||
use Magdev\WpBnb\PostTypes\Room;
|
||||
use Magdev\WpBnb\Pricing\Calculator;
|
||||
|
||||
/**
|
||||
* EmailNotifier class.
|
||||
*/
|
||||
final class EmailNotifier {
|
||||
|
||||
/**
|
||||
* Initialize the email notifier.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void {
|
||||
add_action( 'wp_bnb_booking_status_changed', array( self::class, 'on_status_change' ), 10, 3 );
|
||||
add_action( 'save_post_' . Booking::POST_TYPE, array( self::class, 'on_booking_created' ), 20, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle status change event.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @param string $old_status Previous status.
|
||||
* @param string $new_status New status.
|
||||
* @return void
|
||||
*/
|
||||
public static function on_status_change( int $booking_id, string $old_status, string $new_status ): void {
|
||||
switch ( $new_status ) {
|
||||
case 'confirmed':
|
||||
self::send_guest_confirmation( $booking_id );
|
||||
self::send_admin_confirmation( $booking_id );
|
||||
break;
|
||||
|
||||
case 'cancelled':
|
||||
self::send_cancellation( $booking_id );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle booking created event.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param \WP_Post $post Post object.
|
||||
* @return void
|
||||
*/
|
||||
public static function on_booking_created( int $post_id, \WP_Post $post ): void {
|
||||
// Skip if autosave or revision.
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new booking (created in the last 30 seconds).
|
||||
$created = get_post_time( 'U', true, $post_id );
|
||||
if ( time() - $created > 30 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we've already sent this notification.
|
||||
$sent = get_post_meta( $post_id, '_bnb_booking_new_email_sent', true );
|
||||
if ( $sent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as sent before sending to prevent duplicates.
|
||||
update_post_meta( $post_id, '_bnb_booking_new_email_sent', '1' );
|
||||
|
||||
// Send admin notification for new booking.
|
||||
self::send_admin_new_booking( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send new booking notification to admin.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool Whether email was sent.
|
||||
*/
|
||||
public static function send_admin_new_booking( int $booking_id ): bool {
|
||||
$booking = get_post( $booking_id );
|
||||
if ( ! $booking ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = self::get_booking_data( $booking_id );
|
||||
$to = get_option( 'admin_email' );
|
||||
$subject = sprintf(
|
||||
/* translators: 1: Site name, 2: Booking reference */
|
||||
__( '[%1$s] New Booking: %2$s', 'wp-bnb' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$data['booking_reference']
|
||||
);
|
||||
|
||||
$message = self::get_email_template( 'admin-new-booking', $data );
|
||||
|
||||
return self::send_email( $to, $subject, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send confirmation email to guest.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool Whether email was sent.
|
||||
*/
|
||||
public static function send_guest_confirmation( int $booking_id ): bool {
|
||||
$data = self::get_booking_data( $booking_id );
|
||||
|
||||
if ( empty( $data['guest_email'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$subject = sprintf(
|
||||
/* translators: 1: Site name, 2: Booking reference */
|
||||
__( '[%1$s] Booking Confirmed: %2$s', 'wp-bnb' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$data['booking_reference']
|
||||
);
|
||||
|
||||
$message = self::get_email_template( 'booking-confirmed', $data );
|
||||
|
||||
return self::send_email( $data['guest_email'], $subject, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send confirmation notification to admin.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool Whether email was sent.
|
||||
*/
|
||||
public static function send_admin_confirmation( int $booking_id ): bool {
|
||||
$data = self::get_booking_data( $booking_id );
|
||||
$to = get_option( 'admin_email' );
|
||||
$subject = sprintf(
|
||||
/* translators: 1: Site name, 2: Booking reference */
|
||||
__( '[%1$s] Booking Confirmed: %2$s', 'wp-bnb' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$data['booking_reference']
|
||||
);
|
||||
|
||||
$message = self::get_email_template( 'admin-booking-confirmed', $data );
|
||||
|
||||
return self::send_email( $to, $subject, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send cancellation email.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool Whether emails were sent.
|
||||
*/
|
||||
public static function send_cancellation( int $booking_id ): bool {
|
||||
$data = self::get_booking_data( $booking_id );
|
||||
$result = true;
|
||||
|
||||
// Send to admin.
|
||||
$admin_subject = sprintf(
|
||||
/* translators: 1: Site name, 2: Booking reference */
|
||||
__( '[%1$s] Booking Cancelled: %2$s', 'wp-bnb' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$data['booking_reference']
|
||||
);
|
||||
|
||||
$admin_message = self::get_email_template( 'admin-booking-cancelled', $data );
|
||||
$result = self::send_email( get_option( 'admin_email' ), $admin_subject, $admin_message ) && $result;
|
||||
|
||||
// Send to guest if email exists.
|
||||
if ( ! empty( $data['guest_email'] ) ) {
|
||||
$guest_subject = sprintf(
|
||||
/* translators: 1: Site name, 2: Booking reference */
|
||||
__( '[%1$s] Booking Cancelled: %2$s', 'wp-bnb' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$data['booking_reference']
|
||||
);
|
||||
|
||||
$guest_message = self::get_email_template( 'booking-cancelled', $data );
|
||||
$result = self::send_email( $data['guest_email'], $guest_subject, $guest_message ) && $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get booking data for email templates.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return array Booking data.
|
||||
*/
|
||||
private static function get_booking_data( int $booking_id ): array {
|
||||
$booking = get_post( $booking_id );
|
||||
$room = Booking::get_room( $booking_id );
|
||||
$building = Booking::get_building( $booking_id );
|
||||
|
||||
$check_in = get_post_meta( $booking_id, '_bnb_booking_check_in', true );
|
||||
$check_out = get_post_meta( $booking_id, '_bnb_booking_check_out', true );
|
||||
$status = get_post_meta( $booking_id, '_bnb_booking_status', true );
|
||||
$price = get_post_meta( $booking_id, '_bnb_booking_calculated_price', true );
|
||||
$adults = get_post_meta( $booking_id, '_bnb_booking_adults', true );
|
||||
$children = get_post_meta( $booking_id, '_bnb_booking_children', true );
|
||||
|
||||
$nights = 0;
|
||||
if ( $check_in && $check_out ) {
|
||||
$nights = Booking::calculate_nights( $check_in, $check_out );
|
||||
}
|
||||
|
||||
$statuses = Booking::get_booking_statuses();
|
||||
|
||||
return array(
|
||||
'booking_id' => $booking_id,
|
||||
'booking_reference' => $booking ? $booking->post_title : '',
|
||||
'guest_name' => get_post_meta( $booking_id, '_bnb_booking_guest_name', true ),
|
||||
'guest_email' => get_post_meta( $booking_id, '_bnb_booking_guest_email', true ),
|
||||
'guest_phone' => get_post_meta( $booking_id, '_bnb_booking_guest_phone', true ),
|
||||
'guest_notes' => get_post_meta( $booking_id, '_bnb_booking_guest_notes', true ),
|
||||
'adults' => $adults ?: 1,
|
||||
'children' => $children ?: 0,
|
||||
'room_name' => $room ? $room->post_title : '',
|
||||
'room_id' => $room ? $room->ID : 0,
|
||||
'building_name' => $building ? $building->post_title : '',
|
||||
'building_id' => $building ? $building->ID : 0,
|
||||
'check_in_date' => $check_in ? wp_date( get_option( 'date_format' ), strtotime( $check_in ) ) : '',
|
||||
'check_out_date' => $check_out ? wp_date( get_option( 'date_format' ), strtotime( $check_out ) ) : '',
|
||||
'check_in_raw' => $check_in,
|
||||
'check_out_raw' => $check_out,
|
||||
'nights' => $nights,
|
||||
'total_price' => $price ? Calculator::formatPrice( (float) $price ) : '',
|
||||
'status' => $statuses[ $status ] ?? $status,
|
||||
'status_raw' => $status,
|
||||
'site_name' => get_bloginfo( 'name' ),
|
||||
'site_url' => home_url(),
|
||||
'admin_email' => get_option( 'admin_email' ),
|
||||
'booking_url' => admin_url( 'post.php?post=' . $booking_id . '&action=edit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email template with placeholders replaced.
|
||||
*
|
||||
* @param string $template_name Template name.
|
||||
* @param array $data Template data.
|
||||
* @return string HTML email content.
|
||||
*/
|
||||
private static function get_email_template( string $template_name, array $data ): string {
|
||||
$template = self::get_template_content( $template_name );
|
||||
|
||||
// Replace placeholders.
|
||||
foreach ( $data as $key => $value ) {
|
||||
$template = str_replace( '{' . $key . '}', (string) $value, $template );
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template content.
|
||||
*
|
||||
* @param string $template_name Template name.
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function get_template_content( string $template_name ): string {
|
||||
// Built-in templates. Could be extended to load from files.
|
||||
$templates = array(
|
||||
'admin-new-booking' => self::template_admin_new_booking(),
|
||||
'booking-confirmed' => self::template_booking_confirmed(),
|
||||
'admin-booking-confirmed' => self::template_admin_booking_confirmed(),
|
||||
'booking-cancelled' => self::template_booking_cancelled(),
|
||||
'admin-booking-cancelled' => self::template_admin_booking_cancelled(),
|
||||
);
|
||||
|
||||
return $templates[ $template_name ] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTML email.
|
||||
*
|
||||
* @param string $to Recipient email.
|
||||
* @param string $subject Email subject.
|
||||
* @param string $message HTML message.
|
||||
* @return bool Whether email was sent.
|
||||
*/
|
||||
private static function send_email( string $to, string $subject, string $message ): bool {
|
||||
$headers = array(
|
||||
'Content-Type: text/html; charset=UTF-8',
|
||||
'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>',
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter email recipients.
|
||||
*
|
||||
* @param string $to Recipient email.
|
||||
* @param string $subject Email subject.
|
||||
* @param string $message Email message.
|
||||
*/
|
||||
$to = apply_filters( 'wp_bnb_booking_email_recipients', $to, $subject, $message );
|
||||
|
||||
/**
|
||||
* Filter email subject.
|
||||
*
|
||||
* @param string $subject Email subject.
|
||||
*/
|
||||
$subject = apply_filters( 'wp_bnb_booking_email_subject', $subject );
|
||||
|
||||
/**
|
||||
* Filter email content.
|
||||
*
|
||||
* @param string $message Email message.
|
||||
*/
|
||||
$message = apply_filters( 'wp_bnb_booking_email_content', $message );
|
||||
|
||||
return wp_mail( $to, $subject, $message, $headers );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get base email styles.
|
||||
*
|
||||
* @return string CSS styles.
|
||||
*/
|
||||
private static function get_email_styles(): string {
|
||||
return '
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 14px; line-height: 1.6; color: #333; }
|
||||
.email-container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.email-header { background: #135e96; color: #fff; padding: 20px; text-align: center; }
|
||||
.email-header h1 { margin: 0; font-size: 24px; }
|
||||
.email-body { background: #fff; padding: 30px; border: 1px solid #ddd; }
|
||||
.email-footer { padding: 20px; text-align: center; font-size: 12px; color: #666; }
|
||||
.booking-details { background: #f9f9f9; padding: 15px; margin: 20px 0; border-left: 4px solid #135e96; }
|
||||
.booking-details h3 { margin-top: 0; color: #135e96; }
|
||||
.detail-row { margin: 8px 0; }
|
||||
.detail-label { font-weight: 600; display: inline-block; min-width: 120px; }
|
||||
.btn { display: inline-block; padding: 10px 20px; background: #135e96; color: #fff; text-decoration: none; border-radius: 4px; margin-top: 15px; }
|
||||
.status-badge { display: inline-block; padding: 4px 10px; border-radius: 3px; font-size: 12px; font-weight: 600; text-transform: uppercase; }
|
||||
.status-pending { background: #dba617; color: #fff; }
|
||||
.status-confirmed { background: #00a32a; color: #fff; }
|
||||
.status-cancelled { background: #d63638; color: #fff; }
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Template: Admin new booking notification.
|
||||
*
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function template_admin_new_booking(): string {
|
||||
$styles = self::get_email_styles();
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>{$styles}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">
|
||||
<h1>New Booking Received</h1>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<p>A new booking has been created and is awaiting confirmation.</p>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Booking Details</h3>
|
||||
<div class="detail-row"><span class="detail-label">Reference:</span> {booking_reference}</div>
|
||||
<div class="detail-row"><span class="detail-label">Status:</span> <span class="status-badge status-{status_raw}">{status}</span></div>
|
||||
<div class="detail-row"><span class="detail-label">Room:</span> {room_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Building:</span> {building_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Check-in:</span> {check_in_date}</div>
|
||||
<div class="detail-row"><span class="detail-label">Check-out:</span> {check_out_date}</div>
|
||||
<div class="detail-row"><span class="detail-label">Nights:</span> {nights}</div>
|
||||
<div class="detail-row"><span class="detail-label">Total:</span> {total_price}</div>
|
||||
</div>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Guest Information</h3>
|
||||
<div class="detail-row"><span class="detail-label">Name:</span> {guest_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Email:</span> {guest_email}</div>
|
||||
<div class="detail-row"><span class="detail-label">Phone:</span> {guest_phone}</div>
|
||||
<div class="detail-row"><span class="detail-label">Adults:</span> {adults}</div>
|
||||
<div class="detail-row"><span class="detail-label">Children:</span> {children}</div>
|
||||
</div>
|
||||
|
||||
<a href="{booking_url}" class="btn">View Booking</a>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>This email was sent from {site_name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template: Guest booking confirmed.
|
||||
*
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function template_booking_confirmed(): string {
|
||||
$styles = self::get_email_styles();
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>{$styles}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">
|
||||
<h1>Booking Confirmed</h1>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<p>Dear {guest_name},</p>
|
||||
|
||||
<p>Great news! Your booking has been confirmed. We look forward to welcoming you.</p>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Your Booking Details</h3>
|
||||
<div class="detail-row"><span class="detail-label">Confirmation:</span> {booking_reference}</div>
|
||||
<div class="detail-row"><span class="detail-label">Room:</span> {room_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Location:</span> {building_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Check-in:</span> {check_in_date}</div>
|
||||
<div class="detail-row"><span class="detail-label">Check-out:</span> {check_out_date}</div>
|
||||
<div class="detail-row"><span class="detail-label">Duration:</span> {nights} nights</div>
|
||||
<div class="detail-row"><span class="detail-label">Guests:</span> {adults} adults, {children} children</div>
|
||||
<div class="detail-row"><span class="detail-label">Total:</span> <strong>{total_price}</strong></div>
|
||||
</div>
|
||||
|
||||
<p>If you have any questions or need to make changes to your reservation, please contact us at {admin_email}.</p>
|
||||
|
||||
<p>Thank you for choosing us!</p>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>{site_name}<br>{site_url}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template: Admin booking confirmed.
|
||||
*
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function template_admin_booking_confirmed(): string {
|
||||
$styles = self::get_email_styles();
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>{$styles}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">
|
||||
<h1>Booking Confirmed</h1>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<p>Booking {booking_reference} has been confirmed.</p>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Booking Summary</h3>
|
||||
<div class="detail-row"><span class="detail-label">Guest:</span> {guest_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Room:</span> {room_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Dates:</span> {check_in_date} - {check_out_date}</div>
|
||||
<div class="detail-row"><span class="detail-label">Total:</span> {total_price}</div>
|
||||
</div>
|
||||
|
||||
<a href="{booking_url}" class="btn">View Booking</a>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>This email was sent from {site_name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template: Guest booking cancelled.
|
||||
*
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function template_booking_cancelled(): string {
|
||||
$styles = self::get_email_styles();
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>{$styles}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header" style="background: #d63638;">
|
||||
<h1>Booking Cancelled</h1>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<p>Dear {guest_name},</p>
|
||||
|
||||
<p>We're writing to confirm that your booking has been cancelled.</p>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Cancelled Booking</h3>
|
||||
<div class="detail-row"><span class="detail-label">Reference:</span> {booking_reference}</div>
|
||||
<div class="detail-row"><span class="detail-label">Room:</span> {room_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Dates:</span> {check_in_date} - {check_out_date}</div>
|
||||
</div>
|
||||
|
||||
<p>If you have any questions or would like to make a new reservation, please contact us at {admin_email}.</p>
|
||||
|
||||
<p>We hope to welcome you in the future.</p>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>{site_name}<br>{site_url}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template: Admin booking cancelled.
|
||||
*
|
||||
* @return string Template HTML.
|
||||
*/
|
||||
private static function template_admin_booking_cancelled(): string {
|
||||
$styles = self::get_email_styles();
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>{$styles}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header" style="background: #d63638;">
|
||||
<h1>Booking Cancelled</h1>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<p>Booking {booking_reference} has been cancelled.</p>
|
||||
|
||||
<div class="booking-details">
|
||||
<h3>Cancelled Booking</h3>
|
||||
<div class="detail-row"><span class="detail-label">Guest:</span> {guest_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Email:</span> {guest_email}</div>
|
||||
<div class="detail-row"><span class="detail-label">Room:</span> {room_name}</div>
|
||||
<div class="detail-row"><span class="detail-label">Dates:</span> {check_in_date} - {check_out_date}</div>
|
||||
</div>
|
||||
|
||||
<a href="{booking_url}" class="btn">View Booking</a>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>This email was sent from {site_name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user