ifnex/04_Laravel/app/Filament/Resources/ShipmentChecklistResource.php
Kazem Alghasi b9b83ffecf feat(shipment): implement checklist templating and automated instantiation
Introduce a template-based system for shipment checklists to automate
the creation of required inspection items upon shipment approval.

- Add `ShipmentChecklistTemplate` model and migration to define reusable
  checklist structures.
- Extend `ShipmentChecklist` schema to support item titles, requirement
  flags, sorting, and attachment paths.
- Implement `instantiateChecklist` in `ShipmentReviewService` to
  automatically generate checklist items from active templates based on
  shipment direction (import/export).
- Refactor `ShipmentChecklistResource` and `ChecklistsRelationManager`
  in Filament to support the new data structure and improved UI.
- Update `ShipmentChecklist` model with helper methods for completion
  tracking and improved relationship handling.
2026-09-30 18:35:25 +03:30

197 lines
7.1 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Filament\Resources;
use App\Filament\Resources\ShipmentChecklistResource\Pages;
use App\Models\ShipmentChecklist;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Tables\Actions\Action;
/**
* چک‌لیست سفارشات — نمای گزارش (reporting).
* instance‌ها هنگام approve سفارش از قالب‌ها خودکار ساخته می‌شوند.
*/
class ShipmentChecklistResource extends Resource
{
protected static ?string $model = ShipmentChecklist::class;
protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-check';
protected static ?string $navigationGroup = 'عملیات';
protected static ?string $navigationLabel = 'چک‌لیست سفارشات';
protected static ?string $modelLabel = 'آیتم چک‌لیست';
protected static ?string $pluralModelLabel = 'چک‌لیست سفارشات';
protected static ?int $navigationSort = 2;
public static function form(Form $form): Form
{
return $form->schema([
Forms\Components\Section::make('اطلاعات آیتم')->schema([
Forms\Components\TextInput::make('item_title')
->label('عنوان آیتم')
->disabled(),
Forms\Components\Select::make('shipment_id')
->label('سفارش')
->relationship('shipment', 'awb_no')
->disabled(),
Forms\Components\Toggle::make('is_required')
->label('الزامی')
->disabled(),
Forms\Components\Toggle::make('is_completed')
->label('تکمیل شده')
->disabled(),
Forms\Components\Textarea::make('notes')
->label('یادداشت‌ها')
->rows(3)
->columnSpanFull(),
Forms\Components\TextInput::make('attachment')
->label('پیوست (مسیر فایل)')
->maxLength(255),
])->columns(2),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('shipment.awb_no')
->label('شماره سفارش')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('item_title')
->label('آیتم')
->searchable()
->weight('bold'),
Tables\Columns\IconColumn::make('is_required')
->label('الزامی')
->boolean()
->trueIcon('heroicon-o-exclamation-circle')
->falseIcon('heroicon-o-minus-circle')
->trueColor('danger')
->falseColor('gray'),
Tables\Columns\IconColumn::make('is_completed')
->label('تکمیل')
->boolean()
->trueIcon('heroicon-o-check-circle')
->falseIcon('heroicon-o-x-circle')
->trueColor('success')
->falseColor('danger'),
Tables\Columns\TextColumn::make('completedBy.name')
->label('انجام‌دهنده')
->toggleable(),
Tables\Columns\TextColumn::make('completed_at')
->label('تاریخ تکمیل')
->dateTime('Y/m/d H:i')
->sortable()
->toggleable(),
Tables\Columns\TextColumn::make('notes')
->label('یادداشت')
->limit(40)
->toggleable(),
])
->defaultSort('is_completed', 'asc') // ناقص‌ها اول
->filters([
Tables\Filters\TernaryFilter::make('is_completed')
->label('تکمیل')
->placeholder('همه')
->trueLabel('تکمیل‌شده')
->falseLabel('ناقص'),
Tables\Filters\TernaryFilter::make('is_required')
->label('الزامی')
->placeholder('همه')
->trueLabel('الزامی')
->falseLabel('اختیاری'),
])
->actions([
Action::make('complete')
->label('تکمیل')
->icon('heroicon-o-check-circle')
->color('success')
->visible(fn ($record) => !$record->is_completed)
->form([
Forms\Components\Textarea::make('note')
->label('یادداشت (اختیاری)')
->rows(2),
])
->action(function ($record, array $data) {
$record->complete(auth()->id(), $data['note'] ?? null);
Notification::make()->title('آیتم تکمیل شد')->success()->send();
}),
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make()
->visible(fn () => auth()->user()?->hasRole('super_admin')),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make()
->visible(fn () => auth()->user()?->hasRole('super_admin')),
]),
]);
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => Pages\ListShipmentChecklists::route('/'),
'view' => Pages\ViewShipmentChecklist::route('/{record}'),
'edit' => Pages\EditShipmentChecklist::route('/{record}/edit'),
];
}
public static function canViewAny(): bool
{
return auth()->user()?->hasAnyRole(['super_admin', 'admin', 'staff']) ?? false;
}
public static function canCreate(): bool
{
// instance‌ها خودکار از قالب ساخته می‌شوند
return false;
}
public static function canEdit($record): bool
{
return auth()->user()?->hasRole('super_admin') ?? false;
}
public static function canDelete($record): bool
{
return auth()->user()?->hasRole('super_admin') ?? false;
}
/**
* badge: تعداد آیتم‌های الزامیِ ناقص (نشانگر سفارش‌های مشکل‌دار).
*/
public static function getNavigationBadge(): ?string
{
$count = static::getModel()::where('is_completed', false)->where('is_required', true)->count();
return $count > 0 ? (string) $count : null;
}
public static function getNavigationBadgeColor(): ?string
{
return 'danger';
}
}