173 lines
5.8 KiB
Cheetah
173 lines
5.8 KiB
Cheetah
{{define "booking.tmpl"}}
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Book a Service - Decor By Hannahs</title>
|
|
<link rel="stylesheet" href="/static/css/styles.css">
|
|
<script>
|
|
(function() {
|
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
})();
|
|
</script>
|
|
<script src="/static/js/alpine.js" defer></script>
|
|
<script src="/static/htmx/htmx.min.js" defer></script>
|
|
<script src="/static/js/drawer.js" defer></script>
|
|
</head>
|
|
|
|
<body>
|
|
{{template "header" .}}
|
|
|
|
<main>
|
|
<div style="max-width: 600px; margin: 0 auto;">
|
|
<div style="margin-bottom: 2rem; position: relative;">
|
|
<img src="/assets/party-icons/birthday-cake.png" alt="Birthday cake" style="position: absolute; top: -20px; right: -40px; width: 90px; height: 90px; opacity: 0.85;">
|
|
<h1 style="font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem;">Book a Service</h1>
|
|
<p style="color: hsl(var(--muted-foreground)); font-size: 1.125rem;">Fill out the form below to request a booking</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<form id="bookingForm">
|
|
<div class="form-group">
|
|
<label class="form-label" for="service_option">Service Option</label>
|
|
<select class="form-input" id="service_option" name="service_option" required>
|
|
<option value="">Select a service option...</option>
|
|
<option value="Small Bundle">Small Bundle</option>
|
|
<option value="Medium Bundle">Medium Bundle</option>
|
|
<option value="Large Bundle">Large Bundle</option>
|
|
<option value="Balloons Only">Balloons Only</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="event_type">Event Type</label>
|
|
<select class="form-input" id="event_type" name="event_type" required>
|
|
<option value="">Select an event type...</option>
|
|
<option value="Birthday">Birthday</option>
|
|
<option value="Wedding">Wedding</option>
|
|
<option value="Anniversary">Anniversary</option>
|
|
<option value="Baby Shower">Baby Shower</option>
|
|
<option value="Graduation">Graduation</option>
|
|
<option value="Corporate Event">Corporate Event</option>
|
|
<option value="Other">Other</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="event_date">Event Date & Time</label>
|
|
<input class="form-input" type="datetime-local" id="event_date" name="event_date" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="address">Address <span style="color: hsl(var(--destructive));">*</span></label>
|
|
<input class="form-input" type="text" id="address" name="address" placeholder="123 Main St, Sydney NSW 2000" required>
|
|
<small style="color: hsl(var(--muted-foreground)); font-size: 0.875rem;">Please include street, suburb, state, and postcode</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="notes">Additional Notes</label>
|
|
<textarea class="form-textarea" id="notes" name="notes" placeholder="Any special requests or details..."></textarea>
|
|
</div>
|
|
|
|
<div style="display: flex; gap: 0.5rem; margin-top: 1.5rem;">
|
|
<button type="submit" class="btn btn-primary" style="flex: 1;">Request Booking</button>
|
|
<a href="/catalog" class="btn btn-outline">Back to Catalog</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
{{template "footer"}}
|
|
{{template "theme-script"}}
|
|
<script>
|
|
function validateAustralianAddress(address) {
|
|
if (!address || address.trim().length < 10) {
|
|
return { valid: false, message: 'Address is too short. Please provide a complete address.' };
|
|
}
|
|
|
|
const australianStates = ['NSW', 'VIC', 'QLD', 'SA', 'WA', 'TAS', 'NT', 'ACT'];
|
|
const hasState = australianStates.some(state =>
|
|
address.toUpperCase().includes(state) ||
|
|
address.toUpperCase().includes(state.toLowerCase())
|
|
);
|
|
|
|
const postcodePattern = /\b\d{4}\b/;
|
|
const hasPostcode = postcodePattern.test(address);
|
|
|
|
if (!hasState && !hasPostcode) {
|
|
return {
|
|
valid: false,
|
|
message: 'Please include an Australian state (NSW, VIC, QLD, etc.) and postcode in your address.'
|
|
};
|
|
}
|
|
|
|
if (!hasState) {
|
|
return {
|
|
valid: false,
|
|
message: 'Please include an Australian state (NSW, VIC, QLD, SA, WA, TAS, NT, or ACT) in your address.'
|
|
};
|
|
}
|
|
|
|
if (!hasPostcode) {
|
|
return {
|
|
valid: false,
|
|
message: 'Please include a 4-digit postcode in your address.'
|
|
};
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
document.getElementById('bookingForm').addEventListener('submit', async function(e){
|
|
e.preventDefault();
|
|
const f = e.target;
|
|
const submitBtn = f.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.textContent;
|
|
|
|
const addressValidation = validateAustralianAddress(f.address.value);
|
|
if (!addressValidation.valid) {
|
|
alert(addressValidation.message);
|
|
return;
|
|
}
|
|
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = 'Submitting...';
|
|
|
|
const body = {
|
|
user_id: "",
|
|
service_option: f.service_option.value,
|
|
event_type: f.event_type.value,
|
|
event_date: new Date(f.event_date.value).toISOString(),
|
|
address: f.address.value,
|
|
notes: f.notes.value
|
|
};
|
|
|
|
try {
|
|
const res = await fetch('/api/bookings', {
|
|
method: 'POST',
|
|
headers: {'Content-Type':'application/json'},
|
|
body: JSON.stringify(body)
|
|
});
|
|
|
|
if (res.ok) {
|
|
alert('Booking requested successfully! We will contact you soon.');
|
|
f.reset();
|
|
} else {
|
|
const error = await res.text();
|
|
alert('Error creating booking: ' + error);
|
|
}
|
|
} catch (err) {
|
|
alert('Network error. Please try again.');
|
|
} finally {
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = originalText;
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
{{end}}
|