base commit
This commit is contained in:
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{compose,docker-compose}.{yml,yaml}]
|
||||||
|
indent_size = 4
|
||||||
65
.env.example
Normal file
65
.env.example
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_FALLBACK_LOCALE=en
|
||||||
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
|
APP_MAINTENANCE_DRIVER=file
|
||||||
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
# PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_STACK=single
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
# DB_HOST=127.0.0.1
|
||||||
|
# DB_PORT=3306
|
||||||
|
# DB_DATABASE=laravel
|
||||||
|
# DB_USERNAME=root
|
||||||
|
# DB_PASSWORD=
|
||||||
|
|
||||||
|
SESSION_DRIVER=database
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
|
CACHE_STORE=database
|
||||||
|
# CACHE_PREFIX=
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_SCHEME=null
|
||||||
|
MAIL_HOST=127.0.0.1
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
*.blade.php diff=html
|
||||||
|
*.css diff=css
|
||||||
|
*.html diff=html
|
||||||
|
*.md diff=markdown
|
||||||
|
*.php diff=php
|
||||||
|
|
||||||
|
/.github export-ignore
|
||||||
|
CHANGELOG.md export-ignore
|
||||||
|
.styleci.yml export-ignore
|
||||||
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.env.production
|
||||||
|
.phpactor.json
|
||||||
|
.phpunit.result.cache
|
||||||
|
/.codex
|
||||||
|
/.cursor/
|
||||||
|
/.idea
|
||||||
|
/.nova
|
||||||
|
/.phpunit.cache
|
||||||
|
/.vscode
|
||||||
|
/.zed
|
||||||
|
/auth.json
|
||||||
|
/node_modules
|
||||||
|
/public/build
|
||||||
|
/public/fonts-manifest.dev.json
|
||||||
|
/public/hot
|
||||||
|
/public/storage
|
||||||
|
/storage/*.key
|
||||||
|
/storage/pail
|
||||||
|
/vendor
|
||||||
|
_ide_helper.php
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
Thumbs.db
|
||||||
58
README.md
Normal file
58
README.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## About Laravel
|
||||||
|
|
||||||
|
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||||
|
|
||||||
|
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||||
|
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||||
|
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||||
|
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||||
|
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||||
|
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||||
|
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||||
|
|
||||||
|
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||||
|
|
||||||
|
## Learning Laravel
|
||||||
|
|
||||||
|
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||||
|
|
||||||
|
In addition, [Laracasts](https://laracasts.com) contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||||
|
|
||||||
|
You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals.
|
||||||
|
|
||||||
|
## Agentic Development
|
||||||
|
|
||||||
|
Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require laravel/boost --dev
|
||||||
|
|
||||||
|
php artisan boost:install
|
||||||
|
```
|
||||||
|
|
||||||
|
Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||||
|
|
||||||
|
## Security Vulnerabilities
|
||||||
|
|
||||||
|
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||||
24
app/DTOs/EmployeeSummaryDto.php
Normal file
24
app/DTOs/EmployeeSummaryDto.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs;
|
||||||
|
|
||||||
|
use App\Models\Vacation;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
readonly class EmployeeSummaryDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection<int, Vacation> $upcomingLeave
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public int $vacationTaken,
|
||||||
|
public int $vacationRemaining,
|
||||||
|
public int $sickLeaveCount,
|
||||||
|
public int $reportsCount,
|
||||||
|
public float $bonusesTotal,
|
||||||
|
public int $giftsCount,
|
||||||
|
public Collection $upcomingLeave,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
39
app/DTOs/Import/BonusRowDto.php
Normal file
39
app/DTOs/Import/BonusRowDto.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs\Import;
|
||||||
|
|
||||||
|
readonly class BonusRowDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $employeeNumber,
|
||||||
|
public ?string $bonusDate,
|
||||||
|
public ?string $bonusType,
|
||||||
|
public ?float $amount,
|
||||||
|
public ?string $reason,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $row
|
||||||
|
*/
|
||||||
|
public static function fromMappedRow(array $row): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
employeeNumber: (string) ($row['employee_number'] ?? ''),
|
||||||
|
bonusDate: self::nullableString($row['bonus_date'] ?? null),
|
||||||
|
bonusType: self::nullableString($row['bonus_type'] ?? null),
|
||||||
|
amount: isset($row['amount']) && $row['amount'] !== '' ? (float) $row['amount'] : null,
|
||||||
|
reason: self::nullableString($row['reason'] ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
app/DTOs/Import/EmployeeRowDto.php
Normal file
55
app/DTOs/Import/EmployeeRowDto.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs\Import;
|
||||||
|
|
||||||
|
readonly class EmployeeRowDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $employeeNumber,
|
||||||
|
public string $fullName,
|
||||||
|
public ?string $nationalId,
|
||||||
|
public ?string $gender,
|
||||||
|
public ?string $birthDate,
|
||||||
|
public ?string $phone,
|
||||||
|
public ?string $address,
|
||||||
|
public ?string $department,
|
||||||
|
public ?string $position,
|
||||||
|
public ?string $shift,
|
||||||
|
public ?string $employmentStatus,
|
||||||
|
public ?string $hireDate,
|
||||||
|
public ?string $notes,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $row
|
||||||
|
*/
|
||||||
|
public static function fromMappedRow(array $row): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
employeeNumber: (string) ($row['employee_number'] ?? ''),
|
||||||
|
fullName: (string) ($row['full_name'] ?? ''),
|
||||||
|
nationalId: self::nullableString($row['national_id'] ?? null),
|
||||||
|
gender: self::nullableString($row['gender'] ?? null),
|
||||||
|
birthDate: self::nullableString($row['birth_date'] ?? null),
|
||||||
|
phone: self::nullableString($row['phone'] ?? null),
|
||||||
|
address: self::nullableString($row['address'] ?? null),
|
||||||
|
department: self::nullableString($row['department'] ?? null),
|
||||||
|
position: self::nullableString($row['position'] ?? null),
|
||||||
|
shift: self::nullableString($row['shift'] ?? null),
|
||||||
|
employmentStatus: self::nullableString($row['employment_status'] ?? null),
|
||||||
|
hireDate: self::nullableString($row['hire_date'] ?? null),
|
||||||
|
notes: self::nullableString($row['notes'] ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/DTOs/Import/ImportResultDto.php
Normal file
33
app/DTOs/Import/ImportResultDto.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs\Import;
|
||||||
|
|
||||||
|
readonly class ImportResultDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, string> $errors
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public int $total,
|
||||||
|
public int $imported,
|
||||||
|
public int $skipped,
|
||||||
|
public int $failed,
|
||||||
|
public array $errors = [],
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'total' => $this->total,
|
||||||
|
'imported' => $this->imported,
|
||||||
|
'skipped' => $this->skipped,
|
||||||
|
'failed' => $this->failed,
|
||||||
|
'errors' => $this->errors,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/DTOs/Import/ReportRowDto.php
Normal file
39
app/DTOs/Import/ReportRowDto.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs\Import;
|
||||||
|
|
||||||
|
readonly class ReportRowDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $employeeNumber,
|
||||||
|
public ?string $reportDate,
|
||||||
|
public ?string $title,
|
||||||
|
public ?string $description,
|
||||||
|
public ?string $severity,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $row
|
||||||
|
*/
|
||||||
|
public static function fromMappedRow(array $row): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
employeeNumber: (string) ($row['employee_number'] ?? ''),
|
||||||
|
reportDate: self::nullableString($row['report_date'] ?? null),
|
||||||
|
title: self::nullableString($row['title'] ?? null),
|
||||||
|
description: self::nullableString($row['description'] ?? null),
|
||||||
|
severity: self::nullableString($row['severity'] ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/DTOs/Import/VacationRowDto.php
Normal file
45
app/DTOs/Import/VacationRowDto.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DTOs\Import;
|
||||||
|
|
||||||
|
readonly class VacationRowDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $employeeNumber,
|
||||||
|
public ?string $type,
|
||||||
|
public ?string $startDate,
|
||||||
|
public ?string $endDate,
|
||||||
|
public ?string $returnDate,
|
||||||
|
public ?int $days,
|
||||||
|
public ?string $status,
|
||||||
|
public ?string $reason,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $row
|
||||||
|
*/
|
||||||
|
public static function fromMappedRow(array $row): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
employeeNumber: (string) ($row['employee_number'] ?? ''),
|
||||||
|
type: self::nullableString($row['type'] ?? null),
|
||||||
|
startDate: self::nullableString($row['start_date'] ?? null),
|
||||||
|
endDate: self::nullableString($row['end_date'] ?? null),
|
||||||
|
returnDate: self::nullableString($row['return_date'] ?? null),
|
||||||
|
days: isset($row['days']) && $row['days'] !== '' ? (int) $row['days'] : null,
|
||||||
|
status: self::nullableString($row['status'] ?? null),
|
||||||
|
reason: self::nullableString($row['reason'] ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Enums/ApprovalStatus.php
Normal file
39
app/Enums/ApprovalStatus.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum ApprovalStatus: string
|
||||||
|
{
|
||||||
|
case Pending = 'pending';
|
||||||
|
case Approved = 'approved';
|
||||||
|
case Rejected = 'rejected';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Pending => 'Pending',
|
||||||
|
self::Approved => 'Approved',
|
||||||
|
self::Rejected => 'Rejected',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Pending => 'warning',
|
||||||
|
self::Approved => 'success',
|
||||||
|
self::Rejected => 'danger',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Pending => 'heroicon-o-clock',
|
||||||
|
self::Approved => 'heroicon-o-check-badge',
|
||||||
|
self::Rejected => 'heroicon-o-x-mark',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
43
app/Enums/BonusType.php
Normal file
43
app/Enums/BonusType.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum BonusType: string
|
||||||
|
{
|
||||||
|
case Performance = 'performance';
|
||||||
|
case Holiday = 'holiday';
|
||||||
|
case Retention = 'retention';
|
||||||
|
case Other = 'other';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Performance => 'Performance',
|
||||||
|
self::Holiday => 'Holiday',
|
||||||
|
self::Retention => 'Retention',
|
||||||
|
self::Other => 'Other',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Performance => 'success',
|
||||||
|
self::Holiday => 'primary',
|
||||||
|
self::Retention => 'info',
|
||||||
|
self::Other => 'gray',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Performance => 'heroicon-o-star',
|
||||||
|
self::Holiday => 'heroicon-o-gift',
|
||||||
|
self::Retention => 'heroicon-o-hand-raised',
|
||||||
|
self::Other => 'heroicon-o-banknotes',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Enums/DisciplinarySeverity.php
Normal file
39
app/Enums/DisciplinarySeverity.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum DisciplinarySeverity: string
|
||||||
|
{
|
||||||
|
case Low = 'low';
|
||||||
|
case Medium = 'medium';
|
||||||
|
case High = 'high';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Low => 'Low',
|
||||||
|
self::Medium => 'Medium',
|
||||||
|
self::High => 'High',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Low => 'info',
|
||||||
|
self::Medium => 'warning',
|
||||||
|
self::High => 'danger',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Low => 'heroicon-o-information-circle',
|
||||||
|
self::Medium => 'heroicon-o-exclamation-triangle',
|
||||||
|
self::High => 'heroicon-o-shield-exclamation',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Enums/DocumentType.php
Normal file
47
app/Enums/DocumentType.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum DocumentType: string
|
||||||
|
{
|
||||||
|
case Passport = 'passport';
|
||||||
|
case Contract = 'contract';
|
||||||
|
case MedicalCertificate = 'medical_certificate';
|
||||||
|
case EducationCertificate = 'education_certificate';
|
||||||
|
case Other = 'other';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Passport => 'Passport',
|
||||||
|
self::Contract => 'Contract',
|
||||||
|
self::MedicalCertificate => 'Medical Certificate',
|
||||||
|
self::EducationCertificate => 'Education Certificate',
|
||||||
|
self::Other => 'Other',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Passport => 'primary',
|
||||||
|
self::Contract => 'success',
|
||||||
|
self::MedicalCertificate => 'danger',
|
||||||
|
self::EducationCertificate => 'info',
|
||||||
|
self::Other => 'gray',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Passport => 'heroicon-o-identification',
|
||||||
|
self::Contract => 'heroicon-o-document-text',
|
||||||
|
self::MedicalCertificate => 'heroicon-o-heart',
|
||||||
|
self::EducationCertificate => 'heroicon-o-academic-cap',
|
||||||
|
self::Other => 'heroicon-o-folder',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
43
app/Enums/EmploymentStatus.php
Normal file
43
app/Enums/EmploymentStatus.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum EmploymentStatus: string
|
||||||
|
{
|
||||||
|
case Active = 'active';
|
||||||
|
case Inactive = 'inactive';
|
||||||
|
case Terminated = 'terminated';
|
||||||
|
case OnLeave = 'on_leave';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Active => 'Active',
|
||||||
|
self::Inactive => 'Inactive',
|
||||||
|
self::Terminated => 'Terminated',
|
||||||
|
self::OnLeave => 'On Leave',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Active => 'success',
|
||||||
|
self::Inactive => 'gray',
|
||||||
|
self::Terminated => 'danger',
|
||||||
|
self::OnLeave => 'warning',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Active => 'heroicon-o-check-circle',
|
||||||
|
self::Inactive => 'heroicon-o-pause-circle',
|
||||||
|
self::Terminated => 'heroicon-o-x-circle',
|
||||||
|
self::OnLeave => 'heroicon-o-clock',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Enums/Gender.php
Normal file
39
app/Enums/Gender.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum Gender: string
|
||||||
|
{
|
||||||
|
case Male = 'male';
|
||||||
|
case Female = 'female';
|
||||||
|
case Other = 'other';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Male => 'Male',
|
||||||
|
self::Female => 'Female',
|
||||||
|
self::Other => 'Other',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Male => 'info',
|
||||||
|
self::Female => 'pink',
|
||||||
|
self::Other => 'gray',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Male => 'heroicon-o-user',
|
||||||
|
self::Female => 'heroicon-o-user',
|
||||||
|
self::Other => 'heroicon-o-users',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/Enums/ImportType.php
Normal file
71
app/Enums/ImportType.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum ImportType: string
|
||||||
|
{
|
||||||
|
case Employees = 'employees';
|
||||||
|
case Vacations = 'vacations';
|
||||||
|
case Bonuses = 'bonuses';
|
||||||
|
case Reports = 'reports';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Employees => 'Employees',
|
||||||
|
self::Vacations => 'Vacations',
|
||||||
|
self::Bonuses => 'Bonuses',
|
||||||
|
self::Reports => 'Disciplinary Reports',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public function fields(): array
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Employees => [
|
||||||
|
'employee_number' => 'Employee Number',
|
||||||
|
'full_name' => 'Full Name',
|
||||||
|
'national_id' => 'National ID',
|
||||||
|
'gender' => 'Gender',
|
||||||
|
'birth_date' => 'Birth Date',
|
||||||
|
'phone' => 'Phone',
|
||||||
|
'address' => 'Address',
|
||||||
|
'department' => 'Department',
|
||||||
|
'position' => 'Position',
|
||||||
|
'shift' => 'Shift',
|
||||||
|
'employment_status' => 'Employment Status',
|
||||||
|
'hire_date' => 'Hire Date',
|
||||||
|
'notes' => 'Notes',
|
||||||
|
],
|
||||||
|
self::Vacations => [
|
||||||
|
'employee_number' => 'Employee Number',
|
||||||
|
'type' => 'Type',
|
||||||
|
'start_date' => 'Start Date',
|
||||||
|
'end_date' => 'End Date',
|
||||||
|
'return_date' => 'Return Date',
|
||||||
|
'days' => 'Days',
|
||||||
|
'status' => 'Status',
|
||||||
|
'reason' => 'Reason',
|
||||||
|
],
|
||||||
|
self::Bonuses => [
|
||||||
|
'employee_number' => 'Employee Number',
|
||||||
|
'bonus_date' => 'Bonus Date',
|
||||||
|
'bonus_type' => 'Bonus Type',
|
||||||
|
'amount' => 'Amount',
|
||||||
|
'reason' => 'Reason',
|
||||||
|
],
|
||||||
|
self::Reports => [
|
||||||
|
'employee_number' => 'Employee Number',
|
||||||
|
'report_date' => 'Report Date',
|
||||||
|
'title' => 'Title',
|
||||||
|
'description' => 'Description',
|
||||||
|
'severity' => 'Severity',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
43
app/Enums/VacationType.php
Normal file
43
app/Enums/VacationType.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum VacationType: string
|
||||||
|
{
|
||||||
|
case Annual = 'annual';
|
||||||
|
case Marriage = 'marriage';
|
||||||
|
case Study = 'study';
|
||||||
|
case Other = 'other';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Annual => 'Annual',
|
||||||
|
self::Marriage => 'Marriage',
|
||||||
|
self::Study => 'Study',
|
||||||
|
self::Other => 'Other',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Annual => 'primary',
|
||||||
|
self::Marriage => 'pink',
|
||||||
|
self::Study => 'info',
|
||||||
|
self::Other => 'gray',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Annual => 'heroicon-o-sun',
|
||||||
|
self::Marriage => 'heroicon-o-heart',
|
||||||
|
self::Study => 'heroicon-o-academic-cap',
|
||||||
|
self::Other => 'heroicon-o-calendar-days',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
63
app/Exports/BonusesExport.php
Normal file
63
app/Exports/BonusesExport.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Bonus;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
|
||||||
|
class BonusesExport implements FromQuery, WithHeadings, WithMapping
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, int>|null $ids
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly ?array $ids = null,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function query(): Builder
|
||||||
|
{
|
||||||
|
$query = Bonus::query()->with('employee');
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$query->whereIn('id', $this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderByDesc('bonus_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Employee Number',
|
||||||
|
'Employee Name',
|
||||||
|
'Bonus Date',
|
||||||
|
'Bonus Type',
|
||||||
|
'Amount',
|
||||||
|
'Reason',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Bonus $bonus
|
||||||
|
* @return array<int, mixed>
|
||||||
|
*/
|
||||||
|
public function map($bonus): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$bonus->employee?->employee_number,
|
||||||
|
$bonus->employee?->full_name,
|
||||||
|
$bonus->bonus_date?->toDateString(),
|
||||||
|
$bonus->bonus_type?->value,
|
||||||
|
$bonus->amount,
|
||||||
|
$bonus->reason,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
80
app/Exports/EmployeesExport.php
Normal file
80
app/Exports/EmployeesExport.php
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Employee;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
|
||||||
|
class EmployeesExport implements FromQuery, WithHeadings, WithMapping
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, int>|null $ids
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly ?array $ids = null,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function query(): Builder
|
||||||
|
{
|
||||||
|
$query = Employee::query()
|
||||||
|
->with(['department', 'position', 'shift']);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$query->whereIn('id', $this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderBy('employee_number');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Employee Number',
|
||||||
|
'Full Name',
|
||||||
|
'National ID',
|
||||||
|
'Gender',
|
||||||
|
'Birth Date',
|
||||||
|
'Phone',
|
||||||
|
'Address',
|
||||||
|
'Department',
|
||||||
|
'Position',
|
||||||
|
'Shift',
|
||||||
|
'Employment Status',
|
||||||
|
'Hire Date',
|
||||||
|
'Termination Date',
|
||||||
|
'Notes',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Employee $employee
|
||||||
|
* @return array<int, mixed>
|
||||||
|
*/
|
||||||
|
public function map($employee): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$employee->employee_number,
|
||||||
|
$employee->full_name,
|
||||||
|
$employee->national_id,
|
||||||
|
$employee->gender?->value,
|
||||||
|
$employee->birth_date?->toDateString(),
|
||||||
|
$employee->phone,
|
||||||
|
$employee->address,
|
||||||
|
$employee->department?->name,
|
||||||
|
$employee->position?->name,
|
||||||
|
$employee->shift?->name,
|
||||||
|
$employee->employment_status?->value,
|
||||||
|
$employee->hire_date?->toDateString(),
|
||||||
|
$employee->termination_date?->toDateString(),
|
||||||
|
$employee->notes,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
65
app/Exports/ReportsExport.php
Normal file
65
app/Exports/ReportsExport.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\DisciplinaryReport;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
|
||||||
|
class ReportsExport implements FromQuery, WithHeadings, WithMapping
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, int>|null $ids
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly ?array $ids = null,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function query(): Builder
|
||||||
|
{
|
||||||
|
$query = DisciplinaryReport::query()->with(['employee', 'creator']);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$query->whereIn('id', $this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderByDesc('report_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Employee Number',
|
||||||
|
'Employee Name',
|
||||||
|
'Report Date',
|
||||||
|
'Title',
|
||||||
|
'Description',
|
||||||
|
'Severity',
|
||||||
|
'Created By',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DisciplinaryReport $report
|
||||||
|
* @return array<int, mixed>
|
||||||
|
*/
|
||||||
|
public function map($report): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$report->employee?->employee_number,
|
||||||
|
$report->employee?->full_name,
|
||||||
|
$report->report_date?->toDateString(),
|
||||||
|
$report->title,
|
||||||
|
$report->description,
|
||||||
|
$report->severity?->value,
|
||||||
|
$report->creator?->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
73
app/Exports/VacationsExport.php
Normal file
73
app/Exports/VacationsExport.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Vacation;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
|
||||||
|
class VacationsExport implements FromQuery, WithHeadings, WithMapping
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, int>|null $ids
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly ?array $ids = null,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function query(): Builder
|
||||||
|
{
|
||||||
|
$query = Vacation::query()->with(['employee', 'approver']);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$query->whereIn('id', $this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderByDesc('start_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Employee Number',
|
||||||
|
'Employee Name',
|
||||||
|
'Type',
|
||||||
|
'Start Date',
|
||||||
|
'End Date',
|
||||||
|
'Return Date',
|
||||||
|
'Days',
|
||||||
|
'Status',
|
||||||
|
'Reason',
|
||||||
|
'Approved By',
|
||||||
|
'Approved At',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Vacation $vacation
|
||||||
|
* @return array<int, mixed>
|
||||||
|
*/
|
||||||
|
public function map($vacation): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$vacation->employee?->employee_number,
|
||||||
|
$vacation->employee?->full_name,
|
||||||
|
$vacation->type?->value,
|
||||||
|
$vacation->start_date?->toDateString(),
|
||||||
|
$vacation->end_date?->toDateString(),
|
||||||
|
$vacation->return_date?->toDateString(),
|
||||||
|
$vacation->days,
|
||||||
|
$vacation->status?->value,
|
||||||
|
$vacation->reason,
|
||||||
|
$vacation->approver?->name,
|
||||||
|
$vacation->approved_at?->toDateTimeString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Filament/Pages/Dashboard.php
Normal file
42
app/Filament/Pages/Dashboard.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Widgets\DepartmentDistributionChart;
|
||||||
|
use App\Filament\Widgets\HrStatsOverviewWidget;
|
||||||
|
use App\Filament\Widgets\MonthlyHiringChart;
|
||||||
|
use App\Filament\Widgets\MonthlyLeaveChart;
|
||||||
|
use App\Filament\Widgets\RecentReportsWidget;
|
||||||
|
use App\Filament\Widgets\UpcomingBirthdaysWidget;
|
||||||
|
use App\Filament\Widgets\UpcomingLeaveWidget;
|
||||||
|
use Filament\Pages\Dashboard as BaseDashboard;
|
||||||
|
|
||||||
|
class Dashboard extends BaseDashboard
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<class-string>
|
||||||
|
*/
|
||||||
|
public function getWidgets(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
HrStatsOverviewWidget::class,
|
||||||
|
RecentReportsWidget::class,
|
||||||
|
UpcomingBirthdaysWidget::class,
|
||||||
|
UpcomingLeaveWidget::class,
|
||||||
|
MonthlyHiringChart::class,
|
||||||
|
MonthlyLeaveChart::class,
|
||||||
|
DepartmentDistributionChart::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumns(): int | array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'default' => 1,
|
||||||
|
'md' => 2,
|
||||||
|
'xl' => 3,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
332
app/Filament/Pages/ImportWizard.php
Normal file
332
app/Filament/Pages/ImportWizard.php
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Pages;
|
||||||
|
|
||||||
|
use App\Enums\ImportType;
|
||||||
|
use App\Jobs\ProcessImportJob;
|
||||||
|
use App\Services\Import\ImportService;
|
||||||
|
use BackedEnum;
|
||||||
|
use Filament\Forms\Components\FileUpload;
|
||||||
|
use Filament\Forms\Components\Placeholder;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Pages\Page;
|
||||||
|
use Filament\Schemas\Components\Actions;
|
||||||
|
use Filament\Schemas\Components\EmbeddedSchema;
|
||||||
|
use Filament\Schemas\Components\Form;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Components\Wizard;
|
||||||
|
use Filament\Schemas\Components\Wizard\Step;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Illuminate\Contracts\Support\Htmlable;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\HtmlString;
|
||||||
|
use UnitEnum;
|
||||||
|
|
||||||
|
class ImportWizard extends Page
|
||||||
|
{
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedArrowUpTray;
|
||||||
|
|
||||||
|
protected static string|UnitEnum|null $navigationGroup = 'System';
|
||||||
|
|
||||||
|
protected static ?string $navigationLabel = 'Import Wizard';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 2;
|
||||||
|
|
||||||
|
protected static ?string $title = 'Import Wizard';
|
||||||
|
|
||||||
|
protected static ?string $slug = 'import-wizard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>|null
|
||||||
|
*/
|
||||||
|
public ?array $data = [];
|
||||||
|
|
||||||
|
/** @var array<int, string> */
|
||||||
|
public array $headers = [];
|
||||||
|
|
||||||
|
public ?string $storedFilePath = null;
|
||||||
|
|
||||||
|
/** @var array<string, mixed>|null */
|
||||||
|
public ?array $importResult = null;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->form->fill([
|
||||||
|
'import_type' => ImportType::Employees->value,
|
||||||
|
'column_mapping' => [],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->refreshImportResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defaultForm(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->statePath('data')
|
||||||
|
->columns(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema->components([
|
||||||
|
Wizard::make([
|
||||||
|
Step::make('Upload')
|
||||||
|
->description('Select import type and upload an Excel file')
|
||||||
|
->schema([
|
||||||
|
Select::make('import_type')
|
||||||
|
->label('Import Type')
|
||||||
|
->options(ImportType::class)
|
||||||
|
->required()
|
||||||
|
->live()
|
||||||
|
->afterStateUpdated(fn () => $this->resetColumnMapping()),
|
||||||
|
FileUpload::make('file')
|
||||||
|
->label('Excel File')
|
||||||
|
->disk(config('hr.file_uploads.disk'))
|
||||||
|
->directory(config('hr.file_uploads.directory') . '/imports')
|
||||||
|
->acceptedFileTypes([
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
'application/vnd.ms-excel',
|
||||||
|
'text/csv',
|
||||||
|
])
|
||||||
|
->required()
|
||||||
|
->afterStateUpdated(fn () => $this->loadHeaders()),
|
||||||
|
]),
|
||||||
|
Step::make('Map Columns')
|
||||||
|
->description('Match spreadsheet columns to system fields')
|
||||||
|
->schema([
|
||||||
|
Section::make('Column Mapping')
|
||||||
|
->schema(fn (): array => $this->getColumnMappingFields()),
|
||||||
|
]),
|
||||||
|
Step::make('Preview')
|
||||||
|
->description('Review the first 10 rows with validation')
|
||||||
|
->schema([
|
||||||
|
Placeholder::make('preview_table')
|
||||||
|
->label('Preview')
|
||||||
|
->content(fn (): HtmlString => new HtmlString($this->getPreviewHtml())),
|
||||||
|
]),
|
||||||
|
Step::make('Import')
|
||||||
|
->description('Run the import and review results')
|
||||||
|
->schema([
|
||||||
|
Placeholder::make('import_results')
|
||||||
|
->label('Import Results')
|
||||||
|
->content(fn (): HtmlString => new HtmlString($this->getResultsHtml())),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->alpineSubmitHandler('$wire.runImport()')
|
||||||
|
->submitAction(new HtmlString('<button type="submit" class="fi-btn fi-btn-primary">Start Import</button>'))
|
||||||
|
->skippable(false),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema->components([
|
||||||
|
Form::make([EmbeddedSchema::make('form')])
|
||||||
|
->id('import-wizard-form')
|
||||||
|
->livewireSubmitHandler('runImport')
|
||||||
|
->footer([
|
||||||
|
Actions::make([])
|
||||||
|
->alignment(Alignment::Start)
|
||||||
|
->key('import-wizard-actions'),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runImport(): void
|
||||||
|
{
|
||||||
|
$this->form->validate();
|
||||||
|
|
||||||
|
$filePath = $this->resolveStoredFilePath();
|
||||||
|
|
||||||
|
if ($filePath === null) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Upload required')
|
||||||
|
->body('Please upload an Excel file before importing.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$importType = ImportType::from($this->data['import_type']);
|
||||||
|
$columnMapping = $this->data['column_mapping'] ?? [];
|
||||||
|
|
||||||
|
ProcessImportJob::dispatch(
|
||||||
|
$importType,
|
||||||
|
$filePath,
|
||||||
|
$columnMapping,
|
||||||
|
Auth::id(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Import queued')
|
||||||
|
->body('Your import has been queued and will be processed shortly.')
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshImportResult(): void
|
||||||
|
{
|
||||||
|
$result = ProcessImportJob::getCachedResult(Auth::id());
|
||||||
|
$this->importResult = $result?->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadHeaders(): void
|
||||||
|
{
|
||||||
|
$path = $this->resolveStoredFilePath();
|
||||||
|
|
||||||
|
if ($path === null) {
|
||||||
|
$this->headers = [];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->headers = app(ImportService::class)->readHeaders($path);
|
||||||
|
$this->resetColumnMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resetColumnMapping(): void
|
||||||
|
{
|
||||||
|
$this->data['column_mapping'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, Select>
|
||||||
|
*/
|
||||||
|
protected function getColumnMappingFields(): array
|
||||||
|
{
|
||||||
|
if ($this->headers === []) {
|
||||||
|
return [
|
||||||
|
Placeholder::make('mapping_hint')
|
||||||
|
->content('Upload a file in step 1 to load column headers.'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$importType = ImportType::from($this->data['import_type'] ?? ImportType::Employees->value);
|
||||||
|
$headerOptions = array_combine($this->headers, $this->headers);
|
||||||
|
|
||||||
|
return collect($importType->fields())
|
||||||
|
->map(function (string $label, string $field) use ($headerOptions): Select {
|
||||||
|
return Select::make("column_mapping.{$field}")
|
||||||
|
->label($label)
|
||||||
|
->options($headerOptions)
|
||||||
|
->searchable()
|
||||||
|
->nullable();
|
||||||
|
})
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPreviewHtml(): string
|
||||||
|
{
|
||||||
|
$path = $this->resolveStoredFilePath();
|
||||||
|
|
||||||
|
if ($path === null) {
|
||||||
|
return '<p class="text-sm text-gray-500">Upload a file to preview rows.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$importType = ImportType::from($this->data['import_type'] ?? ImportType::Employees->value);
|
||||||
|
$importService = app(ImportService::class);
|
||||||
|
$rows = $importService->readRows($path, 10);
|
||||||
|
$mappedRows = $importService->mapRows($rows, $this->data['column_mapping'] ?? []);
|
||||||
|
$validation = $importService->validatePreview($importType, $mappedRows);
|
||||||
|
|
||||||
|
if ($mappedRows === []) {
|
||||||
|
return '<p class="text-sm text-gray-500">No rows found in the uploaded file.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = array_keys($importType->fields());
|
||||||
|
$html = '<div class="overflow-x-auto"><table class="fi-ta-table w-full text-sm"><thead><tr>';
|
||||||
|
$html .= '<th class="px-3 py-2 text-left">Row</th>';
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$html .= '<th class="px-3 py-2 text-left">' . e($importType->fields()[$field]) . '</th>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '<th class="px-3 py-2 text-left">Validation</th></tr></thead><tbody>';
|
||||||
|
|
||||||
|
foreach ($mappedRows as $index => $row) {
|
||||||
|
$errors = $validation[$index]['errors'] ?? [];
|
||||||
|
$html .= '<tr class="border-t border-gray-200 dark:border-gray-700">';
|
||||||
|
$html .= '<td class="px-3 py-2">' . ($index + 2) . '</td>';
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$html .= '<td class="px-3 py-2">' . e((string) ($row[$field] ?? '—')) . '</td>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '<td class="px-3 py-2">';
|
||||||
|
|
||||||
|
if ($errors === []) {
|
||||||
|
$html .= '<span class="text-success-600">Valid</span>';
|
||||||
|
} else {
|
||||||
|
$html .= '<span class="text-danger-600">' . e(implode(' ', $errors)) . '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</td></tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</tbody></table></div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getResultsHtml(): string
|
||||||
|
{
|
||||||
|
$this->refreshImportResult();
|
||||||
|
|
||||||
|
if ($this->importResult === null) {
|
||||||
|
return '<p class="text-sm text-gray-500">Submit the import to see results here after processing completes.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = '<div class="space-y-2 text-sm">';
|
||||||
|
$html .= '<p><strong>Total rows:</strong> ' . $this->importResult['total'] . '</p>';
|
||||||
|
$html .= '<p><strong>Imported:</strong> ' . $this->importResult['imported'] . '</p>';
|
||||||
|
$html .= '<p><strong>Skipped (duplicates):</strong> ' . $this->importResult['skipped'] . '</p>';
|
||||||
|
$html .= '<p><strong>Failed:</strong> ' . $this->importResult['failed'] . '</p>';
|
||||||
|
|
||||||
|
if (($this->importResult['errors'] ?? []) !== []) {
|
||||||
|
$html .= '<div class="mt-3"><strong>Errors:</strong><ul class="list-disc ps-5">';
|
||||||
|
|
||||||
|
foreach ($this->importResult['errors'] as $error) {
|
||||||
|
$html .= '<li>' . e($error) . '</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</ul></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolveStoredFilePath(): ?string
|
||||||
|
{
|
||||||
|
$file = $this->data['file'] ?? null;
|
||||||
|
|
||||||
|
if ($file === null || $file === []) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relativePath = is_array($file) ? ($file[0] ?? null) : $file;
|
||||||
|
|
||||||
|
if ($relativePath === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$disk = Storage::disk(config('hr.file_uploads.disk'));
|
||||||
|
$this->storedFilePath = $disk->path($relativePath);
|
||||||
|
|
||||||
|
return $this->storedFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string|Htmlable
|
||||||
|
{
|
||||||
|
return 'Import Wizard';
|
||||||
|
}
|
||||||
|
}
|
||||||
89
app/Filament/Resources/ActivityLogs/ActivityLogResource.php
Normal file
89
app/Filament/Resources/ActivityLogs/ActivityLogResource.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\ActivityLogs;
|
||||||
|
|
||||||
|
use App\Filament\Resources\ActivityLogs\Pages\ListActivityLogs;
|
||||||
|
use App\Filament\Resources\ActivityLogs\Pages\ViewActivityLog;
|
||||||
|
use App\Filament\Resources\ActivityLogs\Schemas\ActivityLogInfolist;
|
||||||
|
use App\Filament\Resources\ActivityLogs\Tables\ActivityLogsTable;
|
||||||
|
use BackedEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
use UnitEnum;
|
||||||
|
|
||||||
|
class ActivityLogResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Activity::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedClipboardDocumentList;
|
||||||
|
|
||||||
|
protected static string|UnitEnum|null $navigationGroup = 'System';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 3;
|
||||||
|
|
||||||
|
protected static ?string $navigationLabel = 'Audit Log';
|
||||||
|
|
||||||
|
protected static ?string $modelLabel = 'Activity Log';
|
||||||
|
|
||||||
|
protected static ?string $pluralModelLabel = 'Activity Logs';
|
||||||
|
|
||||||
|
protected static ?string $slug = 'activity-logs';
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return ActivityLogInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return ActivityLogsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListActivityLogs::route('/'),
|
||||||
|
'view' => ViewActivityLog::route('/{record}'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canCreate(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canEdit($record): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canDelete($record): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canForceDelete($record): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canRestore($record): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\ActivityLogs\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\ActivityLogs\ActivityLogResource;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListActivityLogs extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = ActivityLogResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\ActivityLogs\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\ActivityLogs\ActivityLogResource;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewActivityLog extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ActivityLogResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\ActivityLogs\Schemas;
|
||||||
|
|
||||||
|
use Filament\Infolists\Components\KeyValueEntry;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class ActivityLogInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Section::make('Activity Details')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->label('Date')
|
||||||
|
->dateTime(),
|
||||||
|
TextEntry::make('description'),
|
||||||
|
TextEntry::make('event')
|
||||||
|
->badge(),
|
||||||
|
TextEntry::make('log_name')
|
||||||
|
->label('Log Name'),
|
||||||
|
TextEntry::make('subject_type')
|
||||||
|
->label('Subject Type')
|
||||||
|
->formatStateUsing(fn (?string $state): string => $state ? class_basename($state) : '—'),
|
||||||
|
TextEntry::make('subject_id')
|
||||||
|
->label('Subject ID'),
|
||||||
|
TextEntry::make('causer.name')
|
||||||
|
->label('Causer'),
|
||||||
|
KeyValueEntry::make('properties.attributes')
|
||||||
|
->label('Changes')
|
||||||
|
->visible(fn ($record): bool => filled($record->properties['attributes'] ?? null)),
|
||||||
|
KeyValueEntry::make('properties.old')
|
||||||
|
->label('Previous Values')
|
||||||
|
->visible(fn ($record): bool => filled($record->properties['old'] ?? null)),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
100
app/Filament/Resources/ActivityLogs/Tables/ActivityLogsTable.php
Normal file
100
app/Filament/Resources/ActivityLogs/Tables/ActivityLogsTable.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\ActivityLogs\Tables;
|
||||||
|
|
||||||
|
use App\Models\Bonus;
|
||||||
|
use App\Models\Department;
|
||||||
|
use App\Models\DisciplinaryReport;
|
||||||
|
use App\Models\Employee;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Vacation;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\Filter;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class ActivityLogsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->label('Date')
|
||||||
|
->dateTime()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('description')
|
||||||
|
->searchable()
|
||||||
|
->limit(50),
|
||||||
|
TextColumn::make('event')
|
||||||
|
->badge()
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('subject_type')
|
||||||
|
->label('Subject Type')
|
||||||
|
->formatStateUsing(fn (?string $state): string => $state ? class_basename($state) : '—')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('subject_id')
|
||||||
|
->label('Subject ID')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('causer.name')
|
||||||
|
->label('Causer')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('log_name')
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->defaultSort('created_at', 'desc')
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('subject_type')
|
||||||
|
->label('Subject Type')
|
||||||
|
->options([
|
||||||
|
Employee::class => 'Employee',
|
||||||
|
Vacation::class => 'Vacation',
|
||||||
|
Bonus::class => 'Bonus',
|
||||||
|
DisciplinaryReport::class => 'Disciplinary Report',
|
||||||
|
Department::class => 'Department',
|
||||||
|
User::class => 'User',
|
||||||
|
]),
|
||||||
|
SelectFilter::make('event')
|
||||||
|
->options(fn (): array => \Spatie\Activitylog\Models\Activity::query()
|
||||||
|
->whereNotNull('event')
|
||||||
|
->distinct()
|
||||||
|
->pluck('event', 'event')
|
||||||
|
->all()),
|
||||||
|
SelectFilter::make('causer')
|
||||||
|
->label('Causer')
|
||||||
|
->relationship('causer', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
Filter::make('created_at')
|
||||||
|
->schema([
|
||||||
|
DatePicker::make('from')
|
||||||
|
->label('From'),
|
||||||
|
DatePicker::make('until')
|
||||||
|
->label('Until'),
|
||||||
|
])
|
||||||
|
->query(function (Builder $query, array $data): Builder {
|
||||||
|
return $query
|
||||||
|
->when(
|
||||||
|
$data['from'] ?? null,
|
||||||
|
fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '>=', $date),
|
||||||
|
)
|
||||||
|
->when(
|
||||||
|
$data['until'] ?? null,
|
||||||
|
fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '<=', $date),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Filament/Resources/Bonuses/BonusResource.php
Normal file
77
app/Filament/Resources/Bonuses/BonusResource.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Bonuses\Pages\CreateBonus;
|
||||||
|
use App\Filament\Resources\Bonuses\Pages\EditBonus;
|
||||||
|
use App\Filament\Resources\Bonuses\Pages\ListBonuses;
|
||||||
|
use App\Filament\Resources\Bonuses\Pages\ViewBonus;
|
||||||
|
use App\Filament\Resources\Bonuses\Schemas\BonusForm;
|
||||||
|
use App\Filament\Resources\Bonuses\Schemas\BonusInfolist;
|
||||||
|
use App\Filament\Resources\Bonuses\Tables\BonusesTable;
|
||||||
|
use App\Models\Bonus;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class BonusResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Bonus::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCurrencyDollar;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Records';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 3;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'reason';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return BonusForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return BonusInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return BonusesTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListBonuses::route('/'),
|
||||||
|
'create' => CreateBonus::route('/create'),
|
||||||
|
'view' => ViewBonus::route('/{record}'),
|
||||||
|
'edit' => EditBonus::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Filament/Resources/Bonuses/Pages/CreateBonus.php
Normal file
11
app/Filament/Resources/Bonuses/Pages/CreateBonus.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Bonuses\BonusResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateBonus extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = BonusResource::class;
|
||||||
|
}
|
||||||
25
app/Filament/Resources/Bonuses/Pages/EditBonus.php
Normal file
25
app/Filament/Resources/Bonuses/Pages/EditBonus.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Bonuses\BonusResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditBonus extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = BonusResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/Filament/Resources/Bonuses/Pages/ListBonuses.php
Normal file
24
app/Filament/Resources/Bonuses/Pages/ListBonuses.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Pages;
|
||||||
|
|
||||||
|
use App\Exports\BonusesExport;
|
||||||
|
use App\Filament\Resources\Bonuses\BonusResource;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListBonuses extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = BonusResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ExportExcelAction::make('export', BonusesExport::class, 'bonuses.xlsx'),
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Bonuses/Pages/ViewBonus.php
Normal file
19
app/Filament/Resources/Bonuses/Pages/ViewBonus.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Bonuses\BonusResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewBonus extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = BonusResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Filament/Resources/Bonuses/Schemas/BonusForm.php
Normal file
38
app/Filament/Resources/Bonuses/Schemas/BonusForm.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Schemas;
|
||||||
|
|
||||||
|
use App\Enums\BonusType;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class BonusForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::employeeSelect(),
|
||||||
|
DatePicker::make('bonus_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
Select::make('bonus_type')
|
||||||
|
->options(BonusType::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
TextInput::make('amount')
|
||||||
|
->required()
|
||||||
|
->numeric()
|
||||||
|
->prefix('TMT')
|
||||||
|
->minValue(0)
|
||||||
|
->step(0.01),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Filament/Resources/Bonuses/Schemas/BonusInfolist.php
Normal file
37
app/Filament/Resources/Bonuses/Schemas/BonusInfolist.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Schemas;
|
||||||
|
|
||||||
|
use App\Models\Bonus;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class BonusInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('employee.id')
|
||||||
|
->label('Employee'),
|
||||||
|
TextEntry::make('bonus_date')
|
||||||
|
->date(),
|
||||||
|
TextEntry::make('bonus_type')
|
||||||
|
->badge(),
|
||||||
|
TextEntry::make('amount')
|
||||||
|
->numeric(),
|
||||||
|
TextEntry::make('reason')
|
||||||
|
->placeholder('-')
|
||||||
|
->columnSpanFull(),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (Bonus $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
app/Filament/Resources/Bonuses/Tables/BonusesTable.php
Normal file
76
app/Filament/Resources/Bonuses/Tables/BonusesTable.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Bonuses\Tables;
|
||||||
|
|
||||||
|
use App\Enums\BonusType;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class BonusesTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee.full_name')
|
||||||
|
->label('Employee')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('bonus_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('bonus_type')
|
||||||
|
->label('Type')
|
||||||
|
->badge()
|
||||||
|
->color(fn (BonusType $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (BonusType $state): string => $state->label())
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('amount')
|
||||||
|
->money('TMT')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->limit(40)
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('bonus_type')
|
||||||
|
->label('Type')
|
||||||
|
->options(BonusType::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
ExportExcelAction::bulk('exportSelected', \App\Exports\BonusesExport::class, 'bonuses.xlsx'),
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Filament/Resources/Departments/DepartmentResource.php
Normal file
77
app/Filament/Resources/Departments/DepartmentResource.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Departments\Pages\CreateDepartment;
|
||||||
|
use App\Filament\Resources\Departments\Pages\EditDepartment;
|
||||||
|
use App\Filament\Resources\Departments\Pages\ListDepartments;
|
||||||
|
use App\Filament\Resources\Departments\Pages\ViewDepartment;
|
||||||
|
use App\Filament\Resources\Departments\Schemas\DepartmentForm;
|
||||||
|
use App\Filament\Resources\Departments\Schemas\DepartmentInfolist;
|
||||||
|
use App\Filament\Resources\Departments\Tables\DepartmentsTable;
|
||||||
|
use App\Models\Department;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class DepartmentResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Department::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBuildingOffice2;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Organization';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 1;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'name';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return DepartmentForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return DepartmentInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return DepartmentsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListDepartments::route('/'),
|
||||||
|
'create' => CreateDepartment::route('/create'),
|
||||||
|
'view' => ViewDepartment::route('/{record}'),
|
||||||
|
'edit' => EditDepartment::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Departments\DepartmentResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateDepartment extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DepartmentResource::class;
|
||||||
|
}
|
||||||
25
app/Filament/Resources/Departments/Pages/EditDepartment.php
Normal file
25
app/Filament/Resources/Departments/Pages/EditDepartment.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Departments\DepartmentResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditDepartment extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DepartmentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Departments/Pages/ListDepartments.php
Normal file
19
app/Filament/Resources/Departments/Pages/ListDepartments.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Departments\DepartmentResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListDepartments extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = DepartmentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Departments/Pages/ViewDepartment.php
Normal file
19
app/Filament/Resources/Departments/Pages/ViewDepartment.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Departments\DepartmentResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewDepartment extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DepartmentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Schemas;
|
||||||
|
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Forms\Components\Toggle;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class DepartmentForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextInput::make('name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
TextInput::make('code')
|
||||||
|
->required()
|
||||||
|
->maxLength(50)
|
||||||
|
->unique(ignoreRecord: true),
|
||||||
|
Textarea::make('description')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
Toggle::make('is_active')
|
||||||
|
->label('Active')
|
||||||
|
->default(true)
|
||||||
|
->required(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Schemas;
|
||||||
|
|
||||||
|
use App\Models\Department;
|
||||||
|
use Filament\Infolists\Components\IconEntry;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class DepartmentInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('name'),
|
||||||
|
TextEntry::make('code'),
|
||||||
|
TextEntry::make('description')
|
||||||
|
->placeholder('-')
|
||||||
|
->columnSpanFull(),
|
||||||
|
IconEntry::make('is_active')
|
||||||
|
->boolean(),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (Department $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Departments\Tables;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TernaryFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class DepartmentsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('name')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('code')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('description')
|
||||||
|
->limit(50)
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('is_active')
|
||||||
|
->label('Status')
|
||||||
|
->badge()
|
||||||
|
->formatStateUsing(fn (bool $state): string => $state ? 'Active' : 'Inactive')
|
||||||
|
->color(fn (bool $state): string => $state ? 'success' : 'danger')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TernaryFilter::make('is_active')
|
||||||
|
->label('Status')
|
||||||
|
->placeholder('All departments')
|
||||||
|
->trueLabel('Active')
|
||||||
|
->falseLabel('Inactive'),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Pages\CreateDisciplinaryReport;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Pages\EditDisciplinaryReport;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Pages\ListDisciplinaryReports;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Pages\ViewDisciplinaryReport;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Schemas\DisciplinaryReportForm;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Schemas\DisciplinaryReportInfolist;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\Tables\DisciplinaryReportsTable;
|
||||||
|
use App\Models\DisciplinaryReport;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class DisciplinaryReportResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = DisciplinaryReport::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedExclamationTriangle;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Records';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 1;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'title';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return DisciplinaryReportForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return DisciplinaryReportInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return DisciplinaryReportsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListDisciplinaryReports::route('/'),
|
||||||
|
'create' => CreateDisciplinaryReport::route('/create'),
|
||||||
|
'view' => ViewDisciplinaryReport::route('/{record}'),
|
||||||
|
'edit' => EditDisciplinaryReport::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\DisciplinaryReportResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateDisciplinaryReport extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DisciplinaryReportResource::class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\DisciplinaryReportResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditDisciplinaryReport extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DisciplinaryReportResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Pages;
|
||||||
|
|
||||||
|
use App\Exports\ReportsExport;
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\DisciplinaryReportResource;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListDisciplinaryReports extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = DisciplinaryReportResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ExportExcelAction::make('export', ReportsExport::class, 'disciplinary-reports.xlsx'),
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DisciplinaryReports\DisciplinaryReportResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewDisciplinaryReport extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DisciplinaryReportResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Schemas;
|
||||||
|
|
||||||
|
use App\Enums\DisciplinarySeverity;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class DisciplinaryReportForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::employeeSelect(),
|
||||||
|
DatePicker::make('report_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('title')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('description')
|
||||||
|
->required()
|
||||||
|
->rows(4)
|
||||||
|
->columnSpanFull(),
|
||||||
|
Select::make('severity')
|
||||||
|
->options(DisciplinarySeverity::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
HrForm::fileUpload('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Schemas;
|
||||||
|
|
||||||
|
use App\Models\DisciplinaryReport;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class DisciplinaryReportInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('employee.id')
|
||||||
|
->label('Employee'),
|
||||||
|
TextEntry::make('report_date')
|
||||||
|
->date(),
|
||||||
|
TextEntry::make('title'),
|
||||||
|
TextEntry::make('description')
|
||||||
|
->columnSpanFull(),
|
||||||
|
TextEntry::make('severity')
|
||||||
|
->badge(),
|
||||||
|
TextEntry::make('attachment_path')
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('created_by')
|
||||||
|
->numeric()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (DisciplinaryReport $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DisciplinaryReports\Tables;
|
||||||
|
|
||||||
|
use App\Enums\DisciplinarySeverity;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class DisciplinaryReportsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee.full_name')
|
||||||
|
->label('Employee')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('report_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('title')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('severity')
|
||||||
|
->badge()
|
||||||
|
->color(fn (DisciplinarySeverity $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (DisciplinarySeverity $state): string => $state->label())
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
IconColumn::make('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->boolean()
|
||||||
|
->trueIcon('heroicon-o-paper-clip')
|
||||||
|
->falseIcon('heroicon-o-minus'),
|
||||||
|
TextColumn::make('creator.name')
|
||||||
|
->label('Created By')
|
||||||
|
->sortable()
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('severity')
|
||||||
|
->options(DisciplinarySeverity::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
ExportExcelAction::bulk('exportSelected', \App\Exports\ReportsExport::class, 'disciplinary-reports.xlsx'),
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments;
|
||||||
|
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Pages\CreateEmployeeDocument;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Pages\EditEmployeeDocument;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Pages\ListEmployeeDocuments;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Pages\ViewEmployeeDocument;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Schemas\EmployeeDocumentForm;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Schemas\EmployeeDocumentInfolist;
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\Tables\EmployeeDocumentsTable;
|
||||||
|
use App\Models\EmployeeDocument;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class EmployeeDocumentResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = EmployeeDocument::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocument;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Records';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 5;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'title';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return EmployeeDocumentForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return EmployeeDocumentInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return EmployeeDocumentsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListEmployeeDocuments::route('/'),
|
||||||
|
'create' => CreateEmployeeDocument::route('/create'),
|
||||||
|
'view' => ViewEmployeeDocument::route('/{record}'),
|
||||||
|
'edit' => EditEmployeeDocument::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\EmployeeDocumentResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateEmployeeDocument extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeDocumentResource::class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\EmployeeDocumentResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditEmployeeDocument extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeDocumentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\EmployeeDocumentResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListEmployeeDocuments extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeDocumentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\EmployeeDocuments\EmployeeDocumentResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewEmployeeDocument extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeDocumentResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Schemas;
|
||||||
|
|
||||||
|
use App\Enums\DocumentType;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class EmployeeDocumentForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::employeeSelect(),
|
||||||
|
Select::make('document_type')
|
||||||
|
->options(DocumentType::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
TextInput::make('title')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
HrForm::fileUpload('file_path')
|
||||||
|
->label('Document')
|
||||||
|
->required()
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'])
|
||||||
|
->maxSize(15360),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Schemas;
|
||||||
|
|
||||||
|
use App\Models\EmployeeDocument;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class EmployeeDocumentInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('employee.id')
|
||||||
|
->label('Employee'),
|
||||||
|
TextEntry::make('document_type')
|
||||||
|
->badge(),
|
||||||
|
TextEntry::make('title'),
|
||||||
|
TextEntry::make('file_path'),
|
||||||
|
TextEntry::make('uploaded_at')
|
||||||
|
->dateTime(),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (EmployeeDocument $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\EmployeeDocuments\Tables;
|
||||||
|
|
||||||
|
use App\Enums\DocumentType;
|
||||||
|
use App\Models\EmployeeDocument;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class EmployeeDocumentsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee.full_name')
|
||||||
|
->label('Employee')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('document_type')
|
||||||
|
->label('Type')
|
||||||
|
->badge()
|
||||||
|
->color(fn (DocumentType $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (DocumentType $state): string => $state->label())
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('title')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('uploaded_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('document_type')
|
||||||
|
->label('Type')
|
||||||
|
->options(DocumentType::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
Action::make('download')
|
||||||
|
->label('Download')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->visible(fn (EmployeeDocument $record): bool => filled($record->file_path))
|
||||||
|
->action(function (EmployeeDocument $record) {
|
||||||
|
$disk = Storage::disk(config('hr.file_uploads.disk'));
|
||||||
|
|
||||||
|
return response()->download(
|
||||||
|
$disk->path($record->file_path),
|
||||||
|
basename($record->file_path),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
120
app/Filament/Resources/Employees/EmployeeResource.php
Normal file
120
app/Filament/Resources/Employees/EmployeeResource.php
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Employees\Pages\CreateEmployee;
|
||||||
|
use App\Filament\Resources\Employees\Pages\EditEmployee;
|
||||||
|
use App\Filament\Resources\Employees\Pages\ListEmployees;
|
||||||
|
use App\Filament\Resources\Employees\Pages\ViewEmployee;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\BonusesRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\DisciplinaryReportsRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\DocumentsRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\ExplanationsRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\GiftsRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\SickLeavesRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\UnpaidLeavesRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\RelationManagers\VacationsRelationManager;
|
||||||
|
use App\Filament\Resources\Employees\Schemas\EmployeeForm;
|
||||||
|
use App\Filament\Resources\Employees\Schemas\EmployeeInfolist;
|
||||||
|
use App\Filament\Resources\Employees\Tables\EmployeesTable;
|
||||||
|
use App\Models\Employee;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class EmployeeResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Employee::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Employees';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 1;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'full_name';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = true;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return EmployeeForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return EmployeeInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return EmployeesTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function getGloballySearchableAttributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'employee_number',
|
||||||
|
'full_name',
|
||||||
|
'phone',
|
||||||
|
'department.name',
|
||||||
|
'position.name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public static function getGlobalSearchResultDetails(Model $record): array
|
||||||
|
{
|
||||||
|
/** @var Employee $record */
|
||||||
|
return [
|
||||||
|
'Employee #' => $record->employee_number,
|
||||||
|
'Department' => $record->department?->name ?? '—',
|
||||||
|
'Position' => $record->position?->name ?? '—',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
VacationsRelationManager::class,
|
||||||
|
SickLeavesRelationManager::class,
|
||||||
|
UnpaidLeavesRelationManager::class,
|
||||||
|
DisciplinaryReportsRelationManager::class,
|
||||||
|
ExplanationsRelationManager::class,
|
||||||
|
BonusesRelationManager::class,
|
||||||
|
GiftsRelationManager::class,
|
||||||
|
DocumentsRelationManager::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListEmployees::route('/'),
|
||||||
|
'create' => CreateEmployee::route('/create'),
|
||||||
|
'view' => ViewEmployee::route('/{record}'),
|
||||||
|
'edit' => EditEmployee::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Filament/Resources/Employees/Pages/CreateEmployee.php
Normal file
11
app/Filament/Resources/Employees/Pages/CreateEmployee.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Employees\EmployeeResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateEmployee extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeResource::class;
|
||||||
|
}
|
||||||
25
app/Filament/Resources/Employees/Pages/EditEmployee.php
Normal file
25
app/Filament/Resources/Employees/Pages/EditEmployee.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Employees\EmployeeResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditEmployee extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/Filament/Resources/Employees/Pages/ListEmployees.php
Normal file
26
app/Filament/Resources/Employees/Pages/ListEmployees.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Pages;
|
||||||
|
|
||||||
|
use App\Exports\EmployeesExport;
|
||||||
|
use App\Filament\Resources\Employees\EmployeeResource;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use App\Filament\Support\ExportPdfAction;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListEmployees extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ExportExcelAction::make('export', EmployeesExport::class, 'employees.xlsx'),
|
||||||
|
ExportPdfAction::employeeList(),
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Filament/Resources/Employees/Pages/ViewEmployee.php
Normal file
47
app/Filament/Resources/Employees/Pages/ViewEmployee.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Employees\EmployeeResource;
|
||||||
|
use App\Filament\Resources\Employees\Widgets\EmployeeStatsWidget;
|
||||||
|
use App\Filament\Support\ExportPdfAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewEmployee extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = EmployeeResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ExportPdfAction::employeeProfile(),
|
||||||
|
ExportPdfAction::employeeLeaveSummary(),
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<class-string>
|
||||||
|
*/
|
||||||
|
protected function getHeaderWidgets(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EmployeeStatsWidget::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int | array<string, ?int>
|
||||||
|
*/
|
||||||
|
public function getHeaderWidgetsColumns(): int | array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'default' => 1,
|
||||||
|
'sm' => 2,
|
||||||
|
'xl' => 4,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Enums\BonusType;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class BonusesRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'bonuses';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Bonuses';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
DatePicker::make('bonus_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
Select::make('bonus_type')
|
||||||
|
->options(BonusType::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
TextInput::make('amount')
|
||||||
|
->required()
|
||||||
|
->numeric()
|
||||||
|
->prefix('TMT')
|
||||||
|
->minValue(0)
|
||||||
|
->step(0.01),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('bonus_date')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('bonus_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('bonus_type')
|
||||||
|
->label('Type')
|
||||||
|
->badge()
|
||||||
|
->color(fn (BonusType $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (BonusType $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('amount')
|
||||||
|
->money('TMT')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->limit(40)
|
||||||
|
->toggleable(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('bonus_type')
|
||||||
|
->label('Type')
|
||||||
|
->options(BonusType::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Enums\DisciplinarySeverity;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class DisciplinaryReportsRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'disciplinaryReports';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Disciplinary Reports';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
DatePicker::make('report_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('title')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('description')
|
||||||
|
->required()
|
||||||
|
->rows(4)
|
||||||
|
->columnSpanFull(),
|
||||||
|
Select::make('severity')
|
||||||
|
->options(DisciplinarySeverity::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
HrForm::fileUpload('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('title')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('report_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('title')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('severity')
|
||||||
|
->badge()
|
||||||
|
->color(fn (DisciplinarySeverity $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (DisciplinarySeverity $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
IconColumn::make('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->boolean()
|
||||||
|
->trueIcon('heroicon-o-paper-clip')
|
||||||
|
->falseIcon('heroicon-o-minus'),
|
||||||
|
TextColumn::make('creator.name')
|
||||||
|
->label('Created By')
|
||||||
|
->toggleable(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('severity')
|
||||||
|
->options(DisciplinarySeverity::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Enums\DocumentType;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use App\Models\EmployeeDocument;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class DocumentsRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'documents';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Employee Documents';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Select::make('document_type')
|
||||||
|
->options(DocumentType::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
TextInput::make('title')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
HrForm::fileUpload('file_path')
|
||||||
|
->label('Document')
|
||||||
|
->required()
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'])
|
||||||
|
->maxSize(15360),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('title')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('document_type')
|
||||||
|
->label('Type')
|
||||||
|
->badge()
|
||||||
|
->color(fn (DocumentType $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (DocumentType $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('title')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('uploaded_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('document_type')
|
||||||
|
->label('Type')
|
||||||
|
->options(DocumentType::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
Action::make('download')
|
||||||
|
->label('Download')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->visible(fn (EmployeeDocument $record): bool => filled($record->file_path))
|
||||||
|
->action(function (EmployeeDocument $record) {
|
||||||
|
$disk = Storage::disk(config('hr.file_uploads.disk'));
|
||||||
|
|
||||||
|
return response()->download(
|
||||||
|
$disk->path($record->file_path),
|
||||||
|
basename($record->file_path),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class ExplanationsRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'explanations';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Explanations';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
DatePicker::make('explanation_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('reason')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('description')
|
||||||
|
->required()
|
||||||
|
->rows(4)
|
||||||
|
->columnSpanFull(),
|
||||||
|
HrForm::fileUpload('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('reason')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('explanation_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->searchable()
|
||||||
|
->limit(40),
|
||||||
|
TextColumn::make('description')
|
||||||
|
->limit(50)
|
||||||
|
->toggleable(),
|
||||||
|
IconColumn::make('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->boolean()
|
||||||
|
->trueIcon('heroicon-o-paper-clip')
|
||||||
|
->falseIcon('heroicon-o-minus'),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class GiftsRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'gifts';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Gifts';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
DatePicker::make('gift_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('gift_name')
|
||||||
|
->label('Gift Name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
TextInput::make('value')
|
||||||
|
->required()
|
||||||
|
->numeric()
|
||||||
|
->prefix('TMT')
|
||||||
|
->minValue(0)
|
||||||
|
->step(0.01)
|
||||||
|
->default(0),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('gift_name')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('gift_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('gift_name')
|
||||||
|
->label('Gift')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('value')
|
||||||
|
->money('TMT')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->limit(40)
|
||||||
|
->toggleable(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class SickLeavesRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'sickLeaves';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Sick Leaves';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::leaveDatePicker('start_date', 'Start Date'),
|
||||||
|
HrForm::leaveDatePicker('end_date', 'End Date'),
|
||||||
|
HrForm::leaveDaysDisplay(),
|
||||||
|
TextInput::make('medical_institution')
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
HrForm::fileUpload('document_path')
|
||||||
|
->label('Medical Document')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('start_date')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('start_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('end_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('days')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('medical_institution')
|
||||||
|
->searchable()
|
||||||
|
->toggleable(),
|
||||||
|
IconColumn::make('document_path')
|
||||||
|
->label('Document')
|
||||||
|
->boolean()
|
||||||
|
->trueIcon('heroicon-o-document-check')
|
||||||
|
->falseIcon('heroicon-o-document'),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use App\Models\UnpaidLeave;
|
||||||
|
use App\Services\Leave\UnpaidLeaveService;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class UnpaidLeavesRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'unpaidLeaves';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Unpaid Leaves';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::leaveDatePicker('start_date', 'Start Date'),
|
||||||
|
HrForm::leaveDatePicker('end_date', 'End Date'),
|
||||||
|
HrForm::leaveDaysDisplay(),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('start_date')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('start_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('end_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('days')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('status')
|
||||||
|
->badge()
|
||||||
|
->color(fn (ApprovalStatus $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (ApprovalStatus $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('approver.name')
|
||||||
|
->label('Approved By')
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('status')
|
||||||
|
->options(ApprovalStatus::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
Action::make('approve')
|
||||||
|
->label('Approve')
|
||||||
|
->icon('heroicon-o-check-badge')
|
||||||
|
->color('success')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->visible(fn (UnpaidLeave $record): bool => $record->status === ApprovalStatus::Pending)
|
||||||
|
->action(function (UnpaidLeave $record, UnpaidLeaveService $unpaidLeaveService): void {
|
||||||
|
$unpaidLeaveService->approve($record, Auth::user());
|
||||||
|
}),
|
||||||
|
Action::make('reject')
|
||||||
|
->label('Reject')
|
||||||
|
->icon('heroicon-o-x-mark')
|
||||||
|
->color('danger')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->visible(fn (UnpaidLeave $record): bool => $record->status === ApprovalStatus::Pending)
|
||||||
|
->action(function (UnpaidLeave $record, UnpaidLeaveService $unpaidLeaveService): void {
|
||||||
|
$unpaidLeaveService->reject($record, Auth::user());
|
||||||
|
}),
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\RelationManagers;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\VacationType;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use App\Models\Vacation;
|
||||||
|
use App\Services\Vacation\VacationService;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class VacationsRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'vacations';
|
||||||
|
|
||||||
|
protected static ?string $title = 'Vacations';
|
||||||
|
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Select::make('type')
|
||||||
|
->options(VacationType::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
HrForm::leaveDatePicker('start_date', 'Start Date'),
|
||||||
|
HrForm::leaveDatePicker('end_date', 'End Date'),
|
||||||
|
DatePicker::make('return_date')
|
||||||
|
->label('Return Date'),
|
||||||
|
HrForm::leaveDaysDisplay(),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
HrForm::fileUpload('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->recordTitleAttribute('start_date')
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('type')
|
||||||
|
->badge()
|
||||||
|
->color(fn (VacationType $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (VacationType $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('start_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('end_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('days')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('status')
|
||||||
|
->badge()
|
||||||
|
->color(fn (ApprovalStatus $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (ApprovalStatus $state): string => $state->label())
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('approver.name')
|
||||||
|
->label('Approved By')
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('status')
|
||||||
|
->options(ApprovalStatus::class),
|
||||||
|
SelectFilter::make('type')
|
||||||
|
->options(VacationType::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
CreateAction::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
Action::make('approve')
|
||||||
|
->label('Approve')
|
||||||
|
->icon('heroicon-o-check-badge')
|
||||||
|
->color('success')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->visible(fn (Vacation $record): bool => $record->status === ApprovalStatus::Pending)
|
||||||
|
->action(function (Vacation $record, VacationService $vacationService): void {
|
||||||
|
$vacationService->approve($record, Auth::user());
|
||||||
|
}),
|
||||||
|
Action::make('reject')
|
||||||
|
->label('Reject')
|
||||||
|
->icon('heroicon-o-x-mark')
|
||||||
|
->color('danger')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->visible(fn (Vacation $record): bool => $record->status === ApprovalStatus::Pending)
|
||||||
|
->action(function (Vacation $record, VacationService $vacationService): void {
|
||||||
|
$vacationService->reject($record, Auth::user());
|
||||||
|
}),
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
app/Filament/Resources/Employees/Schemas/EmployeeForm.php
Normal file
90
app/Filament/Resources/Employees/Schemas/EmployeeForm.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Schemas;
|
||||||
|
|
||||||
|
use App\Enums\EmploymentStatus;
|
||||||
|
use App\Enums\Gender;
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Components\Tabs;
|
||||||
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class EmployeeForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Tabs::make('Employee')
|
||||||
|
->tabs([
|
||||||
|
Tab::make('Personal')
|
||||||
|
->schema([
|
||||||
|
HrForm::fileUpload('profile_photo_path')
|
||||||
|
->label('Photo')
|
||||||
|
->image()
|
||||||
|
->avatar()
|
||||||
|
->imageEditor(),
|
||||||
|
TextInput::make('employee_number')
|
||||||
|
->required()
|
||||||
|
->maxLength(50)
|
||||||
|
->unique(ignoreRecord: true),
|
||||||
|
TextInput::make('full_name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
TextInput::make('national_id')
|
||||||
|
->label('National ID')
|
||||||
|
->maxLength(50),
|
||||||
|
Select::make('gender')
|
||||||
|
->options(Gender::class)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
DatePicker::make('birth_date')
|
||||||
|
->required(),
|
||||||
|
TextInput::make('phone')
|
||||||
|
->tel()
|
||||||
|
->default(config('hr.default_phone'))
|
||||||
|
->required(),
|
||||||
|
Textarea::make('address')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
Tab::make('Employment')
|
||||||
|
->schema([
|
||||||
|
Select::make('department_id')
|
||||||
|
->relationship('department', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload()
|
||||||
|
->required(),
|
||||||
|
Select::make('position_id')
|
||||||
|
->relationship('position', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload()
|
||||||
|
->required(),
|
||||||
|
Select::make('shift_id')
|
||||||
|
->relationship('shift', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload()
|
||||||
|
->required(),
|
||||||
|
Select::make('employment_status')
|
||||||
|
->options(EmploymentStatus::class)
|
||||||
|
->default(EmploymentStatus::Active)
|
||||||
|
->required()
|
||||||
|
->native(false),
|
||||||
|
DatePicker::make('hire_date')
|
||||||
|
->required(),
|
||||||
|
DatePicker::make('termination_date'),
|
||||||
|
Textarea::make('notes')
|
||||||
|
->rows(4)
|
||||||
|
->columnSpanFull(),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
])
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
204
app/Filament/Resources/Employees/Schemas/EmployeeInfolist.php
Normal file
204
app/Filament/Resources/Employees/Schemas/EmployeeInfolist.php
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Schemas;
|
||||||
|
|
||||||
|
use App\Enums\EmploymentStatus;
|
||||||
|
use App\Enums\Gender;
|
||||||
|
use App\Models\Employee;
|
||||||
|
use App\Models\Vacation;
|
||||||
|
use App\Services\Employee\EmployeeStatisticsService;
|
||||||
|
use Filament\Infolists\Components\ImageEntry;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Components\Tabs;
|
||||||
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class EmployeeInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Tabs::make('Employee Details')
|
||||||
|
->tabs([
|
||||||
|
Tab::make('Overview')
|
||||||
|
->schema([
|
||||||
|
Section::make('Profile')
|
||||||
|
->schema([
|
||||||
|
ImageEntry::make('profile_photo_path')
|
||||||
|
->label('Photo')
|
||||||
|
->disk(config('hr.file_uploads.disk'))
|
||||||
|
->visibility('private')
|
||||||
|
->circular()
|
||||||
|
->height(120)
|
||||||
|
->placeholder('—')
|
||||||
|
->columnSpanFull(),
|
||||||
|
TextEntry::make('employee_number')
|
||||||
|
->label('Employee #'),
|
||||||
|
TextEntry::make('full_name'),
|
||||||
|
TextEntry::make('national_id')
|
||||||
|
->label('National ID')
|
||||||
|
->placeholder('—'),
|
||||||
|
TextEntry::make('gender')
|
||||||
|
->badge()
|
||||||
|
->color(fn (Gender $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (Gender $state): string => $state->label()),
|
||||||
|
TextEntry::make('birth_date')
|
||||||
|
->date(),
|
||||||
|
TextEntry::make('phone'),
|
||||||
|
TextEntry::make('address')
|
||||||
|
->placeholder('—')
|
||||||
|
->columnSpanFull(),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
Section::make('Organization')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('department.name')
|
||||||
|
->label('Department')
|
||||||
|
->badge()
|
||||||
|
->color('info'),
|
||||||
|
TextEntry::make('position.name')
|
||||||
|
->label('Position')
|
||||||
|
->badge()
|
||||||
|
->color('primary'),
|
||||||
|
TextEntry::make('shift.name')
|
||||||
|
->label('Shift')
|
||||||
|
->badge()
|
||||||
|
->color('gray'),
|
||||||
|
TextEntry::make('employment_status')
|
||||||
|
->badge()
|
||||||
|
->color(fn (EmploymentStatus $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (EmploymentStatus $state): string => $state->label())
|
||||||
|
->icon(fn (EmploymentStatus $state): string => $state->icon()),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
Section::make('Employment Timeline')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('hire_date')
|
||||||
|
->label('Hire Date')
|
||||||
|
->date()
|
||||||
|
->icon('heroicon-o-calendar'),
|
||||||
|
TextEntry::make('termination_date')
|
||||||
|
->label('Termination Date')
|
||||||
|
->date()
|
||||||
|
->placeholder('—')
|
||||||
|
->icon('heroicon-o-calendar-days'),
|
||||||
|
TextEntry::make('tenure')
|
||||||
|
->label('Tenure')
|
||||||
|
->state(fn (Employee $record): string => $record->hire_date
|
||||||
|
? $record->hire_date->diffForHumans(now(), true)
|
||||||
|
: '—'),
|
||||||
|
TextEntry::make('notes')
|
||||||
|
->placeholder('—')
|
||||||
|
->columnSpanFull(),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
]),
|
||||||
|
Tab::make('Statistics')
|
||||||
|
->schema([
|
||||||
|
Section::make('Leave & Attendance')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('stats_vacation_taken')
|
||||||
|
->label('Vacation Taken (days)')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->vacationTaken)
|
||||||
|
->numeric()
|
||||||
|
->icon('heroicon-o-sun'),
|
||||||
|
TextEntry::make('stats_vacation_remaining')
|
||||||
|
->label('Vacation Remaining (days)')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->vacationRemaining)
|
||||||
|
->numeric()
|
||||||
|
->color('success')
|
||||||
|
->icon('heroicon-o-calendar'),
|
||||||
|
TextEntry::make('stats_sick_leave_count')
|
||||||
|
->label('Sick Leave Records')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->sickLeaveCount)
|
||||||
|
->numeric()
|
||||||
|
->icon('heroicon-o-heart'),
|
||||||
|
])
|
||||||
|
->columns(3),
|
||||||
|
Section::make('HR Records')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('stats_reports_count')
|
||||||
|
->label('Disciplinary Reports')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->reportsCount)
|
||||||
|
->numeric()
|
||||||
|
->icon('heroicon-o-exclamation-triangle'),
|
||||||
|
TextEntry::make('stats_bonuses_total')
|
||||||
|
->label('Total Bonuses')
|
||||||
|
->state(fn (Employee $record): float => app(EmployeeStatisticsService::class)->summary($record)->bonusesTotal)
|
||||||
|
->money('TMT')
|
||||||
|
->icon('heroicon-o-banknotes'),
|
||||||
|
TextEntry::make('stats_gifts_count')
|
||||||
|
->label('Gifts Received')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->giftsCount)
|
||||||
|
->numeric()
|
||||||
|
->icon('heroicon-o-gift'),
|
||||||
|
])
|
||||||
|
->columns(3),
|
||||||
|
]),
|
||||||
|
Tab::make('Leave Summary')
|
||||||
|
->schema([
|
||||||
|
Section::make('Annual Leave Balance')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('leave_balance_taken')
|
||||||
|
->label('Days Taken')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->vacationTaken)
|
||||||
|
->suffix(' days'),
|
||||||
|
TextEntry::make('leave_balance_remaining')
|
||||||
|
->label('Days Remaining')
|
||||||
|
->state(fn (Employee $record): int => app(EmployeeStatisticsService::class)->summary($record)->vacationRemaining)
|
||||||
|
->suffix(' days')
|
||||||
|
->color('success'),
|
||||||
|
TextEntry::make('leave_balance_total')
|
||||||
|
->label('Annual Allowance')
|
||||||
|
->state(fn (): int => (int) config('hr.annual_leave_days'))
|
||||||
|
->suffix(' days'),
|
||||||
|
])
|
||||||
|
->columns(3),
|
||||||
|
Section::make('Upcoming Approved Leave')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('upcoming_leave')
|
||||||
|
->label('Scheduled Vacations')
|
||||||
|
->state(function (Employee $record): string {
|
||||||
|
$upcoming = app(EmployeeStatisticsService::class)
|
||||||
|
->summary($record)
|
||||||
|
->upcomingLeave;
|
||||||
|
|
||||||
|
if ($upcoming->isEmpty()) {
|
||||||
|
return 'No upcoming approved leave';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $upcoming
|
||||||
|
->map(fn (Vacation $vacation): string => sprintf(
|
||||||
|
'%s: %s – %s (%d days)',
|
||||||
|
$vacation->type->label(),
|
||||||
|
$vacation->start_date->format('M j, Y'),
|
||||||
|
$vacation->end_date->format('M j, Y'),
|
||||||
|
$vacation->days,
|
||||||
|
))
|
||||||
|
->implode("\n");
|
||||||
|
})
|
||||||
|
->listWithLineBreaks()
|
||||||
|
->columnSpanFull(),
|
||||||
|
]),
|
||||||
|
Section::make('Other Leave Types')
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('unpaid_leaves_count')
|
||||||
|
->label('Unpaid Leave Records')
|
||||||
|
->state(fn (Employee $record): int => $record->unpaidLeaves()->count())
|
||||||
|
->numeric(),
|
||||||
|
TextEntry::make('explanations_count')
|
||||||
|
->label('Explanations')
|
||||||
|
->state(fn (Employee $record): int => $record->explanations()->count())
|
||||||
|
->numeric(),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
146
app/Filament/Resources/Employees/Tables/EmployeesTable.php
Normal file
146
app/Filament/Resources/Employees/Tables/EmployeesTable.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Tables;
|
||||||
|
|
||||||
|
use App\Enums\EmploymentStatus;
|
||||||
|
use App\Enums\Gender;
|
||||||
|
use App\Models\Department;
|
||||||
|
use App\Models\Employee;
|
||||||
|
use App\Filament\Support\ExportExcelAction;
|
||||||
|
use Filament\Actions\BulkAction;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
|
class EmployeesTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee_number')
|
||||||
|
->label('Number')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('full_name')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('national_id')
|
||||||
|
->label('National ID')
|
||||||
|
->searchable()
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('gender')
|
||||||
|
->badge()
|
||||||
|
->color(fn (Gender $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (Gender $state): string => $state->label())
|
||||||
|
->searchable(),
|
||||||
|
TextColumn::make('phone')
|
||||||
|
->searchable()
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('department.name')
|
||||||
|
->label('Department')
|
||||||
|
->badge()
|
||||||
|
->color('info')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('position.name')
|
||||||
|
->label('Position')
|
||||||
|
->badge()
|
||||||
|
->color('primary')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('shift.name')
|
||||||
|
->label('Shift')
|
||||||
|
->badge()
|
||||||
|
->color('gray')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('employment_status')
|
||||||
|
->label('Status')
|
||||||
|
->badge()
|
||||||
|
->color(fn (EmploymentStatus $state): string => $state->color())
|
||||||
|
->formatStateUsing(fn (EmploymentStatus $state): string => $state->label())
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('hire_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('termination_date')
|
||||||
|
->date()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
SelectFilter::make('department_id')
|
||||||
|
->label('Department')
|
||||||
|
->relationship('department', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
SelectFilter::make('position_id')
|
||||||
|
->label('Position')
|
||||||
|
->relationship('position', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
SelectFilter::make('shift_id')
|
||||||
|
->label('Shift')
|
||||||
|
->relationship('shift', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
SelectFilter::make('employment_status')
|
||||||
|
->label('Employment Status')
|
||||||
|
->options(EmploymentStatus::class),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
ExportExcelAction::bulk('exportSelected', \App\Exports\EmployeesExport::class, 'employees.xlsx'),
|
||||||
|
BulkAction::make('changeDepartment')
|
||||||
|
->label('Change Department')
|
||||||
|
->icon('heroicon-o-building-office-2')
|
||||||
|
->schema([
|
||||||
|
Select::make('department_id')
|
||||||
|
->label('Department')
|
||||||
|
->options(fn (): array => Department::query()->orderBy('name')->pluck('name', 'id')->all())
|
||||||
|
->searchable()
|
||||||
|
->required(),
|
||||||
|
])
|
||||||
|
->action(function (Collection $records, array $data): void {
|
||||||
|
$records->each(
|
||||||
|
fn (Employee $employee) => $employee->update([
|
||||||
|
'department_id' => $data['department_id'],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
->deselectRecordsAfterCompletion(),
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Employees\Widgets;
|
||||||
|
|
||||||
|
use App\Models\Employee;
|
||||||
|
use App\Services\Employee\EmployeeStatisticsService;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||||
|
|
||||||
|
class EmployeeStatsWidget extends StatsOverviewWidget
|
||||||
|
{
|
||||||
|
public ?Employee $record = null;
|
||||||
|
|
||||||
|
protected function getStats(): array
|
||||||
|
{
|
||||||
|
if (! $this->record instanceof Employee) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = app(EmployeeStatisticsService::class)->summary($this->record);
|
||||||
|
|
||||||
|
return [
|
||||||
|
Stat::make('Vacation Taken', (string) $summary->vacationTaken)
|
||||||
|
->description("{$summary->vacationRemaining} days remaining")
|
||||||
|
->descriptionIcon('heroicon-o-calendar')
|
||||||
|
->color('primary')
|
||||||
|
->icon('heroicon-o-sun'),
|
||||||
|
Stat::make('Sick Leaves', (string) $summary->sickLeaveCount)
|
||||||
|
->description('Total records')
|
||||||
|
->descriptionIcon('heroicon-o-heart')
|
||||||
|
->color('warning')
|
||||||
|
->icon('heroicon-o-heart'),
|
||||||
|
Stat::make('Disciplinary Reports', (string) $summary->reportsCount)
|
||||||
|
->description('On file')
|
||||||
|
->descriptionIcon('heroicon-o-exclamation-triangle')
|
||||||
|
->color('danger')
|
||||||
|
->icon('heroicon-o-exclamation-triangle'),
|
||||||
|
Stat::make('Total Bonuses', number_format($summary->bonusesTotal, 2).' TMT')
|
||||||
|
->description("{$summary->giftsCount} gifts received")
|
||||||
|
->descriptionIcon('heroicon-o-gift')
|
||||||
|
->color('success')
|
||||||
|
->icon('heroicon-o-banknotes'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Filament/Resources/Explanations/ExplanationResource.php
Normal file
77
app/Filament/Resources/Explanations/ExplanationResource.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Explanations\Pages\CreateExplanation;
|
||||||
|
use App\Filament\Resources\Explanations\Pages\EditExplanation;
|
||||||
|
use App\Filament\Resources\Explanations\Pages\ListExplanations;
|
||||||
|
use App\Filament\Resources\Explanations\Pages\ViewExplanation;
|
||||||
|
use App\Filament\Resources\Explanations\Schemas\ExplanationForm;
|
||||||
|
use App\Filament\Resources\Explanations\Schemas\ExplanationInfolist;
|
||||||
|
use App\Filament\Resources\Explanations\Tables\ExplanationsTable;
|
||||||
|
use App\Models\Explanation;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class ExplanationResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Explanation::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocumentText;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Records';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 2;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'reason';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return ExplanationForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return ExplanationInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return ExplanationsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListExplanations::route('/'),
|
||||||
|
'create' => CreateExplanation::route('/create'),
|
||||||
|
'view' => ViewExplanation::route('/{record}'),
|
||||||
|
'edit' => EditExplanation::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Explanations\ExplanationResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateExplanation extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ExplanationResource::class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Explanations\ExplanationResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditExplanation extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ExplanationResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Explanations\ExplanationResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListExplanations extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = ExplanationResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Explanations\ExplanationResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewExplanation extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ExplanationResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Schemas;
|
||||||
|
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class ExplanationForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::employeeSelect(),
|
||||||
|
DatePicker::make('explanation_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('reason')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('description')
|
||||||
|
->required()
|
||||||
|
->rows(4)
|
||||||
|
->columnSpanFull(),
|
||||||
|
HrForm::fileUpload('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->acceptedFileTypes(['application/pdf', 'image/*'])
|
||||||
|
->maxSize(10240),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Schemas;
|
||||||
|
|
||||||
|
use App\Models\Explanation;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class ExplanationInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('employee.id')
|
||||||
|
->label('Employee'),
|
||||||
|
TextEntry::make('explanation_date')
|
||||||
|
->date(),
|
||||||
|
TextEntry::make('reason'),
|
||||||
|
TextEntry::make('description')
|
||||||
|
->columnSpanFull(),
|
||||||
|
TextEntry::make('attachment_path')
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (Explanation $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Explanations\Tables;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class ExplanationsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee.full_name')
|
||||||
|
->label('Employee')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('explanation_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->searchable()
|
||||||
|
->limit(40),
|
||||||
|
TextColumn::make('description')
|
||||||
|
->limit(50)
|
||||||
|
->toggleable(),
|
||||||
|
IconColumn::make('attachment_path')
|
||||||
|
->label('Attachment')
|
||||||
|
->boolean()
|
||||||
|
->trueIcon('heroicon-o-paper-clip')
|
||||||
|
->falseIcon('heroicon-o-minus'),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Filament/Resources/Gifts/GiftResource.php
Normal file
77
app/Filament/Resources/Gifts/GiftResource.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Gifts\Pages\CreateGift;
|
||||||
|
use App\Filament\Resources\Gifts\Pages\EditGift;
|
||||||
|
use App\Filament\Resources\Gifts\Pages\ListGifts;
|
||||||
|
use App\Filament\Resources\Gifts\Pages\ViewGift;
|
||||||
|
use App\Filament\Resources\Gifts\Schemas\GiftForm;
|
||||||
|
use App\Filament\Resources\Gifts\Schemas\GiftInfolist;
|
||||||
|
use App\Filament\Resources\Gifts\Tables\GiftsTable;
|
||||||
|
use App\Models\Gift;
|
||||||
|
use BackedEnum;
|
||||||
|
use UnitEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||||
|
|
||||||
|
class GiftResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Gift::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedGift;
|
||||||
|
|
||||||
|
protected static string | UnitEnum | null $navigationGroup = 'Records';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 4;
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'gift_name';
|
||||||
|
|
||||||
|
protected static bool $isGloballySearchable = false;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return GiftForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function infolist(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return GiftInfolist::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return GiftsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListGifts::route('/'),
|
||||||
|
'create' => CreateGift::route('/create'),
|
||||||
|
'view' => ViewGift::route('/{record}'),
|
||||||
|
'edit' => EditGift::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Filament/Resources/Gifts/Pages/CreateGift.php
Normal file
11
app/Filament/Resources/Gifts/Pages/CreateGift.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Gifts\GiftResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateGift extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = GiftResource::class;
|
||||||
|
}
|
||||||
25
app/Filament/Resources/Gifts/Pages/EditGift.php
Normal file
25
app/Filament/Resources/Gifts/Pages/EditGift.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Gifts\GiftResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditGift extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = GiftResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Gifts/Pages/ListGifts.php
Normal file
19
app/Filament/Resources/Gifts/Pages/ListGifts.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Gifts\GiftResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListGifts extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = GiftResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Gifts/Pages/ViewGift.php
Normal file
19
app/Filament/Resources/Gifts/Pages/ViewGift.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Gifts\GiftResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewGift extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = GiftResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Filament/Resources/Gifts/Schemas/GiftForm.php
Normal file
37
app/Filament/Resources/Gifts/Schemas/GiftForm.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Schemas;
|
||||||
|
|
||||||
|
use App\Filament\Support\HrForm;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class GiftForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
HrForm::employeeSelect(),
|
||||||
|
DatePicker::make('gift_date')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
TextInput::make('gift_name')
|
||||||
|
->label('Gift Name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
TextInput::make('value')
|
||||||
|
->required()
|
||||||
|
->numeric()
|
||||||
|
->prefix('TMT')
|
||||||
|
->minValue(0)
|
||||||
|
->step(0.01)
|
||||||
|
->default(0),
|
||||||
|
Textarea::make('reason')
|
||||||
|
->rows(3)
|
||||||
|
->columnSpanFull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/Filament/Resources/Gifts/Schemas/GiftInfolist.php
Normal file
36
app/Filament/Resources/Gifts/Schemas/GiftInfolist.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Schemas;
|
||||||
|
|
||||||
|
use App\Models\Gift;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class GiftInfolist
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
TextEntry::make('employee.id')
|
||||||
|
->label('Employee'),
|
||||||
|
TextEntry::make('gift_date')
|
||||||
|
->date(),
|
||||||
|
TextEntry::make('gift_name'),
|
||||||
|
TextEntry::make('value')
|
||||||
|
->numeric(),
|
||||||
|
TextEntry::make('reason')
|
||||||
|
->placeholder('-')
|
||||||
|
->columnSpanFull(),
|
||||||
|
TextEntry::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->placeholder('-'),
|
||||||
|
TextEntry::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->visible(fn (Gift $record): bool => $record->trashed()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Filament/Resources/Gifts/Tables/GiftsTable.php
Normal file
66
app/Filament/Resources/Gifts/Tables/GiftsTable.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Gifts\Tables;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteBulkAction;
|
||||||
|
use Filament\Actions\RestoreBulkAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class GiftsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('employee.full_name')
|
||||||
|
->label('Employee')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('gift_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('gift_name')
|
||||||
|
->label('Gift')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('value')
|
||||||
|
->money('TMT')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('reason')
|
||||||
|
->limit(40)
|
||||||
|
->toggleable(),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('deleted_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
ViewAction::make(),
|
||||||
|
EditAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
ForceDeleteBulkAction::make(),
|
||||||
|
RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Filament/Resources/Positions/Pages/CreatePosition.php
Normal file
11
app/Filament/Resources/Positions/Pages/CreatePosition.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Positions\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Positions\PositionResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreatePosition extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = PositionResource::class;
|
||||||
|
}
|
||||||
25
app/Filament/Resources/Positions/Pages/EditPosition.php
Normal file
25
app/Filament/Resources/Positions/Pages/EditPosition.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Positions\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Positions\PositionResource;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditPosition extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = PositionResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ViewAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Positions/Pages/ListPositions.php
Normal file
19
app/Filament/Resources/Positions/Pages/ListPositions.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Positions\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Positions\PositionResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListPositions extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = PositionResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Positions/Pages/ViewPosition.php
Normal file
19
app/Filament/Resources/Positions/Pages/ViewPosition.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Positions\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Positions\PositionResource;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
|
|
||||||
|
class ViewPosition extends ViewRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = PositionResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user