feat: automate internship end date calculation based on business days in dashboard, Filament admin, and controller
This commit is contained in:
@@ -99,7 +99,13 @@ class CareerApplicationForm
|
|||||||
DatePicker::make('internship_start_date')
|
DatePicker::make('internship_start_date')
|
||||||
->label('Staj Başlangıç Tarihi')
|
->label('Staj Başlangıç Tarihi')
|
||||||
->live()
|
->live()
|
||||||
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateTotalDays($state, $get('internship_end_date'), $set))
|
->afterStateUpdated(function ($state, $get, Set $set) {
|
||||||
|
if ($state && $get('internship_total_days')) {
|
||||||
|
self::calculateEndDate($state, $get('internship_total_days'), $set);
|
||||||
|
} elseif ($state && $get('internship_end_date')) {
|
||||||
|
self::calculateTotalDays($state, $get('internship_end_date'), $set);
|
||||||
|
}
|
||||||
|
})
|
||||||
->nullable(),
|
->nullable(),
|
||||||
|
|
||||||
DatePicker::make('internship_end_date')
|
DatePicker::make('internship_end_date')
|
||||||
@@ -111,8 +117,8 @@ class CareerApplicationForm
|
|||||||
TextInput::make('internship_total_days')
|
TextInput::make('internship_total_days')
|
||||||
->label('Toplam Staj Süresi (İş Günü)')
|
->label('Toplam Staj Süresi (İş Günü)')
|
||||||
->numeric()
|
->numeric()
|
||||||
->disabled()
|
->live()
|
||||||
->dehydrated()
|
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateEndDate($get('internship_start_date'), $state, $set))
|
||||||
->nullable(),
|
->nullable(),
|
||||||
])
|
])
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
@@ -144,4 +150,30 @@ class CareerApplicationForm
|
|||||||
|
|
||||||
$set('internship_total_days', $days);
|
$set('internship_total_days', $days);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function calculateEndDate($start, $totalDays, Set $set): void
|
||||||
|
{
|
||||||
|
if (!$start || !$totalDays || $totalDays <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startDate = \Carbon\Carbon::parse($start);
|
||||||
|
$daysToAdd = intval($totalDays);
|
||||||
|
|
||||||
|
$endDate = $startDate->copy();
|
||||||
|
$count = 0;
|
||||||
|
$temp = $startDate->copy();
|
||||||
|
|
||||||
|
while ($count < $daysToAdd) {
|
||||||
|
if ($temp->isWeekend()) {
|
||||||
|
$temp->addDay();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$endDate = $temp->copy();
|
||||||
|
$temp->addDay();
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set('internship_end_date', $endDate->format('Y-m-d'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class CareerController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'internship_form' => 'required|file|mimes:pdf,docx,jpg,png,jpeg|max:51200',
|
'internship_form' => 'required|file|mimes:pdf,docx,jpg,png,jpeg|max:51200',
|
||||||
'internship_start_date' => 'required|date',
|
'internship_start_date' => 'required|date',
|
||||||
'internship_end_date' => 'required|date|after_or_equal:internship_start_date',
|
'internship_total_days' => 'required|integer|min:1',
|
||||||
], [
|
], [
|
||||||
'internship_form.required' => 'Lütfen bir dosya seçin.',
|
'internship_form.required' => 'Lütfen bir dosya seçin.',
|
||||||
'internship_form.file' => 'Yüklenen öğe geçerli bir dosya olmalıdır.',
|
'internship_form.file' => 'Yüklenen öğe geçerli bir dosya olmalıdır.',
|
||||||
@@ -145,9 +145,9 @@ class CareerController extends Controller
|
|||||||
'internship_form.max' => 'Dosya boyutu en fazla 50MB olabilir.',
|
'internship_form.max' => 'Dosya boyutu en fazla 50MB olabilir.',
|
||||||
'internship_start_date.required' => 'Lütfen staj başlangıç tarihini girin.',
|
'internship_start_date.required' => 'Lütfen staj başlangıç tarihini girin.',
|
||||||
'internship_start_date.date' => 'Geçerli bir başlangıç tarihi girin.',
|
'internship_start_date.date' => 'Geçerli bir başlangıç tarihi girin.',
|
||||||
'internship_end_date.required' => 'Lütfen staj bitiş tarihini girin.',
|
'internship_total_days.required' => 'Lütfen staj süresini girin.',
|
||||||
'internship_end_date.date' => 'Geçerli bir bitiş tarihi girin.',
|
'internship_total_days.integer' => 'Staj süresi geçerli bir tam sayı olmalıdır.',
|
||||||
'internship_end_date.after_or_equal' => 'Bitiş tarihi, başlangıç tarihinden önce olamaz.',
|
'internship_total_days.min' => 'Staj süresi en az 1 gün olmalıdır.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$intern = CareerApplication::findOrFail(session('intern_id'));
|
$intern = CareerApplication::findOrFail(session('intern_id'));
|
||||||
@@ -160,23 +160,29 @@ class CareerController extends Controller
|
|||||||
|
|
||||||
$path = $request->file('internship_form')->store('to_be_signed_interns', 'public');
|
$path = $request->file('internship_form')->store('to_be_signed_interns', 'public');
|
||||||
|
|
||||||
// Calculate business days (excluding weekends)
|
// Calculate end date based on weekdays and duration
|
||||||
$startDate = \Carbon\Carbon::parse($request->internship_start_date);
|
$startDate = \Carbon\Carbon::parse($request->internship_start_date);
|
||||||
$endDate = \Carbon\Carbon::parse($request->internship_end_date);
|
$daysToAdd = intval($request->internship_total_days);
|
||||||
$days = 0;
|
|
||||||
$tempDate = $startDate->copy();
|
$endDate = $startDate->copy();
|
||||||
while ($tempDate->lte($endDate)) {
|
$count = 0;
|
||||||
if (!$tempDate->isWeekend()) {
|
$temp = $startDate->copy();
|
||||||
$days++;
|
|
||||||
|
while ($count < $daysToAdd) {
|
||||||
|
if ($temp->isWeekend()) {
|
||||||
|
$temp->addDay();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
$tempDate->addDay();
|
$endDate = $temp->copy();
|
||||||
|
$temp->addDay();
|
||||||
|
$count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$intern->update([
|
$intern->update([
|
||||||
'to_be_signed_internship_form_path' => $path,
|
'to_be_signed_internship_form_path' => $path,
|
||||||
'internship_start_date' => $request->internship_start_date,
|
'internship_start_date' => $request->internship_start_date,
|
||||||
'internship_end_date' => $request->internship_end_date,
|
'internship_end_date' => $endDate->format('Y-m-d'),
|
||||||
'internship_total_days' => $days,
|
'internship_total_days' => $daysToAdd,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'İmzalanacak staj formunuz ve staj tarihleri başarıyla kaydedildi.');
|
return redirect()->back()->with('success', 'İmzalanacak staj formunuz ve staj tarihleri başarıyla kaydedildi.');
|
||||||
|
|||||||
@@ -180,14 +180,18 @@
|
|||||||
<form action="{{ route('intern.upload-form') }}" method="POST" enctype="multipart/form-data" class="space-y-4">
|
<form action="{{ route('intern.upload-form') }}" method="POST" enctype="multipart/form-data" class="space-y-4">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="internship_start_date" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-1.5">Staj Başlangıç Tarihi</label>
|
<label for="internship_start_date" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-1.5">Staj Başlangıç Tarihi</label>
|
||||||
<input type="date" name="internship_start_date" id="internship_start_date" required value="{{ $intern->internship_start_date }}" class="w-full px-4 py-3 rounded-xl border border-slate-200 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none text-sm font-semibold text-slate-700 bg-white transition-all" />
|
<input type="date" name="internship_start_date" id="internship_start_date" onchange="calculateEndDateFrontend()" oninput="calculateEndDateFrontend()" required value="{{ $intern->internship_start_date }}" class="w-full px-4 py-3 rounded-xl border border-slate-200 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none text-sm font-semibold text-slate-700 bg-white transition-all" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="internship_end_date" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-1.5">Staj Bitiş Tarihi</label>
|
<label for="internship_total_days" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-1.5">Staj Süresi (İş Günü)</label>
|
||||||
<input type="date" name="internship_end_date" id="internship_end_date" required value="{{ $intern->internship_end_date }}" class="w-full px-4 py-3 rounded-xl border border-slate-200 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none text-sm font-semibold text-slate-700 bg-white transition-all" />
|
<input type="number" name="internship_total_days" id="internship_total_days" onchange="calculateEndDateFrontend()" oninput="calculateEndDateFrontend()" min="1" required value="{{ $intern->internship_total_days }}" placeholder="Örn: 20" class="w-full px-4 py-3 rounded-xl border border-slate-200 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none text-sm font-semibold text-slate-700 bg-white transition-all" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="internship_end_date" class="block text-xs font-extrabold text-slate-500 uppercase tracking-wider mb-1.5">Staj Bitiş Tarihi (Otomatik)</label>
|
||||||
|
<input type="date" name="internship_end_date" id="internship_end_date" readonly class="w-full px-4 py-3 rounded-xl border border-slate-200 bg-slate-50 text-slate-500 cursor-not-allowed focus:outline-none text-sm font-semibold transition-all" value="{{ $intern->internship_end_date }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -274,6 +278,43 @@
|
|||||||
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateEndDateFrontend() {
|
||||||
|
const startDateVal = document.getElementById('internship_start_date').value;
|
||||||
|
const totalDaysVal = document.getElementById('internship_total_days').value;
|
||||||
|
const endDateInput = document.getElementById('internship_end_date');
|
||||||
|
|
||||||
|
if (!startDateVal || !totalDaysVal || totalDaysVal <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = new Date(startDateVal);
|
||||||
|
let daysToAdd = parseInt(totalDaysVal);
|
||||||
|
let count = 0;
|
||||||
|
let endDate = new Date(startDateVal);
|
||||||
|
|
||||||
|
while (count < daysToAdd) {
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
if (dayOfWeek === 6 || dayOfWeek === 0) { // 6 = Saturday, 0 = Sunday
|
||||||
|
date.setDate(date.getDate() + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endDate = new Date(date);
|
||||||
|
date.setDate(date.getDate() + 1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format to YYYY-MM-DD
|
||||||
|
const yyyy = endDate.getFullYear();
|
||||||
|
const mm = String(endDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(endDate.getDate()).padStart(2, '0');
|
||||||
|
endDateInput.value = `${yyyy}-${mm}-${dd}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger calculation once on load if values exist
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
calculateEndDateFrontend();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.shadow-xl {
|
.shadow-xl {
|
||||||
|
|||||||
Reference in New Issue
Block a user