transferDepartmentAction(), $this->closeTicketAction(), DeleteAction::make(), ]; } protected function transferDepartmentAction(): Action { return Action::make('transferDepartment') ->label('Transfer Department') ->icon('heroicon-o-arrows-right-left') ->color('warning') ->visible(fn (): bool => auth()->user()->hasPermissionTo('transfer-department')) ->form([ Select::make('to_department_id') ->label('Target Department') ->options(Department::pluck('name', 'id')->toArray()) ->required() ->searchable(), Select::make('new_assignee_id') ->label('Assign To (optional)') ->options(function (\Filament\Forms\Get $get) { $deptId = $get('to_department_id'); if (! $deptId) { return []; } $dept = Department::with('manager')->find($deptId); return \App\Models\User::pluck('name', 'id')->toArray(); }) ->searchable() ->nullable(), Textarea::make('reason') ->label('Reason') ->required() ->rows(3), FileUpload::make('transfer_attachments') ->label('Attachments') ->multiple() ->disk('local') ->directory('feedback-attachments') ->columnSpanFull(), ]) ->action(function (array $data): void { $feedback = $this->getRecord(); $toDepartment = Department::findOrFail($data['to_department_id']); $sender = auth()->user(); $transferService = App::make(TransferService::class); $log = $transferService->transfer( $feedback, $toDepartment, $data['reason'], $sender, $data['new_assignee_id'] ?? null, ); if (! empty($data['transfer_attachments'])) { $fileService = App::make(FileService::class); $fileService->upload( $data['transfer_attachments'], 'general', $feedback, $sender->id, ); } Notification::make() ->title('Transferred successfully') ->success() ->send(); $this->redirect($this->getResource()::getUrl('edit', ['record' => $feedback])); }); } protected function closeTicketAction(): Action { return Action::make('closeTicket') ->label('Close Ticket') ->icon('heroicon-o-check-circle') ->color('success') ->visible(fn (): bool => $this->getRecord()->status !== 'closed' && auth()->user()->hasPermissionTo('close-ticket')) ->requiresConfirmation() ->modalHeading('Close Ticket') ->modalDescription('Bạn có chắc muốn đóng phiếu này? Hành động này KHÔNG thể hoàn tác.') ->modalSubmitActionLabel('Close') ->action(function (): void { $feedback = $this->getRecord(); $actor = auth()->user(); $closingService = App::make(ClosingService::class); try { $closingService->close($feedback, $actor); Notification::make() ->title('Ticket closed successfully') ->success() ->send(); $this->redirect($this->getResource()::getUrl('edit', ['record' => $feedback])); } catch (\Symfony\Component\HttpKernel\Exception\HttpException $e) { Notification::make() ->title($e->getMessage()) ->danger() ->send(); } catch (\Illuminate\Validation\ValidationException $e) { Notification::make() ->title($e->getMessage()) ->danger() ->send(); } }); } public function getFooter(): ?View { return view('filament.resources.feedback.edit-footer'); } protected function mutateFormDataBeforeSave(array $data): array { if (($data['is_general'] ?? false) === true) { $data['customer_product_id'] = null; } unset($data['is_general'], $data['attachment_collection']); $newStatus = $data['status'] ?? null; if ($newStatus === 'closed' && $this->getRecord()->status !== 'closed') { $closingService = App::make(ClosingService::class); $closingService->close($this->getRecord(), auth()->user()); } return $data; } protected function afterSave(): void { $attachments = $this->data['attachments'] ?? []; $collection = $this->data['attachment_collection'] ?? 'general'; if (! empty($attachments)) { $disk = Storage::disk('local'); $feedback = $this->getRecord(); foreach ($attachments as $path) { $feedback->attachments()->create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'user_id' => auth()->id(), 'name' => basename($path), 'path' => $path, 'disk' => 'local', 'collection' => $collection, 'mime_type' => $disk->mimeType($path), 'size' => $disk->size($path), ]); } } } protected function mutateFormDataBeforeFill(array $data): array { $data['is_general'] = $data['customer_product_id'] === null; return $data; } }