feat: Implement AJAX form submission for career application with loading states, SweetAlert2 notifications, and server-side JSON responses.
This commit is contained in:
@@ -42,6 +42,13 @@ class CareerController extends Controller
|
|||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($request->ajax()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('career.success_message')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('success', __('career.success_message'));
|
return redirect()->back()->with('success', __('career.success_message'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(session('success'))
|
@if(session('success'))
|
||||||
<div class="alert alert-success mt-4 mb-10 border-0 shadow-sm rounded-3">
|
<div class="alert alert-success mt-4 mb-10 border-0 shadow-sm rounded-3">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<i class="uil uil-check-circle fs-24 me-3 text-success"></i>
|
<i class="uil uil-check-circle fs-24 me-3 text-success"></i>
|
||||||
<div class="fw-medium text-success">{{ session('success') }}</div>
|
<div class="fw-medium text-success">{{ session('success') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
trackEvent('career_form_success', {form_name: 'career_application'});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" class="career-form">
|
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" class="career-form" id="career-application-form">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<!-- Personal Info Group -->
|
<!-- Personal Info Group -->
|
||||||
@@ -88,9 +83,10 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-9 offset-md-3 text-center text-md-start">
|
<div class="col-md-9 offset-md-3 text-center text-md-start">
|
||||||
<button type="submit" class="btn btn-citrus btn-lg rounded-pill px-12 transition-all shadow-citrus" onclick="trackEvent('career_form_submit_attempt', {form_name: 'career_application'})">
|
<button type="submit" id="submit-btn" class="btn btn-citrus btn-lg rounded-pill px-12 transition-all shadow-citrus d-inline-flex align-items-center justify-content-center" onclick="if(typeof trackEvent === 'function') trackEvent('career_form_submit_attempt', {form_name: 'career_application'})">
|
||||||
{{ __('career.submit', ['default' => 'Başvuruyu Gönder']) }}
|
<span class="btn-text">{{ __('career.submit', ['default' => 'Başvuruyu Gönder']) }}</span>
|
||||||
<i class="uil uil-arrow-right ms-2 fs-20"></i>
|
<span class="spinner-border spinner-border-sm ms-2 d-none" role="status" aria-hidden="true"></span>
|
||||||
|
<i class="uil uil-arrow-right ms-2 fs-20 btn-icon"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,6 +104,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
@push('styles')
|
@push('styles')
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--citrus-red: #e31e24;
|
--citrus-red: #e31e24;
|
||||||
@@ -147,15 +144,13 @@
|
|||||||
|
|
||||||
.career-form .custom-input {
|
.career-form .custom-input {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
padding: 0.85rem 1.15rem;
|
padding: 1rem 1.5rem;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 0.65rem;
|
border-radius: 0.75rem;
|
||||||
transition: all 0.25s ease-in-out;
|
transition: all 0.25s ease-in-out;
|
||||||
font-size: 0.95rem;
|
font-size: 1.05rem;
|
||||||
width: auto;
|
width: 100%;
|
||||||
min-width: 300px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
max-width: 100%;
|
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
@@ -188,4 +183,112 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
@endpush
|
@endpush
|
||||||
|
@push('scripts')
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const form = document.getElementById('career-application-form');
|
||||||
|
const submitBtn = document.getElementById('submit-btn');
|
||||||
|
const btnText = submitBtn.querySelector('.btn-text');
|
||||||
|
const btnIcon = submitBtn.querySelector('.btn-icon');
|
||||||
|
const spinner = submitBtn.querySelector('.spinner-border');
|
||||||
|
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
spinner.classList.remove('d-none');
|
||||||
|
btnIcon.classList.add('d-none');
|
||||||
|
|
||||||
|
// Clear previous errors
|
||||||
|
document.querySelectorAll('.invalid-feedback').forEach(el => el.remove());
|
||||||
|
document.querySelectorAll('.is-invalid').forEach(el => el.classList.remove('is-invalid'));
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async response => {
|
||||||
|
const isJson = response.headers.get('content-type')?.includes('application/json');
|
||||||
|
const data = isJson ? await response.json() : null;
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 422 && data) {
|
||||||
|
return { type: 'validation', errors: data.errors };
|
||||||
|
}
|
||||||
|
throw new Error(data?.message || 'Bir hata oluştu.');
|
||||||
|
}
|
||||||
|
return { type: 'success', data: data };
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
if (result.type === 'success') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Başarılı!',
|
||||||
|
text: result.data.message,
|
||||||
|
confirmButtonColor: '#e31e24',
|
||||||
|
confirmButtonText: 'Tamam'
|
||||||
|
}).then(() => {
|
||||||
|
form.reset();
|
||||||
|
// Optional: redirect or hide form
|
||||||
|
// window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof trackEvent === 'function') {
|
||||||
|
trackEvent('career_form_success', {form_name: 'career_application'});
|
||||||
|
}
|
||||||
|
} else if (result.type === 'validation') {
|
||||||
|
Object.keys(result.errors).forEach(key => {
|
||||||
|
const input = document.getElementById(key) || document.getElementsByName(key)[0];
|
||||||
|
if (input) {
|
||||||
|
input.classList.add('is-invalid');
|
||||||
|
const errorDiv = document.createElement('div');
|
||||||
|
errorDiv.className = 'invalid-feedback text-start d-block';
|
||||||
|
errorDiv.textContent = result.errors[key][0];
|
||||||
|
|
||||||
|
if (input.type === 'file') {
|
||||||
|
input.closest('.col-md-9').appendChild(errorDiv);
|
||||||
|
} else {
|
||||||
|
input.parentElement.appendChild(errorDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Hata!',
|
||||||
|
text: 'Lütfen formdaki hataları kontrol ediniz.',
|
||||||
|
confirmButtonColor: '#e31e24',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
spinner.classList.add('d-none');
|
||||||
|
btnIcon.classList.remove('d-none');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Hata!',
|
||||||
|
text: error.message || 'Başvuru gönderilirken bir hata oluştu. Lütfen tekrar deneyiniz.',
|
||||||
|
confirmButtonColor: '#e31e24',
|
||||||
|
});
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
spinner.classList.add('d-none');
|
||||||
|
btnIcon.classList.remove('d-none');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Reference in New Issue
Block a user