base commit
This commit is contained in:
48
app/Filament/Widgets/DepartmentDistributionChart.php
Normal file
48
app/Filament/Widgets/DepartmentDistributionChart.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Department;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
|
||||
class DepartmentDistributionChart extends ChartWidget
|
||||
{
|
||||
protected ?string $heading = 'Employees by Department';
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'doughnut';
|
||||
}
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$departments = Department::query()
|
||||
->withCount('employees')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Employees',
|
||||
'data' => $departments->pluck('employees_count')->all(),
|
||||
'backgroundColor' => [
|
||||
'#3b82f6',
|
||||
'#10b981',
|
||||
'#f59e0b',
|
||||
'#ef4444',
|
||||
'#8b5cf6',
|
||||
'#06b6d4',
|
||||
'#84cc16',
|
||||
'#f97316',
|
||||
],
|
||||
],
|
||||
],
|
||||
'labels' => $departments->pluck('name')->all(),
|
||||
];
|
||||
}
|
||||
}
|
||||
65
app/Filament/Widgets/HrStatsOverviewWidget.php
Normal file
65
app/Filament/Widgets/HrStatsOverviewWidget.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Enums\EmploymentStatus;
|
||||
use App\Models\Employee;
|
||||
use App\Models\SickLeave;
|
||||
use App\Models\Vacation;
|
||||
use Filament\Widgets\StatsOverviewWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class HrStatsOverviewWidget extends StatsOverviewWidget
|
||||
{
|
||||
protected ?string $heading = 'HR Overview';
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
$today = now()->toDateString();
|
||||
|
||||
$onVacationToday = Vacation::query()
|
||||
->where('status', ApprovalStatus::Approved)
|
||||
->whereDate('start_date', '<=', $today)
|
||||
->whereDate('end_date', '>=', $today)
|
||||
->count();
|
||||
|
||||
$onSickLeaveToday = SickLeave::query()
|
||||
->whereDate('start_date', '<=', $today)
|
||||
->whereDate('end_date', '>=', $today)
|
||||
->count();
|
||||
|
||||
$pendingRequests = Vacation::query()
|
||||
->where('status', ApprovalStatus::Pending)
|
||||
->count();
|
||||
|
||||
return [
|
||||
Stat::make('Total Employees', Employee::query()->count())
|
||||
->description('All employees in the system')
|
||||
->descriptionIcon('heroicon-o-users')
|
||||
->color('primary'),
|
||||
Stat::make('Active', Employee::query()->where('employment_status', EmploymentStatus::Active)->count())
|
||||
->description('Currently active employees')
|
||||
->descriptionIcon('heroicon-o-check-circle')
|
||||
->color('success'),
|
||||
Stat::make('Inactive', Employee::query()->where('employment_status', EmploymentStatus::Inactive)->count())
|
||||
->description('Inactive employees')
|
||||
->descriptionIcon('heroicon-o-pause-circle')
|
||||
->color('gray'),
|
||||
Stat::make('On Vacation Today', $onVacationToday)
|
||||
->description('Approved vacations in progress')
|
||||
->descriptionIcon('heroicon-o-sun')
|
||||
->color('warning'),
|
||||
Stat::make('On Sick Leave Today', $onSickLeaveToday)
|
||||
->description('Employees on sick leave')
|
||||
->descriptionIcon('heroicon-o-heart')
|
||||
->color('danger'),
|
||||
Stat::make('Pending Requests', $pendingRequests)
|
||||
->description('Vacation requests awaiting approval')
|
||||
->descriptionIcon('heroicon-o-clock')
|
||||
->color('info'),
|
||||
];
|
||||
}
|
||||
}
|
||||
46
app/Filament/Widgets/MonthlyHiringChart.php
Normal file
46
app/Filament/Widgets/MonthlyHiringChart.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Employee;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
class MonthlyHiringChart extends ChartWidget
|
||||
{
|
||||
protected ?string $heading = 'Monthly Hiring (Last 12 Months)';
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
for ($i = 11; $i >= 0; $i--) {
|
||||
$month = now()->subMonths($i);
|
||||
$labels[] = $month->format('M Y');
|
||||
$data[] = Employee::query()
|
||||
->whereNotNull('hire_date')
|
||||
->whereYear('hire_date', $month->year)
|
||||
->whereMonth('hire_date', $month->month)
|
||||
->count();
|
||||
}
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'New Hires',
|
||||
'data' => $data,
|
||||
'backgroundColor' => '#3b82f6',
|
||||
],
|
||||
],
|
||||
'labels' => $labels,
|
||||
];
|
||||
}
|
||||
}
|
||||
48
app/Filament/Widgets/MonthlyLeaveChart.php
Normal file
48
app/Filament/Widgets/MonthlyLeaveChart.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\Vacation;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
|
||||
class MonthlyLeaveChart extends ChartWidget
|
||||
{
|
||||
protected ?string $heading = 'Vacation Days by Month';
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
for ($i = 11; $i >= 0; $i--) {
|
||||
$month = now()->subMonths($i);
|
||||
$labels[] = $month->format('M Y');
|
||||
$data[] = (int) Vacation::query()
|
||||
->where('status', ApprovalStatus::Approved)
|
||||
->whereYear('start_date', $month->year)
|
||||
->whereMonth('start_date', $month->month)
|
||||
->sum('days');
|
||||
}
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Vacation Days',
|
||||
'data' => $data,
|
||||
'backgroundColor' => '#f59e0b',
|
||||
],
|
||||
],
|
||||
'labels' => $labels,
|
||||
];
|
||||
}
|
||||
}
|
||||
53
app/Filament/Widgets/RecentReportsWidget.php
Normal file
53
app/Filament/Widgets/RecentReportsWidget.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\DisciplinarySeverity;
|
||||
use App\Models\DisciplinaryReport;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class RecentReportsWidget extends TableWidget
|
||||
{
|
||||
protected static ?string $heading = 'Recent Disciplinary Reports';
|
||||
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
DisciplinaryReport::query()
|
||||
->with(['employee', 'creator'])
|
||||
->latest('report_date')
|
||||
->limit(5),
|
||||
)
|
||||
->columns([
|
||||
TextColumn::make('employee.full_name')
|
||||
->label('Employee'),
|
||||
TextColumn::make('report_date')
|
||||
->date(),
|
||||
TextColumn::make('title')
|
||||
->limit(40),
|
||||
TextColumn::make('severity')
|
||||
->badge()
|
||||
->color(fn (DisciplinarySeverity $state): string => $state->color())
|
||||
->formatStateUsing(fn (DisciplinarySeverity $state): string => $state->label()),
|
||||
TextColumn::make('creator.name')
|
||||
->label('Created By'),
|
||||
])
|
||||
->paginated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<DisciplinaryReport>|null
|
||||
*/
|
||||
protected function getTableQuery(): ?Builder
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
80
app/Filament/Widgets/UpcomingBirthdaysWidget.php
Normal file
80
app/Filament/Widgets/UpcomingBirthdaysWidget.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Employee;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
class UpcomingBirthdaysWidget extends TableWidget
|
||||
{
|
||||
protected static ?string $heading = 'Upcoming Birthdays (30 Days)';
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
Employee::query()
|
||||
->whereNotNull('birth_date')
|
||||
->whereIn('id', $this->upcomingBirthdayEmployeeIds()),
|
||||
)
|
||||
->columns([
|
||||
TextColumn::make('full_name')
|
||||
->label('Employee'),
|
||||
TextColumn::make('birth_date')
|
||||
->label('Birthday')
|
||||
->date()
|
||||
->formatStateUsing(fn (Employee $record): string => $record->birth_date?->format('M d') ?? '—'),
|
||||
TextColumn::make('department.name')
|
||||
->label('Department'),
|
||||
])
|
||||
->paginated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function upcomingBirthdayEmployeeIds(): array
|
||||
{
|
||||
return Employee::query()
|
||||
->whereNotNull('birth_date')
|
||||
->get()
|
||||
->filter(function (Employee $employee): bool {
|
||||
if ($employee->birth_date === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$birthday = $employee->birth_date->copy()->year(now()->year);
|
||||
|
||||
if ($birthday->lt(now()->startOfDay())) {
|
||||
$birthday = $birthday->addYear();
|
||||
}
|
||||
|
||||
return $birthday->lte(now()->addDays(30));
|
||||
})
|
||||
->sortBy(function (Employee $employee): int {
|
||||
$birthday = $employee->birth_date->copy()->year(now()->year);
|
||||
|
||||
if ($birthday->lt(now()->startOfDay())) {
|
||||
$birthday = $birthday->addYear();
|
||||
}
|
||||
|
||||
return $birthday->dayOfYear;
|
||||
})
|
||||
->pluck('id')
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<Employee>|null
|
||||
*/
|
||||
protected function getTableQuery(): ?Builder
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
51
app/Filament/Widgets/UpcomingLeaveWidget.php
Normal file
51
app/Filament/Widgets/UpcomingLeaveWidget.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\Vacation;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class UpcomingLeaveWidget extends TableWidget
|
||||
{
|
||||
protected static ?string $heading = 'Upcoming Approved Leave (14 Days)';
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
Vacation::query()
|
||||
->with('employee')
|
||||
->where('status', ApprovalStatus::Approved)
|
||||
->whereDate('start_date', '>=', now()->toDateString())
|
||||
->whereDate('start_date', '<=', now()->addDays(14)->toDateString())
|
||||
->orderBy('start_date'),
|
||||
)
|
||||
->columns([
|
||||
TextColumn::make('employee.full_name')
|
||||
->label('Employee'),
|
||||
TextColumn::make('start_date')
|
||||
->date(),
|
||||
TextColumn::make('end_date')
|
||||
->date(),
|
||||
TextColumn::make('days')
|
||||
->label('Days'),
|
||||
])
|
||||
->paginated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<Vacation>|null
|
||||
*/
|
||||
protected function getTableQuery(): ?Builder
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user