feat: implement multi-step wizard interface for career application form

This commit is contained in:
Ümit Tunç
2026-06-01 23:30:39 +03:00
parent 1b0800beb6
commit 93e45655eb
+305 -26
View File
@@ -12,8 +12,49 @@
<section class="wrapper bg-white py-12 lg:py-20">
<div class="container px-4">
<div class="max-w-5xl mx-auto mb-16">
<div class="bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-2xl shadow-sm">
<!-- Wizard Progress Tracker -->
<div class="max-w-3xl mx-auto mb-16">
<div class="relative flex items-center justify-between">
<!-- Progress Line -->
<div class="absolute left-0 top-1/2 -translate-y-1/2 w-full h-1 bg-slate-100 -z-10 rounded-full">
<div id="wizard-progress-bar" class="h-full bg-[#e31e24] rounded-full transition-all duration-500 w-[0%]"></div>
</div>
<!-- Step 1 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-1" class="w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 transition-all duration-500 ring-4 ring-red-100">
<i class="uil uil-info-circle text-xl"></i>
</div>
<span id="step-label-1" class="text-xs md:text-sm font-bold text-slate-800 mt-3 transition-all duration-300">1. Şartlar & Koşullar</span>
</div>
<!-- Step 2 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-2" class="w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold transition-all duration-500 ring-4 ring-transparent">
<i class="uil uil-file-download text-xl"></i>
</div>
<span id="step-label-2" class="text-xs md:text-sm font-medium text-slate-400 mt-3 transition-all duration-300">2. Formları İndir</span>
</div>
<!-- Step 3 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-3" class="w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold transition-all duration-500 ring-4 ring-transparent">
<i class="uil uil-check-circle text-xl"></i>
</div>
<span id="step-label-3" class="text-xs md:text-sm font-medium text-slate-400 mt-3 transition-all duration-300">3. Başvuruyu Tamamla</span>
</div>
</div>
</div>
<!-- Main Wizard Form Container -->
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" id="career-application-form">
@csrf
<input type="hidden" name="type" value="job">
<!-- ================= STEP 1: TERMS & CONDITIONS ================= -->
<div id="step-content-1" class="wizard-step transition-all duration-500 opacity-100 block">
<div class="max-w-5xl mx-auto mb-12">
<div class="bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-2xl shadow-sm mb-12">
<h4 class="text-xl font-bold text-blue-900 mb-4">{{ __('career.dear_developer') }}</h4>
<div class="text-blue-800 space-y-4 leading-relaxed">
<p>{!! __('career.intro_text_1') !!}</p>
@@ -26,19 +67,65 @@
</div>
</div>
</div>
</div>
<div class="text-center mb-12">
<h2 class="text-xs uppercase font-bold tracking-widest text-[#e31e24] mb-2">{{ __('career.working_model_title') }}</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">{{ __('career.working_model_subtitle') }}</h3>
</div>
<div class="flex flex-col lg:flex-row items-center gap-12 mb-20 max-w-6xl mx-auto">
<div class="flex flex-col lg:flex-row gap-12 items-start mb-12">
<!-- Mermaid Flowchart Canvas -->
<div class="w-full lg:w-3/5">
<div class="bg-white p-4 md:p-8 rounded-3xl shadow-2xl border border-slate-100 overflow-hidden flex justify-center">
<img src="{{ asset('assets/ik-flowchat.png') }}" alt="{{ __('career.working_model_title') }}" class="w-full h-auto rounded-xl">
<div class="bg-white p-6 md:p-8 rounded-3xl shadow-xl border border-slate-100 overflow-x-auto flex flex-col items-center">
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-4"><i class="uil uil-presentation-play mr-1"></i> İK İlerleme Diyagramı</span>
<div class="mermaid w-full flex justify-center" style="min-height: 500px;">
graph TD
%% Styles
classDef startNode fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff,font-weight:bold,rx:10px,ry:10px;
classDef decisionNode fill:#eab308,stroke:#ca8a04,stroke-width:2px,color:#fff,font-weight:bold;
classDef processNode fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef rejectNode fill:#ef4444,stroke:#b91c1c,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef actionNode fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
Start["Başvuru ve Gizlilik Sözleşmesi"]:::startNode
Dec1{"Aday Onaylandı mı?"}:::decisionNode
Red1["Red - Süreç Sonlanır"]:::rejectNode
GitAccess["GitHub Repo Erişimi Sağlanır"]:::processNode
SampleTask["Aday Projeyi Kurar ve Örnek Görev Üzerinde Çalışır"]:::processNode
Dec2{"Örnek Görev Başarıyla Tamamlandı mı?"}:::decisionNode
Red2["Red - Süreç Sonlanır"]:::rejectNode
Issues["Diğer Issue'lara Devam"]:::processNode
Dec3{"Aday Issue Seçti mi?"}:::decisionNode
WaitTask["Aday Beklemede veya Başka Görev Seçer"]:::actionNode
Assign["Issue Ataması Yapılır"]:::processNode
WorkStart["Çalışma Başlar"]:::processNode
Review["Teslim ve İnceleme"]:::processNode
Dec4{"Görev Onaylandı mı?"}:::decisionNode
Feedback["Düzeltme Talebi ve Geri Bildirim"]:::actionNode
Payment["Ücret Ödemesi Yapılır"]:::processNode
Start --> Dec1
Dec1 -- "Hayır" --> Red1
Dec1 -- "Evet" --> GitAccess
GitAccess --> SampleTask
SampleTask --> Dec2
Dec2 -- "Hayır" --> Red2
Dec2 -- "Evet" --> Issues
Issues --> Dec3
Dec3 -- "Hayır" --> WaitTask
Dec3 -- "Evet" --> Assign
Assign --> WorkStart
WorkStart --> Review
Review --> Dec4
Dec4 -- "Hayır" --> Feedback
Feedback --> WorkStart
Dec4 -- "Evet" --> Payment
Payment --> Issues
</div>
</div>
</div>
<!-- Terms & Conditions Bullet Points -->
<div class="w-full lg:w-2/5 space-y-8">
<h4 class="text-2xl font-bold text-slate-900">{{ __('career.terms_title') }}</h4>
<div class="space-y-6">
@@ -67,20 +154,82 @@
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.mandatory_documents') }}</span>
<p class="text-sm text-slate-500">{{ __('career.mandatory_documents_desc') }}</p>
<div class="mt-2 flex flex-wrap gap-3">
<a href="https://docs.google.com/document/d/1WwDo_zgx4KjdnX6ohJaCfPQSS0-eRN41OB1HYvAmDO4/edit?usp=sharing" target="_blank" class="text-xs font-bold text-blue-600 hover:underline flex items-center gap-1">
<i class="uil uil-file-download"></i> {{ __('career.nda_label') }}
</a>
<a href="https://docs.google.com/document/d/1VofpJIxmQmxSLk6OxsDjCtrMte0UkAFCSaQfZCS2DP8/edit?usp=sharing" target="_blank" class="text-xs font-bold text-blue-600 hover:underline flex items-center gap-1">
<i class="uil uil-file-download"></i> {{ __('career.contract_label') }}
</a>
</div>
</div>
</div>
<!-- Terms Acceptance Checkbox -->
<div class="bg-slate-50 p-6 rounded-2xl border border-slate-100 mt-8 space-y-4">
<div class="flex items-start gap-3">
<input type="checkbox" id="terms-accept-checkbox" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24] mt-1 cursor-pointer">
<label for="terms-accept-checkbox" class="text-sm text-slate-700 font-semibold cursor-pointer">
Çalışma modelini, başvuru şartlarını ve İK süreç akış şemasını okudum, kabul ediyorum. <span class="text-[#e31e24]">*</span>
</label>
</div>
<button type="button" onclick="nextStep(2)" id="step-1-next-btn" class="w-full py-4 bg-slate-300 text-slate-500 font-bold rounded-xl transition-all flex items-center justify-center gap-2 cursor-not-allowed" disabled>
<span>Şartları Kabul Et ve İlerle</span>
<i class="uil uil-arrow-right text-lg"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ================= STEP 2: DOWNLOAD FORMS ================= -->
<div id="step-content-2" class="wizard-step transition-all duration-500 opacity-0 hidden">
<div class="max-w-4xl mx-auto mb-12">
<div class="text-center mb-10">
<h2 class="text-3xl font-extrabold text-slate-900 mb-2">Gerekli Belgeleri İndirin</h2>
<p class="text-slate-500 max-w-xl mx-auto">Başvurunuzu tamamlayabilmek için aşağıdaki yasal sözleşmeleri indirip, dikkatlice okuyup imzalayarak bir sonraki adımda sisteme yüklemeniz gerekmektedir.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<!-- NDA Download Card -->
<div class="bg-white p-8 rounded-3xl border border-slate-100 shadow-xl flex flex-col justify-between hover:shadow-2xl hover:border-red-500/20 transition-all duration-300">
<div>
<div class="w-14 h-14 rounded-2xl bg-red-50 flex items-center justify-center text-[#e31e24] mb-6">
<i class="uil uil-shield-check text-3xl"></i>
</div>
<h4 class="text-xl font-bold text-slate-900 mb-3">{{ __('career.nda_label') }}</h4>
<p class="text-sm text-slate-500 leading-relaxed mb-6">Trunçgil Teknoloji projelerine ait kaynak kodlarının, verilerin ve süreçlerin gizliliğini korumak amacıyla imzalanması zorunlu olan Gizlilik Sözleşmesi'dir.</p>
</div>
<a href="https://docs.google.com/document/d/1WwDo_zgx4KjdnX6ohJaCfPQSS0-eRN41OB1HYvAmDO4/edit?usp=sharing" target="_blank" class="w-full py-3.5 bg-slate-50 hover:bg-red-50 hover:text-[#e31e24] text-slate-700 font-bold rounded-xl transition-all border border-slate-100 hover:border-red-100 flex items-center justify-center gap-2">
<i class="uil uil-file-download"></i> Sözleşmeyi İncele & İndir
</a>
</div>
<!-- Contractor Agreement Download Card -->
<div class="bg-white p-8 rounded-3xl border border-slate-100 shadow-xl flex flex-col justify-between hover:shadow-2xl hover:border-red-500/20 transition-all duration-300">
<div>
<div class="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500 mb-6">
<i class="uil uil-file-contract-sign text-3xl"></i>
</div>
<h4 class="text-xl font-bold text-slate-900 mb-3">{{ __('career.contract_label') }}</h4>
<p class="text-sm text-slate-500 leading-relaxed mb-6">İş/Issue bazlı uzaktan çalışma koşullarını, ödeme şartlarını ve tarafların haklarını tanımlayan Bağımsız Sözleşmeli Hizmet Sözleşmesi'dir.</p>
</div>
<a href="https://docs.google.com/document/d/1VofpJIxmQmxSLk6OxsDjCtrMte0UkAFCSaQfZCS2DP8/edit?usp=sharing" target="_blank" class="w-full py-3.5 bg-slate-50 hover:bg-red-50 hover:text-[#e31e24] text-slate-700 font-bold rounded-xl transition-all border border-slate-100 hover:border-red-100 flex items-center justify-center gap-2">
<i class="uil uil-file-download"></i> Sözleşmeyi İncele & İndir
</a>
</div>
</div>
<!-- Step 2 Navigation -->
<div class="flex items-center justify-between border-t border-slate-100 pt-8">
<button type="button" onclick="prevStep(1)" class="px-6 py-3.5 bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold rounded-xl transition-all flex items-center gap-2">
<i class="uil uil-arrow-left text-lg"></i>
<span>Geri Dön</span>
</button>
<button type="button" onclick="nextStep(3)" class="px-8 py-3.5 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-xl transition-all shadow-lg shadow-red-500/20 flex items-center gap-2">
<span>Sözleşmeleri İndirdim, Son Adıma Geç</span>
<i class="uil uil-arrow-right text-lg"></i>
</button>
</div>
</div>
</div>
<!-- ================= STEP 3: COMPLETE APPLICATION ================= -->
<div id="step-content-3" class="wizard-step transition-all duration-500 opacity-0 hidden">
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-[2rem] shadow-2xl overflow-hidden border border-slate-100">
<div class="p-8 md:p-12">
@@ -90,10 +239,8 @@
<div class="w-12 h-1 bg-[#e31e24] rounded-full mx-auto mt-4"></div>
</div>
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6" id="career-application-form">
@csrf
<input type="hidden" name="type" value="job">
<div class="space-y-6">
<!-- Name -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="name" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.name') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
@@ -101,6 +248,7 @@
</div>
</div>
<!-- Email -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="email" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.email') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
@@ -108,6 +256,7 @@
</div>
</div>
<!-- Phone -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="phone" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.phone') }}</label>
<div class="md:col-span-9">
@@ -115,6 +264,7 @@
</div>
</div>
<!-- CV Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="cv" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.cv') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
@@ -123,6 +273,7 @@
</div>
</div>
<!-- NDA Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="nda" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.nda') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
@@ -131,6 +282,7 @@
</div>
</div>
<!-- Contract Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="contract" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.contract') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
@@ -139,6 +291,7 @@
</div>
</div>
<!-- Checkboxes -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9 space-y-4">
<div class="flex items-center gap-3">
@@ -152,6 +305,7 @@
</div>
</div>
<!-- Message -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-start">
<label for="message" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right pt-3">{{ __('career.message') }}</label>
<div class="md:col-span-9">
@@ -159,20 +313,28 @@
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9">
<button type="submit" id="submit-btn" class="w-full md:w-auto px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<!-- Step 3 Navigation -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 pt-6 border-t border-slate-100">
<div class="md:col-start-4 md:col-span-9 flex items-center justify-between gap-4">
<button type="button" onclick="prevStep(2)" class="px-6 py-4 bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold rounded-xl transition-all flex items-center gap-2">
<i class="uil uil-arrow-left text-lg"></i>
<span>Geri Dön</span>
</button>
<button type="submit" id="submit-btn" class="flex-1 md:flex-none px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<span class="btn-text">{{ __('career.submit') }}</span>
<span class="spinner-border spinner-border-sm hidden animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full" role="status"></span>
<i class="uil uil-arrow-right text-xl btn-icon"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
@push('styles')
@@ -285,6 +447,119 @@
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.2.4/dist/mermaid.min.js"></script>
<script>
// Initialize Mermaid
mermaid.initialize({
startOnLoad: true,
theme: 'neutral',
securityLevel: 'loose',
flowchart: {
useMaxWidth: true,
htmlLabels: true
}
});
// Step variables
let currentStep = 1;
// Track Terms checkbox to enable Step 1 Next button
document.getElementById('terms-accept-checkbox').addEventListener('change', function(e) {
const nextBtn = document.getElementById('step-1-next-btn');
if (e.target.checked) {
nextBtn.disabled = false;
nextBtn.classList.remove('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.add('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
} else {
nextBtn.disabled = true;
nextBtn.classList.add('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.remove('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
}
});
function updateProgressBar() {
const progressPercent = ((currentStep - 1) / 2) * 100;
document.getElementById('wizard-progress-bar').style.width = `${progressPercent}%`;
// Reset indicator classes
for (let i = 1; i <= 3; i++) {
const dot = document.getElementById(`step-dot-${i}`);
const label = document.getElementById(`step-label-${i}`);
if (i < currentStep) {
// Completed Step
dot.className = "w-12 h-12 rounded-full bg-emerald-500 text-white flex items-center justify-center font-extrabold shadow-lg shadow-emerald-500/20 ring-4 ring-emerald-100 transition-all duration-500";
dot.innerHTML = `<i class="uil uil-check text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else if (i === currentStep) {
// Active Step
dot.className = "w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 ring-4 ring-red-100 transition-all duration-500";
if (i === 1) dot.innerHTML = `<i class="uil uil-info-circle text-xl"></i>`;
if (i === 2) dot.innerHTML = `<i class="uil uil-file-download text-xl"></i>`;
if (i === 3) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else {
// Pending Step
dot.className = "w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold ring-4 ring-transparent transition-all duration-500";
if (i === 2) dot.innerHTML = `<i class="uil uil-file-download text-xl"></i>`;
if (i === 3) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-medium text-slate-400 mt-3";
}
}
}
function nextStep(step) {
if (step === 2 && !document.getElementById('terms-accept-checkbox').checked) {
return;
}
// Hide current step content with nice fade animation
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
// Show next step content
const nextStepEl = document.getElementById(`step-content-${step}`);
nextStepEl.classList.remove('hidden');
// Force repaint to trigger animation
nextStepEl.offsetHeight;
nextStepEl.classList.remove('opacity-0');
nextStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
function prevStep(step) {
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
const prevStepEl = document.getElementById(`step-content-${step}`);
prevStepEl.classList.remove('hidden');
prevStepEl.offsetHeight;
prevStepEl.classList.remove('opacity-0');
prevStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
</script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
@@ -337,6 +612,10 @@ document.addEventListener('DOMContentLoaded', function() {
confirmButtonText: '{{ __("career.ok") ?? "Tamam" }}'
}).then(() => {
form.reset();
// Go back to step 1
document.getElementById('terms-accept-checkbox').checked = false;
document.getElementById('terms-accept-checkbox').dispatchEvent(new Event('change'));
prevStep(1);
});
} else if (result.type === 'validation') {
Object.keys(result.errors).forEach(key => {
@@ -344,14 +623,14 @@ document.addEventListener('DOMContentLoaded', function() {
if (input) {
input.classList.add('is-invalid');
const errorDiv = document.createElement('div');
errorDiv.className = 'invalid-feedback text-start d-block';
errorDiv.className = 'invalid-feedback text-start text-xs text-red-500 mt-1 block';
errorDiv.textContent = result.errors[key][0];
// Handle different input structures
if (input.type === 'checkbox') {
input.closest('.space-y-4').appendChild(errorDiv);
input.closest('.flex').parentNode.appendChild(errorDiv);
} else {
input.closest('.md\\:col-span-9').appendChild(errorDiv);
input.parentNode.appendChild(errorDiv);
}
}
});