getSaveFormAction() ->formId('form'), Action::make('surgeryAppointment') ->label(__('patients.actions.surgery_appointment')) ->icon('heroicon-o-calendar-days') ->color(fn () => $this->getRecord()->surgeryAppointment ? 'success' : 'info') ->modalWidth(Width::Large) ->fillForm(function (): array { $appointment = $this->getRecord()->surgeryAppointment; if ($appointment) { $surgeryDate = $appointment->surgery_date; return [ 'surgery_date' => $surgeryDate?->format('Y-m-d'), 'surgery_time' => $surgeryDate?->format('H:i'), 'surgery_center_id' => $appointment->surgery_center_id, ]; } return [ 'surgery_date' => null, 'surgery_time' => '09:00', 'surgery_center_id' => null, ]; }) ->form([ Placeholder::make('patient_name') ->label(__('patients.fields.full_name')) ->content(fn () => $this->getRecord()->full_name), Grid::make(2) ->schema([ DatePicker::make('surgery_date') ->label(__('patients.fields.surgery_date')) ->jalali() ->minDate(today()) ->required(), TimePicker::make('surgery_time') ->label(__('patients.fields.surgery_time')) ->native(false) ->seconds(false) ->minutesStep(15), ]), Select::make('surgery_center_id') ->label(__('patients.fields.surgery_center')) ->options(fn () => SurgeryCenter::pluck('name', 'id')->toArray()) ->searchable() ->required(), ]) ->modalSubmitAction(false) ->extraModalFooterActions(fn (Action $action): array => [ $action->makeModalSubmitAction('saveAppointment', arguments: ['save' => true]) ->label(__('patients.actions.save_appointment')) ->color('primary') ->icon('heroicon-o-check') ->visible(fn () => $this->getRecord()->surgeryAppointment === null), Action::make('printAdmissionFromModal') ->label(__('patients.actions.print_admission')) ->color('warning') ->icon('heroicon-o-printer') ->url(fn () => route('surgery-appointment.print.admission', $this->getRecord()), shouldOpenInNewTab: true) ->visible(fn () => $this->getRecord()->surgeryAppointment !== null), $action->makeModalSubmitAction('deleteAppointment', arguments: ['delete' => true]) ->label(__('patients.actions.delete_appointment')) ->color('danger') ->icon('heroicon-o-trash') ->visible(fn () => $this->getRecord()->surgeryAppointment !== null), ]) ->action(function (array $data, array $arguments): void { $record = $this->getRecord(); if (! empty($arguments['delete'])) { $existingAppointment = $record->surgeryAppointment; SurgeryAppointment::where('patient_id', $record->id)->delete(); $record->unsetRelation('surgeryAppointment'); if ($existingAppointment) { \App\Filament\Pages\SmsLogsPage::cancelAppointmentMessages( $record->hand_phone ?? '', $existingAppointment->surgery_date->toDateTimeString(), ); } \Filament\Notifications\Notification::make() ->title(__('patients.actions.appointment_deleted')) ->success() ->send(); return; } $dateStr = $data['surgery_date']; $timeStr = $data['surgery_time'] ?? '00:00'; $surgeryDateTime = Carbon::parse($dateStr . ' ' . $timeStr); $jalaliDate = Jalalian::fromCarbon($surgeryDateTime)->format('Y/m/d'); $apiKey = \App\Models\OsurgInitial::val('sms_api_key', config('sms.api_key')); $apiUrl = \App\Models\OsurgInitial::val('sms_api_url', config('sms.api_url')); $smsPayload = [ 'phoneNumber' => $record->hand_phone ?? '', 'patientName' => $record->full_name, 'surgeryDate' => $jalaliDate, 'patientId' => $record->id, 'surgeryDateFull' => $surgeryDateTime->toDateTimeString(), ]; // مرحله ۱: ارسال پیامک اولیه try { $response = Http::timeout(30) ->withHeader('X-API-Key', $apiKey) ->asJson() ->post($apiUrl . '/surgery-reminder', $smsPayload); if (! ($response->json('success') ?? false)) { \Filament\Notifications\Notification::make() ->title(__('patients.actions.api_failed')) ->body($response->json('message') ?? '') ->danger() ->send(); return; } } catch (\Throwable $e) { \Filament\Notifications\Notification::make() ->title(__('patients.actions.api_failed')) ->body($e->getMessage()) ->danger() ->send(); return; } $appointment = SurgeryAppointment::updateOrCreate( ['patient_id' => $record->id], [ 'surgery_date' => $surgeryDateTime, 'surgery_center_id' => $data['surgery_center_id'], ] ); $record->unsetRelation('surgeryAppointment'); try { Http::timeout(30) ->withHeader('X-API-Key', $apiKey) ->asJson() ->post($apiUrl . '/surgery-reminder', array_merge($smsPayload, [ 'appointmentId' => $appointment->id, ])); } catch (\Throwable) { } \Filament\Notifications\Notification::make() ->title(__('patients.actions.appointment_saved')) ->body(__('patients.actions.sms_sent')) ->success() ->send(); }), Action::make('printPrescription') ->label(__('prescriptions.actions.print_prescription')) ->icon('heroicon-o-printer') ->color('info') ->visible(fn () => $this->getRecord()->prescriptions()->exists()) ->url(fn () => route('prescription.print', $this->getRecord()->prescriptions()->latest()->first())) ->openUrlInNewTab(), Action::make('printAdmission') ->label(__('prescriptions.actions.print_admission')) ->icon('heroicon-o-printer') ->color('warning') ->visible(fn () => $this->getRecord()->surgeryAppointment !== null) ->url(fn () => route('surgery-appointment.print.admission', $this->getRecord())) ->openUrlInNewTab(), Action::make('printLab') ->label(__('prescriptions.actions.print_lab')) ->icon('heroicon-o-beaker') ->color('gray') ->url(fn () => route('patient.print.lab', $this->getRecord())) ->openUrlInNewTab(), DeleteAction::make(), ]; } protected function mutateFormDataBeforeFill(array $data): array { foreach (['birth_date'] as $field) { if (($data[$field] ?? null) === '') { $data[$field] = null; } } foreach (['surgery_before', 'sergery_after'] as $field) { if (($data[$field] ?? null) === '') { $data[$field] = null; } } foreach (['current_illness_1', 'current_illness_2', 'has_alergyto'] as $field) { if (is_null($data[$field] ?? null)) { $data[$field] = []; } } foreach (['photos_before', 'photos_after', 'videos', 'audio_files'] as $field) { $data[$field] = static::normalizeFileList($data[$field] ?? null); } return $data; } public static function normalizeFileList(mixed $value): array { if (empty($value)) { return []; } if (is_string($value)) { $decoded = json_decode($value, true); $value = is_array($decoded) ? $decoded : [$value]; } return collect((array) $value) ->map(function ($item) { if (is_array($item) && isset($item['name'])) { return $item['name']; } if (is_string($item) && $item !== '') { return $item; } return null; }) ->filter() ->values() ->toArray(); } protected function getFormActions(): array { return []; } }