315 lines
8.7 KiB
Plaintext
315 lines
8.7 KiB
Plaintext
---
|
|
const configuredApiBase = import.meta.env.PUBLIC_PPR_API_BASE ?? 'https://ppr.swansea-airport.wales/api/v1';
|
|
const pprApiBase = configuredApiBase.replace(/\/$/, '');
|
|
const contactRequestEndpoint = `${pprApiBase}/contact-requests/public`;
|
|
---
|
|
|
|
<section class="contact-shell surface" aria-labelledby="contact-form-heading">
|
|
<div class="contact-head">
|
|
<p class="eyebrow">General enquiries</p>
|
|
<h2 id="contact-form-heading" class="section-title">Send us a message</h2>
|
|
<p class="section-copy">
|
|
Use this form for general airport, business, visiting, and community enquiries. For flight
|
|
requests, please use the dedicated PPR or drone request forms.
|
|
</p>
|
|
</div>
|
|
|
|
<form id="contact-form" class="contact-form">
|
|
<div class="contact-honeypot" aria-hidden="true">
|
|
<label for="website">Website</label>
|
|
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off" />
|
|
</div>
|
|
|
|
<div class="contact-grid">
|
|
<div class="contact-field">
|
|
<label for="contact-name">Name <span aria-hidden="true">*</span></label>
|
|
<input type="text" id="contact-name" name="name" autocomplete="name" required />
|
|
</div>
|
|
|
|
<div class="contact-field">
|
|
<label for="contact-email">Email Address <span aria-hidden="true">*</span></label>
|
|
<input type="email" id="contact-email" name="email" autocomplete="email" required />
|
|
</div>
|
|
|
|
<div class="contact-field">
|
|
<label for="contact-phone">Phone Number</label>
|
|
<input type="tel" id="contact-phone" name="phone" autocomplete="tel" />
|
|
</div>
|
|
|
|
<div class="contact-field">
|
|
<label for="contact-enquiry-type">Enquiry Type <span aria-hidden="true">*</span></label>
|
|
<select id="contact-enquiry-type" name="enquiry_type" required>
|
|
<option value="">Select enquiry type</option>
|
|
<option value="general">General enquiry</option>
|
|
<option value="pilot">Pilot or visiting aircraft / Fuel</option>
|
|
<option value="aviation_business">Aviation business / basing</option>
|
|
<option value="events">Events and visits</option>
|
|
<option value="community">Community or local resident</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="contact-field contact-full">
|
|
<label for="contact-subject">Subject <span aria-hidden="true">*</span></label>
|
|
<input type="text" id="contact-subject" name="subject" required />
|
|
</div>
|
|
|
|
<div class="contact-field contact-full">
|
|
<label for="contact-message">Message <span aria-hidden="true">*</span></label>
|
|
<textarea id="contact-message" name="message" rows="6" required></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="contact-actions">
|
|
<button type="submit" class="button primary" id="contact-submit-btn">Send Message</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="contact-loading" id="contact-loading" role="status" aria-live="polite">
|
|
<span class="contact-spinner" aria-hidden="true"></span>
|
|
Sending your message...
|
|
</div>
|
|
|
|
<div class="contact-success notice" id="contact-success-message" role="status" aria-live="polite">
|
|
<h3>Message sent.</h3>
|
|
<p>Thanks for getting in touch. The airport team will review your message and respond where needed.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<div id="contact-notification" class="contact-notification" role="status" aria-live="polite"></div>
|
|
|
|
<script define:vars={{ contactRequestEndpoint }}>
|
|
(() => {
|
|
const CONTACT_REQUEST_ENDPOINT = contactRequestEndpoint;
|
|
const get = (id) => document.getElementById(id);
|
|
|
|
function showNotification(message, isError = false) {
|
|
const notification = get('contact-notification');
|
|
notification.textContent = message;
|
|
notification.classList.toggle('error', isError);
|
|
notification.classList.add('show');
|
|
window.setTimeout(() => notification.classList.remove('show'), 5000);
|
|
}
|
|
|
|
function buildContactRequestData(form) {
|
|
const formData = new FormData(form);
|
|
|
|
return {
|
|
name: String(formData.get('name') || '').trim(),
|
|
email: String(formData.get('email') || '').trim(),
|
|
phone: String(formData.get('phone') || '').trim(),
|
|
enquiry_type: String(formData.get('enquiry_type') || '').trim(),
|
|
subject: String(formData.get('subject') || '').trim(),
|
|
message: String(formData.get('message') || '').trim(),
|
|
source_page: window.location.pathname,
|
|
};
|
|
}
|
|
|
|
async function handleSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
const form = event.currentTarget;
|
|
const submitButton = get('contact-submit-btn');
|
|
const loading = get('contact-loading');
|
|
const successMessage = get('contact-success-message');
|
|
|
|
if (form.website?.value) {
|
|
form.reset();
|
|
successMessage.style.display = 'block';
|
|
form.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
submitButton.disabled = true;
|
|
submitButton.textContent = 'Sending...';
|
|
loading.style.display = 'flex';
|
|
|
|
try {
|
|
const response = await fetch(CONTACT_REQUEST_ENDPOINT, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(buildContactRequestData(form)),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(errorText || `Request failed with status ${response.status}`);
|
|
}
|
|
|
|
form.style.display = 'none';
|
|
successMessage.style.display = 'block';
|
|
showNotification('Message sent successfully.');
|
|
} catch (error) {
|
|
console.error('Error submitting contact request:', error);
|
|
showNotification(`Error sending message: ${error.message}`, true);
|
|
} finally {
|
|
loading.style.display = 'none';
|
|
submitButton.disabled = false;
|
|
submitButton.textContent = 'Send Message';
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
get('contact-form').addEventListener('submit', handleSubmit);
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
.contact-shell {
|
|
display: grid;
|
|
gap: 1.25rem;
|
|
margin-block: 1.5rem 0;
|
|
}
|
|
|
|
.contact-head,
|
|
.contact-form {
|
|
display: grid;
|
|
gap: 1.2rem;
|
|
}
|
|
|
|
.contact-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.contact-field {
|
|
display: grid;
|
|
gap: 0.4rem;
|
|
align-content: start;
|
|
}
|
|
|
|
.contact-full {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.contact-field label {
|
|
color: var(--text);
|
|
font-size: 0.92rem;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.contact-field label span {
|
|
color: var(--critical);
|
|
}
|
|
|
|
.contact-field input,
|
|
.contact-field select,
|
|
.contact-field textarea {
|
|
width: 100%;
|
|
min-height: 2.85rem;
|
|
border: 1px solid rgba(16, 34, 51, 0.16);
|
|
border-radius: 0.7rem;
|
|
background: rgba(255, 255, 255, 0.88);
|
|
color: var(--text);
|
|
font: inherit;
|
|
font-size: 1rem;
|
|
padding: 0.7rem 0.82rem;
|
|
}
|
|
|
|
.contact-field textarea {
|
|
min-height: 9rem;
|
|
resize: vertical;
|
|
}
|
|
|
|
.contact-field input:focus,
|
|
.contact-field select:focus,
|
|
.contact-field textarea:focus {
|
|
outline: none;
|
|
border-color: var(--brand-2);
|
|
box-shadow: 0 0 0 0.2rem rgba(29, 118, 184, 0.16);
|
|
}
|
|
|
|
.contact-actions {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
padding-top: 0.4rem;
|
|
}
|
|
|
|
.contact-actions .button {
|
|
border: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.contact-actions .button:disabled {
|
|
cursor: wait;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.contact-loading {
|
|
display: none;
|
|
align-items: center;
|
|
gap: 0.7rem;
|
|
color: var(--brand);
|
|
font-weight: 800;
|
|
}
|
|
|
|
.contact-spinner {
|
|
width: 1.45rem;
|
|
height: 1.45rem;
|
|
border: 3px solid rgba(29, 118, 184, 0.18);
|
|
border-top-color: var(--brand-2);
|
|
border-radius: 50%;
|
|
animation: contact-spin 0.8s linear infinite;
|
|
}
|
|
|
|
.contact-success {
|
|
display: none;
|
|
border-left-color: #257b4c;
|
|
}
|
|
|
|
.contact-notification {
|
|
position: fixed;
|
|
top: 1rem;
|
|
right: 1rem;
|
|
z-index: 60;
|
|
max-width: min(24rem, calc(100vw - 2rem));
|
|
padding: 0.85rem 1rem;
|
|
border-radius: 0.8rem;
|
|
background: #257b4c;
|
|
color: white;
|
|
box-shadow: var(--shadow);
|
|
font-weight: 800;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transform: translateY(-0.65rem);
|
|
transition: opacity 0.18s ease, transform 0.18s ease;
|
|
}
|
|
|
|
.contact-notification.show {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.contact-notification.error {
|
|
background: var(--critical);
|
|
}
|
|
|
|
.contact-honeypot {
|
|
position: absolute;
|
|
left: -100vw;
|
|
width: 1px;
|
|
height: 1px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
@keyframes contact-spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.contact-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.contact-actions,
|
|
.contact-actions .button {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|