608 lines
37 KiB
PHP
608 lines
37 KiB
PHP
<?php
|
||
|
||
namespace App\Filament\Admin\Resources\InternApplications;
|
||
|
||
use App\Models\CareerApplication;
|
||
use Filament\Resources\Resource;
|
||
use Filament\Schemas\Schema;
|
||
use Filament\Tables\Table;
|
||
use Filament\Forms\Components\FileUpload;
|
||
use Filament\Forms\Components\Select;
|
||
use Filament\Forms\Components\Textarea;
|
||
use Filament\Forms\Components\TextInput;
|
||
use Filament\Forms\Components\DatePicker;
|
||
use Filament\Forms\Components\MarkdownEditor;
|
||
use Filament\Schemas\Components\Tabs;
|
||
use Filament\Schemas\Components\Tabs\Tab;
|
||
use Filament\Schemas\Components\Livewire;
|
||
use Filament\Tables\Columns\TextColumn;
|
||
use Filament\Tables\Filters\SelectFilter;
|
||
use Filament\Actions\Action;
|
||
use Filament\Actions\DeleteAction;
|
||
use Filament\Actions\BulkActionGroup;
|
||
use Filament\Actions\DeleteBulkAction;
|
||
use Illuminate\Support\Facades\Storage;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Support\Str;
|
||
use Filament\Schemas\Components\Utilities\Set;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
|
||
use Filament\Forms\Components\Radio;
|
||
|
||
class InternApplicationResource extends Resource
|
||
{
|
||
protected static ?string $model = CareerApplication::class;
|
||
|
||
protected static \BackedEnum|string|null $navigationIcon = 'heroicon-o-academic-cap';
|
||
|
||
public static function getNavigationLabel(): string
|
||
{
|
||
return __('career.internship_title', ['default' => 'Staj Başvuruları']);
|
||
}
|
||
|
||
public static function getModelLabel(): string
|
||
{
|
||
return __('career.internship', ['default' => 'Staj Başvurusu']);
|
||
}
|
||
|
||
public static function getPluralModelLabel(): string
|
||
{
|
||
return __('career.internship_title', ['default' => 'Staj Başvuruları']);
|
||
}
|
||
|
||
public static function getEloquentQuery(): Builder
|
||
{
|
||
return parent::getEloquentQuery()->where('type', 'internship');
|
||
}
|
||
|
||
public static function form(Schema $schema): Schema
|
||
{
|
||
return $schema
|
||
->components([
|
||
Tabs::make('Tabs')
|
||
->tabs([
|
||
Tab::make('Kişisel ve Başvuru Bilgileri')
|
||
->icon('heroicon-m-user')
|
||
->schema([
|
||
TextInput::make('name')
|
||
->label(__('career.name'))
|
||
->required()
|
||
->disabled(),
|
||
|
||
TextInput::make('email')
|
||
->label(__('career.email'))
|
||
->email()
|
||
->required()
|
||
->disabled(),
|
||
|
||
TextInput::make('phone')
|
||
->label(__('career.phone'))
|
||
->disabled(),
|
||
|
||
Textarea::make('message')
|
||
->label(__('career.message'))
|
||
->disabled(),
|
||
|
||
Radio::make('status')
|
||
->label('Stajyer Başvuru & İlerleme Aşaması')
|
||
->options([
|
||
'pending' => '1. Aşama: Başvuru Alındı (Beklemede)',
|
||
'reviewed' => '2. Aşama: Ön Değerlendirme Yapıldı (İncelendi)',
|
||
'waiting_document' => '3. Aşama: Staj Formu Bekleniyor (Stajyer Form Yükleyecek)',
|
||
'accepted' => '4. Aşama: Kabul Edildi & Staj Aktif (İmzalı Form Onaylandı)',
|
||
'rejected' => 'Reddedildi (Başvuru İptal / Olumsuz)',
|
||
])
|
||
->descriptions([
|
||
'pending' => 'Stajyer yeni başvurdu. CV ve başvuru bilgileri incelenmeyi bekliyor.',
|
||
'reviewed' => 'CV ve ön başvuru incelendi, uygunluk değerlendirmesi tamamlandı.',
|
||
'waiting_document' => 'Stajyer kabul sürecine alındı. Okulundan alacağı staj formunu ve tarihlerini panelinden yüklemesi bekleniyor.',
|
||
'accepted' => 'İmzalı staj formu sisteme yüklendi/onaylandı ve staj defteri doldurma süreci başladı.',
|
||
'rejected' => 'Başvuru kriterlere uymadığı için olumsuz sonuçlandırıldı.',
|
||
])
|
||
->columnSpanFull()
|
||
->required(),
|
||
])->columns(2),
|
||
|
||
Tab::make('Staj Belgeleri & Giriş Bilgileri')
|
||
->icon('heroicon-m-document-text')
|
||
->schema([
|
||
FileUpload::make('cv_path')
|
||
->label(__('career.cv'))
|
||
->disk('public')
|
||
->directory('cvs')
|
||
->required()
|
||
->disabled()
|
||
->downloadable(),
|
||
|
||
TextInput::make('username')
|
||
->label('Kullanıcı Adı')
|
||
->default(fn ($record) => $record?->email)
|
||
->disabled()
|
||
->dehydrated()
|
||
->autocomplete('new-username'),
|
||
|
||
TextInput::make('password')
|
||
->label('Şifre')
|
||
->password()
|
||
->revealable()
|
||
->autocomplete('new-password')
|
||
->formatStateUsing(fn () => null)
|
||
->dehydrateStateUsing(fn ($state) => filled($state) ? Hash::make($state) : null)
|
||
->dehydrated(fn ($state) => filled($state))
|
||
->placeholder('Şifreyi değiştirmek istemiyorsanız boş bırakın')
|
||
->nullable()
|
||
->suffixAction(
|
||
\Filament\Actions\Action::make('generatePassword')
|
||
->icon('heroicon-m-arrow-path')
|
||
->action(fn (Set $set) => $set('password', Str::random(12)))
|
||
),
|
||
|
||
FileUpload::make('to_be_signed_internship_form_path')
|
||
->label('İmzalanacak Staj Formu (Stajyerden)')
|
||
->disk('public')
|
||
->directory('to_be_signed_interns')
|
||
->downloadable()
|
||
->nullable(),
|
||
|
||
FileUpload::make('signed_internship_form_path')
|
||
->label('İmzalı Staj Formu')
|
||
->disk('public')
|
||
->directory('signed_interns')
|
||
->downloadable()
|
||
->live()
|
||
->afterStateUpdated(function ($state, Set $set) {
|
||
if ($state) {
|
||
$set('status', 'accepted');
|
||
}
|
||
})
|
||
->nullable(),
|
||
|
||
DatePicker::make('internship_start_date')
|
||
->label('Staj Başlangıç Tarihi')
|
||
->live()
|
||
->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(),
|
||
|
||
DatePicker::make('internship_end_date')
|
||
->label('Staj Bitiş Tarihi')
|
||
->live()
|
||
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateTotalDays($get('internship_start_date'), $state, $set))
|
||
->nullable(),
|
||
|
||
TextInput::make('internship_total_days')
|
||
->label('Toplam Staj Süresi (İş Günü)')
|
||
->numeric()
|
||
->live()
|
||
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateEndDate($get('internship_start_date'), $state, $set))
|
||
->nullable(),
|
||
])->columns(2),
|
||
|
||
Tab::make('Staj Günlüğü')
|
||
->icon('heroicon-m-squares-plus')
|
||
->schema([
|
||
TextInput::make('github_repo')
|
||
->label('GitHub Depo URL\'si')
|
||
->url()
|
||
->nullable()
|
||
->live(),
|
||
|
||
Livewire::make(\App\Livewire\InternJournalTimeline::class)
|
||
->columnSpanFull()
|
||
]),
|
||
|
||
Tab::make('Staj Defteri & Onay')
|
||
->icon('heroicon-o-book-open')
|
||
->schema([
|
||
\Filament\Forms\Components\Placeholder::make('notebook_view')
|
||
->label('Doldurulan Staj Defteri')
|
||
->content(function ($record) {
|
||
if (!$record) return 'Henüz başvuru bulunmamaktadır.';
|
||
$days = \App\Http\Controllers\CareerController::getInternshipDates($record->internship_start_date, $record->internship_total_days);
|
||
if (empty($days)) return 'Staj başlangıç tarihi veya süresi girilmemiş.';
|
||
|
||
$saved = $record->journalEntries()->get()->keyBy('day_number');
|
||
|
||
$html = '<style>
|
||
.rich-text-content p { margin-bottom: 8px; }
|
||
.rich-text-content ul { list-style-type: disc; padding-left: 20px; margin-bottom: 8px; }
|
||
.rich-text-content ol { list-style-type: decimal; padding-left: 20px; margin-bottom: 8px; }
|
||
.rich-text-content li { margin-bottom: 4px; }
|
||
</style>';
|
||
$html .= '<div class="space-y-4" style="max-height: 400px; overflow-y: auto; padding-right: 10px; border: 1px solid #cbd5e1; border-radius: 8px; padding: 15px;">';
|
||
foreach ($days as $d) {
|
||
$dayNum = $d['day_number'];
|
||
$dateF = $d['formatted_date'];
|
||
$entry = $saved->get($dayNum);
|
||
$content = $entry ? $entry->content : '';
|
||
$isRetro = $entry ? $entry->is_retroactive : false;
|
||
$updatedAt = $entry ? $entry->updated_at->format('d.m.Y H:i') : null;
|
||
|
||
$html .= '<div style="margin-bottom: 12px; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;">';
|
||
$html .= ' <div style="display:flex; justify-content:between; font-size:11px; font-weight:700; color:#475569; border-bottom:1px solid #e2e8f0; padding-bottom:6px; margin-bottom:8px;">';
|
||
$html .= ' <span style="font-weight: 800; color: #2563eb;">' . $dayNum . '. Gün Raporu</span>';
|
||
if ($isRetro) {
|
||
$html .= ' <span style="margin-left: 10px; background: #fee2e2; color: #991b1b; padding: 1px 6px; border-radius: 4px; font-size: 9px; font-weight: 800;">GERİYE DÖNÜK KAYIT</span>';
|
||
}
|
||
if ($updatedAt) {
|
||
$html .= ' <span style="margin-left: auto;">Son Güncelleme: ' . $updatedAt . '</span>';
|
||
} else {
|
||
$html .= ' <span style="margin-left: auto;">' . $dateF . '</span>';
|
||
}
|
||
$html .= ' </div>';
|
||
|
||
$cleanContent = $content ? strip_tags($content, ['p', 'strong', 'ul', 'li', 'em', 'br', 'b', 'i', 'ol', 'span']) : '<em style="color:#94a3b8;">Rapor yazılmamış</em>';
|
||
$html .= ' <div class="rich-text-content" style="font-size:12px; color:#1e293b; line-height:1.5;">' . $cleanContent . '</div>';
|
||
|
||
if ($entry && trim($content) !== '') {
|
||
$supApproved = $entry->supervisor_approved;
|
||
$supName = $entry->supervisor_name;
|
||
|
||
$html .= ' <div style="display:flex; align-items:center; gap:12px; margin-top:12px; padding-top:10px; border-top:1px dashed #e2e8f0; font-size:11px;">';
|
||
|
||
// Supervisor approval
|
||
$supBg = $supApproved ? '#d1fae5' : '#f1f5f9';
|
||
$supColor = $supApproved ? '#065f46' : '#64748b';
|
||
$supText = $supApproved ? 'Sorumlu Onayladı' . ($supName ? ' (Onaylayan: ' . e($supName) . ')' : '') : 'Sorumlu Onayı Bekliyor';
|
||
$html .= ' <span id="sup-badge-' . $entry->id . '" style="background:' . $supBg . '; color:' . $supColor . '; padding: 2px 8px; border-radius: 4px; font-weight: 700;">' . $supText . '</span>';
|
||
|
||
// Buttons
|
||
$supBtnText = $supApproved ? 'Onayı Kaldır' : 'Onayla';
|
||
$supBtnBg = $supApproved ? '#ef4444' : '#2563eb';
|
||
$html .= ' <button type="button" onclick="toggleApproval(' . $entry->id . ', this)" style="margin-left:auto; background:' . $supBtnBg . '; color:white; border:none; padding:4px 10px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:10px;">Sorumlu ' . $supBtnText . '</button>';
|
||
|
||
$html .= ' </div>';
|
||
}
|
||
|
||
$html .= '</div>';
|
||
}
|
||
$html .= '</div>';
|
||
|
||
// JS handler
|
||
$html .= '
|
||
<script>
|
||
if (typeof window.toggleApproval !== "function") {
|
||
window.toggleApproval = function(entryId, btn) {
|
||
btn.disabled = true;
|
||
btn.style.opacity = "0.5";
|
||
|
||
fetch("' . route('intern.admin.toggle-journal-approval') . '", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-TOKEN": "' . csrf_token() . '"
|
||
},
|
||
body: JSON.stringify({
|
||
entry_id: entryId
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const badge = document.getElementById("sup-badge-" + entryId);
|
||
if (data.status) {
|
||
badge.style.background = "#d1fae5";
|
||
badge.style.color = "#065f46";
|
||
badge.textContent = "Sorumlu Onayladı (Onaylayan: " + data.supervisor_name + ")";
|
||
btn.textContent = "Sorumlu Onayı Kaldır";
|
||
btn.style.background = "#ef4444";
|
||
} else {
|
||
badge.style.background = "#f1f5f9";
|
||
badge.style.color = "#64748b";
|
||
badge.textContent = "Sorumlu Onayı Bekliyor";
|
||
btn.textContent = "Sorumlu Onayla";
|
||
btn.style.background = "#2563eb";
|
||
}
|
||
} else {
|
||
alert(data.message || "Bir hata oluştu.");
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
alert("Bağlantı hatası oluştu.");
|
||
})
|
||
.finally(() => {
|
||
btn.disabled = false;
|
||
btn.style.opacity = "1";
|
||
});
|
||
};
|
||
}
|
||
</script>
|
||
';
|
||
|
||
// Add preview buttons
|
||
$html .= '<div style="margin-top: 15px; display: flex; gap: 10px;">';
|
||
$html .= ' <a href="' . route('intern.print-journal') . '?size=a4&intern_id=' . $record->id . '" target="_blank" style="display:inline-flex; align-items:center; justify-content:center; padding: 8px 16px; background:#2563eb; color:white; border-radius:8px; font-weight:bold; font-size:12px; text-decoration:none; box-shadow: 0 1px 3px rgba(37, 99, 235, 0.2);">A4 Defteri Önizle / Yazdır</a>';
|
||
$html .= ' <a href="' . route('intern.print-journal') . '?size=a5&intern_id=' . $record->id . '" target="_blank" style="display:inline-flex; align-items:center; justify-content:center; padding: 8px 16px; background:#4b5563; color:white; border-radius:8px; font-weight:bold; font-size:12px; text-decoration:none; box-shadow: 0 1px 3px rgba(75, 85, 99, 0.2);">A5 Defteri Önizle / Yazdır</a>';
|
||
$html .= '</div>';
|
||
|
||
return new \Illuminate\Support\HtmlString($html);
|
||
})
|
||
->columnSpanFull(),
|
||
|
||
\Filament\Schemas\Components\Section::make('Onay ve İmza Bilgileri')
|
||
->schema([
|
||
\Filament\Forms\Components\Toggle::make('notebook_supervisor_signed')
|
||
->label('Staj Sorumlusu İmzala / Onayla')
|
||
->live(),
|
||
TextInput::make('notebook_supervisor_name')
|
||
->label('Staj Sorumlusu Adı / Ünvanı')
|
||
->placeholder('Örn: Alperen Trunç')
|
||
->default('Alperen Trunç'),
|
||
\Filament\Forms\Components\Toggle::make('notebook_approved')
|
||
->label('Staj Defterini Genel Olarak Onayla')
|
||
->columnSpanFull(),
|
||
])->columns(2),
|
||
]),
|
||
|
||
Tab::make('Sertifika & Transkript')
|
||
->icon('heroicon-o-academic-cap')
|
||
->schema([
|
||
TextInput::make('certificate_code')
|
||
->label('Doğrulama Kodu')
|
||
->helperText('Belge kaydedildiğinde benzersiz doğrulama kodu otomatik olarak üretilir.')
|
||
->readonly()
|
||
->nullable(),
|
||
|
||
MarkdownEditor::make('transcript_markdown')
|
||
->label('Akademik Transkript (Markdown)')
|
||
->columnSpanFull()
|
||
->default(function () {
|
||
return "### STAJ AKADEMİK TRANSKRİPTİ VE PERFORMANS RAPORU\n\n" .
|
||
"#### 🛠️ Deneyimlenen Teknolojiler ve Kazanımlar\n" .
|
||
"| Modül / Çalışma Alanı | Kullanılan Teknolojiler / Araçlar | Değerlendirme |\n" .
|
||
"| --- | --- | --- |\n" .
|
||
"| Backend Mimari & API | Laravel framework, RESTful API, MySQL | Başarılı |\n" .
|
||
"| Arayüz & UI/UX Uygulamaları | Flutter, CSS, Glassmorphic Tasarım Prensipleri | Üstün Başarı |\n" .
|
||
"| Masaüstü & Sistem Entegrasyonu | Electron.js, Git / GitHub | Başarılı |\n" .
|
||
"| Takım Çalışması & Proje Yönetimi | Agile / Scrum, Slack, JIRA | Başarılı |\n\n" .
|
||
"#### 📊 Performans Değerlendirme Kriterleri\n" .
|
||
"| Değerlendirme Kriteri | Puan (100 Üzerinden) | Harf Notu |\n" .
|
||
"| --- | --- | --- |\n" .
|
||
"| Teknik Sorumluluk ve Görev Bilinci | 95 | AA |\n" .
|
||
"| Problem Çözme ve Analitik Düşünme | 90 | BA |\n" .
|
||
"| Ekip Çalışması ve İletişim Uyum | 95 | AA |\n" .
|
||
"| Öğrenme Hızı ve Adaptasyon | 100 | AA |\n" .
|
||
"| **GENEL BAŞARI ORTALAMASI** | **95.00** | **AA (Mükemmel)** |\n\n" .
|
||
"#### 📝 Danışman Görüşü ve Değerlendirme Notu\n" .
|
||
"\"Stajyerimiz, staj süresi boyunca kendisine verilen görevleri büyük bir titizlikle yerine getirmiştir. Özellikle karşılaştığı teknik problemlere getirdiği pratik çözümler ve yeni teknolojileri öğrenme isteği takdir edilmeye değerdir. Kurumumuz bünyesinde yürüttüğümüz projelere sağladığı katkılardan ötürü teşekkür eder, profesyonel kariyerinde başarılar dileriz.\"";
|
||
}),
|
||
])->columns(1),
|
||
|
||
Tab::make('Stajyer Blog Yazıları')
|
||
->icon('heroicon-o-newspaper')
|
||
->schema([
|
||
\Filament\Forms\Components\Placeholder::make('intern_blogs_view')
|
||
->label('Yazılan Blog Yazıları')
|
||
->content(function ($record) {
|
||
if (!$record) return 'Henüz kayıt bulunmuyor.';
|
||
$blogs = $record->blogs()->orderBy('created_at', 'desc')->get();
|
||
if ($blogs->isEmpty()) {
|
||
return new \Illuminate\Support\HtmlString('<div style="padding: 15px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; color: #64748b; font-size: 13px;">Bu stajyer henüz blog yazısı oluşturmadı.</div>');
|
||
}
|
||
|
||
$html = '<div style="space-y: 12px;">';
|
||
foreach ($blogs as $b) {
|
||
$catLabel = match ($b->intern_category) {
|
||
'experience' => '1. Staj Tecrübesi',
|
||
'technical_challenge' => '2. Teknik Zorluklar',
|
||
'product_showcase' => '3. Ürün Tanıtımı',
|
||
default => $b->intern_category ?? '-',
|
||
};
|
||
$statusBg = match ($b->status) {
|
||
'published' => '#d1fae5',
|
||
'pending' => '#fef3c7',
|
||
'rejected' => '#fee2e2',
|
||
default => '#f1f5f9',
|
||
};
|
||
$statusColor = match ($b->status) {
|
||
'published' => '#065f46',
|
||
'pending' => '#92400e',
|
||
'rejected' => '#991b1b',
|
||
default => '#475569',
|
||
};
|
||
$statusText = match ($b->status) {
|
||
'published' => 'Yayınlandı',
|
||
'pending' => 'Onay Bekliyor',
|
||
'rejected' => 'Revize İstendi',
|
||
default => 'Taslak',
|
||
};
|
||
|
||
$html .= '<div style="padding: 14px; border: 1px solid #e2e8f0; border-radius: 10px; background: #ffffff; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">';
|
||
$html .= ' <div>';
|
||
$html .= ' <div style="display: flex; gap: 8px; align-items: center; margin-bottom: 4px;">';
|
||
$html .= ' <span style="font-size: 11px; font-weight: 800; background: #eff6ff; color: #1d4ed8; padding: 2px 8px; border-radius: 6px;">' . e($catLabel) . '</span>';
|
||
$html .= ' <span style="font-size: 11px; font-weight: 800; background: ' . $statusBg . '; color: ' . $statusColor . '; padding: 2px 8px; border-radius: 6px;">' . $statusText . '</span>';
|
||
$html .= ' </div>';
|
||
$html .= ' <h4 style="margin: 0; font-size: 14px; font-weight: bold; color: #1e293b;">' . e($b->title) . '</h4>';
|
||
if ($b->admin_feedback) {
|
||
$html .= ' <p style="margin: 4px 0 0 0; font-size: 11px; color: #dc2626;"><strong>Revizyon Notu:</strong> ' . e($b->admin_feedback) . '</p>';
|
||
}
|
||
$html .= ' </div>';
|
||
$html .= ' <div>';
|
||
if ($b->status === 'published') {
|
||
$html .= ' <a href="/blog/' . $b->slug . '" target="_blank" style="padding: 6px 12px; background: #059669; color: white; border-radius: 6px; font-size: 11px; font-weight: bold; text-decoration: none;">Sitede Gör</a>';
|
||
}
|
||
$html .= ' </div>';
|
||
$html .= '</div>';
|
||
}
|
||
$html .= '</div>';
|
||
|
||
return new \Illuminate\Support\HtmlString($html);
|
||
})
|
||
->columnSpanFull(),
|
||
])
|
||
])->columnSpanFull()
|
||
]);
|
||
}
|
||
|
||
public static function table(Table $table): Table
|
||
{
|
||
return $table
|
||
->columns([
|
||
TextColumn::make('name')
|
||
->label(__('career.name'))
|
||
->searchable()
|
||
->sortable(),
|
||
|
||
TextColumn::make('email')
|
||
->label(__('career.email'))
|
||
->searchable()
|
||
->sortable(),
|
||
|
||
TextColumn::make('phone')
|
||
->label(__('career.phone'))
|
||
->searchable(),
|
||
|
||
TextColumn::make('status')
|
||
->label(__('career.status'))
|
||
->badge()
|
||
->color(fn (string $state): string => match ($state) {
|
||
'pending' => 'gray',
|
||
'reviewed' => 'info',
|
||
'rejected' => 'danger',
|
||
'accepted' => 'success',
|
||
'waiting_document' => 'warning',
|
||
default => 'gray',
|
||
})
|
||
->formatStateUsing(fn (string $state): string => __("career.{$state}")),
|
||
|
||
TextColumn::make('certificate_code')
|
||
->label('Sertifika Kodu')
|
||
->searchable()
|
||
->placeholder('Yok'),
|
||
|
||
TextColumn::make('created_at')
|
||
->label(__('career.created_at'))
|
||
->dateTime('d.m.Y H:i')
|
||
->sortable(),
|
||
])
|
||
->filters([
|
||
SelectFilter::make('status')
|
||
->label(__('career.status'))
|
||
->options([
|
||
'pending' => '1. Aşama: Beklemede',
|
||
'reviewed' => '2. Aşama: İncelendi',
|
||
'waiting_document' => '3. Aşama: Staj Formu Bekleniyor',
|
||
'accepted' => '4. Aşama: Kabul Edildi',
|
||
'rejected' => 'Reddedildi',
|
||
]),
|
||
])
|
||
->actions([
|
||
Action::make('view_certificate')
|
||
->label('Sertifika & Transkript')
|
||
->icon('heroicon-o-academic-cap')
|
||
->color('success')
|
||
->url(fn ($record) => $record->certificate_code ? route('internship.verify', $record->certificate_code) : null)
|
||
->visible(fn ($record) => !empty($record->certificate_code))
|
||
->openUrlInNewTab(),
|
||
Action::make('print_journal_a4')
|
||
->label('A4 Defter')
|
||
->icon('heroicon-o-printer')
|
||
->color('info')
|
||
->url(fn ($record) => route('intern.print-journal') . '?size=a4&intern_id=' . $record->id)
|
||
->visible(fn ($record) => !empty($record->internship_total_days))
|
||
->openUrlInNewTab(),
|
||
Action::make('print_journal_a5')
|
||
->label('A5 Defter')
|
||
->icon('heroicon-o-printer')
|
||
->color('gray')
|
||
->url(fn ($record) => route('intern.print-journal') . '?size=a5&intern_id=' . $record->id)
|
||
->visible(fn ($record) => !empty($record->internship_total_days))
|
||
->openUrlInNewTab(),
|
||
Action::make('download_markdown')
|
||
->label('Günlük (.md)')
|
||
->icon('heroicon-o-arrow-down-tray')
|
||
->color('warning')
|
||
->url(fn ($record) => route('intern.download-journal') . '?intern_id=' . $record->id)
|
||
->visible(fn ($record) => !empty($record->github_repo))
|
||
->openUrlInNewTab(),
|
||
Action::make('download_signed_form')
|
||
->label('İmzalı Form')
|
||
->icon('heroicon-o-document-check')
|
||
->url(fn ($record) => $record->signed_internship_form_path ? Storage::disk('public')->url($record->signed_internship_form_path) : null)
|
||
->visible(fn ($record) => !empty($record->signed_internship_form_path))
|
||
->openUrlInNewTab(),
|
||
Action::make('download_cv')
|
||
->label(__('career.download_cv'))
|
||
->icon('heroicon-o-arrow-down-tray')
|
||
->url(fn ($record) => Storage::disk('public')->url($record->cv_path))
|
||
->openUrlInNewTab(),
|
||
DeleteAction::make(),
|
||
])
|
||
->bulkActions([
|
||
BulkActionGroup::make([
|
||
DeleteBulkAction::make(),
|
||
]),
|
||
])
|
||
->defaultSort('created_at', 'desc');
|
||
}
|
||
|
||
public static function calculateTotalDays($start, $end, Set $set): void
|
||
{
|
||
if (!$start || !$end) {
|
||
$set('internship_total_days', null);
|
||
return;
|
||
}
|
||
|
||
$startDate = \Carbon\Carbon::parse($start);
|
||
$endDate = \Carbon\Carbon::parse($end);
|
||
|
||
if ($startDate->gt($endDate)) {
|
||
$set('internship_total_days', 0);
|
||
return;
|
||
}
|
||
|
||
$days = 0;
|
||
while ($startDate->lte($endDate)) {
|
||
if (!$startDate->isWeekend() && !\App\Helpers\TurkeyHolidayHelper::isHoliday($startDate)) {
|
||
$days++;
|
||
}
|
||
$startDate->addDay();
|
||
}
|
||
|
||
$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() || \App\Helpers\TurkeyHolidayHelper::isHoliday($temp)) {
|
||
$temp->addDay();
|
||
continue;
|
||
}
|
||
$endDate = $temp->copy();
|
||
$temp->addDay();
|
||
$count++;
|
||
}
|
||
|
||
$set('internship_end_date', $endDate->format('Y-m-d'));
|
||
}
|
||
|
||
public static function getPages(): array
|
||
{
|
||
return [
|
||
'index' => Pages\ListInternApplications::route('/'),
|
||
'create' => Pages\CreateInternApplication::route('/create'),
|
||
'edit' => Pages\EditInternApplication::route('/{record}/edit'),
|
||
];
|
||
}
|
||
|
||
}
|