From 1b34a3c217eff8344122048acdddb871ce33410f Mon Sep 17 00:00:00 2001 From: Vasyka Date: Mon, 7 Sep 2026 12:37:39 +0000 Subject: [PATCH] Add multilingual support to labor categories (RO/RU/EN) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migration: adds name_ro, name_ru, name_en columns; copies name→name_ro; seeds RU and EN translations for all 11 default categories - LaborCategory: label() returns localized name, optionsForSelect() keyed by canonical name (stored in labors.category) with localized label - LaborCategoryResource: 3-column form with RO/RU/EN inputs; table shows all three columns side by side - LaborResource: category select and filter use optionsForSelect() Co-Authored-By: Claude Sonnet 4.6 --- .../Resources/LaborCategoryResource.php | 46 +++++++++++------ .../Tenant/Resources/LaborResource.php | 4 +- app/Models/Tenant/LaborCategory.php | 20 +++++++- ...0000_add_multilang_to_labor_categories.php | 49 +++++++++++++++++++ 4 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 database/migrations/2026_09_07_120000_add_multilang_to_labor_categories.php diff --git a/app/Filament/Tenant/Resources/LaborCategoryResource.php b/app/Filament/Tenant/Resources/LaborCategoryResource.php index e8c8a7f..8df767e 100644 --- a/app/Filament/Tenant/Resources/LaborCategoryResource.php +++ b/app/Filament/Tenant/Resources/LaborCategoryResource.php @@ -42,13 +42,31 @@ class LaborCategoryResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Forms\Components\TextInput::make('name') - ->label(__('Denumire')) - ->required() - ->maxLength(80) - ->columnSpanFull(), + Forms\Components\Section::make(__('Denumire')) + ->columns(3) + ->schema([ + Forms\Components\TextInput::make('name_ro') + ->label(__('Română (RO)')) + ->required() + ->maxLength(80) + ->live(onBlur: true) + ->afterStateUpdated(function ($state, callable $set, $get) { + // Auto-fill canonical name from name_ro + if (! $get('name')) { + $set('name', $state); + } + }), + Forms\Components\TextInput::make('name_ru') + ->label(__('Rusă (RU)')) + ->maxLength(80), + Forms\Components\TextInput::make('name_en') + ->label(__('Engleză (EN)')) + ->maxLength(80), + ]), + Forms\Components\Hidden::make('name') + ->dehydrateStateUsing(fn ($state, $get) => $state ?: $get('name_ro')), Forms\Components\TextInput::make('sort_order') - ->label(__('Ordine')) + ->label(__('Ordine afișare')) ->numeric() ->default(0), ]); @@ -63,15 +81,16 @@ class LaborCategoryResource extends Resource ->sortable() ->alignRight() ->width(50), - Tables\Columns\TextColumn::make('name') - ->label(__('Categorie')) + Tables\Columns\TextColumn::make('name_ro') + ->label('RO') ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('updated_at') - ->label(__('Modificat')) - ->dateTime('d.m.Y') - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('name_ru') + ->label('RU') + ->placeholder('—'), + Tables\Columns\TextColumn::make('name_en') + ->label('EN') + ->placeholder('—'), ]) ->defaultSort('sort_order') ->reorderable('sort_order') @@ -80,7 +99,6 @@ class LaborCategoryResource extends Resource Actions\DeleteAction::make(), ]) ->bulkActions([]); - } public static function getPages(): array diff --git a/app/Filament/Tenant/Resources/LaborResource.php b/app/Filament/Tenant/Resources/LaborResource.php index 569f21d..4e58a82 100644 --- a/app/Filament/Tenant/Resources/LaborResource.php +++ b/app/Filament/Tenant/Resources/LaborResource.php @@ -53,7 +53,7 @@ class LaborResource extends Resource ->schema([ Forms\Components\Select::make('category') ->label(__('Categorie')) - ->options(fn () => \App\Models\Tenant\LaborCategory::orderBy('sort_order')->orderBy('name')->pluck('name', 'name')) + ->options(fn () => \App\Models\Tenant\LaborCategory::optionsForSelect()) ->required() ->searchable(), Forms\Components\TextInput::make('code')->label(__('Cod'))->maxLength(32), @@ -100,7 +100,7 @@ class LaborResource extends Resource ]) ->filters([ Tables\Filters\SelectFilter::make('category') - ->options(fn () => \App\Models\Tenant\LaborCategory::orderBy('sort_order')->orderBy('name')->pluck('name', 'name')), + ->options(fn () => \App\Models\Tenant\LaborCategory::optionsForSelect()), Tables\Filters\TernaryFilter::make('is_active')->label(__('Doar active')), ]) ->actions([ diff --git a/app/Models/Tenant/LaborCategory.php b/app/Models/Tenant/LaborCategory.php index 314b2e8..953af4b 100644 --- a/app/Models/Tenant/LaborCategory.php +++ b/app/Models/Tenant/LaborCategory.php @@ -9,7 +9,25 @@ class LaborCategory extends Model { use BelongsToTenant; - protected $fillable = ['company_id', 'name', 'sort_order']; + protected $fillable = ['company_id', 'name', 'name_ro', 'name_ru', 'name_en', 'sort_order']; protected $casts = ['sort_order' => 'integer']; + + /** Localized display label — falls back through ru→en→name_ro→name. */ + public function label(?string $locale = null): string + { + $locale = $locale ?: app()->getLocale(); + return $this->{"name_{$locale}"} + ?: $this->name_ro + ?: $this->name + ?: ''; + } + + /** Options array keyed by canonical name (stored in labors.category). */ + public static function optionsForSelect(): array + { + return static::orderBy('sort_order')->orderBy('name')->get() + ->mapWithKeys(fn ($c) => [$c->name => $c->label()]) + ->toArray(); + } } diff --git a/database/migrations/2026_09_07_120000_add_multilang_to_labor_categories.php b/database/migrations/2026_09_07_120000_add_multilang_to_labor_categories.php new file mode 100644 index 0000000..d6eccb2 --- /dev/null +++ b/database/migrations/2026_09_07_120000_add_multilang_to_labor_categories.php @@ -0,0 +1,49 @@ +string('name_ro')->nullable()->after('name'); + $table->string('name_ru')->nullable()->after('name_ro'); + $table->string('name_en')->nullable()->after('name_ru'); + }); + + // Copy existing name → name_ro + DB::table('labor_categories')->update(['name_ro' => DB::raw('`name`')]); + + // Seed Russian and English for the default categories + $translations = [ + 'Motor' => ['ru' => 'Двигатель', 'en' => 'Engine'], + 'Frâne' => ['ru' => 'Тормоза', 'en' => 'Brakes'], + 'Suspensie' => ['ru' => 'Подвеска', 'en' => 'Suspension'], + 'Anvelope' => ['ru' => 'Шины', 'en' => 'Tires'], + 'ITP' => ['ru' => 'Техосмотр', 'en' => 'Inspection'], + 'Cutie viteze' => ['ru' => 'Коробка передач', 'en' => 'Gearbox'], + 'Caroserie' => ['ru' => 'Кузов', 'en' => 'Bodywork'], + 'Electrică' => ['ru' => 'Электрика', 'en' => 'Electrical'], + 'Climatizare' => ['ru' => 'Климат', 'en' => 'AC / Climate'], + 'Eșapament' => ['ru' => 'Выхлоп', 'en' => 'Exhaust'], + 'Altele' => ['ru' => 'Прочее', 'en' => 'Other'], + ]; + + foreach ($translations as $name => $t) { + DB::table('labor_categories') + ->where('name', $name) + ->update(['name_ru' => $t['ru'], 'name_en' => $t['en']]); + } + } + + public function down(): void + { + Schema::table('labor_categories', function (Blueprint $table) { + $table->dropColumn(['name_ro', 'name_ru', 'name_en']); + }); + } +};