Files
com2care_website/templates/pages/contact.html
T
2026-06-07 17:09:04 +03:30

306 lines
13 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{% if not site_contact.has_contact_sidebar %} contact-layout--full{% endif %}">
<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" enctype="multipart/form-data">
{% 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">
<label for="id_attachments">Attachments <span class="optional-label">(optional)</span></label>
<input type="file" name="attachments" id="id_attachments" multiple
accept=".png,.jpg,.jpeg,.gif,.webp,.bmp,.zip,.log,.txt"
class="file-input" />
<p class="form-hint">Images, ZIP, or log/text files — up to 3 files, 10 MB each.</p>
<p class="form-file-list" id="attachmentFileList" aria-live="polite"></p>
<span class="form-field-error" data-field="attachments"></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>
{% if site_contact.has_contact_sidebar %}
<aside class="contact-sidebar">
{% if site_contact.has_support_email %}
<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">{{ site_contact.email_card_title_display }}</h3>
{% if site_contact.email_card_description %}
<p class="contact-card-desc">{{ site_contact.email_card_description }}</p>
{% endif %}
{% include "partials/_contact_email.html" with link_class="contact-email-link" %}
</div>
{% endif %}
{% if site_contact.has_discord %}
<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">{{ site_contact.discord_card_title_display }}</h3>
{% if site_contact.discord_card_description %}
<p class="contact-card-desc">{{ site_contact.discord_card_description }}</p>
{% endif %}
{% include "partials/_contact_discord.html" with button_class="btn-primary btn--sm" %}
</div>
{% endif %}
{% if site_contact.has_office_address %}
<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">{{ site_contact.office_card_title_display }}</h3>
{% include "partials/_contact_office.html" with address_class="contact-address" %}
</div>
{% endif %}
</aside>
{% endif %}
</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 fileInput = form.querySelector('#id_attachments');
const fileList = form.querySelector('#attachmentFileList');
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();
if (fileList) fileList.textContent = '';
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);
}
}
fileInput?.addEventListener('change', () => {
if (!fileList) return;
const names = [...fileInput.files].map(file => file.name);
fileList.textContent = names.length ? names.join(', ') : '';
});
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 %}