components([ Grid::make() ->schema([ Section::make(__('visits.sections.visit_info')) ->schema([ DatePicker::make('visit_date') ->label(__('visits.fields.visit_date')) ->jalali() ->default(now()) ->required(), TimePicker::make('visit_time') ->label(__('visits.fields.visit_time')) ->nullable() ->native(false) ->seconds(false) ->minutesStep(15), Select::make('doctor') ->label(__('visits.fields.doctor')) ->options(fn () => Doctor::all()->mapWithKeys( fn ($d) => [$d->id => $d->full_name] )->toArray()) ->searchable() ->nullable(), Select::make('treatment') ->label(__('visits.fields.treatment')) ->options(fn () => Treatment::all()->mapWithKeys( fn ($t) => [$t->id => ($t->treatment_name ?: $t->treatment_type)] )->toArray()) ->searchable() ->nullable() ->live() ->afterStateUpdated(function ($state, callable $set) { if ($state) { $treatment = Treatment::find($state); if ($treatment && $treatment->treatment_cost > 0) { $set('treatment_cost', $treatment->treatment_cost); } } }), ]) ->columns(2) ->columnSpanFull(), Section::make(__('visits.sections.financial_info')) ->schema([ TextInput::make('treatment_cost') ->label(__('visits.fields.treatment_cost')) ->numeric() ->nullable() ->suffix(__('visits.fields.currency')), TextInput::make('paid_amount') ->label(__('visits.fields.paid_amount')) ->numeric() ->nullable() ->suffix(__('visits.fields.currency')), Select::make('payment_type') ->label(__('visits.fields.payment_type')) ->options(fn () => PaymentType::all()->mapWithKeys( fn ($p) => [$p->id => $p->payment_type] )->toArray()) ->searchable() ->nullable(), ]) ->columns(3) ->columnSpanFull(), Section::make(__('visits.sections.treatment_details')) ->schema([ Textarea::make('treatment_description') ->label(__('visits.fields.treatment_description')) ->rows(3) ->columnSpanFull(), ]) ->columnSpanFull(), ]) ->columnSpanFull(), ]); } public function table(Table $table): Table { return $table ->recordTitleAttribute('visit_date') ->columns([ TextColumn::make('visit_date') ->label(__('visits.fields.visit_date')) ->sortable() ->formatStateUsing(function ($state) { if (! $state) { return '-'; } try { $date = \Carbon\Carbon::parse($state); return \Morilog\Jalali\Jalalian::fromCarbon($date)->format('Y/m/d'); } catch (\Exception $e) { return $state; } }) ->placeholder('-'), TextColumn::make('doctorRecord.first_name') ->label(__('visits.fields.doctor')) ->formatStateUsing(fn ($record) => $record->doctorRecord?->full_name ?? '-') ->placeholder('-'), TextColumn::make('treatmentRecord.treatment_name') ->label(__('visits.fields.treatment')) ->formatStateUsing(fn ($record) => $record->treatmentRecord ? ($record->treatmentRecord->treatment_name ?: $record->treatmentRecord->treatment_type) : '-') ->placeholder('-'), TextColumn::make('treatment_cost') ->label(__('visits.fields.treatment_cost')) ->formatStateUsing(fn ($state) => $state !== null ? number_format((int) $state) : '-') ->placeholder('-') ->summarize( Sum::make() ->label(__('visits.fields.treatment_cost')) ->formatStateUsing(fn ($state) => number_format((int) ($state ?? 0))) ), TextColumn::make('paid_amount') ->label(__('visits.fields.paid_amount')) ->formatStateUsing(fn ($state) => $state !== null ? number_format((int) $state) : '-') ->placeholder('-') ->summarize( Sum::make() ->label(__('visits.fields.paid_amount')) ->formatStateUsing(fn ($state) => number_format((int) ($state ?? 0))) ), TextColumn::make('remained_amount') ->label(__('visits.fields.remained_amount')) ->state(fn ($record) => $record->remained_amount) ->formatStateUsing(fn ($state) => number_format((int) $state)) ->color(fn ($record) => $record->remained_amount > 0 ? 'danger' : 'success'), TextColumn::make('paymentTypeRecord.payment_type') ->label(__('visits.fields.payment_type')) ->placeholder('-'), ]) ->defaultSort('visit_date', 'desc') ->headerActions([ CreateAction::make() ->label(__('visits.actions.create')) ->icon('heroicon-o-plus') ->slideOver() ->modalWidth('4xl'), ]) ->recordActions([ EditAction::make() ->slideOver() ->modalWidth('4xl'), DeleteAction::make(), ]) ->toolbarActions([]); } }