basePrices->first()->price->decimal with no * null-guard, which crashes on any rate with no basePrices row — routine * for a live rate that has never had a fallback price configured. Same * logic, just null-safe. * * Also replaces the vendor's `shipping_method_id` Select, which uses * ->relationship(titleAttribute: 'name') — Filament builds that option * list with `orderBy('name')`/`pluck('name', ...)` against the DB, but * ShippingMethod.name is now a locale-keyed JSON column (see database/ * migrations/..._make_shipping_methods_name_translatable.php) that * Postgres has no default ordering operator for, crashing with * "could not identify an ordering operator for type json" the moment * this page loads. Resolved app-side instead via ShippingMethodName, * same as every other read site for this column. */ class ManageShippingRates extends BaseManageShippingRates { public function form(Schema $schema): Schema { $schema = parent::form($schema); return $schema->components( $this->labelPriceFieldsAsFallbackWhenLive( $this->replaceShippingMethodField($schema->getComponents()) ) ); } private function replaceShippingMethodField(array $components): array { return array_map(function ($component) { if (method_exists($component, 'getName') && $component->getName() === 'shipping_method_id') { return Select::make('shipping_method_id') ->label($component->getLabel()) ->required() ->live() ->options(fn () => ShippingMethod::all() ->mapWithKeys(fn (ShippingMethod $method) => [$method->id => ShippingMethodName::resolve($method)])) ->searchable() ->columnSpan(2); } return $component; }, $components); } private function labelPriceFieldsAsFallbackWhenLive(array $components): array { $isLive = fn (Get $get) => static::methodChargeBy($get('shipping_method_id')) === 'live'; foreach ($components as $component) { if (! method_exists($component, 'getName')) { continue; } if ($component->getName() === 'price') { $component->required(fn (Get $get) => ! $isLive($get)) ->helperText(fn (Get $get) => $isLive($get) ? 'Used only if the live API call fails.' : null) ->afterStateHydrated(static function (TextInput $component, ?Model $record = null): void { if (! $record) { return; } $basePrice = $record->basePrices->first(); $component->state($basePrice?->price->decimal); }); } if ($component->getName() === 'prices') { $component->helperText(fn (Get $get) => $isLive($get) ? 'Used only if the live API call fails.' : null); } } return $components; } public function table(Table $table): Table { $table = parent::table($table); return $table->columns( array_map(function ($column) { if (method_exists($column, 'getName') && $column->getName() === 'shippingMethod.name') { return TextColumn::make('shippingMethod.name') ->label(__('lunarpanel.shipping::relationmanagers.shipping_rates.table.shipping_method.label')) ->state(fn (ShippingRate $record) => $record->shippingMethod ? ShippingMethodName::resolve($record->shippingMethod) : null); } if (method_exists($column, 'getName') && $column->getName() === 'basePrices.0') { return TextColumn::make('basePrices.0') ->label(__('lunarpanel.shipping::relationmanagers.shipping_rates.table.price.label')) ->formatStateUsing(function ($state, ShippingRate $record) { if (static::methodChargeBy($record->shipping_method_id) === 'live') { return $state === null ? 'Live API pricing, no fallback set' : $state->price->formatted.' (fallback)'; } return $state?->price->formatted; }); } return $column; }, $table->getColumns()) ); } protected static function methodChargeBy(ShippingMethod|int|string|null $method): ?string { if (blank($method)) { return null; } if (! $method instanceof ShippingMethod) { $method = ShippingMethod::find($method); } return $method?->data['charge_by'] ?? null; } }