First Commit
This commit is contained in:
Executable
+313
@@ -0,0 +1,313 @@
|
||||
{% extends "@index/layout.twig" %}
|
||||
|
||||
{% block title %}{{ __('contact.title') }} - {{ platformName }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
:root {
|
||||
--primary-gradient: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
--ai-gradient: linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%);
|
||||
--dark-bg: #0f172a;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
background: #f8fafc !important;
|
||||
}
|
||||
|
||||
.contact-hero {
|
||||
background: var(--dark-bg);
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(59, 130, 246, 0.1) 0%, transparent 40%),
|
||||
radial-gradient(circle at 90% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 40%);
|
||||
color: white;
|
||||
padding: 100px 0 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-badge {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
color: #60a5fa;
|
||||
padding: 8px 20px;
|
||||
border-radius: 50px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.contact-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.contact-card {
|
||||
background: white;
|
||||
border-radius: 24px;
|
||||
border: 1px solid #e2e8f0;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.05);
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.contact-info-sidebar {
|
||||
background: #f1f5f9;
|
||||
padding: 40px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
color: #3b82f6;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-content h5 {
|
||||
font-weight: 700;
|
||||
margin-bottom: 5px;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.info-content p {
|
||||
color: #64748b;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contact-form-body {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #e2e8f0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 14px 30px;
|
||||
border-radius: 12px;
|
||||
font-weight: 700;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(59, 130, 246, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.captcha-box {
|
||||
background: #f8fafc;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
border: 1px dashed #cbd5e1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alert-toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1060;
|
||||
min-width: 300px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block layout_body %}
|
||||
<div class="alert-toast" x-data="{ show: false, message: '', type: 'success' }"
|
||||
@show-alert.window="show = true; message = $event.detail.message; type = $event.detail.type; setTimeout(() => show = false, 5000)">
|
||||
<div x-show="show" x-transition
|
||||
:class="`alert alert-${type} shadow-lg border-0 rounded-4`" role="alert">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi" :class="type === 'success' ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill'"></i>
|
||||
<span x-text="message"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="contact-hero">
|
||||
<div class="container">
|
||||
<div class="contact-badge">Contato</div>
|
||||
<h1 class="contact-title">{{ __('contact.title') }}</h1>
|
||||
<p class="lead opacity-75 mx-auto" style="max-width: 600px;">
|
||||
{{ __('contact.subtitle') }}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="pb-5">
|
||||
<div class="container">
|
||||
<div class="contact-card">
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-5">
|
||||
<div class="contact-info-sidebar">
|
||||
<h3 class="fw-bold mb-4" style="color: #0f172a;">Informações</h3>
|
||||
|
||||
<div class="info-item">
|
||||
<div class="info-icon"><i class="bi bi-geo-alt"></i></div>
|
||||
<div class="info-content">
|
||||
<h5>{{ __('contact.address_title') }}</h5>
|
||||
<p>{{ __('contact.address_line1') }}<br>{{ __('contact.address_line2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<div class="info-icon"><i class="bi bi-telephone"></i></div>
|
||||
<div class="info-content">
|
||||
<h5>{{ __('contact.phone_title') }}</h5>
|
||||
<p>{{ __('contact.phone_number') }}<br><small>{{ __('contact.phone_hours') }}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<div class="info-icon"><i class="bi bi-envelope"></i></div>
|
||||
<div class="info-content">
|
||||
<h5>{{ __('contact.email_title') }}</h5>
|
||||
<p>{{ __('contact.email_info') }}{% if __('contact.email_support') %}<br>{{ __('contact.email_support') }}{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="contact-form-body" x-data="contactForm()">
|
||||
<h3 class="fw-bold mb-4" style="color: #0f172a;">{{ __('contact.form_title') }}</h3>
|
||||
<form @submit.prevent="submitForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">{{ __('contact.form_name') }}</label>
|
||||
<input type="text" class="form-control" x-model="formData.name" required placeholder="Seu nome completo">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">{{ __('contact.form_email') }}</label>
|
||||
<input type="email" class="form-control" x-model="formData.email" required placeholder="seu@email.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ __('contact.form_subject') }}</label>
|
||||
<input type="text" class="form-control" x-model="formData.subject" required placeholder="Como podemos ajudar?">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label">{{ __('contact.form_message') }}</label>
|
||||
<textarea class="form-control" rows="5" x-model="formData.message" required placeholder="Descreva sua dúvida ou sugestão..."></textarea>
|
||||
</div>
|
||||
|
||||
{% if require_captcha_contact %}
|
||||
<div class="captcha-box" x-data="{
|
||||
num1: 0, num2: 0, userAnswer: '', solved: false,
|
||||
generate() {
|
||||
this.num1 = Math.floor(Math.random() * 10) + 1;
|
||||
this.num2 = Math.floor(Math.random() * 10) + 1;
|
||||
this.userAnswer = ''; this.solved = false;
|
||||
},
|
||||
check() { this.solved = (parseInt(this.userAnswer) === (this.num1 + this.num2)); }
|
||||
}" x-init="generate()" @reset-captcha.window="generate()">
|
||||
<label class="form-label d-block mb-2">{{ __('contact.security_check') }}</label>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="bg-white px-3 py-2 rounded border fw-bold">
|
||||
<span x-text="num1"></span> + <span x-text="num2"></span> =
|
||||
</div>
|
||||
<input type="number" class="form-control" style="width: 100px;" x-model="userAnswer" @input="check()" placeholder="?">
|
||||
<span x-show="solved" class="text-success"><i class="bi bi-check-circle-fill"></i></span>
|
||||
</div>
|
||||
<input type="hidden" name="captcha_valid" :value="solved ? '1' : ''">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" class="btn-submit" :disabled="loading">
|
||||
<span x-show="!loading"><i class="bi bi-send-fill me-2"></i> {{ __('contact.form_submit') }}</span>
|
||||
<span x-show="loading" class="spinner-border spinner-border-sm me-2"></span>
|
||||
<span x-show="loading">{{ __('contact.sending') }}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function contactForm() {
|
||||
return {
|
||||
formData: { name: '', email: '', subject: '', message: '' },
|
||||
loading: false,
|
||||
submitForm() {
|
||||
const captchaInput = document.querySelector('input[name="captcha_valid"]');
|
||||
if (captchaInput && captchaInput.value !== '1') {
|
||||
window.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: { message: '{{ __("contact.js_invalid_captcha") }}', type: 'danger' }
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
// Inclui o captcha_valid no corpo da requisição JSON para o backend
|
||||
const payload = { ...this.formData };
|
||||
if (captchaInput) {
|
||||
payload.captcha_valid = captchaInput.value;
|
||||
}
|
||||
|
||||
fetch('/contact/submit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: { message: '{{ __("contact.js_success") }}', type: 'success' }
|
||||
}));
|
||||
this.formData = { name: '', email: '', subject: '', message: '' };
|
||||
window.dispatchEvent(new CustomEvent('reset-captcha'));
|
||||
} else {
|
||||
throw new Error(data.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
window.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: { message: err.message || '{{ __("contact.js_error") }}', type: 'danger' }
|
||||
}));
|
||||
})
|
||||
.finally(() => { this.loading = false; });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user