ifnex/04_Laravel/app/Filament/Resources/ShipmentResource/RelationManagers/CommitmentFormsRelationManager.php
Kazem Alghasi 0209b6b32e feat(commitment-forms): implement end-to-end management and secure file access
Integrate a complete workflow for commitment forms including secure template downloads, customer uploads via WordPress AJAX, and administrative verification with automated notifications.

- Implement `downloadTemplate` and `downloadSigned` logic in `CommitmentFormController` to replace public symlinks with ownership-verified routes.
- Add `CommitmentFormVerifiedNotification` and `CommitmentFormRejectedNotification` to alert customers of status changes.
- Enhance WordPress bridge with AJAX-driven form listing, status badges, and upload capabilities.
- Update Filament `CommitmentFormsRelationManager` to provide better visibility into form directions and descriptions while restricting status edits to specific actions.
- Refactor Laravel 11 bootstrap configuration to handle guest redirection for API and web routes correctly.
- Add administrative controllers for secure file access within the admin panel.
2026-10-03 05:58:59 +03:30

173 lines
8.3 KiB
PHP

<?php
namespace App\Filament\Resources\ShipmentResource\RelationManagers;
use App\Models\ShipmentCommitmentForm;
use App\Notifications\CommitmentFormRejectedNotification;
use App\Notifications\CommitmentFormVerifiedNotification;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
class CommitmentFormsRelationManager extends RelationManager
{
protected static string $relationship = 'commitmentForms';
protected static ?string $title = 'تعهدنامه‌ها';
protected static ?string $icon = 'heroicon-o-document-text';
public function form(Form $form): Form
{
// رکوردها فقط از آپلود مشتری یا مقداردهی خودکار ساخته می‌شوند؛
// از این فرم فقط یادداشت قابل ویرایش است (وضعیت با اکشن‌های تأیید/رد تغییر می‌کند)
return $form->schema([
Forms\Components\Textarea::make('notes')
->rows(3)
->label('یادداشت')
->maxLength(1000),
]);
}
public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('form.title')
->label('فرم تعهدنامه')
->description(fn ($record) => $record->form?->description)
->limit(40),
Tables\Columns\TextColumn::make('form.direction')
->label('جهت')
->badge()
->color('gray')
->formatStateUsing(fn ($state): string => match ($state) {
'export' => 'صادرات',
'import' => 'واردات',
'both' => 'هردو',
default => (string) $state,
})
->toggleable(),
Tables\Columns\TextColumn::make('status')
->label('وضعیت')
->badge()
->color(fn (string $state): string => match ($state) {
'pending' => 'warning',
'uploaded' => 'info',
'verified' => 'success',
'rejected' => 'danger',
default => 'gray',
})
->formatStateUsing(fn (string $state): string => match ($state) {
'pending' => 'در انتظار آپلود',
'uploaded' => 'آپلود شده',
'verified' => 'تأیید شده',
'rejected' => 'رد شده',
default => $state,
}),
Tables\Columns\TextColumn::make('uploaded_file_type')
->label('فایل مشتری')
->formatStateUsing(function ($state, $record) {
if (!$state) return '—';
$size = $record->uploaded_file_size ? ' · ' . number_format($record->uploaded_file_size / 1024, 0) . ' KB' : '';
return strtoupper($state) . $size;
}),
Tables\Columns\TextColumn::make('created_at')
->label('تاریخ آپلود')
->dateTime('Y/m/d H:i')
->toggleable(),
Tables\Columns\TextColumn::make('uploader.name')
->label('بارگذاری توسط')
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('verifier.name')
->label('تأییدکننده')
->toggleable(),
Tables\Columns\TextColumn::make('verified_at')
->label('تاریخ تأیید')
->dateTime('Y/m/d H:i')
->toggleable(),
Tables\Columns\TextColumn::make('notes')
->label('یادداشت / دلیل')
->limit(40)
->toggleable(),
])
->defaultSort('created_at', 'desc')
// ساخت دستی مجاز نیست — این رکوردها از آپلود تعهدنامه توسط مشتری ایجاد می‌شوند
->headerActions([])
->actions([
Tables\Actions\Action::make('downloadTemplate')
->label('قالب')
->icon('heroicon-o-arrow-down-tray')
->color('gray')
->url(fn ($record) => $record->form?->file_path
? route('admin.commitment-forms.template', ['shipment' => $record->shipment_id, 'form' => $record->commitment_form_id])
: null)
->openUrlInNewTab()
->visible(fn ($record) => !empty($record->form?->file_path))
->tooltip('دانلود قالب خام تعهدنامه'),
Tables\Actions\Action::make('downloadSigned')
->label('فایل مشتری')
->icon('heroicon-o-document-arrow-down')
->color('info')
->url(fn ($record) => $record->uploaded_file_path
? route('admin.commitment-forms.signed', ['shipment' => $record->shipment_id, 'record' => $record->id])
: null)
->openUrlInNewTab()
->visible(fn ($record) => !empty($record->uploaded_file_path))
->tooltip('دانلود فایل امضاشدهٔ آپلودشده توسط مشتری'),
Tables\Actions\Action::make('verify')
->label('تأیید')
->icon('heroicon-o-check-circle')
->color('success')
->requiresConfirmation()
->modalDescription('با تأیید، به مشتری اعلان ارسال می‌شود.')
->form([
Forms\Components\Textarea::make('notes')
->label('یادداشت برای مشتری (اختیاری)')
->rows(2)
->maxLength(1000),
])
->action(function (ShipmentCommitmentForm $record, array $data): void {
$record->update([
'status' => 'verified',
'notes' => $data['notes'] ?: $record->notes,
'verified_by' => auth()->id(),
'verified_at' => now(),
]);
$record->shipment->user?->notify(new CommitmentFormVerifiedNotification($record));
})
->visible(fn ($record) => in_array($record->status, ['uploaded', 'rejected'])),
Tables\Actions\Action::make('reject')
->label('رد')
->icon('heroicon-o-x-circle')
->color('danger')
->form([
Forms\Components\Textarea::make('notes')
->label('دلیل رد (برای مشتری نمایش داده می‌شود)')
->rows(2)
->required()
->maxLength(1000),
])
->action(function (ShipmentCommitmentForm $record, array $data): void {
$record->update([
'status' => 'rejected',
'notes' => $data['notes'],
'verified_by' => auth()->id(),
'verified_at' => now(),
]);
$record->shipment->user?->notify(new CommitmentFormRejectedNotification($record));
})
->visible(fn ($record) => in_array($record->status, ['uploaded', 'verified'])),
Tables\Actions\EditAction::make()
->label('یادداشت')
->icon('heroicon-o-pencil-square')
->tooltip('ویرایش یادداشت'),
])
->bulkActions([]);
}
}