280 lines
12 KiB
HTML
280 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Contact{% endblock %}
|
|
{% block meta_description %}Contact Radiuma — support for Radiuma software and general inquiries.{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<section class="page-hero" aria-labelledby="contact-heading">
|
|
<div class="page-hero-blobs" aria-hidden="true">
|
|
<div class="blob blob--1"></div>
|
|
<div class="blob blob--2"></div>
|
|
</div>
|
|
<div class="container">
|
|
<div class="page-hero-content">
|
|
<div class="section-badge">Get in Touch</div>
|
|
<h1 class="page-hero-title" id="contact-heading">Contact Us</h1>
|
|
<p class="page-hero-subtitle">Have a question or want to reach the Radiuma team? Fill out the form or contact us directly below.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section" aria-labelledby="contact-form-heading">
|
|
<div class="container">
|
|
<div class="contact-layout">
|
|
|
|
<div class="contact-form-wrap glass-card fade-in">
|
|
<h2 class="contact-form-title" id="contact-form-heading">Send a Message</h2>
|
|
<form id="contactForm" novalidate action="{% url 'pages:contact' %}" method="post">
|
|
{% csrf_token %}
|
|
|
|
<div class="form-group">
|
|
<label for="id_name">Full Name <span class="required-star">*</span></label>
|
|
{{ form.name }}
|
|
<span class="form-field-error" data-field="name"></span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="id_title">Subject / Title <span class="required-star">*</span></label>
|
|
{{ form.title }}
|
|
<span class="form-field-error" data-field="title"></span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="id_description">Message <span class="required-star">*</span></label>
|
|
{{ form.description }}
|
|
<span class="form-field-error" data-field="description"></span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="id_email">Email <span class="optional-label">(optional)</span></label>
|
|
{{ form.email }}
|
|
<span class="form-field-error" data-field="email"></span>
|
|
</div>
|
|
|
|
<div class="form-group form-group--captcha">
|
|
<label for="id_captcha">
|
|
Verification: what is <strong id="captchaQuestion">{{ captcha_question }}</strong>?
|
|
<span class="required-star">*</span>
|
|
</label>
|
|
<input type="number" name="captcha_answer" id="id_captcha"
|
|
placeholder="Answer" min="0" max="18" required
|
|
class="captcha-input" />
|
|
<span class="form-field-error" data-field="captcha"></span>
|
|
</div>
|
|
|
|
<button type="submit" class="btn-primary btn-submit" id="submitBtn">
|
|
<span class="btn-label">Send Message</span>
|
|
<span class="btn-spinner" hidden aria-hidden="true">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<aside class="contact-sidebar">
|
|
<div class="contact-card glass-card fade-in">
|
|
<div class="contact-icon" aria-hidden="true">
|
|
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
|
|
<path d="M5 8C5 6.895 5.895 6 7 6H25C26.105 6 27 6.895 27 8V22C27 23.105 26.105 24 25 24H7C5.895 24 5 23.105 5 22V8Z" stroke="currentColor" stroke-width="1.5"/>
|
|
<path d="M5 9L16 16L27 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="contact-card-title">Email Support</h3>
|
|
<p class="contact-card-desc">For direct software support:</p>
|
|
<a href="mailto:support@visera.ca" class="contact-email-link">support@visera.ca</a>
|
|
</div>
|
|
|
|
<div class="contact-card glass-card fade-in">
|
|
<div class="contact-icon" aria-hidden="true">
|
|
<svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057.1 18.08.114 18.1.133 18.11a19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="contact-card-title">Discord Community</h3>
|
|
<p class="contact-card-desc">Join for community support and announcements.</p>
|
|
<a href="https://discord.gg/9XxA6pV9hb" class="btn-primary btn--sm" target="_blank" rel="noopener noreferrer">Join Discord</a>
|
|
</div>
|
|
|
|
<div class="contact-card glass-card fade-in">
|
|
<div class="contact-icon" aria-hidden="true">
|
|
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
|
|
<path d="M16 3C11.029 3 7 7.029 7 12C7 19 16 29 16 29C16 29 25 19 25 12C25 7.029 20.971 3 16 3Z" stroke="currentColor" stroke-width="1.5"/>
|
|
<circle cx="16" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="contact-card-title">Office</h3>
|
|
<address class="contact-address">
|
|
<p>BC Cancer Research Center</p>
|
|
<p>675 West 10th Ave, Office 6-112</p>
|
|
<p>Vancouver, BC, V5Z 1L3</p>
|
|
<p>Canada</p>
|
|
</address>
|
|
</div>
|
|
</aside>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div id="noEmailModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="noEmailTitle" hidden>
|
|
<div class="modal-box glass-card">
|
|
<div class="modal-icon-wrap">
|
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="modal-title" id="noEmailTitle">No reply address</h3>
|
|
<p class="modal-body">You haven't provided an email address. Without it, we won't be able to respond to your message. Would you like to add one, or send without a reply address?</p>
|
|
<div class="modal-actions">
|
|
<button class="btn-ghost" id="modalAddEmail">Add Email</button>
|
|
<button class="btn-primary" id="modalSendAnyway">Send Without Email</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="successModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="successTitle" hidden>
|
|
<div class="modal-box glass-card">
|
|
<div class="modal-icon-wrap modal-icon-wrap--success">
|
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="modal-title" id="successTitle">Message Sent!</h3>
|
|
<p class="modal-body">Thank you for reaching out. We've received your message and will get back to you as soon as possible.</p>
|
|
<button class="btn-primary" id="successClose">Done</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="errorModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="errorTitle" hidden>
|
|
<div class="modal-box glass-card">
|
|
<div class="modal-icon-wrap modal-icon-wrap--error">
|
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="modal-title" id="errorTitle">Something Went Wrong</h3>
|
|
<p class="modal-body" id="errorModalMsg">Please check your answers and try again.</p>
|
|
<button class="btn-ghost" id="errorClose">Try Again</button>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
(function () {
|
|
const form = document.getElementById('contactForm');
|
|
if (!form) return;
|
|
|
|
const emailField = form.querySelector('#id_email');
|
|
const submitBtn = form.querySelector('#submitBtn');
|
|
const btnLabel = submitBtn.querySelector('.btn-label');
|
|
const btnSpinner = submitBtn.querySelector('.btn-spinner');
|
|
|
|
function showModal(id) {
|
|
const el = document.getElementById(id);
|
|
if (el) { el.hidden = false; el.focus(); }
|
|
}
|
|
function hideModal(id) {
|
|
const el = document.getElementById(id);
|
|
if (el) el.hidden = true;
|
|
}
|
|
|
|
function clearErrors() {
|
|
form.querySelectorAll('.form-field-error').forEach(el => { el.textContent = ''; });
|
|
form.querySelectorAll('.form-group').forEach(el => el.classList.remove('has-error'));
|
|
}
|
|
|
|
function showErrors(errors) {
|
|
clearErrors();
|
|
Object.entries(errors).forEach(([field, msgs]) => {
|
|
const span = form.querySelector(`[data-field="${field}"]`);
|
|
if (span) {
|
|
span.textContent = msgs[0] || '';
|
|
span.closest('.form-group')?.classList.add('has-error');
|
|
}
|
|
});
|
|
}
|
|
|
|
function setLoading(on) {
|
|
submitBtn.disabled = on;
|
|
btnLabel.hidden = on;
|
|
btnSpinner.hidden = !on;
|
|
}
|
|
|
|
async function submitForm() {
|
|
clearErrors();
|
|
setLoading(true);
|
|
const formData = new FormData(form);
|
|
try {
|
|
const res = await fetch(form.action || window.location.href, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
form.reset();
|
|
showModal('successModal');
|
|
} else {
|
|
if (data.errors) showErrors(data.errors);
|
|
if (data.errors?.captcha) {
|
|
document.getElementById('errorModalMsg').textContent = data.errors.captcha[0];
|
|
showModal('errorModal');
|
|
} else if (!res.ok) {
|
|
showModal('errorModal');
|
|
}
|
|
if (data.captcha_question) {
|
|
document.getElementById('captchaQuestion').textContent = data.captcha_question;
|
|
const captchaInput = form.querySelector('#id_captcha');
|
|
if (captchaInput) captchaInput.value = '';
|
|
}
|
|
}
|
|
} catch {
|
|
showModal('errorModal');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
form.addEventListener('submit', e => {
|
|
e.preventDefault();
|
|
if (!emailField.value.trim()) {
|
|
showModal('noEmailModal');
|
|
} else {
|
|
submitForm();
|
|
}
|
|
});
|
|
|
|
document.getElementById('modalAddEmail')?.addEventListener('click', () => {
|
|
hideModal('noEmailModal');
|
|
emailField.focus();
|
|
});
|
|
|
|
document.getElementById('modalSendAnyway')?.addEventListener('click', () => {
|
|
hideModal('noEmailModal');
|
|
submitForm();
|
|
});
|
|
|
|
document.getElementById('successClose')?.addEventListener('click', () => hideModal('successModal'));
|
|
document.getElementById('errorClose')?.addEventListener('click', () => hideModal('errorModal'));
|
|
|
|
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
|
overlay.addEventListener('click', e => {
|
|
if (e.target === overlay) overlay.hidden = true;
|
|
});
|
|
});
|
|
|
|
document.addEventListener('keydown', e => {
|
|
if (e.key === 'Escape') {
|
|
document.querySelectorAll('.modal-overlay').forEach(m => { m.hidden = true; });
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|