359 lines
17 KiB
PHP
359 lines
17 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;
|
||
|
||
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(),
|
||
|
||
Select::make('status')
|
||
->label(__('career.status'))
|
||
->options([
|
||
'pending' => __('career.pending'),
|
||
'reviewed' => __('career.reviewed'),
|
||
'rejected' => __('career.rejected'),
|
||
'accepted' => __('career.accepted'),
|
||
'waiting_document' => __('career.waiting_document'),
|
||
])
|
||
->required(),
|
||
|
||
Textarea::make('message')
|
||
->label(__('career.message'))
|
||
->disabled()
|
||
->columnSpanFull(),
|
||
])->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ı')
|
||
->unique(ignoreRecord: true)
|
||
->nullable(),
|
||
|
||
TextInput::make('password')
|
||
->label('Şifre')
|
||
->password()
|
||
->revealable()
|
||
->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()
|
||
->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('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)
|
||
])->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' => __('career.pending'),
|
||
'reviewed' => __('career.reviewed'),
|
||
'rejected' => __('career.rejected'),
|
||
'accepted' => __('career.accepted'),
|
||
'waiting_document' => __('career.waiting_document'),
|
||
]),
|
||
])
|
||
->actions([
|
||
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(),
|
||
Action::make('download_signed_form')
|
||
->label('İmzalı Form İndir')
|
||
->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('view_certificate')
|
||
->label('Sertifika Doğrulama')
|
||
->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(),
|
||
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()) {
|
||
$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()) {
|
||
$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'),
|
||
];
|
||
}
|
||
}
|