279 lines
11 KiB
PHP
279 lines
11 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Filament\Resources\PatientResource\Pages;
|
||
|
||
use App\Filament\Resources\PatientResource;
|
||
use App\Models\SurgeryAppointment;
|
||
use App\Models\SurgeryCenter;
|
||
use Carbon\Carbon;
|
||
use Filament\Actions\Action;
|
||
use Filament\Actions\DeleteAction;
|
||
use Filament\Forms\Components\DatePicker;
|
||
use Filament\Schemas\Components\Grid;
|
||
use Filament\Forms\Components\Placeholder;
|
||
use Filament\Forms\Components\Select;
|
||
use Filament\Forms\Components\TimePicker;
|
||
use Filament\Resources\Pages\EditRecord;
|
||
use Filament\Support\Enums\Width;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Morilog\Jalali\Jalalian;
|
||
|
||
class EditPatient extends EditRecord
|
||
{
|
||
protected static string $resource = PatientResource::class;
|
||
|
||
protected Width|string|null $maxContentWidth = Width::SevenExtraLarge;
|
||
|
||
protected function getHeaderActions(): array
|
||
{
|
||
return [
|
||
$this->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 [];
|
||
}
|
||
}
|