components([ Grid::make() ->schema([ Section::make(__('prescriptions.sections.info')) ->schema([ Select::make('patient') ->label(__('prescriptions.fields.patient')) ->options(fn () => Patient::all()->mapWithKeys( fn ($p) => [$p->id => $p->full_name] )->toArray()) ->searchable() ->nullable(), DatePicker::make('issue_date') ->label(__('prescriptions.fields.issue_date')) ->jalali() ->default(now()), ]) ->columns(2) ->columnSpanFull(), Section::make() ->schema([ Tabs::make() ->tabs([ Tab::make(__('prescriptions.sections.medications')) ->icon('heroicon-o-clipboard-document-list') ->schema([ Hidden::make('medication_order'), CheckboxList::make('medication_ids') ->label(__('prescriptions.fields.medication_ids')) ->options(fn () => Medication::where('is_active', true) ->orderBy('name') ->get() ->mapWithKeys(fn ($m) => [ $m->id => implode(' ', array_filter([ $m->dosage_form, $m->name, $m->strength, $m->quantity ? '#' . $m->quantity : null, $m->timing, ])), ]) ->toArray() ) ->columns(3) ->live() ->afterStateUpdated(function (array $state, callable $set, callable $get) { $currentOrder = array_map('intval', (array) ($get('medication_order') ?: [])); $state = array_map('intval', $state); $newOrder = array_values( array_filter($currentOrder, fn ($id) => in_array($id, $state)) ); foreach ($state as $id) { if (! in_array($id, $newOrder)) { $newOrder[] = $id; } } $set('medication_order', $newOrder); if (empty($newOrder)) { $set('content', null); return; } $medications = Medication::whereIn('id', $newOrder)->get()->keyBy('id'); $html = ''; $set('content', $html); }) ->columnSpanFull(), RichEditor::make('content') ->label(__('prescriptions.fields.content')) ->columnSpanFull(), ]), Tab::make(__('prescriptions.sections.lab_tests')) ->icon('heroicon-o-beaker') ->schema([ Hidden::make('lab_test_order'), CheckboxList::make('lab_test_ids') ->label(__('prescriptions.fields.lab_test_ids')) ->options(fn () => LabTest::where('is_active', true) ->orderBy('name') ->get() ->mapWithKeys(fn ($t) => [$t->id => $t->name]) ->toArray() ) ->columns(3) ->live() ->afterStateHydrated(function (array $state, callable $set, callable $get) { if (empty($state) || ! empty($get('lab_content'))) { return; } $order = array_map('intval', $state); $set('lab_test_order', $order); $tests = LabTest::whereIn('id', $order)->get()->keyBy('id'); $html = ''; $set('lab_content', $html); }) ->afterStateUpdated(function (array $state, callable $set, callable $get) { $currentOrder = array_map('intval', (array) ($get('lab_test_order') ?: [])); $state = array_map('intval', $state); $newOrder = array_values( array_filter($currentOrder, fn ($id) => in_array($id, $state)) ); foreach ($state as $id) { if (! in_array($id, $newOrder)) { $newOrder[] = $id; } } $set('lab_test_order', $newOrder); if (empty($newOrder)) { $set('lab_content', null); return; } $tests = LabTest::whereIn('id', $newOrder)->get()->keyBy('id'); $html = ''; $set('lab_content', $html); }) ->columnSpanFull(), RichEditor::make('lab_content') ->label(__('prescriptions.fields.lab_content')) ->columnSpanFull(), ]), ]) ->columnSpanFull(), ]) ->columnSpanFull() ->footerActions([ fn (string $operation) => Action::make('createAndPrint') ->label(__('prescriptions.actions.save_and_print')) ->color('primary') ->icon('heroicon-o-printer') ->action(function ($livewire) { $livewire->printType = 'prescription'; $livewire->create(); }) ->visible($operation === 'create'), fn (string $operation) => Action::make('createAndPrintLab') ->label(__('prescriptions.actions.print_lab')) ->color('gray') ->icon('heroicon-o-beaker') ->action(function ($livewire) { $livewire->printType = 'lab'; $livewire->create(); }) ->visible($operation === 'create'), fn (string $operation) => Action::make('createAndPrintBoth') ->label(__('prescriptions.actions.save_and_print_both')) ->color('warning') ->icon('heroicon-o-document-duplicate') ->action(function ($livewire) { $livewire->printType = 'both'; $livewire->create(); }) ->visible($operation === 'create'), fn (string $operation) => Action::make('cancelCreate') ->label(__('filament-panels::resources/pages/create-record.form.actions.cancel.label')) ->url(static::getUrl('index')) ->color('gray') ->visible($operation === 'create'), fn (string $operation) => Action::make('save') ->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label')) ->submit('save') ->keyBindings(['mod+s']) ->visible($operation === 'edit'), fn (string $operation) => Action::make('cancelEdit') ->label(__('filament-panels::resources/pages/edit-record.form.actions.cancel.label')) ->url(static::getUrl('index')) ->color('gray') ->visible($operation === 'edit'), ]), ]) ->columnSpanFull(), ]); } public static function table(Table $table): Table { return $table ->modifyQueryUsing(fn ($query) => $query->with(['patientRecord', 'creator'])) ->deferLoading() ->columns([ TextColumn::make('patientRecord.first_name') ->label(__('prescriptions.fields.patient')) ->formatStateUsing(fn ($record) => $record->patientRecord?->full_name ?? '-') ->searchable() ->sortable(), TextColumn::make('doctor') ->label(__('prescriptions.fields.doctor')) ->searchable() ->sortable(), TextColumn::make('issue_date') ->label(__('prescriptions.fields.issue_date')) ->sortable() ->formatStateUsing(function ($state) { if (! $state) { return '-'; } try { $date = \Carbon\Carbon::parse($state); return Jalalian::fromCarbon($date)->format('Y/m/d - H:i'); } catch (\Exception $e) { return $state; } }) ->placeholder('-'), TextColumn::make('content') ->label(__('prescriptions.fields.content')) ->formatStateUsing(fn ($state) => strip_tags($state ?? '')) ->limit(80) ->placeholder('-'), TextColumn::make('creator.name') ->label(__('prescriptions.fields.created_by')) ->sortable(), TextColumn::make('created_at') ->label(__('prescriptions.fields.created_at')) ->sortable() ->formatStateUsing(function ($state) { if (! $state) { return '-'; } try { $date = \Carbon\Carbon::parse($state); return Jalalian::fromCarbon($date)->format('Y/m/d - H:i'); } catch (\Exception $e) { return $state; } }), ]) ->defaultSort('created_at', 'desc') ->recordActions([ Action::make('printBoth') ->label(__('prescriptions.actions.print_both')) ->icon('heroicon-o-document-duplicate') ->color('purple') ->modalContent(fn ($record) => view('filament.prescription-print-modal', [ 'url' => route('prescription.print', ['prescription' => $record, 'type' => 'both']), ])) ->modalHeading(fn ($record) => ($record->patientRecord?->full_name ?? '') . ' — نسخه و آزمایش') ->modalWidth('7xl') ->modalSubmitAction(false) ->modalCancelActionLabel('بستن'), Action::make('printPrescription') ->label(__('prescriptions.actions.print_prescription')) ->icon('heroicon-o-printer') ->color('info') ->url(fn ($record) => route('prescription.print', $record)) ->openUrlInNewTab(), Action::make('printAdmission') ->label(__('prescriptions.actions.print_admission')) ->icon('heroicon-o-printer') ->color('warning') ->url(fn ($record) => route('prescription.print.admission', $record)) ->openUrlInNewTab(), Action::make('printLab') ->label(__('prescriptions.actions.print_lab')) ->icon('heroicon-o-beaker') ->color('gray') ->url(fn ($record) => route('prescription.print', ['prescription' => $record, 'type' => 'lab'])) ->openUrlInNewTab(), EditAction::make(), DeleteAction::make(), ]) ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make(), BulkAction::make('export_excel_selected') ->label(__('table.export_excel_selected')) ->icon('heroicon-o-table-cells') ->color('success') ->action(function (Collection $records) { $tempPath = tempnam(sys_get_temp_dir(), 'export_') . '.xlsx'; $writer = new Writer(); $writer->openToFile($tempPath); $writer->addRow(Row::fromValues([ 'ID', __('prescriptions.fields.patient'), __('prescriptions.fields.doctor'), __('prescriptions.fields.issue_date'), __('prescriptions.fields.content'), ])); foreach ($records as $record) { $writer->addRow(Row::fromValues([ $record->id, $record->patientRecord?->full_name ?? '', $record->doctor, $record->issue_date, strip_tags($record->content ?? ''), ])); } $writer->close(); return response()->streamDownload(function () use ($tempPath) { readfile($tempPath); @unlink($tempPath); }, 'prescriptions-' . now()->format('Y-m-d') . '.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); }), ]), ActionGroup::make([ Action::make('export_excel') ->label(__('table.export_excel')) ->color('success') ->icon('heroicon-o-table-cells') ->action(function () { $records = Prescription::with('patientRecord')->orderBy('created_at', 'desc')->get(); $tempPath = tempnam(sys_get_temp_dir(), 'export_') . '.xlsx'; $writer = new Writer(); $writer->openToFile($tempPath); $writer->addRow(Row::fromValues([ 'ID', __('prescriptions.fields.patient'), __('prescriptions.fields.doctor'), __('prescriptions.fields.issue_date'), __('prescriptions.fields.content'), ])); foreach ($records as $record) { $writer->addRow(Row::fromValues([ $record->id, $record->patientRecord?->full_name ?? '', $record->doctor, $record->issue_date, strip_tags($record->content ?? ''), ])); } $writer->close(); return response()->streamDownload(function () use ($tempPath) { readfile($tempPath); @unlink($tempPath); }, 'prescriptions-' . now()->format('Y-m-d') . '.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); }), Action::make('export_pdf') ->label(__('table.export_pdf')) ->color('danger') ->icon('heroicon-o-document-arrow-down') ->action(function () { $records = Prescription::with('patientRecord')->orderBy('created_at', 'desc')->get(); return PdfService::download( 'exports.prescriptions-pdf', ['records' => $records], 'prescriptions-' . now()->format('Y-m-d') . '.pdf', ); }), ]) ->label(__('table.export')) ->color('orange') ->button(), ]); } public static function getPages(): array { return [ 'index' => Pages\ListPrescriptions::route('/'), 'create' => Pages\CreatePrescription::route('/create'), 'edit' => Pages\EditPrescription::route('/{record}/edit'), ]; } }