diff --git a/app/Filament/Central/Pages/PaymentSettings.php b/app/Filament/Central/Pages/PaymentSettings.php index e23faf2..7fb1606 100644 --- a/app/Filament/Central/Pages/PaymentSettings.php +++ b/app/Filament/Central/Pages/PaymentSettings.php @@ -19,7 +19,12 @@ class PaymentSettings extends Page return __('nav.label.Setări plăți'); } - protected static ?string $title = 'Integrări de plată'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Integrări de plată'); + } protected static ?int $navigationSort = 90; @@ -84,132 +89,132 @@ class PaymentSettings extends Page { return $schema ->components([ - Schemas\Components\Section::make('General') + Schemas\Components\Section::make(__('General')) ->columns(3) ->schema([ Forms\Components\Select::make('platform_currency') - ->label('Monedă platformă') + ->label(__('Monedă platformă')) ->options(['MDL' => 'MDL', 'EUR' => 'EUR', 'USD' => 'USD']) ->required(), Forms\Components\TextInput::make('invoice_prefix') - ->label('Prefix facturi')->maxLength(10)->default('INV'), + ->label(__('Prefix facturi'))->maxLength(10)->default('INV'), Forms\Components\Textarea::make('terms') - ->label('Termeni de plată')->rows(2)->columnSpanFull(), + ->label(__('Termeni de plată'))->rows(2)->columnSpanFull(), ]), - Schemas\Components\Section::make('Date legale (apar pe facturi)') + Schemas\Components\Section::make(__('Date legale (apar pe facturi)')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('legal_name')->label('Denumire companie')->maxLength(160), - Forms\Components\TextInput::make('legal_idno')->label('IDNO / CUI')->maxLength(40), - Forms\Components\TextInput::make('legal_address')->label('Adresă')->columnSpanFull()->maxLength(255), - Forms\Components\TextInput::make('legal_phone')->label('Telefon')->tel()->maxLength(40), - Forms\Components\TextInput::make('legal_email')->label('Email')->email()->maxLength(120), + Forms\Components\TextInput::make('legal_name')->label(__('Denumire companie'))->maxLength(160), + Forms\Components\TextInput::make('legal_idno')->label(__('IDNO / CUI'))->maxLength(40), + Forms\Components\TextInput::make('legal_address')->label(__('Adresă'))->columnSpanFull()->maxLength(255), + Forms\Components\TextInput::make('legal_phone')->label(__('Telefon'))->tel()->maxLength(40), + Forms\Components\TextInput::make('legal_email')->label(__('Email'))->email()->maxLength(120), ]), - Schemas\Components\Section::make('💳 Stripe') - ->description('Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.') + Schemas\Components\Section::make(__('💳 Stripe')) + ->description(__('Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.')) ->columns(3) ->collapsible() ->collapsed(fn (Get $get) => ! $get('stripe_enabled')) ->schema([ - Forms\Components\Toggle::make('stripe_enabled')->label('Activează Stripe')->default(false)->live(), + Forms\Components\Toggle::make('stripe_enabled')->label(__('Activează Stripe'))->default(false)->live(), Forms\Components\Select::make('stripe_mode') - ->label('Mod') + ->label(__('Mod')) ->options(['test' => '🧪 Test', 'live' => '🟢 Live']) ->default('test') ->required(fn (Get $get) => $get('stripe_enabled')) ->visible(fn (Get $get) => $get('stripe_enabled')), Forms\Components\TextInput::make('stripe_publishable') - ->label('Publishable key')->placeholder('pk_test_...') + ->label(__('Publishable key'))->placeholder('pk_test_...') ->visible(fn (Get $get) => $get('stripe_enabled')), Forms\Components\TextInput::make('stripe_secret') - ->label('Secret key')->password()->revealable()->placeholder('sk_test_...') + ->label(__('Secret key'))->password()->revealable()->placeholder('sk_test_...') ->visible(fn (Get $get) => $get('stripe_enabled')), Forms\Components\TextInput::make('stripe_webhook') - ->label('Webhook secret')->password()->revealable()->placeholder('whsec_...') + ->label(__('Webhook secret'))->password()->revealable()->placeholder('whsec_...') ->columnSpanFull() - ->helperText('Endpoint webhook: https://service.mir.md/payments/stripe/webhook') + ->helperText(__('Endpoint webhook: https://service.mir.md/payments/stripe/webhook')) ->visible(fn (Get $get) => $get('stripe_enabled')), ]), - Schemas\Components\Section::make('🅿️ PayPal') - ->description('Plăți internaționale. Mai potrivit pentru clienți din afara MD.') + Schemas\Components\Section::make(__('🅿️ PayPal')) + ->description(__('Plăți internaționale. Mai potrivit pentru clienți din afara MD.')) ->columns(3) ->collapsible() ->collapsed(fn (Get $get) => ! $get('paypal_enabled')) ->schema([ - Forms\Components\Toggle::make('paypal_enabled')->label('Activează PayPal')->default(false)->live(), + Forms\Components\Toggle::make('paypal_enabled')->label(__('Activează PayPal'))->default(false)->live(), Forms\Components\Select::make('paypal_mode') - ->label('Mod') + ->label(__('Mod')) ->options(['sandbox' => '🧪 Sandbox', 'live' => '🟢 Live']) ->default('sandbox') ->visible(fn (Get $get) => $get('paypal_enabled')), Forms\Components\Placeholder::make('paypal_webhook_info') - ->label('Webhook') + ->label(__('Webhook')) ->content('https://service.mir.md/payments/paypal/webhook') ->visible(fn (Get $get) => $get('paypal_enabled')), Forms\Components\TextInput::make('paypal_client_id') - ->label('Client ID')->placeholder('AYS...') + ->label(__('Client ID'))->placeholder(__('AYS...')) ->visible(fn (Get $get) => $get('paypal_enabled')), Forms\Components\TextInput::make('paypal_secret') - ->label('Secret')->password()->revealable()->placeholder('EJk...') + ->label(__('Secret'))->password()->revealable()->placeholder(__('EJk...')) ->visible(fn (Get $get) => $get('paypal_enabled')), ]), - Schemas\Components\Section::make('🇲🇩 Paynet (Moldova)') - ->description('Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.') + Schemas\Components\Section::make(__('🇲🇩 Paynet (Moldova)')) + ->description(__('Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.')) ->columns(3) ->collapsible() ->collapsed(fn (Get $get) => ! $get('paynet_enabled')) ->schema([ - Forms\Components\Toggle::make('paynet_enabled')->label('Activează Paynet')->default(false)->live(), + Forms\Components\Toggle::make('paynet_enabled')->label(__('Activează Paynet'))->default(false)->live(), Forms\Components\Select::make('paynet_mode') - ->label('Mod') + ->label(__('Mod')) ->options(['test' => '🧪 Test', 'live' => '🟢 Live']) ->default('test') ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\Placeholder::make('paynet_webhook_info') - ->label('Webhook (Notify URL)') + ->label(__('Webhook (Notify URL)')) ->content('https://service.mir.md/payments/paynet/webhook') ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\TextInput::make('paynet_merchant_code') - ->label('Merchant Code') - ->placeholder('AUTOCRM_001') + ->label(__('Merchant Code')) + ->placeholder(__('AUTOCRM_001')) ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\TextInput::make('paynet_service_id') - ->label('Service ID') + ->label(__('Service ID')) ->numeric() ->placeholder('12345') ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\TextInput::make('paynet_user') - ->label('User API') + ->label(__('User API')) ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\TextInput::make('paynet_password') - ->label('Parolă API')->password()->revealable() + ->label(__('Parolă API'))->password()->revealable() ->visible(fn (Get $get) => $get('paynet_enabled')), Forms\Components\TextInput::make('paynet_secret') - ->label('Secret semnătură')->password()->revealable() + ->label(__('Secret semnătură'))->password()->revealable() ->columnSpanFull() - ->helperText('Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.') + ->helperText(__('Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.')) ->visible(fn (Get $get) => $get('paynet_enabled')), ]), - Schemas\Components\Section::make('🏦 Transfer bancar (manual)') - ->description('Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.') + Schemas\Components\Section::make(__('🏦 Transfer bancar (manual)')) + ->description(__('Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.')) ->columns(2) ->collapsible() ->schema([ - Forms\Components\Toggle::make('bank_enabled')->label('Activează plata prin transfer')->default(true)->live(), + Forms\Components\Toggle::make('bank_enabled')->label(__('Activează plata prin transfer'))->default(true)->live(), Forms\Components\TextInput::make('bank_beneficiary') - ->label('Beneficiar')->placeholder('AutoCRM SRL') + ->label(__('Beneficiar'))->placeholder(__('AutoCRM SRL')) ->visible(fn (Get $get) => $get('bank_enabled')), Forms\Components\TextInput::make('bank_iban') - ->label('IBAN')->placeholder('MD00 AG00 0000 ...') + ->label(__('IBAN'))->placeholder(__('MD00 AG00 0000 ...')) ->visible(fn (Get $get) => $get('bank_enabled')), Forms\Components\TextInput::make('bank_bic') - ->label('BIC / SWIFT')->placeholder('AGRNMD2X') + ->label(__('BIC / SWIFT'))->placeholder(__('AGRNMD2X')) ->visible(fn (Get $get) => $get('bank_enabled')), Forms\Components\TextInput::make('bank_name') - ->label('Banca')->placeholder('Moldova-Agroindbank') + ->label(__('Banca'))->placeholder(__('Moldova-Agroindbank')) ->visible(fn (Get $get) => $get('bank_enabled')), Forms\Components\Textarea::make('bank_instructions') - ->label('Instrucțiuni adiționale')->rows(2)->columnSpanFull() + ->label(__('Instrucțiuni adiționale'))->rows(2)->columnSpanFull() ->visible(fn (Get $get) => $get('bank_enabled')), ]), ]) @@ -266,14 +271,14 @@ class PaymentSettings extends Page 'instructions' => $data['bank_instructions'] ?? null, ]); - Notification::make()->title('Setări plăți salvate')->success()->send(); + Notification::make()->title(__('Setări plăți salvate'))->success()->send(); } protected function getFormActions(): array { return [ \Filament\Actions\Action::make('save') - ->label('Salvează') + ->label(__('Salvează')) ->submit('save'), ]; } diff --git a/app/Filament/Central/Resources/CompanyResource.php b/app/Filament/Central/Resources/CompanyResource.php index abe5c57..3aee636 100644 --- a/app/Filament/Central/Resources/CompanyResource.php +++ b/app/Filament/Central/Resources/CompanyResource.php @@ -40,7 +40,7 @@ class CompanyResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ Forms\Components\TextInput::make('slug') @@ -53,19 +53,19 @@ class CompanyResource extends Resource ) ->dehydrateStateUsing(fn ($state) => strtolower((string) $state)) ->extraInputAttributes(['style' => 'text-transform: lowercase']) - ->helperText('Subdomeniul: .service.mir.md'), + ->helperText(__('Subdomeniul: .service.mir.md')), Forms\Components\TextInput::make('name')->required()->maxLength(120), Forms\Components\TextInput::make('display_name')->maxLength(120), Forms\Components\TextInput::make('city')->maxLength(60), ]), - Schemas\Components\Section::make('Contact') + Schemas\Components\Section::make(__('Contact')) ->columns(2) ->schema([ Forms\Components\TextInput::make('contact_name')->maxLength(120), Forms\Components\TextInput::make('phone')->tel()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), ]), - Schemas\Components\Section::make('Abonament') + Schemas\Components\Section::make(__('Abonament')) ->columns(2) ->schema([ Forms\Components\Select::make('status') @@ -79,31 +79,31 @@ class CompanyResource extends Resource ->default('trial') ->required(), Forms\Components\Select::make('plan_id') - ->label('Plan') + ->label(__('Plan')) ->options(fn () => Plan::pluck('name', 'id')) ->searchable(), - Forms\Components\DateTimePicker::make('trial_ends_at')->label('Trial expiră la'), - Forms\Components\DateTimePicker::make('active_until')->label('Abonament până la'), + Forms\Components\DateTimePicker::make('trial_ends_at')->label(__('Trial expiră la')), + Forms\Components\DateTimePicker::make('active_until')->label(__('Abonament până la')), Forms\Components\Toggle::make('is_demo') - ->label('Tenant demo') - ->helperText('Marchează acest tenant ca demo (datele pot fi resetate periodic).'), + ->label(__('Tenant demo')) + ->helperText(__('Marchează acest tenant ca demo (datele pot fi resetate periodic).')), ]), - Schemas\Components\Section::make('Admin tenant (la creare)') + Schemas\Components\Section::make(__('Admin tenant (la creare)')) ->columns(2) ->visible(fn (string $operation) => $operation === 'create') ->schema([ Forms\Components\TextInput::make('admin_name') - ->label('Nume admin') + ->label(__('Nume admin')) ->default('Administrator'), Forms\Components\TextInput::make('admin_email') - ->label('Email admin') + ->label(__('Email admin')) ->email() ->required(), Forms\Components\TextInput::make('admin_password') - ->label('Parolă (lasă gol pentru auto-generat)') + ->label(__('Parolă (lasă gol pentru auto-generat)')) ->password() ->minLength(8) - ->helperText('Dacă e gol, generăm 10 caractere random.'), + ->helperText(__('Dacă e gol, generăm 10 caractere random.')), ]), ]); } @@ -129,9 +129,9 @@ class CompanyResource extends Resource 'danger' => ['suspended', 'expired'], 'gray' => ['archived'], ]), - Tables\Columns\TextColumn::make('plan.name')->label('Plan')->placeholder('—'), + Tables\Columns\TextColumn::make('plan.name')->label(__('Plan'))->placeholder('—'), Tables\Columns\TextColumn::make('city')->toggleable(), - Tables\Columns\TextColumn::make('users_count')->counts('users')->label('Useri'), + Tables\Columns\TextColumn::make('users_count')->counts('users')->label(__('Useri')), Tables\Columns\TextColumn::make('created_at')->date()->sortable(), ]) ->filters([ @@ -145,20 +145,20 @@ class CompanyResource extends Resource Actions\ViewAction::make() ->url(fn (Company $record) => CompanyResource::getUrl('view', ['record' => $record])), Actions\Action::make('open_tenant') - ->label('Deschide') + ->label(__('Deschide')) ->icon('heroicon-m-arrow-top-right-on-square') ->color('primary') ->url(fn (Company $record) => $record->url('/app')) ->openUrlInNewTab(), Actions\Action::make('suspend') - ->label('Suspendă') + ->label(__('Suspendă')) ->icon('heroicon-m-no-symbol') ->color('danger') ->visible(fn (Company $record) => in_array($record->status, ['active', 'trial'])) ->requiresConfirmation() ->action(fn (Company $record) => app(\App\Services\CompanyProvisioner::class)->suspend($record)), Actions\Action::make('activate') - ->label('Activează') + ->label(__('Activează')) ->icon('heroicon-m-check-circle') ->color('success') ->visible(fn (Company $record) => in_array($record->status, ['suspended', 'expired'])) @@ -167,8 +167,8 @@ class CompanyResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun tenant încă') - ->emptyStateDescription('Adaugă primul client SaaS folosind butonul „Adaugă companie" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.') + ->emptyStateHeading(__('Niciun tenant încă')) + ->emptyStateDescription(__('Adaugă primul client SaaS folosind butonul „Adaugă companie" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.')) ->emptyStateIcon('heroicon-o-building-office-2') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Central/Resources/CompanyResource/Pages/ViewCompany.php b/app/Filament/Central/Resources/CompanyResource/Pages/ViewCompany.php index 0cc9754..7816462 100644 --- a/app/Filament/Central/Resources/CompanyResource/Pages/ViewCompany.php +++ b/app/Filament/Central/Resources/CompanyResource/Pages/ViewCompany.php @@ -110,13 +110,13 @@ class ViewCompany extends Page { return [ Actions\Action::make('view_as') - ->label('Deschide tenantul') + ->label(__('Deschide tenantul')) ->icon('heroicon-m-arrow-top-right-on-square') ->color('primary') ->url(fn () => $this->record->url('/app')) ->openUrlInNewTab(), Actions\Action::make('issue_invoice') - ->label('Generează factură') + ->label(__('Generează factură')) ->icon('heroicon-m-document-plus') ->color('success') ->visible(fn () => (bool) $this->record->plan_id) @@ -147,7 +147,7 @@ class ViewCompany extends Page $this->dispatch('$refresh'); }), Actions\Action::make('upload_logo') - ->label('Logo') + ->label(__('Logo')) ->icon('heroicon-m-photo') ->color('gray') ->schema([ @@ -166,29 +166,29 @@ class ViewCompany extends Page $this->record->clearMediaCollection('logo'); $this->record->addMedia($abs)->preservingOriginal()->toMediaCollection('logo'); @unlink($abs); - Notification::make()->title('Logo actualizat')->success()->send(); + Notification::make()->title(__('Logo actualizat'))->success()->send(); } }), Actions\Action::make('edit') - ->label('Editează') + ->label(__('Editează')) ->icon('heroicon-m-pencil') ->url(fn () => CompanyResource::getUrl('edit', ['record' => $this->record])), Actions\Action::make('suspend') - ->label('Suspendă')->icon('heroicon-m-no-symbol')->color('danger') + ->label(__('Suspendă'))->icon('heroicon-m-no-symbol')->color('danger') ->visible(fn () => in_array($this->record->status, ['active', 'trial'])) ->requiresConfirmation() ->action(function () { app(\App\Services\CompanyProvisioner::class)->suspend($this->record); - Notification::make()->title('Tenant suspendat.')->success()->send(); + Notification::make()->title(__('Tenant suspendat.'))->success()->send(); $this->dispatch('$refresh'); }), Actions\Action::make('activate') - ->label('Reactivează')->icon('heroicon-m-check-circle')->color('success') + ->label(__('Reactivează'))->icon('heroicon-m-check-circle')->color('success') ->visible(fn () => in_array($this->record->status, ['suspended', 'expired'])) ->requiresConfirmation() ->action(function () { app(\App\Services\CompanyProvisioner::class)->reactivate($this->record); - Notification::make()->title('Tenant activat.')->success()->send(); + Notification::make()->title(__('Tenant activat.'))->success()->send(); $this->dispatch('$refresh'); }), ]; diff --git a/app/Filament/Central/Resources/PlanResource.php b/app/Filament/Central/Resources/PlanResource.php index f649abb..b57d734 100644 --- a/app/Filament/Central/Resources/PlanResource.php +++ b/app/Filament/Central/Resources/PlanResource.php @@ -46,7 +46,7 @@ class PlanResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ Forms\Components\TextInput::make('slug') @@ -54,25 +54,25 @@ class PlanResource extends Resource ->dehydrateStateUsing(fn ($s) => strtolower((string) $s)) ->extraInputAttributes(['style' => 'text-transform:lowercase']), Forms\Components\TextInput::make('name')->required()->maxLength(60), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), - Forms\Components\Toggle::make('is_public')->label('Public (afișat la signup)')->default(true), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), + Forms\Components\Toggle::make('is_public')->label(__('Public (afișat la signup)'))->default(true), Forms\Components\Toggle::make('is_demo') - ->label('Demo (pentru demonstrații sales)') - ->helperText('Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.') + ->label(__('Demo (pentru demonstrații sales)')) + ->helperText(__('Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.')) ->default(false), Forms\Components\TextInput::make('trial_days') - ->label('Perioadă trial (zile)') - ->numeric()->placeholder('null = fără trial') - ->helperText('Numărul de zile gratis după ce un tenant alege acest plan.'), + ->label(__('Perioadă trial (zile)')) + ->numeric()->placeholder(__('null = fără trial')) + ->helperText(__('Numărul de zile gratis după ce un tenant alege acest plan.')), ]), - Schemas\Components\Section::make('Preț') + Schemas\Components\Section::make(__('Preț')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('price_monthly')->label('Preț lunar')->numeric()->required()->suffix(fn (Get $get) => $get('currency') ?? 'MDL'), - Forms\Components\TextInput::make('price_yearly')->label('Preț anual')->numeric()->helperText('De obicei 10× preț lunar (2 luni gratis).')->suffix(fn (Get $get) => $get('currency') ?? 'MDL'), + Forms\Components\TextInput::make('price_monthly')->label(__('Preț lunar'))->numeric()->required()->suffix(fn (Get $get) => $get('currency') ?? 'MDL'), + Forms\Components\TextInput::make('price_yearly')->label(__('Preț anual'))->numeric()->helperText(__('De obicei 10× preț lunar (2 luni gratis).'))->suffix(fn (Get $get) => $get('currency') ?? 'MDL'), Forms\Components\Select::make('currency')->options(['MDL' => 'MDL', 'EUR' => 'EUR', 'USD' => 'USD'])->default('MDL'), ]), - Schemas\Components\Section::make('Funcționalități incluse') + Schemas\Components\Section::make(__('Funcționalități incluse')) ->columns(2) ->schema([ Forms\Components\CheckboxList::make('features') @@ -80,15 +80,15 @@ class PlanResource extends Resource ->columns(2) ->columnSpanFull(), ]), - Schemas\Components\Section::make('Limite') + Schemas\Components\Section::make(__('Limite')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('limits.max_users')->label('Max useri')->numeric()->placeholder('∞'), - Forms\Components\TextInput::make('limits.max_clients')->label('Max clienți')->numeric()->placeholder('∞'), - Forms\Components\TextInput::make('limits.max_vehicles')->label('Max mașini')->numeric()->placeholder('∞'), - Forms\Components\TextInput::make('limits.max_work_orders_month')->label('Max fișe/lună')->numeric()->placeholder('∞'), - Forms\Components\TextInput::make('limits.storage_mb')->label('Storage (MB)')->numeric()->placeholder('∞'), - Forms\Components\TextInput::make('limits.ai_messages_month')->label('Mesaje AI/lună')->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.max_users')->label(__('Max useri'))->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.max_clients')->label(__('Max clienți'))->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.max_vehicles')->label(__('Max mașini'))->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.max_work_orders_month')->label(__('Max fișe/lună'))->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.storage_mb')->label(__('Storage (MB)'))->numeric()->placeholder('∞'), + Forms\Components\TextInput::make('limits.ai_messages_month')->label(__('Mesaje AI/lună'))->numeric()->placeholder('∞'), ]), ]); } @@ -103,29 +103,29 @@ class PlanResource extends Resource ->description(fn ($record) => $record->is_demo ? '🎬 Demo' : ($record->trial_days ? "🎁 {$record->trial_days} zile trial" : null)), Tables\Columns\TextColumn::make('price_monthly') ->money(fn ($record) => $record->currency) - ->label('Lunar') + ->label(__('Lunar')) ->sortable(), Tables\Columns\TextColumn::make('price_yearly') ->money(fn ($record) => $record->currency) - ->label('Anual'), + ->label(__('Anual')), Tables\Columns\TextColumn::make('companies_count') ->counts('companies') - ->label('Abonați') + ->label(__('Abonați')) ->badge() ->color('primary'), Tables\Columns\TextColumn::make('features') - ->label('Funcționalități') + ->label(__('Funcționalități')) ->formatStateUsing(fn ($s) => is_array($s) ? count($s) . ' incluse' : '—') ->badge(), - Tables\Columns\IconColumn::make('is_active')->boolean()->label('Activ'), - Tables\Columns\IconColumn::make('is_public')->boolean()->label('Public'), + Tables\Columns\IconColumn::make('is_active')->boolean()->label(__('Activ')), + Tables\Columns\IconColumn::make('is_public')->boolean()->label(__('Public')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun plan definit') - ->emptyStateDescription('Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.') + ->emptyStateHeading(__('Niciun plan definit')) + ->emptyStateDescription(__('Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.')) ->defaultSort('price_monthly'); } diff --git a/app/Filament/Central/Resources/SubscriptionResource.php b/app/Filament/Central/Resources/SubscriptionResource.php index b099d4d..b906bd7 100644 --- a/app/Filament/Central/Resources/SubscriptionResource.php +++ b/app/Filament/Central/Resources/SubscriptionResource.php @@ -28,7 +28,12 @@ class SubscriptionResource extends Resource return __('nav.label.Facturi & abonamente'); } - protected static ?string $modelLabel = 'factură'; + protected static ?string $modelLabel = null; + + public static function getModelLabel(): string + { + return __('factură'); + } protected static ?string $pluralModelLabel = 'facturi'; @@ -45,15 +50,15 @@ class SubscriptionResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Companie & plan') + Schemas\Components\Section::make(__('Companie & plan')) ->columns(2) ->schema([ Forms\Components\Select::make('company_id') - ->label('Companie') + ->label(__('Companie')) ->options(fn () => Company::orderBy('name')->pluck('name', 'id')) ->required()->searchable()->live(), Forms\Components\Select::make('plan_id') - ->label('Plan') + ->label(__('Plan')) ->options(fn () => Plan::pluck('name', 'id')) ->required()->searchable()->live() ->afterStateUpdated(function ($state, Set $set, Get $get) { @@ -80,7 +85,7 @@ class SubscriptionResource extends Resource Forms\Components\Select::make('status') ->options(Subscription::STATUSES)->default('pending')->required(), ]), - Schemas\Components\Section::make('Sumă') + Schemas\Components\Section::make(__('Sumă')) ->columns(3) ->schema([ Forms\Components\TextInput::make('amount')->numeric()->required()->suffix(fn (Get $get) => $get('currency') ?? 'MDL'), @@ -88,19 +93,19 @@ class SubscriptionResource extends Resource Forms\Components\Select::make('payment_method') ->options(Subscription::PAYMENT_METHODS), ]), - Schemas\Components\Section::make('Perioadă') + Schemas\Components\Section::make(__('Perioadă')) ->columns(3) ->schema([ Forms\Components\DatePicker::make('period_start')->required()->default(today()), Forms\Components\DatePicker::make('period_end')->required(), - Forms\Components\DateTimePicker::make('due_at')->label('Scadent la'), - Forms\Components\DateTimePicker::make('paid_at')->label('Plătit la'), + Forms\Components\DateTimePicker::make('due_at')->label(__('Scadent la')), + Forms\Components\DateTimePicker::make('paid_at')->label(__('Plătit la')), ]), - Schemas\Components\Section::make('Detalii') + Schemas\Components\Section::make(__('Detalii')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('invoice_number')->label('Nr. factură')->placeholder('auto-generat')->maxLength(30), - Forms\Components\TextInput::make('reference')->label('Referință (Stripe id, transfer)')->maxLength(100), + Forms\Components\TextInput::make('invoice_number')->label(__('Nr. factură'))->placeholder('auto-generat')->maxLength(30), + Forms\Components\TextInput::make('reference')->label(__('Referință (Stripe id, transfer)'))->maxLength(100), Forms\Components\Textarea::make('notes')->columnSpanFull()->rows(2), ]), ]); @@ -111,15 +116,15 @@ class SubscriptionResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('invoice_number') - ->label('Factură') + ->label(__('Factură')) ->placeholder(fn ($record) => '—') ->copyable() ->searchable(), Tables\Columns\TextColumn::make('company.name') - ->label('Companie') + ->label(__('Companie')) ->searchable() ->url(fn ($record) => \App\Filament\Central\Resources\CompanyResource::getUrl('view', ['record' => $record->company_id])), - Tables\Columns\TextColumn::make('plan.name')->label('Plan')->placeholder('—'), + Tables\Columns\TextColumn::make('plan.name')->label(__('Plan'))->placeholder('—'), Tables\Columns\TextColumn::make('period') ->formatStateUsing(fn ($s) => Subscription::PERIODS[$s] ?? $s) ->badge(), @@ -137,8 +142,8 @@ class SubscriptionResource extends Resource 'cancelled', 'refunded' => 'gray', default => 'primary', }), - Tables\Columns\TextColumn::make('period_end')->label('Până la')->date(), - Tables\Columns\TextColumn::make('paid_at')->label('Plătit')->date()->placeholder('—'), + Tables\Columns\TextColumn::make('period_end')->label(__('Până la'))->date(), + Tables\Columns\TextColumn::make('paid_at')->label(__('Plătit'))->date()->placeholder('—'), ]) ->filters([ Tables\Filters\SelectFilter::make('status')->options(Subscription::STATUSES), @@ -146,7 +151,7 @@ class SubscriptionResource extends Resource ]) ->actions([ Actions\Action::make('mark_paid') - ->label('Marchează plătit') + ->label(__('Marchează plătit')) ->icon('heroicon-m-check-circle') ->color('success') ->visible(fn ($record) => $record->status !== 'paid') @@ -163,8 +168,8 @@ class SubscriptionResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Nicio factură generată') - ->emptyStateDescription('Crează manual prima factură sau folosește butonul „Generează factură" din pagina Companiei.') + ->emptyStateHeading(__('Nicio factură generată')) + ->emptyStateDescription(__('Crează manual prima factură sau folosește butonul „Generează factură" din pagina Companiei.')) ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Central/Resources/SuperAdminResource.php b/app/Filament/Central/Resources/SuperAdminResource.php index 21b84eb..e56d8f3 100644 --- a/app/Filament/Central/Resources/SuperAdminResource.php +++ b/app/Filament/Central/Resources/SuperAdminResource.php @@ -36,24 +36,24 @@ class SuperAdminResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ Forms\Components\TextInput::make('name')->required()->maxLength(120), Forms\Components\TextInput::make('email')->email()->required()->unique(ignoreRecord: true)->maxLength(120), Forms\Components\TextInput::make('phone')->tel()->maxLength(40), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), - Schemas\Components\Section::make('Permisiuni') + Schemas\Components\Section::make(__('Permisiuni')) ->columns(1) ->schema([ Forms\Components\Select::make('role') ->options(SuperAdmin::ROLES) ->default('support') ->required() - ->helperText('Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.'), + ->helperText(__('Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.')), ]), - Schemas\Components\Section::make('Parolă') + Schemas\Components\Section::make(__('Parolă')) ->columns(1) ->schema([ Forms\Components\TextInput::make('password') @@ -65,7 +65,7 @@ class SuperAdminResource extends Resource ->minLength(8) ->helperText(fn (string $operation) => $operation === 'edit' ? 'Lasă gol ca să o păstrezi.' : 'Min 8 caractere.'), ]), - Forms\Components\Textarea::make('notes')->label('Note interne')->rows(2)->columnSpanFull(), + Forms\Components\Textarea::make('notes')->label(__('Note interne'))->rows(2)->columnSpanFull(), ]); } @@ -92,25 +92,25 @@ class SuperAdminResource extends Resource 'sales' => 'info', default => 'gray', }), - Tables\Columns\IconColumn::make('is_active')->boolean()->label('Activ'), + Tables\Columns\IconColumn::make('is_active')->boolean()->label(__('Activ')), Tables\Columns\IconColumn::make('app_authentication_secret') - ->label('2FA') + ->label(__('2FA')) ->boolean() ->getStateUsing(fn ($record) => $record?->app_authentication_secret !== null) ->trueIcon('heroicon-o-lock-closed') ->falseIcon('heroicon-o-lock-open') ->trueColor('success') ->falseColor('warning'), - Tables\Columns\TextColumn::make('last_login_at')->label('Ultim login')->dateTime()->placeholder('—'), + Tables\Columns\TextColumn::make('last_login_at')->label(__('Ultim login'))->dateTime()->placeholder('—'), Tables\Columns\TextColumn::make('created_at')->date(), ]) ->filters([ Tables\Filters\SelectFilter::make('role')->options(SuperAdmin::ROLES), - Tables\Filters\TernaryFilter::make('is_active')->label('Activ'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Activ')), ]) ->actions([ Actions\Action::make('reset_password') - ->label('Reset parolă') + ->label(__('Reset parolă')) ->icon('heroicon-m-key') ->color('warning') ->schema([ @@ -119,7 +119,7 @@ class SuperAdminResource extends Resource ]) ->action(function (SuperAdmin $record, array $data) { $record->update(['password' => Hash::make($data['new_password'])]); - Notification::make()->title('Parolă resetată.')->success()->send(); + Notification::make()->title(__('Parolă resetată.'))->success()->send(); }), Actions\Action::make('toggle_2fa') ->label(fn (SuperAdmin $record) => $record->app_authentication_secret ? 'Dezactivează 2FA' : 'Forțează re-setare 2FA') @@ -133,19 +133,19 @@ class SuperAdminResource extends Resource 'app_authentication_recovery_codes' => null, 'email_authentication_at' => null, ])->saveQuietly(); - Notification::make()->title('2FA dezactivat.')->success()->send(); + Notification::make()->title(__('2FA dezactivat.'))->success()->send(); }), Actions\EditAction::make(), Actions\DeleteAction::make() ->before(function (SuperAdmin $record) { if (auth('central')->id() === $record->id) { - Notification::make()->title('Nu te poți șterge pe tine!')->danger()->send(); + Notification::make()->title(__('Nu te poți șterge pe tine!'))->danger()->send(); return false; } }), ]) - ->emptyStateHeading('Doar tu ești aici') - ->emptyStateDescription('Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.') + ->emptyStateHeading(__('Doar tu ești aici')) + ->emptyStateDescription(__('Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.')) ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Central/Widgets/PendingPayments.php b/app/Filament/Central/Widgets/PendingPayments.php index 39c855b..b6a3b3b 100644 --- a/app/Filament/Central/Widgets/PendingPayments.php +++ b/app/Filament/Central/Widgets/PendingPayments.php @@ -17,7 +17,7 @@ class PendingPayments extends BaseWidget public function table(Table $table): Table { return $table - ->heading('Facturi pending / overdue') + ->heading(__('Facturi pending / overdue')) ->query( Subscription::query() ->whereIn('status', ['pending', 'overdue']) @@ -27,10 +27,10 @@ class PendingPayments extends BaseWidget ) ->columns([ Tables\Columns\TextColumn::make('invoice_number') - ->label('Factură') + ->label(__('Factură')) ->copyable() ->url(fn ($record) => SubscriptionResource::getUrl('edit', ['record' => $record])), - Tables\Columns\TextColumn::make('company.name')->label('Companie'), + Tables\Columns\TextColumn::make('company.name')->label(__('Companie')), Tables\Columns\TextColumn::make('plan.name')->placeholder('—'), Tables\Columns\TextColumn::make('amount')->money(fn ($record) => $record->currency)->weight('bold'), Tables\Columns\TextColumn::make('status') @@ -38,11 +38,11 @@ class PendingPayments extends BaseWidget ->color(fn ($s) => $s === 'overdue' ? 'danger' : 'warning') ->formatStateUsing(fn ($s) => Subscription::STATUSES[$s] ?? $s), Tables\Columns\TextColumn::make('due_at') - ->label('Scadent') + ->label(__('Scadent')) ->dateTime() ->color(fn ($record) => $record->due_at && $record->due_at->isPast() ? 'danger' : null), ]) - ->emptyStateHeading('🎉 Toate facturile sunt plătite') + ->emptyStateHeading(__('🎉 Toate facturile sunt plătite')) ->paginated(false); } } diff --git a/app/Filament/Central/Widgets/PlatformStats.php b/app/Filament/Central/Widgets/PlatformStats.php index 8468a83..f9d74ca 100644 --- a/app/Filament/Central/Widgets/PlatformStats.php +++ b/app/Filament/Central/Widgets/PlatformStats.php @@ -39,7 +39,7 @@ class PlatformStats extends BaseWidget ->color('primary') ->url(\App\Filament\Central\Resources\CompanyResource::getUrl('index')), Stat::make('Active', $active) - ->description("✓ Pe abonament plătit") + ->description(__("✓ Pe abonament plătit")) ->descriptionIcon('heroicon-m-check-circle') ->color('success'), Stat::make('În trial', $trial) @@ -51,7 +51,7 @@ class PlatformStats extends BaseWidget ->descriptionIcon('heroicon-m-exclamation-triangle') ->color($expiring > 0 ? 'danger' : 'success'), Stat::make('Venit luna curentă', number_format($mrr, 0, ',', ' ') . ' MDL') - ->description('MRR realizat') + ->description(__('MRR realizat')) ->descriptionIcon('heroicon-m-banknotes') ->color('success'), Stat::make('Facturi neplătite', $overdue) diff --git a/app/Filament/Central/Widgets/RecentTenants.php b/app/Filament/Central/Widgets/RecentTenants.php index ec6b489..c59592b 100644 --- a/app/Filament/Central/Widgets/RecentTenants.php +++ b/app/Filament/Central/Widgets/RecentTenants.php @@ -17,7 +17,7 @@ class RecentTenants extends BaseWidget public function table(Table $table): Table { return $table - ->heading('Tenants recenți') + ->heading(__('Tenants recenți')) ->query(Company::query()->latest()->limit(8)) ->columns([ Tables\Columns\TextColumn::make('slug') @@ -33,8 +33,8 @@ class RecentTenants extends BaseWidget default => 'gray', }), Tables\Columns\TextColumn::make('plan.name')->placeholder('—'), - Tables\Columns\TextColumn::make('users_count')->counts('users')->label('Useri'), - Tables\Columns\TextColumn::make('created_at')->label('Creat')->since(), + Tables\Columns\TextColumn::make('users_count')->counts('users')->label(__('Useri')), + Tables\Columns\TextColumn::make('created_at')->label(__('Creat'))->since(), ]) ->paginated(false); } diff --git a/app/Filament/Tenant/Pages/AiAssistant.php b/app/Filament/Tenant/Pages/AiAssistant.php index 706289c..bb3decc 100644 --- a/app/Filament/Tenant/Pages/AiAssistant.php +++ b/app/Filament/Tenant/Pages/AiAssistant.php @@ -24,7 +24,12 @@ class AiAssistant extends Page protected static ?int $navigationSort = 71; - protected static ?string $title = 'Asistent AI'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Asistent AI'); + } protected string $view = 'filament.tenant.pages.ai-assistant'; diff --git a/app/Filament/Tenant/Pages/ApiTokens.php b/app/Filament/Tenant/Pages/ApiTokens.php index 8ee1238..83b6fed 100644 --- a/app/Filament/Tenant/Pages/ApiTokens.php +++ b/app/Filament/Tenant/Pages/ApiTokens.php @@ -23,7 +23,12 @@ class ApiTokens extends Page protected static ?int $navigationSort = 95; - protected static ?string $title = 'API Tokens'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('API Tokens'); + } protected string $view = 'filament.tenant.pages.api-tokens'; @@ -46,13 +51,13 @@ class ApiTokens extends Page { return [ Actions\Action::make('create') - ->label('Generează token') + ->label(__('Generează token')) ->icon('heroicon-m-plus') ->schema([ Forms\Components\TextInput::make('name') - ->label('Nume (descriere)') + ->label(__('Nume (descriere)')) ->required() - ->placeholder('ex: app-mobil, integrare-erp'), + ->placeholder(__('ex: app-mobil, integrare-erp')), ]) ->action(function (array $data) { $u = auth()->user(); @@ -60,8 +65,8 @@ class ApiTokens extends Page $token = $u->createToken($data['name']); $this->newToken = $token->plainTextToken; Notification::make() - ->title('Token generat!') - ->body('Copiază token-ul ACUM — nu va mai fi afișat.') + ->title(__('Token generat!')) + ->body(__('Copiază token-ul ACUM — nu va mai fi afișat.')) ->success() ->persistent() ->send(); diff --git a/app/Filament/Tenant/Pages/Backup.php b/app/Filament/Tenant/Pages/Backup.php index e5076ef..3094a95 100644 --- a/app/Filament/Tenant/Pages/Backup.php +++ b/app/Filament/Tenant/Pages/Backup.php @@ -24,7 +24,12 @@ class Backup extends Page protected static ?int $navigationSort = 96; - protected static ?string $title = 'Backup & Export date'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Backup & Export date'); + } protected string $view = 'filament.tenant.pages.backup'; diff --git a/app/Filament/Tenant/Pages/CalendarBoard.php b/app/Filament/Tenant/Pages/CalendarBoard.php index 0fa3869..972b4de 100644 --- a/app/Filament/Tenant/Pages/CalendarBoard.php +++ b/app/Filament/Tenant/Pages/CalendarBoard.php @@ -27,7 +27,12 @@ class CalendarBoard extends Page protected static ?int $navigationSort = 8; - protected static ?string $title = 'Calendar vizual'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Calendar vizual'); + } protected string $view = 'filament.tenant.pages.calendar'; @@ -290,7 +295,7 @@ class CalendarBoard extends Page } $a->date = $toDate; $a->save(); - Notification::make()->title('Programare mutată')->body($a->title . ' → ' . $toDate)->success()->send(); + Notification::make()->title(__('Programare mutată'))->body($a->title . ' → ' . $toDate)->success()->send(); } public function openEvent(int $id): void @@ -330,7 +335,7 @@ class CalendarBoard extends Page { Appointment::where('id', $id)->delete(); $this->openEventId = null; - Notification::make()->title('Programare ștearsă')->success()->send(); + Notification::make()->title(__('Programare ștearsă'))->success()->send(); } public function openNewForm(int $rowId = 0, string $date = ''): void @@ -372,7 +377,7 @@ class CalendarBoard extends Page { $name = trim($this->newPost['name'] ?? ''); if ($name === '') { - Notification::make()->title('Numele este obligatoriu')->danger()->send(); + Notification::make()->title(__('Numele este obligatoriu'))->danger()->send(); return; } Post::create([ @@ -384,7 +389,7 @@ class CalendarBoard extends Page 'sort_order' => 100, ]); $this->showNewPostForm = false; - Notification::make()->title('Spațiu de lucru adăugat')->success()->send(); + Notification::make()->title(__('Spațiu de lucru adăugat'))->success()->send(); } /** Inline rename + reassign default master from row label click. */ @@ -409,7 +414,7 @@ class CalendarBoard extends Page 'default_master_id' => $this->renamingPostMasterId ?: null, ]); $this->renamingPostId = null; - Notification::make()->title('Post actualizat')->success()->send(); + Notification::make()->title(__('Post actualizat'))->success()->send(); } /** Generate PDF for all appointments in the visible period. */ @@ -467,7 +472,7 @@ class CalendarBoard extends Page { $d = $this->newAppt; if (empty($d['title']) || empty($d['date'])) { - Notification::make()->title('Subiect și data sunt obligatorii')->danger()->send(); + Notification::make()->title(__('Subiect și data sunt obligatorii'))->danger()->send(); return; } Appointment::create([ @@ -483,7 +488,7 @@ class CalendarBoard extends Page 'status' => 'scheduled', ]); $this->showNewForm = false; - Notification::make()->title('Programare adăugată')->success()->send(); + Notification::make()->title(__('Programare adăugată'))->success()->send(); } public function getMasterOptions(): array diff --git a/app/Filament/Tenant/Pages/ExcelImportWizard.php b/app/Filament/Tenant/Pages/ExcelImportWizard.php index 5c8eef6..3033e91 100644 --- a/app/Filament/Tenant/Pages/ExcelImportWizard.php +++ b/app/Filament/Tenant/Pages/ExcelImportWizard.php @@ -27,7 +27,12 @@ class ExcelImportWizard extends Page protected static ?int $navigationSort = 65; - protected static ?string $title = 'Import factură Excel/CSV'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Import factură Excel/CSV'); + } protected string $view = 'filament.tenant.pages.excel-import-wizard'; @@ -62,11 +67,11 @@ class ExcelImportWizard extends Page public function goToStep2(): void { if (! $this->supplierId) { - Notification::make()->title('Selectează furnizorul')->danger()->send(); + Notification::make()->title(__('Selectează furnizorul'))->danger()->send(); return; } if (! $this->upload) { - Notification::make()->title('Încarcă fișierul Excel sau CSV')->danger()->send(); + Notification::make()->title(__('Încarcă fișierul Excel sau CSV'))->danger()->send(); return; } @@ -96,7 +101,7 @@ class ExcelImportWizard extends Page $this->previewSummary = $result['summary']; if (empty($this->previewRows)) { - Notification::make()->title('Nu am găsit linii valide — verifică maparea coloanelor')->warning()->send(); + Notification::make()->title(__('Nu am găsit linii valide — verifică maparea coloanelor'))->warning()->send(); return; } diff --git a/app/Filament/Tenant/Pages/Finance.php b/app/Filament/Tenant/Pages/Finance.php index 2b77363..aed2226 100644 --- a/app/Filament/Tenant/Pages/Finance.php +++ b/app/Filament/Tenant/Pages/Finance.php @@ -26,7 +26,12 @@ class Finance extends Page protected static ?int $navigationSort = 49; - protected static ?string $title = 'Finanțe'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Finanțe'); + } protected string $view = 'filament.tenant.pages.finance'; diff --git a/app/Filament/Tenant/Pages/Integrations.php b/app/Filament/Tenant/Pages/Integrations.php index a24e381..e6281c1 100644 --- a/app/Filament/Tenant/Pages/Integrations.php +++ b/app/Filament/Tenant/Pages/Integrations.php @@ -22,7 +22,12 @@ class Integrations extends Page protected static ?int $navigationSort = 63; - protected static ?string $title = 'Integrări externe'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Integrări externe'); + } protected string $view = 'filament.tenant.pages.integrations'; @@ -54,7 +59,7 @@ class Integrations extends Page 'integrations' => $this->config, ]), ]); - Notification::make()->title('Integrări salvate')->success()->send(); + Notification::make()->title(__('Integrări salvate'))->success()->send(); } public function integrations(): array diff --git a/app/Filament/Tenant/Pages/MechanicBoard.php b/app/Filament/Tenant/Pages/MechanicBoard.php index 51d7c4c..dcbf078 100644 --- a/app/Filament/Tenant/Pages/MechanicBoard.php +++ b/app/Filament/Tenant/Pages/MechanicBoard.php @@ -28,7 +28,12 @@ class MechanicBoard extends Page protected static ?int $navigationSort = 25; - protected static ?string $title = 'Atelierul meu'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Atelierul meu'); + } protected string $view = 'filament.tenant.pages.mechanic-board'; @@ -46,25 +51,25 @@ class MechanicBoard extends Page return [ [ 'key' => 'in_work', - 'label' => 'În lucru', + 'label' => __('În lucru'), 'color' => '#f59e0b', 'items' => $all->where('status', 'in_work')->values(), ], [ 'key' => 'awaiting_parts', - 'label' => 'Așteaptă piese', + 'label' => __('Așteaptă piese'), 'color' => '#8b5cf6', 'items' => $all->whereIn('status', ['awaiting_parts'])->values(), ], [ 'key' => 'ready', - 'label' => 'Gata', + 'label' => __('Gata'), 'color' => '#10b981', 'items' => $all->where('status', 'ready')->values(), ], [ 'key' => 'recent', - 'label' => 'Recente / restul', + 'label' => __('Recente / restul'), 'color' => '#64748b', 'items' => $all->whereIn('status', ['done', 'approved', 'diagnosis']) ->take(20) @@ -119,7 +124,7 @@ class MechanicBoard extends Page return; } $work->block($this->blockReason, trim($this->blockNote) ?: null); - Notification::make()->title('Lucrare blocată')->body($work->name . ' · ' . WorkOrderWork::BLOCK_REASONS[$this->blockReason])->warning()->send(); + Notification::make()->title(__('Lucrare blocată'))->body($work->name . ' · ' . WorkOrderWork::BLOCK_REASONS[$this->blockReason])->warning()->send(); $this->blockingWorkId = null; } diff --git a/app/Filament/Tenant/Pages/MechanicKpi.php b/app/Filament/Tenant/Pages/MechanicKpi.php index 82fcb75..be602f9 100644 --- a/app/Filament/Tenant/Pages/MechanicKpi.php +++ b/app/Filament/Tenant/Pages/MechanicKpi.php @@ -28,7 +28,12 @@ class MechanicKpi extends Page protected static ?int $navigationSort = 28; - protected static ?string $title = 'KPI mecanici'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('KPI mecanici'); + } protected string $view = 'filament.tenant.pages.mechanic-kpi'; diff --git a/app/Filament/Tenant/Pages/Onboarding.php b/app/Filament/Tenant/Pages/Onboarding.php index 17e0490..594fd1c 100644 --- a/app/Filament/Tenant/Pages/Onboarding.php +++ b/app/Filament/Tenant/Pages/Onboarding.php @@ -19,7 +19,12 @@ class Onboarding extends Page protected static bool $shouldRegisterNavigation = false; - protected static ?string $title = 'Bun venit în AutoCRM!'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Bun venit în AutoCRM!'); + } protected string $view = 'filament.tenant.pages.onboarding'; @@ -54,18 +59,18 @@ class Onboarding extends Page { return $schema ->components([ - Schemas\Components\Section::make('Pas 1 — Datele afacerii') + Schemas\Components\Section::make(__('Pas 1 — Datele afacerii')) ->visible(fn () => $this->step === 1) ->columns(2) ->schema([ Forms\Components\TextInput::make('display_name') - ->label('Denumire afișată')->required()->maxLength(120), + ->label(__('Denumire afișată'))->required()->maxLength(120), Forms\Components\TextInput::make('city') - ->label('Oraș')->maxLength(60), + ->label(__('Oraș'))->maxLength(60), Forms\Components\TextInput::make('phone') - ->label('Telefon principal')->tel()->maxLength(40), + ->label(__('Telefon principal'))->tel()->maxLength(40), Forms\Components\Select::make('currency') - ->label('Monedă') + ->label(__('Monedă')) ->options([ 'MDL' => 'MDL — Leu moldovenesc', 'EUR' => 'EUR — Euro', @@ -78,28 +83,28 @@ class Onboarding extends Page ->required() ->searchable(), ]), - Schemas\Components\Section::make('Pas 2 — Brand & limbă') + Schemas\Components\Section::make(__('Pas 2 — Brand & limbă')) ->visible(fn () => $this->step === 2) ->columns(2) ->schema([ Forms\Components\Select::make('language') - ->label('Limbă') + ->label(__('Limbă')) ->options(['ro' => 'Română', 'ru' => 'Русский', 'en' => 'English']) ->required(), Forms\Components\ColorPicker::make('theme_color') - ->label('Culoare brand'), + ->label(__('Culoare brand')), Forms\Components\FileUpload::make('logo') - ->label('Logo (opțional)') + ->label(__('Logo (opțional)')) ->image()->imageEditor()->disk('public') ->directory('tmp-uploads')->visibility('public') ->maxSize(2048), ]), - Schemas\Components\Section::make('Pas 3 — Tarif & terminat') + Schemas\Components\Section::make(__('Pas 3 — Tarif & terminat')) ->visible(fn () => $this->step === 3) ->columns(1) ->schema([ Forms\Components\TextInput::make('labor_rate') - ->label('Tarif normo-oră (poți schimba oricând)') + ->label(__('Tarif normo-oră (poți schimba oricând)')) ->numeric()->required(), ]), ]) @@ -147,8 +152,8 @@ class Onboarding extends Page } Notification::make() - ->title('🎉 Bun venit în AutoCRM!') - ->body('Setările au fost salvate. Hai să adăugăm primul client.') + ->title(__('🎉 Bun venit în AutoCRM!')) + ->body(__('Setările au fost salvate. Hai să adăugăm primul client.')) ->success() ->send(); diff --git a/app/Filament/Tenant/Pages/PipelineBoard.php b/app/Filament/Tenant/Pages/PipelineBoard.php index 0245c72..e292e67 100644 --- a/app/Filament/Tenant/Pages/PipelineBoard.php +++ b/app/Filament/Tenant/Pages/PipelineBoard.php @@ -27,7 +27,12 @@ class PipelineBoard extends Page protected static ?int $navigationSort = 5; - protected static ?string $title = 'Pipeline'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Pipeline'); + } protected string $view = 'filament.tenant.pages.pipeline-board'; diff --git a/app/Filament/Tenant/Pages/Recommendations.php b/app/Filament/Tenant/Pages/Recommendations.php index 3f11ad4..a3f18f8 100644 --- a/app/Filament/Tenant/Pages/Recommendations.php +++ b/app/Filament/Tenant/Pages/Recommendations.php @@ -23,7 +23,12 @@ class Recommendations extends Page protected static ?int $navigationSort = 72; - protected static ?string $title = 'Recomandări — clienți de urmărit'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Recomandări — clienți de urmărit'); + } protected string $view = 'filament.tenant.pages.recommendations'; diff --git a/app/Filament/Tenant/Pages/Reports.php b/app/Filament/Tenant/Pages/Reports.php index 8cc6198..c6037ab 100644 --- a/app/Filament/Tenant/Pages/Reports.php +++ b/app/Filament/Tenant/Pages/Reports.php @@ -30,7 +30,12 @@ class Reports extends Page protected static ?int $navigationSort = 70; - protected static ?string $title = 'Rapoarte'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Rapoarte'); + } protected string $view = 'filament.tenant.pages.reports'; diff --git a/app/Filament/Tenant/Pages/Scanner.php b/app/Filament/Tenant/Pages/Scanner.php index da57e63..972ddd3 100644 --- a/app/Filament/Tenant/Pages/Scanner.php +++ b/app/Filament/Tenant/Pages/Scanner.php @@ -33,7 +33,12 @@ class Scanner extends Page protected static ?int $navigationSort = 39; - protected static ?string $title = 'Scaner cod QR / Bare'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Scaner cod QR / Bare'); + } protected string $view = 'filament.tenant.pages.scanner'; diff --git a/app/Filament/Tenant/Pages/Settings.php b/app/Filament/Tenant/Pages/Settings.php index 9500cfa..9ba5d63 100644 --- a/app/Filament/Tenant/Pages/Settings.php +++ b/app/Filament/Tenant/Pages/Settings.php @@ -27,7 +27,12 @@ class Settings extends Page protected static ?int $navigationSort = 90; - protected static ?string $title = 'Setări companie'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Setări companie'); + } protected string $view = 'filament.tenant.pages.settings'; @@ -81,23 +86,23 @@ class Settings extends Page { return $schema ->components([ - Schemas\Components\Section::make('Brand & contact') + Schemas\Components\Section::make(__('Brand & contact')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('display_name')->label('Denumire afișată')->maxLength(120), - Forms\Components\TextInput::make('city')->label('Oraș')->maxLength(60), - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->maxLength(40), + Forms\Components\TextInput::make('display_name')->label(__('Denumire afișată'))->maxLength(120), + Forms\Components\TextInput::make('city')->label(__('Oraș'))->maxLength(60), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), ]), - Schemas\Components\Section::make('Localizare & monedă') + Schemas\Components\Section::make(__('Localizare & monedă')) ->columns(3) ->schema([ Forms\Components\Select::make('language') - ->label('Limbă default') + ->label(__('Limbă default')) ->options(['ro' => 'Română', 'ru' => 'Русский', 'en' => 'English']) ->required(), Forms\Components\Select::make('currency') - ->label('Monedă') + ->label(__('Monedă')) ->options([ 'MDL' => 'MDL — Leu moldovenesc', 'EUR' => 'EUR — Euro', @@ -108,133 +113,133 @@ class Settings extends Page ]) ->required() ->searchable(), - Forms\Components\ColorPicker::make('theme_color')->label('Culoare brand'), + Forms\Components\ColorPicker::make('theme_color')->label(__('Culoare brand')), ]), - Schemas\Components\Section::make('Servicii & tarif') + Schemas\Components\Section::make(__('Servicii & tarif')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('labor_rate')->label('Tarif normo-oră')->numeric()->required(), + Forms\Components\TextInput::make('labor_rate')->label(__('Tarif normo-oră'))->numeric()->required(), ]), - Schemas\Components\Section::make('Marjă internă (nu TVA)') - ->description('Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin".') + Schemas\Components\Section::make(__('Marjă internă (nu TVA)')) + ->description(__('Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin".')) ->columns(2) ->visible(fn () => auth()->user()?->canDo(\App\Auth\Permissions::FINANCE_VIEW_INTERNAL_MARGIN) ?? false) ->schema([ Forms\Components\TextInput::make('default_internal_margin_pct') - ->label('% marjă implicit') + ->label(__('% marjă implicit')) ->numeric() ->step(0.01) ->minValue(0) ->maxValue(90) ->suffix('%') - ->placeholder('Ex: 20') - ->helperText('Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.'), + ->placeholder(__('Ex: 20')) + ->helperText(__('Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.')), Forms\Components\Toggle::make('show_internal_margin_details') - ->label('Afișează detalii marjă la procesele calculate') - ->helperText('On = arată „Bază salariu: X · marjă Y%" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).') + ->label(__('Afișează detalii marjă la procesele calculate')) + ->helperText(__('On = arată „Bază salariu: X · marjă Y%" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).')) ->default(true), ]), - Schemas\Components\Section::make('Liste configurabile') + Schemas\Components\Section::make(__('Liste configurabile')) ->columns(1) ->schema([ Forms\Components\Textarea::make('services') - ->label('Servicii oferite (separate prin virgulă)') + ->label(__('Servicii oferite (separate prin virgulă)')) ->rows(2), Forms\Components\Textarea::make('cars') - ->label('Mărci auto suportate (separate prin virgulă)') + ->label(__('Mărci auto suportate (separate prin virgulă)')) ->rows(2), ]), - Schemas\Components\Section::make('Logo & favicon') + Schemas\Components\Section::make(__('Logo & favicon')) ->columns(2) ->schema([ Forms\Components\FileUpload::make('logo') - ->label('Logo') + ->label(__('Logo')) ->image() ->imageEditor() ->disk('public') ->directory('tmp-uploads') ->visibility('public') ->maxSize(2048) - ->helperText('PNG/SVG, max 2 MB. Apare în sidebar.'), + ->helperText(__('PNG/SVG, max 2 MB. Apare în sidebar.')), Forms\Components\FileUpload::make('favicon') - ->label('Favicon') + ->label(__('Favicon')) ->image() ->disk('public') ->directory('tmp-uploads') ->visibility('public') ->maxSize(512) - ->helperText('PNG/ICO, max 512 KB.'), + ->helperText(__('PNG/ICO, max 512 KB.')), ]), - Schemas\Components\Section::make('Notificări') - ->description('Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.') + Schemas\Components\Section::make(__('Notificări')) + ->description(__('Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.')) ->columns(2) ->schema([ - Forms\Components\Toggle::make('notify_wo_ready')->label('Mașina e gata de ridicat')->default(true), - Forms\Components\Toggle::make('notify_payment')->label('Confirmare plată primită')->default(true), - Forms\Components\Toggle::make('notify_appointment')->label('Programare confirmată')->default(true), - Forms\Components\Toggle::make('notify_reminder')->label('Reminder ITP / revizie')->default(true), + Forms\Components\Toggle::make('notify_wo_ready')->label(__('Mașina e gata de ridicat'))->default(true), + Forms\Components\Toggle::make('notify_payment')->label(__('Confirmare plată primită'))->default(true), + Forms\Components\Toggle::make('notify_appointment')->label(__('Programare confirmată'))->default(true), + Forms\Components\Toggle::make('notify_reminder')->label(__('Reminder ITP / revizie'))->default(true), ]), - Schemas\Components\Section::make('Telegram bot') - ->description('Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.') + Schemas\Components\Section::make(__('Telegram bot')) + ->description(__('Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.')) ->columns(1) ->schema([ Forms\Components\TextInput::make('telegram_bot_token') - ->label('Bot token') + ->label(__('Bot token')) ->password() ->revealable() - ->placeholder('123456:ABC-XYZ...') + ->placeholder(__('123456:ABC-XYZ...')) ->helperText(fn () => 'Webhook URL: ' . app(\App\Services\Notifications\TelegramService::class) ->webhookUrlFor(app(\App\Tenancy\TenantManager::class)->current())), ]), - Schemas\Components\Section::make('Reminder service auto') + Schemas\Components\Section::make(__('Reminder service auto')) ->columns(2) ->schema([ Forms\Components\TextInput::make('reminder_after_days') - ->label('Trimite reminder după X zile fără vizită') + ->label(__('Trimite reminder după X zile fără vizită')) ->numeric() ->minValue(30) ->default(365), Forms\Components\TextInput::make('reminder_cooldown_days') - ->label('Nu re-trimite mai des de X zile') + ->label(__('Nu re-trimite mai des de X zile')) ->numeric() ->minValue(7) ->default(30), ]), - Schemas\Components\Section::make('Magazin online') - ->description('Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat".') + Schemas\Components\Section::make(__('Magazin online')) + ->description(__('Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat".')) ->columns(2) ->schema([ - Forms\Components\Toggle::make('shop_enabled')->label('Magazin activ')->columnSpanFull(), + Forms\Components\Toggle::make('shop_enabled')->label(__('Magazin activ'))->columnSpanFull(), Forms\Components\CheckboxList::make('shop_delivery_methods') - ->label('Metode de livrare') + ->label(__('Metode de livrare')) ->options(\App\Models\Tenant\OnlineOrder::DELIVERY) ->default(['pickup']) ->columnSpanFull(), - Forms\Components\TextInput::make('shop_delivery_fee')->label('Taxă livrare')->numeric()->default(0), - Forms\Components\TextInput::make('shop_free_delivery_over')->label('Livrare gratuită peste')->numeric()->default(0)->helperText('0 = dezactivat'), + Forms\Components\TextInput::make('shop_delivery_fee')->label(__('Taxă livrare'))->numeric()->default(0), + Forms\Components\TextInput::make('shop_free_delivery_over')->label(__('Livrare gratuită peste'))->numeric()->default(0)->helperText(__('0 = dezactivat')), ]), - Schemas\Components\Section::make('Asistent AI') - ->description('Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.') + Schemas\Components\Section::make(__('Asistent AI')) + ->description(__('Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.')) ->columns(2) ->schema([ Forms\Components\Select::make('ai_default_provider') - ->label('Provider implicit') + ->label(__('Provider implicit')) ->options(['claude' => 'Claude (Anthropic)', 'gpt' => 'ChatGPT (OpenAI)', 'gemini' => 'Gemini (Google)']) ->default('claude'), - Forms\Components\TextInput::make('ai_claude_key')->label('Claude API Key')->password()->revealable()->placeholder('sk-ant-...'), + Forms\Components\TextInput::make('ai_claude_key')->label(__('Claude API Key'))->password()->revealable()->placeholder('sk-ant-...'), Forms\Components\Select::make('ai_model_claude') - ->label('Model Claude') + ->label(__('Model Claude')) ->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['claude']) ->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['claude']), - Forms\Components\TextInput::make('ai_gpt_key')->label('OpenAI API Key')->password()->revealable()->placeholder('sk-proj-...'), + Forms\Components\TextInput::make('ai_gpt_key')->label(__('OpenAI API Key'))->password()->revealable()->placeholder('sk-proj-...'), Forms\Components\Select::make('ai_model_gpt') - ->label('Model OpenAI') + ->label(__('Model OpenAI')) ->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['gpt']) ->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gpt']), - Forms\Components\TextInput::make('ai_gemini_key')->label('Gemini API Key')->password()->revealable(), + Forms\Components\TextInput::make('ai_gemini_key')->label(__('Gemini API Key'))->password()->revealable(), Forms\Components\Select::make('ai_model_gemini') - ->label('Model Gemini') + ->label(__('Model Gemini')) ->options(\App\Services\Ai\AiAssistantService::MODEL_OPTIONS['gemini']) ->default(\App\Services\Ai\AiAssistantService::MODEL_DEFAULTS['gemini']), ]), @@ -247,7 +252,7 @@ class Settings extends Page $data = $this->form->getState(); $company = app(TenantManager::class)->current(); if (! $company) { - Notification::make()->title('Tenant not resolved')->danger()->send(); + Notification::make()->title(__('Tenant not resolved'))->danger()->send(); return; } @@ -312,22 +317,22 @@ class Settings extends Page } } - Notification::make()->title('Setări salvate')->success()->send(); + Notification::make()->title(__('Setări salvate'))->success()->send(); } protected function getHeaderActions(): array { return [ Actions\Action::make('push_test') - ->label('Test notificare push') + ->label(__('Test notificare push')) ->icon('heroicon-m-bell-alert') ->color('gray') ->action(function () { $svc = app(\App\Services\Notifications\WebPushService::class); if (! $svc->configured()) { Notification::make() - ->title('Web Push neconfigurat') - ->body('Rulează `php artisan push:vapid` și adaugă cheile în .env.') + ->title(__('Web Push neconfigurat')) + ->body(__('Rulează `php artisan push:vapid` și adaugă cheile în .env.')) ->warning()->send(); return; } @@ -344,7 +349,7 @@ class Settings extends Page ->send(); }), Actions\Action::make('telegram_test') - ->label('Testează bot Telegram') + ->label(__('Testează bot Telegram')) ->icon('heroicon-m-bolt') ->color('gray') ->action(function () { @@ -353,7 +358,7 @@ class Settings extends Page $r = app(TelegramService::class)->getMe($company); if (! ($r['ok'] ?? false)) { Notification::make() - ->title('Bot Telegram nu răspunde') + ->title(__('Bot Telegram nu răspunde')) ->body($r['error'] ?? 'Verifică token-ul.') ->danger()->send(); return; @@ -364,24 +369,24 @@ class Settings extends Page ->success()->send(); }), Actions\Action::make('telegram_webhook') - ->label('Setează webhook') + ->label(__('Setează webhook')) ->icon('heroicon-m-link') ->color('primary') ->requiresConfirmation() - ->modalDescription('Telegram va trimite mesajele primite la URL-ul webhook de mai jos.') + ->modalDescription(__('Telegram va trimite mesajele primite la URL-ul webhook de mai jos.')) ->action(function () { $company = app(TenantManager::class)->current(); if (! $company) return; $r = app(TelegramService::class)->setWebhook($company); if (! ($r['ok'] ?? false)) { Notification::make() - ->title('Webhook eșuat') + ->title(__('Webhook eșuat')) ->body($r['error'] ?? json_encode($r['response'] ?? [])) ->danger()->send(); return; } Notification::make() - ->title('Webhook setat — botul e gata') + ->title(__('Webhook setat — botul e gata')) ->success()->send(); }), ]; diff --git a/app/Filament/Tenant/Pages/VinSearch.php b/app/Filament/Tenant/Pages/VinSearch.php index 4f92c3e..74a332b 100644 --- a/app/Filament/Tenant/Pages/VinSearch.php +++ b/app/Filament/Tenant/Pages/VinSearch.php @@ -23,7 +23,12 @@ class VinSearch extends Page protected static ?int $navigationSort = 41; - protected static ?string $title = 'VIN căutare & piese'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('VIN căutare & piese'); + } protected string $view = 'filament.tenant.pages.vin-search'; diff --git a/app/Filament/Tenant/Pages/Workload.php b/app/Filament/Tenant/Pages/Workload.php index 9e72d4b..7b2312b 100644 --- a/app/Filament/Tenant/Pages/Workload.php +++ b/app/Filament/Tenant/Pages/Workload.php @@ -23,7 +23,12 @@ class Workload extends Page protected static ?int $navigationSort = 73; - protected static ?string $title = 'Încărcare service'; + protected static ?string $title = null; + + public function getTitle(): string + { + return __('Încărcare service'); + } protected string $view = 'filament.tenant.pages.workload'; diff --git a/app/Filament/Tenant/Resources/ActivityResource.php b/app/Filament/Tenant/Resources/ActivityResource.php index 9230012..930ceca 100644 --- a/app/Filament/Tenant/Resources/ActivityResource.php +++ b/app/Filament/Tenant/Resources/ActivityResource.php @@ -48,8 +48,8 @@ class ActivityResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('created_at')->label('Când')->dateTime('d.m.Y H:i')->sortable(), - Tables\Columns\TextColumn::make('description')->label('Acțiune')->badge() + Tables\Columns\TextColumn::make('created_at')->label(__('Când'))->dateTime('d.m.Y H:i')->sortable(), + Tables\Columns\TextColumn::make('description')->label(__('Acțiune'))->badge() ->colors([ 'success' => ['creat'], 'info' => ['modificat'], @@ -57,12 +57,12 @@ class ActivityResource extends Resource 'warning' => ['restaurat'], ]), Tables\Columns\TextColumn::make('subject_type') - ->label('Tip') + ->label(__('Tip')) ->formatStateUsing(fn ($s) => $s ? class_basename($s) : '—'), - Tables\Columns\TextColumn::make('subject_id')->label('ID')->placeholder('—'), - Tables\Columns\TextColumn::make('causer.name')->label('De către')->placeholder('Sistem'), + Tables\Columns\TextColumn::make('subject_id')->label(__('ID'))->placeholder('—'), + Tables\Columns\TextColumn::make('causer.name')->label(__('De către'))->placeholder(__('Sistem')), Tables\Columns\TextColumn::make('attribute_changes') - ->label('Detalii') + ->label(__('Detalii')) ->formatStateUsing(function ($state) { if (! $state) return '—'; $arr = is_string($state) ? json_decode($state, true) : $state; @@ -75,10 +75,10 @@ class ActivityResource extends Resource ]) ->filters([ Tables\Filters\SelectFilter::make('description') - ->label('Acțiune') + ->label(__('Acțiune')) ->options(['creat' => 'creat', 'modificat' => 'modificat', 'șters' => 'șters']), Tables\Filters\Filter::make('today') - ->label('Astăzi') + ->label(__('Astăzi')) ->query(fn ($q) => $q->whereDate('created_at', today())), ]) ->defaultSort('created_at', 'desc') diff --git a/app/Filament/Tenant/Resources/AppointmentResource.php b/app/Filament/Tenant/Resources/AppointmentResource.php index fc23f18..eba7e97 100644 --- a/app/Filament/Tenant/Resources/AppointmentResource.php +++ b/app/Filament/Tenant/Resources/AppointmentResource.php @@ -34,25 +34,30 @@ class AppointmentResource extends Resource protected static ?string $modelLabel = 'programare'; - protected static ?string $pluralModelLabel = 'programări'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('programări'); + } protected static ?int $navigationSort = 7; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Când & unde') + Schemas\Components\Section::make(__('Când & unde')) ->columns(3) ->schema([ - Forms\Components\DatePicker::make('date')->label('Data')->default(today())->required(), - Forms\Components\TimePicker::make('time_start')->label('De la')->required()->seconds(false), - Forms\Components\TimePicker::make('time_end')->label('Până la')->required()->seconds(false), + Forms\Components\DatePicker::make('date')->label(__('Data'))->default(today())->required(), + Forms\Components\TimePicker::make('time_start')->label(__('De la'))->required()->seconds(false), + Forms\Components\TimePicker::make('time_end')->label(__('Până la'))->required()->seconds(false), Forms\Components\Select::make('post_id') - ->label('Pod') + ->label(__('Pod')) ->options(fn () => Post::where('is_active', true)->orderBy('sort_order')->pluck('name', 'id')) ->searchable(), Forms\Components\Select::make('master_id') - ->label('Maistru / Mecanic') + ->label(__('Maistru / Mecanic')) ->options(fn () => User::pluck('name', 'id')) ->searchable(), Forms\Components\Select::make('status') @@ -60,23 +65,23 @@ class AppointmentResource extends Resource ->default('scheduled') ->required(), ]), - Schemas\Components\Section::make('Client & Auto') + Schemas\Components\Section::make(__('Client & Auto')) ->columns(2) ->schema([ Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->live(), Forms\Components\Select::make('vehicle_id') - ->label('Auto') + ->label(__('Auto')) ->options(fn (Schemas\Components\Utilities\Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id'))->pluck('plate', 'id') : []) ->searchable(), ]), - Forms\Components\TextInput::make('title')->label('Subiect')->required()->maxLength(160), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\TextInput::make('title')->label(__('Subiect'))->required()->maxLength(160), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -84,14 +89,14 @@ class AppointmentResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('date')->label('Data')->date('d.m.Y')->sortable(), - Tables\Columns\TextColumn::make('time_start')->label('De la')->time('H:i'), - Tables\Columns\TextColumn::make('time_end')->label('Până la')->time('H:i'), - Tables\Columns\TextColumn::make('post.name')->label('Pod')->placeholder('—'), - Tables\Columns\TextColumn::make('title')->label('Subiect')->searchable()->limit(40), - Tables\Columns\TextColumn::make('client.name')->label('Client')->placeholder('—'), - Tables\Columns\TextColumn::make('vehicle.plate')->label('Auto')->placeholder('—'), - Tables\Columns\TextColumn::make('master.name')->label('Maistru')->placeholder('—'), + Tables\Columns\TextColumn::make('date')->label(__('Data'))->date('d.m.Y')->sortable(), + Tables\Columns\TextColumn::make('time_start')->label(__('De la'))->time('H:i'), + Tables\Columns\TextColumn::make('time_end')->label(__('Până la'))->time('H:i'), + Tables\Columns\TextColumn::make('post.name')->label(__('Pod'))->placeholder('—'), + Tables\Columns\TextColumn::make('title')->label(__('Subiect'))->searchable()->limit(40), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->placeholder('—'), + Tables\Columns\TextColumn::make('vehicle.plate')->label(__('Auto'))->placeholder('—'), + Tables\Columns\TextColumn::make('master.name')->label(__('Maistru'))->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($state) => Appointment::STATUSES[$state] ?? $state) ->badge() @@ -104,14 +109,14 @@ class AppointmentResource extends Resource ]) ->filters([ Tables\Filters\Filter::make('today') - ->label('Astăzi') + ->label(__('Astăzi')) ->query(fn ($q) => $q->whereDate('date', today())), Tables\Filters\Filter::make('upcoming') - ->label('Viitoare') + ->label(__('Viitoare')) ->query(fn ($q) => $q->where('date', '>=', today())), Tables\Filters\SelectFilter::make('status')->options(Appointment::STATUSES), Tables\Filters\SelectFilter::make('post_id') - ->label('Pod') + ->label(__('Pod')) ->options(fn () => Post::pluck('name', 'id')), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/BodyshopJobResource.php b/app/Filament/Tenant/Resources/BodyshopJobResource.php index 10e26a0..64d024d 100644 --- a/app/Filament/Tenant/Resources/BodyshopJobResource.php +++ b/app/Filament/Tenant/Resources/BodyshopJobResource.php @@ -32,10 +32,6 @@ class BodyshopJobResource extends Resource return __('nav.group.Tinichigerie'); } - protected static ?string $modelLabel = 'lucrare caroserie'; - - protected static ?string $pluralModelLabel = 'lucrări caroserie'; - protected static ?int $navigationSort = 80; public static function getNavigationBadge(): ?string @@ -48,50 +44,50 @@ class BodyshopJobResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Lucrare') + Schemas\Components\Section::make(__('Lucrare')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('number')->label('Nr.')->disabled()->dehydrated(false)->placeholder('Generat automat'), - Forms\Components\Select::make('type')->label('Tip')->options(BodyshopJob::TYPES)->default('body_repair')->required(), - Forms\Components\Select::make('status')->label('Status')->options(BodyshopJob::STATUSES)->default('estimate')->required(), + Forms\Components\TextInput::make('number')->label(__('Nr.'))->disabled()->dehydrated(false)->placeholder(__('Generat automat')), + Forms\Components\Select::make('type')->label(__('Tip'))->options(BodyshopJob::TYPES)->default('body_repair')->required(), + Forms\Components\Select::make('status')->label(__('Status'))->options(BodyshopJob::STATUSES)->default('estimate')->required(), Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable()->live(), Forms\Components\Select::make('vehicle_id') - ->label('Auto') + ->label(__('Auto')) ->options(fn (Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id'))->get() ->mapWithKeys(fn ($v) => [$v->id => "{$v->make} {$v->model} {$v->plate}"])->toArray() : []) ->searchable(), - Forms\Components\TextInput::make('estimate_amount')->label('Deviz')->numeric()->default(0), - Forms\Components\TextInput::make('approved_amount')->label('Aprobat')->numeric()->default(0), + Forms\Components\TextInput::make('estimate_amount')->label(__('Deviz'))->numeric()->default(0), + Forms\Components\TextInput::make('approved_amount')->label(__('Aprobat'))->numeric()->default(0), ]), - Schemas\Components\Section::make('Asigurare') + Schemas\Components\Section::make(__('Asigurare')) ->collapsible() ->columns(3) ->schema([ - Forms\Components\Toggle::make('is_insurance')->label('Caz de asigurare')->live()->columnSpanFull(), - Forms\Components\TextInput::make('insurer')->label('Asigurător') + Forms\Components\Toggle::make('is_insurance')->label(__('Caz de asigurare'))->live()->columnSpanFull(), + Forms\Components\TextInput::make('insurer')->label(__('Asigurător')) ->visible(fn (Get $get) => $get('is_insurance')), - Forms\Components\TextInput::make('policy_no')->label('Nr. poliță') + Forms\Components\TextInput::make('policy_no')->label(__('Nr. poliță')) ->visible(fn (Get $get) => $get('is_insurance')), - Forms\Components\TextInput::make('claim_no')->label('Nr. dosar daună') + Forms\Components\TextInput::make('claim_no')->label(__('Nr. dosar daună')) ->visible(fn (Get $get) => $get('is_insurance')), - Forms\Components\Select::make('insurance_status')->label('Status dosar') + Forms\Components\Select::make('insurance_status')->label(__('Status dosar')) ->options(BodyshopJob::INSURANCE_STATUSES) ->visible(fn (Get $get) => $get('is_insurance')), ]), - Schemas\Components\Section::make('Foto înainte / după') + Schemas\Components\Section::make(__('Foto înainte / după')) ->columns(2) ->schema([ \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('photos_before') - ->label('Înainte')->collection('photos_before')->multiple()->image()->reorderable()->maxFiles(20), + ->label(__('Înainte'))->collection('photos_before')->multiple()->image()->reorderable()->maxFiles(20), \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('photos_after') - ->label('După')->collection('photos_after')->multiple()->image()->reorderable()->maxFiles(20), + ->label(__('După'))->collection('photos_after')->multiple()->image()->reorderable()->maxFiles(20), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -99,15 +95,15 @@ class BodyshopJobResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), - Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable()->placeholder('—'), - Tables\Columns\TextColumn::make('vehicle.plate')->label('Auto')->placeholder('—'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->searchable()->placeholder('—'), + Tables\Columns\TextColumn::make('vehicle.plate')->label(__('Auto'))->placeholder('—'), Tables\Columns\TextColumn::make('type') ->formatStateUsing(fn ($s) => BodyshopJob::TYPES[$s] ?? $s) ->badge()->color('info'), - Tables\Columns\IconColumn::make('is_insurance')->label('Asig.')->boolean()->toggleable(), - Tables\Columns\TextColumn::make('damage_points_count')->counts('damagePoints')->label('Daune')->alignRight(), - Tables\Columns\TextColumn::make('approved_amount')->label('Aprobat')->money('MDL')->alignRight(), + Tables\Columns\IconColumn::make('is_insurance')->label(__('Asig.'))->boolean()->toggleable(), + Tables\Columns\TextColumn::make('damage_points_count')->counts('damagePoints')->label(__('Daune'))->alignRight(), + Tables\Columns\TextColumn::make('approved_amount')->label(__('Aprobat'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => BodyshopJob::STATUSES[$s] ?? $s) ->badge() @@ -121,14 +117,14 @@ class BodyshopJobResource extends Resource ->filters([ Tables\Filters\SelectFilter::make('type')->options(BodyshopJob::TYPES), Tables\Filters\SelectFilter::make('status')->options(BodyshopJob::STATUSES), - Tables\Filters\TernaryFilter::make('is_insurance')->label('Caz asigurare'), + Tables\Filters\TernaryFilter::make('is_insurance')->label(__('Caz asigurare')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Nicio lucrare de caroserie') - ->emptyStateDescription('Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.') + ->emptyStateHeading(__('Nicio lucrare de caroserie')) + ->emptyStateDescription(__('Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.')) ->emptyStateIcon('heroicon-o-paint-brush') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/BodyshopJobResource/RelationManagers/DamagePointsRelationManager.php b/app/Filament/Tenant/Resources/BodyshopJobResource/RelationManagers/DamagePointsRelationManager.php index 40163f9..7fd2691 100644 --- a/app/Filament/Tenant/Resources/BodyshopJobResource/RelationManagers/DamagePointsRelationManager.php +++ b/app/Filament/Tenant/Resources/BodyshopJobResource/RelationManagers/DamagePointsRelationManager.php @@ -14,26 +14,31 @@ class DamagePointsRelationManager extends RelationManager { protected static string $relationship = 'damagePoints'; - protected static ?string $title = 'Hartă daune'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Hartă daune'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('zone') - ->label('Zonă') + ->label(__('Zonă')) ->options(array_combine(DamagePoint::ZONES, DamagePoint::ZONES)) ->searchable() ->required(), Forms\Components\Select::make('kind') - ->label('Tip daună') + ->label(__('Tip daună')) ->options(array_combine(DamagePoint::KINDS, DamagePoint::KINDS)) ->required(), Forms\Components\Select::make('severity') - ->label('Gravitate') + ->label(__('Gravitate')) ->options(DamagePoint::SEVERITIES) ->default('minor') ->required(), - Forms\Components\Textarea::make('notes')->label('Observații')->rows(2)->columnSpanFull(), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->rows(2)->columnSpanFull(), ]); } @@ -42,10 +47,10 @@ class DamagePointsRelationManager extends RelationManager return $table ->recordTitleAttribute('zone') ->columns([ - Tables\Columns\TextColumn::make('zone')->label('Zonă')->badge()->color('gray'), - Tables\Columns\TextColumn::make('kind')->label('Tip'), + Tables\Columns\TextColumn::make('zone')->label(__('Zonă'))->badge()->color('gray'), + Tables\Columns\TextColumn::make('kind')->label(__('Tip')), Tables\Columns\TextColumn::make('severity') - ->label('Gravitate') + ->label(__('Gravitate')) ->formatStateUsing(fn ($s) => DamagePoint::SEVERITIES[$s] ?? $s) ->badge() ->colors(['gray' => ['minor'], 'warning' => ['medium'], 'danger' => ['severe']]), @@ -53,7 +58,7 @@ class DamagePointsRelationManager extends RelationManager ]) ->headerActions([Actions\CreateAction::make()]) ->actions([Actions\EditAction::make(), Actions\DeleteAction::make()]) - ->emptyStateHeading('Nicio daună marcată') - ->emptyStateDescription('Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.'); + ->emptyStateHeading(__('Nicio daună marcată')) + ->emptyStateDescription(__('Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.')); } } diff --git a/app/Filament/Tenant/Resources/CallResource.php b/app/Filament/Tenant/Resources/CallResource.php index 398b1de..6fbc240 100644 --- a/app/Filament/Tenant/Resources/CallResource.php +++ b/app/Filament/Tenant/Resources/CallResource.php @@ -38,26 +38,26 @@ class CallResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Apel') + Schemas\Components\Section::make(__('Apel')) ->columns(2) ->schema([ - Forms\Components\DateTimePicker::make('called_at')->label('Data & ora')->default(now())->required(), + Forms\Components\DateTimePicker::make('called_at')->label(__('Data & ora'))->default(now())->required(), Forms\Components\Select::make('direction') ->options(Call::DIRECTIONS) ->default('incoming') ->required(), - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->required()->maxLength(40), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->required()->maxLength(40), Forms\Components\Select::make('status') ->options(Call::STATUSES) ->default('answered') ->required(), - Forms\Components\TextInput::make('duration_sec')->label('Durată (sec)')->numeric()->default(0), + Forms\Components\TextInput::make('duration_sec')->label(__('Durată (sec)'))->numeric()->default(0), Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable(), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -65,7 +65,7 @@ class CallResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('called_at')->label('Data')->dateTime('d.m.Y H:i')->sortable(), + Tables\Columns\TextColumn::make('called_at')->label(__('Data'))->dateTime('d.m.Y H:i')->sortable(), Tables\Columns\TextColumn::make('direction') ->formatStateUsing(fn ($s) => Call::DIRECTIONS[$s] ?? $s) ->badge() @@ -75,8 +75,8 @@ class CallResource extends Resource 'danger' => ['missed'], ]), Tables\Columns\TextColumn::make('phone')->copyable()->searchable(), - Tables\Columns\TextColumn::make('client.name')->label('Client')->placeholder('—'), - Tables\Columns\TextColumn::make('duration_formatted')->label('Durată')->state(fn (Call $r) => $r->duration_formatted), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->placeholder('—'), + Tables\Columns\TextColumn::make('duration_formatted')->label(__('Durată'))->state(fn (Call $r) => $r->duration_formatted), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => Call::STATUSES[$s] ?? $s) ->badge(), @@ -84,10 +84,10 @@ class CallResource extends Resource ->filters([ Tables\Filters\SelectFilter::make('direction')->options(Call::DIRECTIONS), Tables\Filters\Filter::make('today') - ->label('Astăzi') + ->label(__('Astăzi')) ->query(fn ($q) => $q->whereDate('called_at', today())), Tables\Filters\Filter::make('missed') - ->label('Pierdute') + ->label(__('Pierdute')) ->query(fn ($q) => $q->where('direction', 'missed')->orWhere('status', 'missed')), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/ClientResource.php b/app/Filament/Tenant/Resources/ClientResource.php index a72b71b..426f714 100644 --- a/app/Filament/Tenant/Resources/ClientResource.php +++ b/app/Filament/Tenant/Resources/ClientResource.php @@ -25,7 +25,12 @@ class ClientResource extends Resource protected static ?string $modelLabel = 'client'; - protected static ?string $pluralModelLabel = 'clienți'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('clienți'); + } protected static ?int $navigationSort = 10; @@ -47,18 +52,18 @@ class ClientResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Date generale') + Schemas\Components\Section::make(__('Date generale')) ->columns(2) ->schema([ Forms\Components\Select::make('type') - ->label('Tip') + ->label(__('Tip')) ->options(['individual' => 'Persoană fizică', 'company' => 'Persoană juridică']) ->default('individual') ->required() ->live(), - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(120), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(120), Forms\Components\TextInput::make('company_name') - ->label('Denumire companie') + ->label(__('Denumire companie')) ->visible(fn (Schemas\Components\Utilities\Get $get) => $get('type') === 'company') ->maxLength(160), Forms\Components\Select::make('status') @@ -69,40 +74,40 @@ class ClientResource extends Resource ->default('active') ->required(), Forms\Components\Toggle::make('is_vip') - ->label('Client VIP') - ->helperText('Activează coeficienții de preț VIP pe fișele acestui client.'), + ->label(__('Client VIP')) + ->helperText(__('Activează coeficienții de preț VIP pe fișele acestui client.')), ]), - Schemas\Components\Section::make('Contacte') + Schemas\Components\Section::make(__('Contacte')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->required()->maxLength(40), - Forms\Components\TextInput::make('phone_alt')->label('Telefon alternativ')->tel()->maxLength(40), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->required()->maxLength(40), + Forms\Components\TextInput::make('phone_alt')->label(__('Telefon alternativ'))->tel()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), Forms\Components\TextInput::make('telegram')->maxLength(60), Forms\Components\TextInput::make('telegram_chat_id') - ->label('Telegram chat ID') + ->label(__('Telegram chat ID')) ->disabled() ->dehydrated(false) - ->placeholder('Se completează automat când clientul scrie la bot') + ->placeholder(__('Se completează automat când clientul scrie la bot')) ->helperText(fn ($record) => $record?->telegram_chat_id ? '✅ Telegram legat — notificările vor merge prin bot' : null), Forms\Components\TextInput::make('whatsapp')->maxLength(60), Forms\Components\TextInput::make('viber')->maxLength(60), ]), - Schemas\Components\Section::make('Marketing') + Schemas\Components\Section::make(__('Marketing')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('source')->label('Sursă')->maxLength(60), - Forms\Components\TextInput::make('marketing_channel')->label('Canal marketing')->maxLength(60), + Forms\Components\TextInput::make('source')->label(__('Sursă'))->maxLength(60), + Forms\Components\TextInput::make('marketing_channel')->label(__('Canal marketing'))->maxLength(60), ]), - Schemas\Components\Section::make('Financiar') + Schemas\Components\Section::make(__('Financiar')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('balance')->label('Sold')->numeric()->default(0), - Forms\Components\TextInput::make('discount_pct')->label('Discount %')->numeric()->default(0), + Forms\Components\TextInput::make('balance')->label(__('Sold'))->numeric()->default(0), + Forms\Components\TextInput::make('discount_pct')->label(__('Discount %'))->numeric()->default(0), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(3), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(3), ]); } @@ -113,7 +118,7 @@ class ClientResource extends Resource Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('phone')->searchable()->copyable(), Tables\Columns\TextColumn::make('email')->searchable()->toggleable(), - Tables\Columns\TextColumn::make('vehicles_count')->counts('vehicles')->label('Mașini'), + Tables\Columns\TextColumn::make('vehicles_count')->counts('vehicles')->label(__('Mașini')), Tables\Columns\TextColumn::make('status') ->badge() ->colors([ @@ -136,8 +141,8 @@ class ClientResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun client încă') - ->emptyStateDescription('Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.') + ->emptyStateHeading(__('Niciun client încă')) + ->emptyStateDescription(__('Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.')) ->emptyStateIcon('heroicon-o-users') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/ClientResource/Pages/ListClients.php b/app/Filament/Tenant/Resources/ClientResource/Pages/ListClients.php index 8d0be46..c991855 100644 --- a/app/Filament/Tenant/Resources/ClientResource/Pages/ListClients.php +++ b/app/Filament/Tenant/Resources/ClientResource/Pages/ListClients.php @@ -17,16 +17,16 @@ class ListClients extends ListRecords { return [ Actions\Action::make('export') - ->label('Export CSV') + ->label(__('Export CSV')) ->icon('heroicon-m-arrow-down-tray') ->color('gray') ->action(fn () => app(CsvImportExport::class)->exportClients()), Actions\Action::make('import') - ->label('Import CSV') + ->label(__('Import CSV')) ->icon('heroicon-m-arrow-up-tray') ->color('gray') - ->modalHeading('Import clienți din CSV') - ->modalDescription('CSV cu header: ' . implode(', ', CsvImportExport::CLIENT_COLUMNS) . '. Deduplicare după telefon.') + ->modalHeading(__('Import clienți din CSV')) + ->modalDescription(__('CSV cu header: ' . implode(', ', CsvImportExport::CLIENT_COLUMNS) . '. Deduplicare după telefon.')) ->schema([ Forms\Components\FileUpload::make('file') ->required() diff --git a/app/Filament/Tenant/Resources/DealResource.php b/app/Filament/Tenant/Resources/DealResource.php index 9faf45b..9747fab 100644 --- a/app/Filament/Tenant/Resources/DealResource.php +++ b/app/Filament/Tenant/Resources/DealResource.php @@ -46,22 +46,22 @@ class DealResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Detalii') + Schemas\Components\Section::make(__('Detalii')) ->columns(2) ->schema([ Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->required(), Forms\Components\Select::make('vehicle_id') - ->label('Auto') + ->label(__('Auto')) ->options(fn (Schemas\Components\Utilities\Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id'))->pluck('plate', 'id') : []) ->searchable(), - Forms\Components\TextInput::make('name')->label('Subiect')->required()->maxLength(160), - Forms\Components\TextInput::make('price')->label('Valoare')->numeric()->default(0), + Forms\Components\TextInput::make('name')->label(__('Subiect'))->required()->maxLength(160), + Forms\Components\TextInput::make('price')->label(__('Valoare'))->numeric()->default(0), Forms\Components\Select::make('stage') ->options(Deal::STAGES) ->default('new') @@ -70,11 +70,11 @@ class DealResource extends Resource ->options(Lead::SOURCES) ->searchable(), Forms\Components\Select::make('assigned_to') - ->label('Responsabil') + ->label(__('Responsabil')) ->options(fn () => User::pluck('name', 'id')) ->searchable(), ]), - Forms\Components\Textarea::make('note')->label('Notițe')->columnSpanFull()->rows(3), + Forms\Components\Textarea::make('note')->label(__('Notițe'))->columnSpanFull()->rows(3), ]); } @@ -83,9 +83,9 @@ class DealResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('id')->label('#')->sortable(), - Tables\Columns\TextColumn::make('name')->label('Subiect')->searchable()->limit(40), - Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable(), - Tables\Columns\TextColumn::make('vehicle.plate')->label('Auto')->placeholder('—'), + Tables\Columns\TextColumn::make('name')->label(__('Subiect'))->searchable()->limit(40), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->searchable(), + Tables\Columns\TextColumn::make('vehicle.plate')->label(__('Auto'))->placeholder('—'), Tables\Columns\TextColumn::make('stage') ->formatStateUsing(fn ($state) => Deal::STAGES[$state] ?? $state) ->badge() @@ -97,14 +97,14 @@ class DealResource extends Resource 'danger' => ['lost'], ]), Tables\Columns\TextColumn::make('price')->money('MDL')->sortable(), - Tables\Columns\TextColumn::make('source')->label('Sursă')->formatStateUsing(fn ($state) => Lead::SOURCES[$state] ?? $state)->placeholder('—'), - Tables\Columns\TextColumn::make('assignedTo.name')->label('Responsabil')->placeholder('—'), + Tables\Columns\TextColumn::make('source')->label(__('Sursă'))->formatStateUsing(fn ($state) => Lead::SOURCES[$state] ?? $state)->placeholder('—'), + Tables\Columns\TextColumn::make('assignedTo.name')->label(__('Responsabil'))->placeholder('—'), Tables\Columns\TextColumn::make('created_at')->date()->sortable(), ]) ->filters([ Tables\Filters\SelectFilter::make('stage')->options(Deal::STAGES), Tables\Filters\SelectFilter::make('assigned_to') - ->label('Responsabil') + ->label(__('Responsabil')) ->options(fn () => User::pluck('name', 'id')), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/EmployeeProfileResource.php b/app/Filament/Tenant/Resources/EmployeeProfileResource.php index 24ae437..996c501 100644 --- a/app/Filament/Tenant/Resources/EmployeeProfileResource.php +++ b/app/Filament/Tenant/Resources/EmployeeProfileResource.php @@ -31,41 +31,46 @@ class EmployeeProfileResource extends Resource protected static ?string $modelLabel = 'angajat'; - protected static ?string $pluralModelLabel = 'angajați'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('angajați'); + } protected static ?int $navigationSort = 52; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Profil') + Schemas\Components\Section::make(__('Profil')) ->columns(2) ->schema([ Forms\Components\Select::make('user_id') - ->label('Utilizator') + ->label(__('Utilizator')) ->options(fn () => User::orderBy('name')->pluck('name', 'id')) ->searchable() ->required() ->unique(ignoreRecord: true), - Forms\Components\TextInput::make('position')->label('Funcție')->maxLength(120), - Forms\Components\DatePicker::make('hire_date')->label('Angajat din'), + Forms\Components\TextInput::make('position')->label(__('Funcție'))->maxLength(120), + Forms\Components\DatePicker::make('hire_date')->label(__('Angajat din')), ]), - Schemas\Components\Section::make('Salariu & comisioane') + Schemas\Components\Section::make(__('Salariu & comisioane')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('base_salary')->label('Salariu bază')->numeric()->default(0), + Forms\Components\TextInput::make('base_salary')->label(__('Salariu bază'))->numeric()->default(0), Forms\Components\TextInput::make('works_pct') - ->label('% manopere') + ->label(__('% manopere')) ->numeric()->default(0) ->suffix('%') - ->helperText('% din venitul manoperelor finalizate.'), + ->helperText(__('% din venitul manoperelor finalizate.')), Forms\Components\TextInput::make('parts_pct') - ->label('% marja piese') + ->label(__('% marja piese')) ->numeric()->default(0) ->suffix('%') - ->helperText('% din marja (sell-buy) pieselor montate.'), + ->helperText(__('% din marja (sell-buy) pieselor montate.')), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -73,12 +78,12 @@ class EmployeeProfileResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('user.name')->label('Nume')->searchable()->sortable(), - Tables\Columns\TextColumn::make('position')->label('Funcție')->placeholder('—'), + Tables\Columns\TextColumn::make('user.name')->label(__('Nume'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('position')->label(__('Funcție'))->placeholder('—'), Tables\Columns\TextColumn::make('base_salary')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('works_pct')->label('% Manopere') + Tables\Columns\TextColumn::make('works_pct')->label(__('% Manopere')) ->formatStateUsing(fn ($s) => $s . '%')->alignRight(), - Tables\Columns\TextColumn::make('parts_pct')->label('% Piese') + Tables\Columns\TextColumn::make('parts_pct')->label(__('% Piese')) ->formatStateUsing(fn ($s) => $s . '%')->alignRight(), Tables\Columns\TextColumn::make('hire_date')->date('d.m.Y')->placeholder('—'), ]) diff --git a/app/Filament/Tenant/Resources/ExpenseResource.php b/app/Filament/Tenant/Resources/ExpenseResource.php index 033147e..68a07da 100644 --- a/app/Filament/Tenant/Resources/ExpenseResource.php +++ b/app/Filament/Tenant/Resources/ExpenseResource.php @@ -29,7 +29,12 @@ class ExpenseResource extends Resource return __('nav.group.Finanțe'); } - protected static ?string $modelLabel = 'cheltuială'; + protected static ?string $modelLabel = null; + + public static function getModelLabel(): string + { + return __('cheltuială'); + } protected static ?string $pluralModelLabel = 'cheltuieli'; @@ -48,27 +53,27 @@ class ExpenseResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Cheltuială') + Schemas\Components\Section::make(__('Cheltuială')) ->columns(2) ->schema([ - Forms\Components\DatePicker::make('paid_at')->label('Data')->default(today())->required(), + Forms\Components\DatePicker::make('paid_at')->label(__('Data'))->default(today())->required(), Forms\Components\Select::make('category') ->options(Expense::CATEGORIES) ->default('other') ->required(), - Forms\Components\TextInput::make('name')->label('Denumire')->required()->maxLength(160)->columnSpanFull(), - Forms\Components\TextInput::make('amount')->label('Sumă')->numeric()->required(), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->maxLength(160)->columnSpanFull(), + Forms\Components\TextInput::make('amount')->label(__('Sumă'))->numeric()->required(), Forms\Components\Select::make('method') ->options(Expense::METHODS) ->default('cash') ->required(), Forms\Components\Select::make('supplier_id') - ->label('Furnizor (opțional)') + ->label(__('Furnizor (opțional)')) ->options(fn () => Supplier::pluck('name', 'id')) ->searchable(), - Forms\Components\TextInput::make('reference')->label('Ref.')->maxLength(64), + Forms\Components\TextInput::make('reference')->label(__('Ref.'))->maxLength(64), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -76,22 +81,22 @@ class ExpenseResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('paid_at')->label('Data')->date('d.m.Y')->sortable(), + Tables\Columns\TextColumn::make('paid_at')->label(__('Data'))->date('d.m.Y')->sortable(), Tables\Columns\TextColumn::make('category') ->formatStateUsing(fn ($s) => Expense::CATEGORIES[$s] ?? $s) ->badge(), Tables\Columns\TextColumn::make('name')->searchable()->wrap(), - Tables\Columns\TextColumn::make('supplier.name')->label('Furnizor')->placeholder('—')->toggleable(), + Tables\Columns\TextColumn::make('supplier.name')->label(__('Furnizor'))->placeholder('—')->toggleable(), Tables\Columns\TextColumn::make('method') ->formatStateUsing(fn ($s) => Expense::METHODS[$s] ?? $s), Tables\Columns\TextColumn::make('amount')->money('MDL')->alignRight()->sortable() ->color('danger') - ->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label('Total')), + ->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label(__('Total'))), ]) ->filters([ Tables\Filters\SelectFilter::make('category')->options(Expense::CATEGORIES), Tables\Filters\Filter::make('this_month') - ->label('Luna curentă') + ->label(__('Luna curentă')) ->query(fn ($q) => $q->whereMonth('paid_at', now()->month)->whereYear('paid_at', now()->year)), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/LaborResource.php b/app/Filament/Tenant/Resources/LaborResource.php index 09b3d2a..e0f0eb9 100644 --- a/app/Filament/Tenant/Resources/LaborResource.php +++ b/app/Filament/Tenant/Resources/LaborResource.php @@ -29,7 +29,12 @@ class LaborResource extends Resource return __('nav.group.Service'); } - protected static ?string $modelLabel = 'normă'; + protected static ?string $modelLabel = null; + + public static function getModelLabel(): string + { + return __('normă'); + } protected static ?string $pluralModelLabel = 'norme-ore'; @@ -38,31 +43,31 @@ class LaborResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Manoperă') + Schemas\Components\Section::make(__('Manoperă')) ->columns(2) ->schema([ Forms\Components\Select::make('category') - ->label('Categorie') + ->label(__('Categorie')) ->options(array_combine(Labor::CATEGORIES, Labor::CATEGORIES)) ->required() ->searchable(), - Forms\Components\TextInput::make('code')->label('Cod')->maxLength(32), - Forms\Components\TextInput::make('name_ro')->label('Nume (RO)')->required()->maxLength(160), - Forms\Components\TextInput::make('name_ru')->label('Nume (RU)')->maxLength(160), + Forms\Components\TextInput::make('code')->label(__('Cod'))->maxLength(32), + Forms\Components\TextInput::make('name_ro')->label(__('Nume (RO)'))->required()->maxLength(160), + Forms\Components\TextInput::make('name_ru')->label(__('Nume (RU)'))->maxLength(160), Forms\Components\Select::make('pricing_mode') - ->label('Mod tarifare') + ->label(__('Mod tarifare')) ->options(Labor::PRICING_MODES) ->default('hourly') ->live() ->required(), - Forms\Components\TextInput::make('hours')->label('Ore (normă)')->numeric()->default(1) + Forms\Components\TextInput::make('hours')->label(__('Ore (normă)'))->numeric()->default(1) ->visible(fn (Schemas\Components\Utilities\Get $get) => $get('pricing_mode') !== 'fixed'), - Forms\Components\TextInput::make('fixed_price')->label('Preț fix (MDL)')->numeric()->default(0) + Forms\Components\TextInput::make('fixed_price')->label(__('Preț fix (MDL)'))->numeric()->default(0) ->visible(fn (Schemas\Components\Utilities\Get $get) => $get('pricing_mode') === 'fixed'), - Forms\Components\TextInput::make('price')->label('Preț orientativ (MDL)')->numeric()->default(0), - Forms\Components\Toggle::make('is_active')->label('Activă')->default(true), + Forms\Components\TextInput::make('price')->label(__('Preț orientativ (MDL)'))->numeric()->default(0), + Forms\Components\Toggle::make('is_active')->label(__('Activă'))->default(true), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -70,23 +75,23 @@ class LaborResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('category')->label('Categorie')->badge()->sortable(), - Tables\Columns\TextColumn::make('name_ro')->label('Manoperă')->searchable()->sortable(), + Tables\Columns\TextColumn::make('category')->label(__('Categorie'))->badge()->sortable(), + Tables\Columns\TextColumn::make('name_ro')->label(__('Manoperă'))->searchable()->sortable(), Tables\Columns\TextColumn::make('pricing_mode') - ->label('Tarifare') + ->label(__('Tarifare')) ->formatStateUsing(fn ($s) => $s === 'fixed' ? 'Fix' : 'Pe oră') ->badge() ->color(fn ($s) => $s === 'fixed' ? 'info' : 'gray'), - Tables\Columns\TextColumn::make('hours')->label('Ore')->numeric(decimalPlaces: 2)->alignRight(), - Tables\Columns\TextColumn::make('fixed_price')->label('Preț fix')->money('MDL')->alignRight() + Tables\Columns\TextColumn::make('hours')->label(__('Ore'))->numeric(decimalPlaces: 2)->alignRight(), + Tables\Columns\TextColumn::make('fixed_price')->label(__('Preț fix'))->money('MDL')->alignRight() ->placeholder('—')->toggleable(), - Tables\Columns\TextColumn::make('laborParts_count')->counts('laborParts')->label('Piese impl.')->alignRight()->toggleable(), - Tables\Columns\IconColumn::make('is_active')->label('Activă')->boolean(), + Tables\Columns\TextColumn::make('laborParts_count')->counts('laborParts')->label(__('Piese impl.'))->alignRight()->toggleable(), + Tables\Columns\IconColumn::make('is_active')->label(__('Activă'))->boolean(), ]) ->filters([ Tables\Filters\SelectFilter::make('category') ->options(array_combine(Labor::CATEGORIES, Labor::CATEGORIES)), - Tables\Filters\TernaryFilter::make('is_active')->label('Doar active'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Doar active')), ]) ->actions([ Actions\EditAction::make(), diff --git a/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php b/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php index 53f6edf..4e9e1dd 100644 --- a/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php +++ b/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php @@ -15,13 +15,18 @@ class DefaultPartsRelationManager extends RelationManager { protected static string $relationship = 'laborParts'; - protected static ?string $title = 'Piese implicite'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Piese implicite'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('part_id') - ->label('Piesă') + ->label(__('Piesă')) ->options(fn () => Part::where('is_active', true) ->get() ->mapWithKeys(fn ($p) => [$p->id => "{$p->name} " . ($p->article ? "[{$p->article}]" : '')]) @@ -34,8 +39,8 @@ class DefaultPartsRelationManager extends RelationManager $set('unit', $p->unit); } }), - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label('UM')->default('buc')->maxLength(16), + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), + Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc')->maxLength(16), ]); } @@ -44,14 +49,14 @@ class DefaultPartsRelationManager extends RelationManager return $table ->recordTitleAttribute('part.name') ->columns([ - Tables\Columns\TextColumn::make('part.name')->label('Piesă')->wrap(), - Tables\Columns\TextColumn::make('part.article')->label('Cod')->placeholder('—'), - Tables\Columns\TextColumn::make('qty')->label('Cant.')->alignRight(), - Tables\Columns\TextColumn::make('unit')->label('UM'), + Tables\Columns\TextColumn::make('part.name')->label(__('Piesă'))->wrap(), + Tables\Columns\TextColumn::make('part.article')->label(__('Cod'))->placeholder('—'), + Tables\Columns\TextColumn::make('qty')->label(__('Cant.'))->alignRight(), + Tables\Columns\TextColumn::make('unit')->label(__('UM')), ]) ->headerActions([Actions\CreateAction::make()]) ->actions([Actions\EditAction::make(), Actions\DeleteAction::make()]) - ->emptyStateHeading('Nicio piesă implicită') - ->emptyStateDescription('Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.'); + ->emptyStateHeading(__('Nicio piesă implicită')) + ->emptyStateDescription(__('Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.')); } } diff --git a/app/Filament/Tenant/Resources/LeadResource.php b/app/Filament/Tenant/Resources/LeadResource.php index 3809689..6343abc 100644 --- a/app/Filament/Tenant/Resources/LeadResource.php +++ b/app/Filament/Tenant/Resources/LeadResource.php @@ -52,25 +52,25 @@ class LeadResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Contact') + Schemas\Components\Section::make(__('Contact')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(120), - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->required()->maxLength(40), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(120), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->required()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), Forms\Components\Select::make('status') ->options(Lead::STATUSES) ->default('new') ->required(), ]), - Schemas\Components\Section::make('Auto') + Schemas\Components\Section::make(__('Auto')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('car')->label('Marca')->maxLength(60), + Forms\Components\TextInput::make('car')->label(__('Marca'))->maxLength(60), Forms\Components\TextInput::make('model')->maxLength(60), ]), - Forms\Components\Textarea::make('message')->label('Mesaj client')->columnSpanFull()->rows(3), - Schemas\Components\Section::make('Sursă & Atribuire') + Forms\Components\Textarea::make('message')->label(__('Mesaj client'))->columnSpanFull()->rows(3), + Schemas\Components\Section::make(__('Sursă & Atribuire')) ->columns(2) ->schema([ Forms\Components\Select::make('source') @@ -78,12 +78,12 @@ class LeadResource extends Resource ->searchable() ->default('manual'), Forms\Components\Select::make('assigned_to') - ->label('Responsabil') + ->label(__('Responsabil')) ->options(fn () => User::pluck('name', 'id')) ->searchable(), - Forms\Components\TextInput::make('budget')->label('Buget')->numeric(), + Forms\Components\TextInput::make('budget')->label(__('Buget'))->numeric(), ]), - Schemas\Components\Section::make('Marketing (UTM)') + Schemas\Components\Section::make(__('Marketing (UTM)')) ->collapsed() ->columns(2) ->schema([ @@ -93,7 +93,7 @@ class LeadResource extends Resource Forms\Components\TextInput::make('utm_term'), Forms\Components\TextInput::make('utm_content'), ]), - Forms\Components\Textarea::make('notes')->label('Notițe interne')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe interne'))->columnSpanFull()->rows(2), ]); } @@ -101,11 +101,11 @@ class LeadResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('created_at')->label('Data')->dateTime('d.m.Y H:i')->sortable(), + Tables\Columns\TextColumn::make('created_at')->label(__('Data'))->dateTime('d.m.Y H:i')->sortable(), Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('phone')->copyable()->searchable(), - Tables\Columns\TextColumn::make('car')->label('Auto')->formatStateUsing(fn ($state, $record) => trim($state . ' ' . ($record->model ?? ''))), - Tables\Columns\TextColumn::make('source')->label('Sursă')->formatStateUsing(fn ($state) => Lead::SOURCES[$state] ?? $state)->badge(), + Tables\Columns\TextColumn::make('car')->label(__('Auto'))->formatStateUsing(fn ($state, $record) => trim($state . ' ' . ($record->model ?? ''))), + Tables\Columns\TextColumn::make('source')->label(__('Sursă'))->formatStateUsing(fn ($state) => Lead::SOURCES[$state] ?? $state)->badge(), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($state) => Lead::STATUSES[$state] ?? $state) ->badge() @@ -116,7 +116,7 @@ class LeadResource extends Resource 'success' => ['converted'], 'danger' => ['lost'], ]), - Tables\Columns\TextColumn::make('assignedTo.name')->label('Responsabil')->placeholder('—'), + Tables\Columns\TextColumn::make('assignedTo.name')->label(__('Responsabil'))->placeholder('—'), Tables\Columns\TextColumn::make('budget')->money('MDL')->placeholder('—'), ]) ->filters([ @@ -125,7 +125,7 @@ class LeadResource extends Resource ]) ->actions([ Actions\Action::make('convert') - ->label('Convertește') + ->label(__('Convertește')) ->icon('heroicon-m-arrow-right-circle') ->color('success') ->visible(fn (Lead $r) => $r->status !== 'converted') @@ -141,7 +141,7 @@ class LeadResource extends Resource Actions\DeleteAction::make(), ]) ->emptyStateHeading('Nicio cerere primită') - ->emptyStateDescription('Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește".') + ->emptyStateDescription(__('Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește".')) ->emptyStateIcon('heroicon-o-inbox-arrow-down') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/MarketingChannelResource.php b/app/Filament/Tenant/Resources/MarketingChannelResource.php index d066b26..0ae9142 100644 --- a/app/Filament/Tenant/Resources/MarketingChannelResource.php +++ b/app/Filament/Tenant/Resources/MarketingChannelResource.php @@ -30,31 +30,36 @@ class MarketingChannelResource extends Resource protected static ?string $modelLabel = 'canal'; - protected static ?string $pluralModelLabel = 'canale marketing'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('canale marketing'); + } protected static ?int $navigationSort = 61; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume canal')->required()->maxLength(120), - Forms\Components\TextInput::make('icon')->label('Iconiță (emoji)')->maxLength(8)->placeholder('🔍 / 📘 / 📸'), + Forms\Components\TextInput::make('name')->label(__('Nume canal'))->required()->maxLength(120), + Forms\Components\TextInput::make('icon')->label(__('Iconiță (emoji)'))->maxLength(8)->placeholder('🔍 / 📘 / 📸'), Forms\Components\ColorPicker::make('color'), ]), - Schemas\Components\Section::make('Buget & rezultate (luna curentă)') + Schemas\Components\Section::make(__('Buget & rezultate (luna curentă)')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('budget_monthly')->label('Buget')->numeric()->default(0), - Forms\Components\TextInput::make('spent_monthly')->label('Cheltuit')->numeric()->default(0), - Forms\Components\TextInput::make('revenue')->label('Venit generat')->numeric()->default(0), - Forms\Components\TextInput::make('leads_count')->label('Lead-uri')->numeric()->default(0), - Forms\Components\TextInput::make('converted_count')->label('Convertite')->numeric()->default(0), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\TextInput::make('budget_monthly')->label(__('Buget'))->numeric()->default(0), + Forms\Components\TextInput::make('spent_monthly')->label(__('Cheltuit'))->numeric()->default(0), + Forms\Components\TextInput::make('revenue')->label(__('Venit generat'))->numeric()->default(0), + Forms\Components\TextInput::make('leads_count')->label(__('Lead-uri'))->numeric()->default(0), + Forms\Components\TextInput::make('converted_count')->label(__('Convertite'))->numeric()->default(0), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -65,26 +70,26 @@ class MarketingChannelResource extends Resource Tables\Columns\ColorColumn::make('color')->label(''), Tables\Columns\TextColumn::make('icon')->label('')->width(40), Tables\Columns\TextColumn::make('name')->searchable()->sortable(), - Tables\Columns\TextColumn::make('budget_monthly')->label('Buget')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('spent_monthly')->label('Cheltuit')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('leads_count')->label('Lead-uri')->alignRight(), - Tables\Columns\TextColumn::make('converted_count')->label('Convertite')->alignRight(), - Tables\Columns\TextColumn::make('revenue')->label('Venit')->money('MDL')->alignRight()->color('success'), + Tables\Columns\TextColumn::make('budget_monthly')->label(__('Buget'))->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('spent_monthly')->label(__('Cheltuit'))->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('leads_count')->label(__('Lead-uri'))->alignRight(), + Tables\Columns\TextColumn::make('converted_count')->label(__('Convertite'))->alignRight(), + Tables\Columns\TextColumn::make('revenue')->label(__('Venit'))->money('MDL')->alignRight()->color('success'), Tables\Columns\TextColumn::make('roi') - ->label('ROI') + ->label(__('ROI')) ->state(fn (MarketingChannel $r) => $r->roi) ->formatStateUsing(fn ($s) => $s . '%') ->color(fn ($s) => $s >= 0 ? 'success' : 'danger') ->alignRight(), Tables\Columns\TextColumn::make('cost_per_lead') - ->label('Cost/lead') + ->label(__('Cost/lead')) ->state(fn (MarketingChannel $r) => $r->cost_per_lead) ->money('MDL') ->alignRight(), Tables\Columns\IconColumn::make('is_active')->boolean(), ]) ->filters([ - Tables\Filters\TernaryFilter::make('is_active')->label('Active'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Active')), ]) ->actions([ Actions\EditAction::make(), diff --git a/app/Filament/Tenant/Resources/MarkupRuleResource.php b/app/Filament/Tenant/Resources/MarkupRuleResource.php index 305ce6b..b78c235 100644 --- a/app/Filament/Tenant/Resources/MarkupRuleResource.php +++ b/app/Filament/Tenant/Resources/MarkupRuleResource.php @@ -31,20 +31,30 @@ class MarkupRuleResource extends Resource return __('nav.group.Depozit'); } - protected static ?string $modelLabel = 'regulă'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'reguli markup'; + public static function getModelLabel(): string + { + return __('regulă'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('reguli markup'); + } protected static ?int $navigationSort = 44; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Regulă') + Schemas\Components\Section::make(__('Regulă')) ->columns(2) ->schema([ Forms\Components\Select::make('type') - ->label('Tip') + ->label(__('Tip')) ->options(MarkupRule::TYPES) ->default('category') ->required() @@ -58,25 +68,25 @@ class MarkupRuleResource extends Resource ->searchable() ->required(fn (Get $get) => in_array($get('type'), ['category', 'brand'], true)), Forms\Components\TextInput::make('range_from') - ->label('De la (preț achiziție)') + ->label(__('De la (preț achiziție)')) ->numeric() ->visible(fn (Get $get) => $get('type') === 'range'), Forms\Components\TextInput::make('range_to') - ->label('Până la (gol = ∞)') + ->label(__('Până la (gol = ∞)')) ->numeric() ->visible(fn (Get $get) => $get('type') === 'range'), Forms\Components\TextInput::make('markup_pct') - ->label('Markup %') + ->label(__('Markup %')) ->numeric() ->required() ->suffix('%') - ->helperText('Ex 30 → preț vânzare = preț achiziție × 1.30'), + ->helperText(__('Ex 30 → preț vânzare = preț achiziție × 1.30')), Forms\Components\TextInput::make('priority') - ->label('Prioritate') + ->label(__('Prioritate')) ->numeric() ->default(100) - ->helperText('Mai mic = aplicat primul.'), - Forms\Components\Toggle::make('is_active')->label('Activă')->default(true), + ->helperText(__('Mai mic = aplicat primul.')), + Forms\Components\Toggle::make('is_active')->label(__('Activă'))->default(true), ]), ]); } @@ -89,11 +99,11 @@ class MarkupRuleResource extends Resource Tables\Columns\TextColumn::make('type') ->formatStateUsing(fn ($s) => MarkupRule::TYPES[$s] ?? $s) ->badge(), - Tables\Columns\TextColumn::make('key')->label('Cheie')->placeholder('—'), - Tables\Columns\TextColumn::make('range_from')->label('De la')->placeholder('—'), - Tables\Columns\TextColumn::make('range_to')->label('Până la')->placeholder('∞'), + Tables\Columns\TextColumn::make('key')->label(__('Cheie'))->placeholder('—'), + Tables\Columns\TextColumn::make('range_from')->label(__('De la'))->placeholder('—'), + Tables\Columns\TextColumn::make('range_to')->label(__('Până la'))->placeholder('∞'), Tables\Columns\TextColumn::make('markup_pct') - ->label('Markup') + ->label(__('Markup')) ->formatStateUsing(fn ($s) => '+' . $s . '%') ->color('success') ->weight('bold'), @@ -104,11 +114,11 @@ class MarkupRuleResource extends Resource ]) ->headerActions([ Actions\Action::make('apply_all') - ->label('Aplică toate regulile la stoc') + ->label(__('Aplică toate regulile la stoc')) ->icon('heroicon-m-bolt') ->color('warning') ->requiresConfirmation() - ->modalDescription('Va recalcula sell_price pentru TOATE piesele active. Continui?') + ->modalDescription(__('Va recalcula sell_price pentru TOATE piesele active. Continui?')) ->action(function () { $count = 0; Part::where('is_active', true)->where('buy_price', '>', 0)->chunk(100, function ($parts) use (&$count) { diff --git a/app/Filament/Tenant/Resources/MasterResource.php b/app/Filament/Tenant/Resources/MasterResource.php index 3febdc5..f70f6f1 100644 --- a/app/Filament/Tenant/Resources/MasterResource.php +++ b/app/Filament/Tenant/Resources/MasterResource.php @@ -47,34 +47,34 @@ class MasterResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Date personale') + Schemas\Components\Section::make(__('Date personale')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(120), - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->maxLength(40), - Forms\Components\TextInput::make('email')->label('Email')->email()->maxLength(120), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(120), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->maxLength(40), + Forms\Components\TextInput::make('email')->label(__('Email'))->email()->maxLength(120), Forms\Components\Select::make('status') ->options(['active' => 'Activ', 'inactive' => 'Inactiv', 'blocked' => 'Blocat']) ->default('active') ->required(), ]), - Schemas\Components\Section::make('Profesie') + Schemas\Components\Section::make(__('Profesie')) ->columns(2) ->schema([ Forms\Components\TextInput::make('specialization') - ->label('Specializare') - ->placeholder('Motor / Frâne / Electrică ...') + ->label(__('Specializare')) + ->placeholder(__('Motor / Frâne / Electrică ...')) ->maxLength(120), - Forms\Components\ColorPicker::make('color')->label('Culoare în calendar'), - Forms\Components\TextInput::make('hourly_rate')->label('Tarif/oră')->numeric(), + Forms\Components\ColorPicker::make('color')->label(__('Culoare în calendar')), + Forms\Components\TextInput::make('hourly_rate')->label(__('Tarif/oră'))->numeric(), Forms\Components\Hidden::make('role')->default('mechanic'), ]), - Schemas\Components\Section::make('Acces în aplicație (opțional)') + Schemas\Components\Section::make(__('Acces în aplicație (opțional)')) ->columns(1) ->collapsed() ->schema([ Forms\Components\TextInput::make('password') - ->label('Parolă (lasă gol pentru a nu schimba)') + ->label(__('Parolă (lasă gol pentru a nu schimba)')) ->password() ->minLength(6) ->dehydrated(fn ($state) => filled($state)) @@ -89,9 +89,9 @@ class MasterResource extends Resource ->columns([ Tables\Columns\ColorColumn::make('color')->label(''), Tables\Columns\TextColumn::make('name')->searchable()->sortable(), - Tables\Columns\TextColumn::make('specialization')->label('Specializare')->placeholder('—'), + Tables\Columns\TextColumn::make('specialization')->label(__('Specializare'))->placeholder('—'), Tables\Columns\TextColumn::make('phone')->copyable()->placeholder('—'), - Tables\Columns\TextColumn::make('hourly_rate')->label('Tarif/h')->money('MDL')->alignRight()->placeholder('—'), + Tables\Columns\TextColumn::make('hourly_rate')->label(__('Tarif/h'))->money('MDL')->alignRight()->placeholder('—'), Tables\Columns\TextColumn::make('status') ->badge() ->colors([ diff --git a/app/Filament/Tenant/Resources/MasterResource/Pages/ListMasters.php b/app/Filament/Tenant/Resources/MasterResource/Pages/ListMasters.php index 575f6ba..ed53b61 100644 --- a/app/Filament/Tenant/Resources/MasterResource/Pages/ListMasters.php +++ b/app/Filament/Tenant/Resources/MasterResource/Pages/ListMasters.php @@ -10,5 +10,5 @@ class ListMasters extends ListRecords { protected static string $resource = MasterResource::class; - protected function getHeaderActions(): array { return [Actions\CreateAction::make()->label('Nou tehnician')]; } + protected function getHeaderActions(): array { return [Actions\CreateAction::make()->label(__('Nou tehnician'))]; } } diff --git a/app/Filament/Tenant/Resources/MessageTemplateResource.php b/app/Filament/Tenant/Resources/MessageTemplateResource.php index 79cd9cb..91da40f 100644 --- a/app/Filament/Tenant/Resources/MessageTemplateResource.php +++ b/app/Filament/Tenant/Resources/MessageTemplateResource.php @@ -37,25 +37,25 @@ class MessageTemplateResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume template')->required()->maxLength(120), + Forms\Components\TextInput::make('name')->label(__('Nume template'))->required()->maxLength(120), Forms\Components\Select::make('channel') ->options(MessageTemplate::CHANNELS) ->default('telegram') ->required(), - Forms\Components\TextInput::make('subject')->label('Subiect (email)')->maxLength(160)->columnSpanFull(), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\TextInput::make('subject')->label(__('Subiect (email)'))->maxLength(160)->columnSpanFull(), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), - Schemas\Components\Section::make('Conținut') + Schemas\Components\Section::make(__('Conținut')) ->columns(1) ->schema([ Forms\Components\Textarea::make('body') - ->label('Mesaj') + ->label(__('Mesaj')) ->required() ->rows(6) - ->helperText('Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}'), + ->helperText(__('Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}')), ]), ]); } @@ -68,7 +68,7 @@ class MessageTemplateResource extends Resource Tables\Columns\TextColumn::make('channel') ->formatStateUsing(fn ($s) => MessageTemplate::CHANNELS[$s] ?? $s) ->badge(), - Tables\Columns\TextColumn::make('body')->label('Preview')->limit(60), + Tables\Columns\TextColumn::make('body')->label(__('Preview'))->limit(60), Tables\Columns\IconColumn::make('is_active')->boolean(), ]) ->filters([ diff --git a/app/Filament/Tenant/Resources/OnlineOrderResource.php b/app/Filament/Tenant/Resources/OnlineOrderResource.php index 4b60eac..f759af1 100644 --- a/app/Filament/Tenant/Resources/OnlineOrderResource.php +++ b/app/Filament/Tenant/Resources/OnlineOrderResource.php @@ -30,9 +30,19 @@ class OnlineOrderResource extends Resource return __('nav.group.Magazin'); } - protected static ?string $modelLabel = 'comandă'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'comenzi online'; + public static function getModelLabel(): string + { + return __('comandă'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('comenzi online'); + } protected static ?int $navigationSort = 50; @@ -50,18 +60,18 @@ class OnlineOrderResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Comandă') + Schemas\Components\Section::make(__('Comandă')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('number')->label('Nr.')->disabled()->dehydrated(false), + Forms\Components\TextInput::make('number')->label(__('Nr.'))->disabled()->dehydrated(false), Forms\Components\Select::make('status')->options(OnlineOrder::STATUSES)->required(), - Forms\Components\Select::make('delivery_method')->label('Livrare')->options(OnlineOrder::DELIVERY)->required(), - Forms\Components\TextInput::make('customer_name')->label('Client')->required(), - Forms\Components\TextInput::make('customer_phone')->label('Telefon')->required(), - Forms\Components\TextInput::make('customer_email')->label('Email'), - Forms\Components\TextInput::make('address')->label('Adresă')->columnSpan(2), - Forms\Components\TextInput::make('delivery_fee')->label('Taxă livrare')->numeric(), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Select::make('delivery_method')->label(__('Livrare'))->options(OnlineOrder::DELIVERY)->required(), + Forms\Components\TextInput::make('customer_name')->label(__('Client'))->required(), + Forms\Components\TextInput::make('customer_phone')->label(__('Telefon'))->required(), + Forms\Components\TextInput::make('customer_email')->label(__('Email')), + Forms\Components\TextInput::make('address')->label(__('Adresă'))->columnSpan(2), + Forms\Components\TextInput::make('delivery_fee')->label(__('Taxă livrare'))->numeric(), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]), ]); } @@ -70,12 +80,12 @@ class OnlineOrderResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), - Tables\Columns\TextColumn::make('created_at')->label('Data')->dateTime('d.m.Y H:i')->sortable(), - Tables\Columns\TextColumn::make('customer_name')->label('Client')->searchable(), - Tables\Columns\TextColumn::make('customer_phone')->label('Telefon')->copyable(), + Tables\Columns\TextColumn::make('number')->label(__('Nr.'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('created_at')->label(__('Data'))->dateTime('d.m.Y H:i')->sortable(), + Tables\Columns\TextColumn::make('customer_name')->label(__('Client'))->searchable(), + Tables\Columns\TextColumn::make('customer_phone')->label(__('Telefon'))->copyable(), Tables\Columns\TextColumn::make('delivery_method') - ->label('Livrare') + ->label(__('Livrare')) ->formatStateUsing(fn ($s) => OnlineOrder::DELIVERY[$s] ?? $s), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => OnlineOrder::STATUSES[$s] ?? $s) @@ -94,12 +104,12 @@ class OnlineOrderResource extends Resource ]) ->actions([ Actions\Action::make('fulfill') - ->label('Onorează (scade stoc)') + ->label(__('Onorează (scade stoc)')) ->icon('heroicon-m-check-badge') ->color('success') ->visible(fn (OnlineOrder $r) => ! in_array($r->status, ['delivered', 'cancelled'], true)) ->requiresConfirmation() - ->modalDescription('Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.') + ->modalDescription(__('Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.')) ->action(function (OnlineOrder $r) { $svc = app(\App\Services\Warehouse\WarehouseService::class); $issued = 0; $skipped = 0; diff --git a/app/Filament/Tenant/Resources/OnlineOrderResource/RelationManagers/ItemsRelationManager.php b/app/Filament/Tenant/Resources/OnlineOrderResource/RelationManagers/ItemsRelationManager.php index 6ddcb67..7b58c25 100644 --- a/app/Filament/Tenant/Resources/OnlineOrderResource/RelationManagers/ItemsRelationManager.php +++ b/app/Filament/Tenant/Resources/OnlineOrderResource/RelationManagers/ItemsRelationManager.php @@ -10,19 +10,24 @@ class ItemsRelationManager extends RelationManager { protected static string $relationship = 'items'; - protected static ?string $title = 'Produse'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Produse'); + } public function table(Table $table): Table { return $table ->recordTitleAttribute('name') ->columns([ - Tables\Columns\TextColumn::make('name')->label('Piesă')->wrap(), - Tables\Columns\TextColumn::make('article')->label('Cod')->placeholder('—'), - Tables\Columns\TextColumn::make('qty')->label('Cant.')->alignRight(), - Tables\Columns\TextColumn::make('price')->label('Preț')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('name')->label(__('Piesă'))->wrap(), + Tables\Columns\TextColumn::make('article')->label(__('Cod'))->placeholder('—'), + Tables\Columns\TextColumn::make('qty')->label(__('Cant.'))->alignRight(), + Tables\Columns\TextColumn::make('price')->label(__('Preț'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight(), - Tables\Columns\IconColumn::make('fulfilled')->label('Onorat')->boolean(), + Tables\Columns\IconColumn::make('fulfilled')->label(__('Onorat'))->boolean(), ]); } } diff --git a/app/Filament/Tenant/Resources/PartResource.php b/app/Filament/Tenant/Resources/PartResource.php index 98054a1..176f17e 100644 --- a/app/Filament/Tenant/Resources/PartResource.php +++ b/app/Filament/Tenant/Resources/PartResource.php @@ -30,7 +30,12 @@ class PartResource extends Resource return __('nav.group.Depozit'); } - protected static ?string $modelLabel = 'piesă'; + protected static ?string $modelLabel = null; + + public static function getModelLabel(): string + { + return __('piesă'); + } protected static ?string $pluralModelLabel = 'piese'; @@ -66,50 +71,50 @@ class PartResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('name')->label('Denumire')->required()->columnSpan(3)->maxLength(200), - Forms\Components\TextInput::make('article')->label('Cod articol')->maxLength(64), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->columnSpan(3)->maxLength(200), + Forms\Components\TextInput::make('article')->label(__('Cod articol'))->maxLength(64), Forms\Components\TextInput::make('brand')->maxLength(64), Forms\Components\Select::make('category') - ->label('Categorie') + ->label(__('Categorie')) ->options(array_combine(Part::CATEGORIES, Part::CATEGORIES)) ->searchable(), - Forms\Components\TextInput::make('barcode')->label('Cod bare')->maxLength(64), - Forms\Components\TextInput::make('location')->label('Locație rack/bin')->maxLength(64), + Forms\Components\TextInput::make('barcode')->label(__('Cod bare'))->maxLength(64), + Forms\Components\TextInput::make('location')->label(__('Locație rack/bin'))->maxLength(64), ]), - Schemas\Components\Section::make('Stoc') + Schemas\Components\Section::make(__('Stoc')) ->columns(4) ->schema([ - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->default(0)->required(), - Forms\Components\TextInput::make('unit')->label('UM')->default('buc')->maxLength(16), - Forms\Components\TextInput::make('min_qty')->label('Minim')->numeric()->default(0), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(0)->required(), + Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc')->maxLength(16), + Forms\Components\TextInput::make('min_qty')->label(__('Minim'))->numeric()->default(0), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), Forms\Components\Toggle::make('is_published') - ->label('Publicat în magazin') - ->helperText('Apare în magazinul online public.') + ->label(__('Publicat în magazin')) + ->helperText(__('Apare în magazinul online public.')) ->default(false), ]), - Schemas\Components\Section::make('Prețuri') + Schemas\Components\Section::make(__('Prețuri')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('buy_price')->label('Preț achiziție')->numeric()->default(0), - Forms\Components\TextInput::make('sell_price')->label('Preț vânzare')->numeric()->default(0), + Forms\Components\TextInput::make('buy_price')->label(__('Preț achiziție'))->numeric()->default(0), + Forms\Components\TextInput::make('sell_price')->label(__('Preț vânzare'))->numeric()->default(0), ]), - Schemas\Components\Section::make('Furnizor preferat') + Schemas\Components\Section::make(__('Furnizor preferat')) ->columns(1) ->schema([ Forms\Components\Select::make('preferred_supplier_id') - ->label('Furnizor') + ->label(__('Furnizor')) ->options(fn () => Supplier::pluck('name', 'id')) ->searchable(), ]), - Schemas\Components\Section::make('Imagine') + Schemas\Components\Section::make(__('Imagine')) ->collapsible() ->schema([ \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('image') - ->label('Foto piesă') + ->label(__('Foto piesă')) ->collection('image') ->multiple() ->reorderable() @@ -118,9 +123,9 @@ class PartResource extends Resource ->maxFiles(8) ->maxSize(2048) ->columnSpanFull() - ->helperText('Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.'), + ->helperText(__('Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.')), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -134,40 +139,40 @@ class PartResource extends Resource ->circular() ->size(32), Tables\Columns\TextColumn::make('name')->searchable()->sortable()->wrap(), - Tables\Columns\TextColumn::make('article')->label('Cod')->searchable()->copyable()->placeholder('—'), + Tables\Columns\TextColumn::make('article')->label(__('Cod'))->searchable()->copyable()->placeholder('—'), Tables\Columns\TextColumn::make('brand')->placeholder('—'), Tables\Columns\TextColumn::make('category')->badge()->placeholder('—'), Tables\Columns\TextColumn::make('qty') - ->label('Stoc') + ->label(__('Stoc')) ->numeric(decimalPlaces: 2) ->alignRight() ->color(fn ($state, $record) => $record->qty <= 0 ? 'danger' : ($record->qty <= $record->min_qty ? 'warning' : null)) ->weight(fn ($state, $record) => $record->qty <= $record->min_qty ? 'bold' : null), Tables\Columns\TextColumn::make('qty_reserved') - ->label('Rezervat') + ->label(__('Rezervat')) ->numeric(decimalPlaces: 2) ->alignRight() ->color(fn ($state) => (float) $state > 0 ? 'info' : null) ->toggleable(), - Tables\Columns\TextColumn::make('unit')->label('UM'), - Tables\Columns\TextColumn::make('location')->label('Loc.')->placeholder('—'), - Tables\Columns\TextColumn::make('sell_price')->label('Preț vz.')->money('MDL')->alignRight(), - Tables\Columns\IconColumn::make('is_published')->label('Magazin')->boolean()->toggleable(), - Tables\Columns\TextColumn::make('preferredSupplier.name')->label('Furnizor')->placeholder('—')->toggleable(), + Tables\Columns\TextColumn::make('unit')->label(__('UM')), + Tables\Columns\TextColumn::make('location')->label(__('Loc.'))->placeholder('—'), + Tables\Columns\TextColumn::make('sell_price')->label(__('Preț vz.'))->money('MDL')->alignRight(), + Tables\Columns\IconColumn::make('is_published')->label(__('Magazin'))->boolean()->toggleable(), + Tables\Columns\TextColumn::make('preferredSupplier.name')->label(__('Furnizor'))->placeholder('—')->toggleable(), ]) ->filters([ Tables\Filters\SelectFilter::make('category') ->options(array_combine(Part::CATEGORIES, Part::CATEGORIES)), Tables\Filters\Filter::make('low_stock') - ->label('Stoc minim') + ->label(__('Stoc minim')) ->query(fn ($q) => $q->whereColumn('qty', '<=', 'min_qty')), Tables\Filters\Filter::make('out_of_stock') - ->label('Lipsă') + ->label(__('Lipsă')) ->query(fn ($q) => $q->where('qty', '<=', 0)), ]) ->actions([ Actions\Action::make('qr') - ->label('QR') + ->label(__('QR')) ->icon('heroicon-m-qr-code') ->color('gray') ->modalHeading(fn (Part $r) => 'QR pentru ' . $r->name) @@ -187,7 +192,7 @@ class PartResource extends Resource ]); }), Actions\Action::make('ai_price') - ->label('AI: preț recomandat') + ->label(__('AI: preț recomandat')) ->icon('heroicon-m-sparkles') ->color('primary') ->modalHeading(fn (Part $r) => "AI: preț pentru {$r->name}") @@ -199,20 +204,20 @@ class PartResource extends Resource return view('filament.tenant.ai-reply', ['reply' => $reply, 'meta' => $meta]); }), Actions\Action::make('receive') - ->label('Recepție') + ->label(__('Recepție')) ->icon('heroicon-m-arrow-down-tray') ->color('success') ->schema([ - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->required()->minValue(0.001), - Forms\Components\TextInput::make('buy_price')->label('Preț unitar')->numeric()->required(), + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->required()->minValue(0.001), + Forms\Components\TextInput::make('buy_price')->label(__('Preț unitar'))->numeric()->required(), Forms\Components\Select::make('supplier_id') - ->label('Furnizor') + ->label(__('Furnizor')) ->options(fn () => \App\Models\Tenant\Supplier::pluck('name', 'id')), Forms\Components\Select::make('warehouse_id') - ->label('Depozit') + ->label(__('Depozit')) ->options(fn () => \App\Models\Tenant\Warehouse::where('is_active', true)->pluck('name', 'id')) ->default(fn () => \App\Models\Tenant\Warehouse::where('is_default', true)->value('id')), - Forms\Components\TextInput::make('batch_ref')->label('Ref. lot/factură')->maxLength(64), + Forms\Components\TextInput::make('batch_ref')->label(__('Ref. lot/factură'))->maxLength(64), ]) ->action(function (Part $record, array $data) { $warehouse = $data['warehouse_id'] @@ -230,7 +235,7 @@ class PartResource extends Resource batchRef: $data['batch_ref'] ?? null, ); \Filament\Notifications\Notification::make() - ->title('Stoc adăugat') + ->title(__('Stoc adăugat')) ->success() ->send(); }), @@ -239,7 +244,7 @@ class PartResource extends Resource ]) ->bulkActions([ Actions\BulkAction::make('print_labels') - ->label('Tipărește etichete QR') + ->label(__('Tipărește etichete QR')) ->icon('heroicon-m-printer') ->color('gray') ->action(function ($records) { @@ -248,20 +253,20 @@ class PartResource extends Resource }) ->deselectRecordsAfterCompletion(), Actions\BulkAction::make('publish') - ->label('Publică în magazin') + ->label(__('Publică în magazin')) ->icon('heroicon-m-globe-alt') ->color('success') ->action(fn ($records) => collect($records)->each->update(['is_published' => true])) ->deselectRecordsAfterCompletion(), Actions\BulkAction::make('unpublish') - ->label('Scoate din magazin') + ->label(__('Scoate din magazin')) ->icon('heroicon-m-eye-slash') ->color('gray') ->action(fn ($records) => collect($records)->each->update(['is_published' => false])) ->deselectRecordsAfterCompletion(), ]) - ->emptyStateHeading('Depozit gol') - ->emptyStateDescription('Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.') + ->emptyStateHeading(__('Depozit gol')) + ->emptyStateDescription(__('Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.')) ->emptyStateIcon('heroicon-o-cube') ->defaultSort('name'); } diff --git a/app/Filament/Tenant/Resources/PartResource/RelationManagers/BatchesRelationManager.php b/app/Filament/Tenant/Resources/PartResource/RelationManagers/BatchesRelationManager.php index 1dfa811..a1e63ae 100644 --- a/app/Filament/Tenant/Resources/PartResource/RelationManagers/BatchesRelationManager.php +++ b/app/Filament/Tenant/Resources/PartResource/RelationManagers/BatchesRelationManager.php @@ -10,36 +10,41 @@ class BatchesRelationManager extends RelationManager { protected static string $relationship = 'batches'; - protected static ?string $title = 'Loturi (FIFO)'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Loturi (FIFO)'); + } public function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('received_at') - ->label('Recepție') + ->label(__('Recepție')) ->dateTime('d.m.Y H:i') ->sortable(), - Tables\Columns\TextColumn::make('warehouse.code')->label('Depozit')->placeholder('—'), - Tables\Columns\TextColumn::make('batch_ref')->label('Ref.')->placeholder('—'), - Tables\Columns\TextColumn::make('supplier.name')->label('Furnizor')->placeholder('—'), + Tables\Columns\TextColumn::make('warehouse.code')->label(__('Depozit'))->placeholder('—'), + Tables\Columns\TextColumn::make('batch_ref')->label(__('Ref.'))->placeholder('—'), + Tables\Columns\TextColumn::make('supplier.name')->label(__('Furnizor'))->placeholder('—'), Tables\Columns\TextColumn::make('qty_in') - ->label('Intrat') + ->label(__('Intrat')) ->numeric(decimalPlaces: 2) ->alignRight(), Tables\Columns\TextColumn::make('qty_remaining') - ->label('Rămas') + ->label(__('Rămas')) ->numeric(decimalPlaces: 2) ->alignRight() ->weight('bold') ->color(fn ($state) => (float) $state <= 0 ? 'gray' : 'success'), Tables\Columns\TextColumn::make('buy_price') - ->label('Preț unit.') + ->label(__('Preț unit.')) ->money('MDL') ->alignRight(), ]) ->defaultSort('received_at') - ->emptyStateHeading('Niciun lot înregistrat') - ->emptyStateDescription('Apasă „Recepție" pe lista de piese pentru a înregistra prima intrare în depozit.'); + ->emptyStateHeading(__('Niciun lot înregistrat')) + ->emptyStateDescription(__('Apasă „Recepție" pe lista de piese pentru a înregistra prima intrare în depozit.')); } } diff --git a/app/Filament/Tenant/Resources/PartResource/RelationManagers/CrossRefsRelationManager.php b/app/Filament/Tenant/Resources/PartResource/RelationManagers/CrossRefsRelationManager.php index f37c2e7..6818d24 100644 --- a/app/Filament/Tenant/Resources/PartResource/RelationManagers/CrossRefsRelationManager.php +++ b/app/Filament/Tenant/Resources/PartResource/RelationManagers/CrossRefsRelationManager.php @@ -13,13 +13,18 @@ class CrossRefsRelationManager extends RelationManager { protected static string $relationship = 'crossRefs'; - protected static ?string $title = 'Coduri cross (OEM/echivalente)'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Coduri cross (OEM/echivalente)'); + } public function form(Schema $schema): Schema { return $schema->components([ - Forms\Components\TextInput::make('cross_article')->label('Cod echivalent')->required()->maxLength(64), - Forms\Components\TextInput::make('brand')->label('Brand')->maxLength(64), + Forms\Components\TextInput::make('cross_article')->label(__('Cod echivalent'))->required()->maxLength(64), + Forms\Components\TextInput::make('brand')->label(__('Brand'))->maxLength(64), ]); } @@ -28,12 +33,12 @@ class CrossRefsRelationManager extends RelationManager return $table ->recordTitleAttribute('cross_article') ->columns([ - Tables\Columns\TextColumn::make('cross_article')->label('Cod')->searchable(), + Tables\Columns\TextColumn::make('cross_article')->label(__('Cod'))->searchable(), Tables\Columns\TextColumn::make('brand')->placeholder('—'), ]) ->headerActions([Actions\CreateAction::make()]) ->actions([Actions\EditAction::make(), Actions\DeleteAction::make()]) - ->emptyStateHeading('Niciun cod cross') - ->emptyStateDescription('Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.'); + ->emptyStateHeading(__('Niciun cod cross')) + ->emptyStateDescription(__('Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.')); } } diff --git a/app/Filament/Tenant/Resources/PartResource/RelationManagers/PriceHistoryRelationManager.php b/app/Filament/Tenant/Resources/PartResource/RelationManagers/PriceHistoryRelationManager.php index 7bb697a..33d1be5 100644 --- a/app/Filament/Tenant/Resources/PartResource/RelationManagers/PriceHistoryRelationManager.php +++ b/app/Filament/Tenant/Resources/PartResource/RelationManagers/PriceHistoryRelationManager.php @@ -10,26 +10,31 @@ class PriceHistoryRelationManager extends RelationManager { protected static string $relationship = 'priceHistory'; - protected static ?string $title = 'Istoric prețuri furnizori'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Istoric prețuri furnizori'); + } public function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('observed_at') - ->label('Data') + ->label(__('Data')) ->dateTime('d.m.Y H:i') ->sortable(), - Tables\Columns\TextColumn::make('supplier.name')->label('Furnizor')->searchable(), - Tables\Columns\TextColumn::make('purchase.number')->label('PO')->placeholder('—'), + Tables\Columns\TextColumn::make('supplier.name')->label(__('Furnizor'))->searchable(), + Tables\Columns\TextColumn::make('purchase.number')->label(__('PO'))->placeholder('—'), Tables\Columns\TextColumn::make('price') ->money('MDL') ->alignRight() ->sortable(), - Tables\Columns\TextColumn::make('currency')->label('Val.'), + Tables\Columns\TextColumn::make('currency')->label(__('Val.')), ]) ->defaultSort('observed_at', 'desc') - ->emptyStateHeading('Niciun preț înregistrat') - ->emptyStateDescription('Prețurile se înregistrează automat la fiecare recepție de PO.'); + ->emptyStateHeading(__('Niciun preț înregistrat')) + ->emptyStateDescription(__('Prețurile se înregistrează automat la fiecare recepție de PO.')); } } diff --git a/app/Filament/Tenant/Resources/PaymentResource.php b/app/Filament/Tenant/Resources/PaymentResource.php index 4054a67..ac1835b 100644 --- a/app/Filament/Tenant/Resources/PaymentResource.php +++ b/app/Filament/Tenant/Resources/PaymentResource.php @@ -30,9 +30,19 @@ class PaymentResource extends Resource return __('nav.group.Finanțe'); } - protected static ?string $modelLabel = 'plată'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'plăți'; + public static function getModelLabel(): string + { + return __('plată'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('plăți'); + } protected static ?int $navigationSort = 50; @@ -54,28 +64,28 @@ class PaymentResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Plată') + Schemas\Components\Section::make(__('Plată')) ->columns(2) ->schema([ - Forms\Components\DatePicker::make('paid_at')->label('Data')->default(today())->required(), + Forms\Components\DatePicker::make('paid_at')->label(__('Data'))->default(today())->required(), Forms\Components\Select::make('method') ->options(Payment::METHODS) ->default('cash') ->required(), - Forms\Components\TextInput::make('amount')->label('Sumă')->numeric()->required(), - Forms\Components\TextInput::make('reference')->label('Referință (chitanță / tranzacție)')->maxLength(64), + Forms\Components\TextInput::make('amount')->label(__('Sumă'))->numeric()->required(), + Forms\Components\TextInput::make('reference')->label(__('Referință (chitanță / tranzacție)'))->maxLength(64), Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable(), Forms\Components\Select::make('work_order_id') - ->label('Fișă lucru') + ->label(__('Fișă lucru')) ->options(fn () => WorkOrder::orderBy('id', 'desc')->limit(50) ->get()->mapWithKeys(fn ($w) => [$w->id => "{$w->number} — " . ($w->client?->name ?? '?')]) ->toArray()) ->searchable() - ->helperText('Plata stabilește automat starea fișei (unpaid/partial/paid)'), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + ->helperText(__('Plata stabilește automat starea fișei (unpaid/partial/paid)')), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]), ]); } @@ -84,22 +94,22 @@ class PaymentResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('paid_at')->label('Data')->date('d.m.Y')->sortable(), - Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable(), - Tables\Columns\TextColumn::make('workOrder.number')->label('Fișă')->placeholder('—'), + Tables\Columns\TextColumn::make('paid_at')->label(__('Data'))->date('d.m.Y')->sortable(), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->searchable(), + Tables\Columns\TextColumn::make('workOrder.number')->label(__('Fișă'))->placeholder('—'), Tables\Columns\TextColumn::make('method') ->formatStateUsing(fn ($s) => Payment::METHODS[$s] ?? $s) ->badge(), - Tables\Columns\TextColumn::make('amount')->money('MDL')->alignRight()->sortable()->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label('Total')), - Tables\Columns\TextColumn::make('reference')->label('Ref.')->placeholder('—')->toggleable(), + Tables\Columns\TextColumn::make('amount')->money('MDL')->alignRight()->sortable()->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label(__('Total'))), + Tables\Columns\TextColumn::make('reference')->label(__('Ref.'))->placeholder('—')->toggleable(), ]) ->filters([ Tables\Filters\SelectFilter::make('method')->options(Payment::METHODS), Tables\Filters\Filter::make('today') - ->label('Astăzi') + ->label(__('Astăzi')) ->query(fn ($q) => $q->whereDate('paid_at', today())), Tables\Filters\Filter::make('this_month') - ->label('Luna curentă') + ->label(__('Luna curentă')) ->query(fn ($q) => $q->whereMonth('paid_at', now()->month)->whereYear('paid_at', now()->year)), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/PayrollAdjustmentResource.php b/app/Filament/Tenant/Resources/PayrollAdjustmentResource.php index f2fe0d9..06a08cb 100644 --- a/app/Filament/Tenant/Resources/PayrollAdjustmentResource.php +++ b/app/Filament/Tenant/Resources/PayrollAdjustmentResource.php @@ -31,7 +31,12 @@ class PayrollAdjustmentResource extends Resource protected static ?string $modelLabel = 'ajustare'; - protected static ?string $pluralModelLabel = 'bonusuri/avansuri'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('bonusuri/avansuri'); + } protected static ?int $navigationSort = 54; @@ -48,11 +53,11 @@ class PayrollAdjustmentResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Detalii') + Schemas\Components\Section::make(__('Detalii')) ->columns(2) ->schema([ Forms\Components\Select::make('user_id') - ->label('Utilizator') + ->label(__('Utilizator')) ->options(fn () => User::orderBy('name')->pluck('name', 'id')) ->searchable() ->required(), @@ -60,14 +65,14 @@ class PayrollAdjustmentResource extends Resource ->options(PayrollAdjustment::TYPES) ->default('bonus') ->required(), - Forms\Components\TextInput::make('amount')->label('Sumă')->numeric()->required(), + Forms\Components\TextInput::make('amount')->label(__('Sumă'))->numeric()->required(), Forms\Components\TextInput::make('period') - ->label('Perioadă (YYYY-MM)') + ->label(__('Perioadă (YYYY-MM)')) ->placeholder(now()->format('Y-m')) ->regex('/^\d{4}-\d{2}$/') ->required(), Forms\Components\DatePicker::make('date')->default(today())->required(), - Forms\Components\TextInput::make('reason')->label('Motiv')->maxLength(160), + Forms\Components\TextInput::make('reason')->label(__('Motiv'))->maxLength(160), ]), ]); } @@ -77,7 +82,7 @@ class PayrollAdjustmentResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('date')->date('d.m.Y')->sortable(), - Tables\Columns\TextColumn::make('user.name')->label('Utilizator')->searchable(), + Tables\Columns\TextColumn::make('user.name')->label(__('Utilizator'))->searchable(), Tables\Columns\TextColumn::make('type') ->formatStateUsing(fn ($s) => PayrollAdjustment::TYPES[$s] ?? $s) ->badge() @@ -89,12 +94,12 @@ class PayrollAdjustmentResource extends Resource Tables\Columns\TextColumn::make('period'), Tables\Columns\TextColumn::make('amount')->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('reason')->limit(40), - Tables\Columns\IconColumn::make('applied')->boolean()->label('Aplicat'), + Tables\Columns\IconColumn::make('applied')->boolean()->label(__('Aplicat')), ]) ->filters([ Tables\Filters\SelectFilter::make('type')->options(PayrollAdjustment::TYPES), Tables\Filters\SelectFilter::make('user_id') - ->label('Utilizator') + ->label(__('Utilizator')) ->options(fn () => User::pluck('name', 'id')), ]) ->actions([ diff --git a/app/Filament/Tenant/Resources/PayrollRunResource.php b/app/Filament/Tenant/Resources/PayrollRunResource.php index 0a68975..0abbcad 100644 --- a/app/Filament/Tenant/Resources/PayrollRunResource.php +++ b/app/Filament/Tenant/Resources/PayrollRunResource.php @@ -50,40 +50,40 @@ class PayrollRunResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Detalii') + Schemas\Components\Section::make(__('Detalii')) ->columns(2) ->schema([ Forms\Components\Select::make('user_id') - ->label('Utilizator') + ->label(__('Utilizator')) ->options(fn () => User::orderBy('name')->pluck('name', 'id')) ->searchable() ->required(), Forms\Components\TextInput::make('period') - ->label('Perioada (YYYY-MM)') + ->label(__('Perioada (YYYY-MM)')) ->placeholder(now()->format('Y-m')) ->required() ->regex('/^\d{4}-\d{2}$/'), ]), - Schemas\Components\Section::make('Calcul') + Schemas\Components\Section::make(__('Calcul')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('base')->label('Bază')->numeric()->default(0), - Forms\Components\TextInput::make('works_revenue')->label('Venit manopere')->numeric()->disabled(), - Forms\Components\TextInput::make('works_pct_amount')->label('Comision manopere')->numeric()->disabled(), - Forms\Components\TextInput::make('parts_margin')->label('Marja piese')->numeric()->disabled(), - Forms\Components\TextInput::make('parts_pct_amount')->label('Comision piese')->numeric()->disabled(), + Forms\Components\TextInput::make('base')->label(__('Bază'))->numeric()->default(0), + Forms\Components\TextInput::make('works_revenue')->label(__('Venit manopere'))->numeric()->disabled(), + Forms\Components\TextInput::make('works_pct_amount')->label(__('Comision manopere'))->numeric()->disabled(), + Forms\Components\TextInput::make('parts_margin')->label(__('Marja piese'))->numeric()->disabled(), + Forms\Components\TextInput::make('parts_pct_amount')->label(__('Comision piese'))->numeric()->disabled(), Forms\Components\TextInput::make('bonus')->numeric()->default(0), - Forms\Components\TextInput::make('fines')->label('Penalizări')->numeric()->default(0), - Forms\Components\TextInput::make('advance')->label('Avans')->numeric()->default(0), - Forms\Components\TextInput::make('total')->label('Total net')->numeric()->disabled(), + Forms\Components\TextInput::make('fines')->label(__('Penalizări'))->numeric()->default(0), + Forms\Components\TextInput::make('advance')->label(__('Avans'))->numeric()->default(0), + Forms\Components\TextInput::make('total')->label(__('Total net'))->numeric()->disabled(), ]), - Schemas\Components\Section::make('Plată') + Schemas\Components\Section::make(__('Plată')) ->columns(2) ->schema([ - Forms\Components\Toggle::make('paid')->label('Achitat'), - Forms\Components\DatePicker::make('paid_at')->label('Data plății'), + Forms\Components\Toggle::make('paid')->label(__('Achitat')), + Forms\Components\DatePicker::make('paid_at')->label(__('Data plății')), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -91,21 +91,21 @@ class PayrollRunResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('period')->label('Perioadă')->sortable(), - Tables\Columns\TextColumn::make('user.name')->label('Utilizator')->searchable(), + Tables\Columns\TextColumn::make('period')->label(__('Perioadă'))->sortable(), + Tables\Columns\TextColumn::make('user.name')->label(__('Utilizator'))->searchable(), Tables\Columns\TextColumn::make('base')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('works_pct_amount')->label('% manopere')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('parts_pct_amount')->label('% piese')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('works_pct_amount')->label(__('% manopere'))->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('parts_pct_amount')->label(__('% piese'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('bonus')->money('MDL')->alignRight()->color('success'), Tables\Columns\TextColumn::make('fines')->money('MDL')->alignRight()->color('danger'), Tables\Columns\TextColumn::make('advance')->money('MDL')->alignRight()->color('warning'), - Tables\Columns\TextColumn::make('total')->label('Total')->money('MDL')->alignRight()->weight('bold') + Tables\Columns\TextColumn::make('total')->label(__('Total'))->money('MDL')->alignRight()->weight('bold') ->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')), Tables\Columns\IconColumn::make('paid')->boolean(), ]) ->headerActions([ Actions\Action::make('compute_all') - ->label('Calculează luna curentă') + ->label(__('Calculează luna curentă')) ->icon('heroicon-m-calculator') ->color('primary') ->action(function () { @@ -122,13 +122,13 @@ class PayrollRunResource extends Resource ]) ->filters([ Tables\Filters\SelectFilter::make('period') - ->label('Perioadă') + ->label(__('Perioadă')) ->options(fn () => PayrollRun::distinct()->pluck('period', 'period')->toArray()), - Tables\Filters\TernaryFilter::make('paid')->label('Achitat'), + Tables\Filters\TernaryFilter::make('paid')->label(__('Achitat')), ]) ->actions([ Actions\Action::make('recompute') - ->label('Recalculează') + ->label(__('Recalculează')) ->icon('heroicon-m-arrow-path') ->action(fn (PayrollRun $r) => app(PayrollCalculator::class)->compute($r->user_id, $r->period)), Actions\EditAction::make(), diff --git a/app/Filament/Tenant/Resources/PostResource.php b/app/Filament/Tenant/Resources/PostResource.php index ebbdb74..48f72d5 100644 --- a/app/Filament/Tenant/Resources/PostResource.php +++ b/app/Filament/Tenant/Resources/PostResource.php @@ -32,7 +32,12 @@ class PostResource extends Resource protected static ?string $modelLabel = 'pod'; - protected static ?string $pluralModelLabel = 'posturi de lucru'; + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('posturi de lucru'); + } protected static ?int $navigationSort = 76; @@ -44,37 +49,37 @@ class PostResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Pod / Spațiu lucru') + Schemas\Components\Section::make(__('Pod / Spațiu lucru')) ->columns(2) ->schema([ Forms\Components\TextInput::make('name') - ->label('Nume') + ->label(__('Nume')) ->required() ->maxLength(80) - ->placeholder('Ex: Pod 1, Curte 1, Atelier electric'), + ->placeholder(__('Ex: Pod 1, Curte 1, Atelier electric')), Forms\Components\ColorPicker::make('color') ->default('#3b82f6'), Forms\Components\TextInput::make('hours_per_day') - ->label('Ore disponibile / zi') + ->label(__('Ore disponibile / zi')) ->numeric() ->step(0.5) ->default(10) - ->helperText('Capacitatea zilnică în ore'), + ->helperText(__('Capacitatea zilnică în ore')), Forms\Components\Select::make('default_master_id') - ->label('Mecanic implicit') + ->label(__('Mecanic implicit')) ->options(fn () => User::where('status', 'active')->pluck('name', 'id')) ->searchable() - ->placeholder('Niciun mecanic implicit') - ->helperText('Va fi pre-completat când creezi o programare pentru acest pod'), + ->placeholder(__('Niciun mecanic implicit')) + ->helperText(__('Va fi pre-completat când creezi o programare pentru acest pod')), Forms\Components\TextInput::make('description') - ->label('Descriere') + ->label(__('Descriere')) ->maxLength(255) - ->placeholder('Ex: cu lift, fără lift, doar diagnoză...') + ->placeholder(__('Ex: cu lift, fără lift, doar diagnoză...')) ->columnSpanFull(), Forms\Components\TextInput::make('sort_order') ->numeric() ->default(100), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), ]); } @@ -85,11 +90,11 @@ class PostResource extends Resource ->columns([ Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\ColorColumn::make('color'), - Tables\Columns\TextColumn::make('hours_per_day')->label('Ore/zi')->sortable(), - Tables\Columns\TextColumn::make('defaultMaster.name')->label('Mecanic implicit')->placeholder('—'), + Tables\Columns\TextColumn::make('hours_per_day')->label(__('Ore/zi'))->sortable(), + Tables\Columns\TextColumn::make('defaultMaster.name')->label(__('Mecanic implicit'))->placeholder('—'), Tables\Columns\TextColumn::make('description')->placeholder('—')->limit(40)->toggleable(), - Tables\Columns\TextColumn::make('appointments_count')->counts('appointments')->label('Programări')->badge(), - Tables\Columns\ToggleColumn::make('is_active')->label('Activ'), + Tables\Columns\TextColumn::make('appointments_count')->counts('appointments')->label(__('Programări'))->badge(), + Tables\Columns\ToggleColumn::make('is_active')->label(__('Activ')), ]) ->actions([ Actions\EditAction::make(), diff --git a/app/Filament/Tenant/Resources/PricingCoefficientResource.php b/app/Filament/Tenant/Resources/PricingCoefficientResource.php index edbbe48..9a2f3d9 100644 --- a/app/Filament/Tenant/Resources/PricingCoefficientResource.php +++ b/app/Filament/Tenant/Resources/PricingCoefficientResource.php @@ -28,57 +28,53 @@ class PricingCoefficientResource extends Resource return __('nav.group.Depozit'); } - protected static ?string $modelLabel = 'coeficient'; - - protected static ?string $pluralModelLabel = 'coeficienți preț'; - protected static ?int $navigationSort = 46; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Coeficient') + Schemas\Components\Section::make(__('Coeficient')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Denumire')->required() - ->placeholder('ex: Mașină veche, Client VIP, Express')->columnSpanFull(), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required() + ->placeholder(__('ex: Mașină veche, Client VIP, Express'))->columnSpanFull(), Forms\Components\TextInput::make('multiplier') - ->label('Multiplicator') + ->label(__('Multiplicator')) ->numeric() ->required() ->default(1.10) - ->helperText('1.15 = +15% peste prețul de bază. 0.95 = -5%.'), - Forms\Components\TextInput::make('priority')->label('Prioritate')->numeric()->default(100), + ->helperText(__('1.15 = +15% peste prețul de bază. 0.95 = -5%.')), + Forms\Components\TextInput::make('priority')->label(__('Prioritate'))->numeric()->default(100), Forms\Components\Toggle::make('stackable') - ->label('Cumulabil') + ->label(__('Cumulabil')) ->default(true) - ->helperText('Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.'), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + ->helperText(__('Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.')), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), - Schemas\Components\Section::make('Condiții (toate trebuie îndeplinite)') - ->description('Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.') + Schemas\Components\Section::make(__('Condiții (toate trebuie îndeplinite)')) + ->description(__('Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.')) ->columns(2) ->schema([ Forms\Components\CheckboxList::make('conditions.classes') - ->label('Clase auto') + ->label(__('Clase auto')) ->options(PricingCoefficient::VEHICLE_CLASSES) ->columns(2) ->columnSpanFull(), Forms\Components\CheckboxList::make('conditions.body_types') - ->label('Caroserie') + ->label(__('Caroserie')) ->options(\App\Models\Tenant\Vehicle::BODY_TYPES) ->columns(3) ->columnSpanFull(), Forms\Components\CheckboxList::make('conditions.transmissions') - ->label('Cutie de viteze') + ->label(__('Cutie de viteze')) ->options(\App\Models\Tenant\Vehicle::TRANSMISSION_TYPES) ->columns(3) ->columnSpanFull(), - Forms\Components\TextInput::make('conditions.age_min')->label('Vârstă min (ani)')->numeric(), - Forms\Components\TextInput::make('conditions.age_max')->label('Vârstă max (ani)')->numeric(), - Forms\Components\Toggle::make('conditions.client_vip')->label('Doar clienți VIP'), + Forms\Components\TextInput::make('conditions.age_min')->label(__('Vârstă min (ani)'))->numeric(), + Forms\Components\TextInput::make('conditions.age_max')->label(__('Vârstă max (ani)'))->numeric(), + Forms\Components\Toggle::make('conditions.client_vip')->label(__('Doar clienți VIP')), Forms\Components\CheckboxList::make('conditions.urgency') - ->label('Urgență') + ->label(__('Urgență')) ->options(PricingCoefficient::URGENCY) ->columns(3) ->columnSpanFull(), @@ -90,25 +86,25 @@ class PricingCoefficientResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('priority')->label('Prio')->sortable()->alignRight(), + Tables\Columns\TextColumn::make('priority')->label(__('Prio'))->sortable()->alignRight(), Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('multiplier') - ->label('Multiplicator') + ->label(__('Multiplicator')) ->formatStateUsing(fn ($s) => '×' . rtrim(rtrim(number_format((float) $s, 3), '0'), '.')) ->alignRight() ->color(fn ($s) => (float) $s >= 1 ? 'success' : 'warning'), - Tables\Columns\IconColumn::make('stackable')->label('Cumul.')->boolean(), - Tables\Columns\IconColumn::make('is_active')->label('Activ')->boolean(), + Tables\Columns\IconColumn::make('stackable')->label(__('Cumul.'))->boolean(), + Tables\Columns\IconColumn::make('is_active')->label(__('Activ'))->boolean(), ]) ->filters([ - Tables\Filters\TernaryFilter::make('is_active')->label('Active'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Active')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun coeficient') - ->emptyStateDescription('Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.') + ->emptyStateHeading(__('Niciun coeficient')) + ->emptyStateDescription(__('Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.')) ->emptyStateIcon('heroicon-o-adjustments-horizontal') ->defaultSort('priority'); } diff --git a/app/Filament/Tenant/Resources/PurchaseResource.php b/app/Filament/Tenant/Resources/PurchaseResource.php index 0a338c7..1d92a4b 100644 --- a/app/Filament/Tenant/Resources/PurchaseResource.php +++ b/app/Filament/Tenant/Resources/PurchaseResource.php @@ -32,26 +32,36 @@ class PurchaseResource extends Resource return __('nav.group.Depozit'); } - protected static ?string $modelLabel = 'achiziție'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'achiziții'; + public static function getModelLabel(): string + { + return __('achiziție'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('achiziții'); + } protected static ?int $navigationSort = 43; public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Antet') + Schemas\Components\Section::make(__('Antet')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('number')->label('Nr.')->disabled()->dehydrated(false)->placeholder('Generat automat'), + Forms\Components\TextInput::make('number')->label(__('Nr.'))->disabled()->dehydrated(false)->placeholder(__('Generat automat')), Forms\Components\Select::make('supplier_id') - ->label('Furnizor') + ->label(__('Furnizor')) ->options(fn () => Supplier::where('is_active', true)->pluck('name', 'id')) ->searchable() ->required(), Forms\Components\Select::make('warehouse_id') - ->label('Depozit țintă') + ->label(__('Depozit țintă')) ->options(fn () => Warehouse::where('is_active', true)->pluck('name', 'id')) ->default(fn () => Warehouse::where('is_default', true)->value('id')) ->required(), @@ -59,12 +69,12 @@ class PurchaseResource extends Resource ->options(Purchase::STATUSES) ->default('draft') ->required(), - Forms\Components\DatePicker::make('order_date')->label('Data comandă')->default(today())->required(), - Forms\Components\DatePicker::make('expected_at')->label('Așteptată'), - Forms\Components\DatePicker::make('received_at')->label('Recepționată'), - Forms\Components\DatePicker::make('paid_at')->label('Plătită')->columnSpanFull(), + Forms\Components\DatePicker::make('order_date')->label(__('Data comandă'))->default(today())->required(), + Forms\Components\DatePicker::make('expected_at')->label(__('Așteptată')), + Forms\Components\DatePicker::make('received_at')->label(__('Recepționată')), + Forms\Components\DatePicker::make('paid_at')->label(__('Plătită'))->columnSpanFull(), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -72,11 +82,11 @@ class PurchaseResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), - Tables\Columns\TextColumn::make('supplier.name')->label('Furnizor')->searchable(), - Tables\Columns\TextColumn::make('order_date')->label('Comandată')->date('d.m.Y'), - Tables\Columns\TextColumn::make('expected_at')->label('Așteptată')->date('d.m.Y')->placeholder('—'), - Tables\Columns\TextColumn::make('received_at')->label('Recepționată')->date('d.m.Y')->placeholder('—'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('supplier.name')->label(__('Furnizor'))->searchable(), + Tables\Columns\TextColumn::make('order_date')->label(__('Comandată'))->date('d.m.Y'), + Tables\Columns\TextColumn::make('expected_at')->label(__('Așteptată'))->date('d.m.Y')->placeholder('—'), + Tables\Columns\TextColumn::make('received_at')->label(__('Recepționată'))->date('d.m.Y')->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => Purchase::STATUSES[$s] ?? $s) ->badge() @@ -88,7 +98,7 @@ class PurchaseResource extends Resource 'danger' => ['cancelled'], ]), Tables\Columns\TextColumn::make('received_progress') - ->label('Progres') + ->label(__('Progres')) ->state(function (Purchase $r) { $items = $r->items; $ord = (float) $items->sum('qty'); @@ -101,27 +111,27 @@ class PurchaseResource extends Resource ->filters([ Tables\Filters\SelectFilter::make('status')->options(Purchase::STATUSES), Tables\Filters\SelectFilter::make('supplier_id') - ->label('Furnizor') + ->label(__('Furnizor')) ->options(fn () => Supplier::pluck('name', 'id')), ]) ->actions([ Actions\Action::make('receive_all') - ->label('Recepție totală') + ->label(__('Recepție totală')) ->icon('heroicon-m-check-circle') ->color('success') ->visible(fn (Purchase $r) => ! in_array($r->status, ['received', 'cancelled', 'draft'], true)) ->requiresConfirmation() - ->modalDescription('Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.') + ->modalDescription(__('Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.')) ->action(function (Purchase $r) { try { $r->receiveAllRemaining(); Notification::make() - ->title('Recepție completă — batch-uri create') + ->title(__('Recepție completă — batch-uri create')) ->success() ->send(); } catch (\Throwable $e) { Notification::make() - ->title('Eroare') + ->title(__('Eroare')) ->body($e->getMessage()) ->danger() ->send(); diff --git a/app/Filament/Tenant/Resources/PurchaseResource/Pages/ListPurchases.php b/app/Filament/Tenant/Resources/PurchaseResource/Pages/ListPurchases.php index 7505698..4ff2f5b 100644 --- a/app/Filament/Tenant/Resources/PurchaseResource/Pages/ListPurchases.php +++ b/app/Filament/Tenant/Resources/PurchaseResource/Pages/ListPurchases.php @@ -21,14 +21,14 @@ class ListPurchases extends ListRecords { return [ Actions\Action::make('ocr') - ->label('Import factură (OCR)') + ->label(__('Import factură (OCR)')) ->icon('heroicon-m-document-arrow-up') ->color('gray') - ->modalHeading('Import factură via OCR') - ->modalDescription('Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.') + ->modalHeading(__('Import factură via OCR')) + ->modalDescription(__('Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.')) ->schema([ Forms\Components\FileUpload::make('invoice') - ->label('Foto factură') + ->label(__('Foto factură')) ->image() ->disk('local') ->directory('ocr-imports') @@ -41,7 +41,7 @@ class ListPurchases extends ListRecords if (! ($result['ok'] ?? false)) { Notification::make() - ->title('OCR eșuat') + ->title(__('OCR eșuat')) ->body($result['error'] ?? 'Eroare necunoscută.') ->danger()->send(); @unlink($abs); @@ -83,7 +83,7 @@ class ListPurchases extends ListRecords @unlink($abs); Notification::make() - ->title('Factură importată') + ->title(__('Factură importată')) ->body(sprintf('%d linii, total %.2f. Verifică și ajustează înainte de a confirma.', count($payload['items']), (float) $purchase->total)) ->success()->send(); diff --git a/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php b/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php index 6edebae..3b861b2 100644 --- a/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php +++ b/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php @@ -18,13 +18,18 @@ class ItemsRelationManager extends RelationManager { protected static string $relationship = 'items'; - protected static ?string $title = 'Articole'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Articole'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('part_id') - ->label('Piesă din catalog') + ->label(__('Piesă din catalog')) ->options(fn () => Part::where('is_active', true) ->get() ->mapWithKeys(fn ($p) => [$p->id => "{$p->name} " . ($p->article ? "[{$p->article}]" : '')]) @@ -40,11 +45,11 @@ class ItemsRelationManager extends RelationManager } }) ->columnSpanFull(), - Forms\Components\TextInput::make('name')->label('Denumire')->required()->columnSpanFull(), - Forms\Components\TextInput::make('article')->label('Cod articol'), - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label('UM')->default('buc'), - Forms\Components\TextInput::make('buy_price')->label('Preț achiziție')->numeric()->required(), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->columnSpanFull(), + Forms\Components\TextInput::make('article')->label(__('Cod articol')), + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), + Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc'), + Forms\Components\TextInput::make('buy_price')->label(__('Preț achiziție'))->numeric()->required(), ]); } @@ -55,13 +60,13 @@ class ItemsRelationManager extends RelationManager ->columns([ Tables\Columns\TextColumn::make('name')->wrap(), Tables\Columns\TextColumn::make('article')->placeholder('—'), - Tables\Columns\TextColumn::make('qty')->label('Comandat')->alignRight(), + Tables\Columns\TextColumn::make('qty')->label(__('Comandat'))->alignRight(), Tables\Columns\TextColumn::make('qty_received') - ->label('Recepționat') + ->label(__('Recepționat')) ->alignRight() ->color(fn ($state, $record) => $record->isFullyReceived() ? 'success' : ((float) $state > 0 ? 'warning' : 'gray')) ->formatStateUsing(fn ($state, $record) => sprintf('%.2f / %.2f', (float) $state, (float) $record->qty)), - Tables\Columns\TextColumn::make('unit')->label('UM'), + Tables\Columns\TextColumn::make('unit')->label(__('UM')), Tables\Columns\TextColumn::make('buy_price')->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight(), ]) @@ -70,22 +75,22 @@ class ItemsRelationManager extends RelationManager ]) ->actions([ Actions\Action::make('receive_item') - ->label('Recepționează') + ->label(__('Recepționează')) ->icon('heroicon-m-arrow-down-tray') ->color('success') ->visible(fn (PurchaseItem $r) => ! $r->isFullyReceived()) ->schema([ Forms\Components\Placeholder::make('outstanding') - ->label('Restanță') + ->label(__('Restanță')) ->content(fn (PurchaseItem $r) => sprintf('%.2f %s', $r->outstanding(), $r->unit ?? 'buc')), Forms\Components\TextInput::make('qty') - ->label('Cantitate recepționată') + ->label(__('Cantitate recepționată')) ->numeric() ->required() ->minValue(0.001) ->default(fn (PurchaseItem $r) => $r->outstanding()), Forms\Components\Select::make('warehouse_id') - ->label('Depozit țintă') + ->label(__('Depozit țintă')) ->options(fn () => Warehouse::where('is_active', true)->pluck('name', 'id')) ->default(fn (PurchaseItem $r) => $r->purchase?->warehouse_id ?? Warehouse::where('is_default', true)->value('id')) @@ -96,12 +101,12 @@ class ItemsRelationManager extends RelationManager try { $r->purchase->receiveItem($r, (float) $data['qty'], $wh); Notification::make() - ->title('Recepționat — batch creat') + ->title(__('Recepționat — batch creat')) ->success() ->send(); } catch (\Throwable $e) { Notification::make() - ->title('Eroare la recepție') + ->title(__('Eroare la recepție')) ->body($e->getMessage()) ->danger() ->send(); diff --git a/app/Filament/Tenant/Resources/RoleResource.php b/app/Filament/Tenant/Resources/RoleResource.php index 9e95dc8..cca04ad 100644 --- a/app/Filament/Tenant/Resources/RoleResource.php +++ b/app/Filament/Tenant/Resources/RoleResource.php @@ -43,16 +43,16 @@ class RoleResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Rol') + Schemas\Components\Section::make(__('Rol')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Slug')->required()->maxLength(64) + Forms\Components\TextInput::make('name')->label(__('Slug'))->required()->maxLength(64) ->disabled(fn ($record) => $record && in_array($record->name, array_keys(Permissions::roleMatrix()), true)) - ->helperText('Rolurile sistem (owner/admin/etc.) au numele blocat'), + ->helperText(__('Rolurile sistem (owner/admin/etc.) au numele blocat')), Forms\Components\TextInput::make('guard_name')->default('web')->disabled(), ]), - Schemas\Components\Section::make('Drepturi') - ->description('Bifează ce poate face acest rol. Modificările au efect imediat.') + Schemas\Components\Section::make(__('Drepturi')) + ->description(__('Bifează ce poate face acest rol. Modificările au efect imediat.')) ->schema(self::permissionFields()) ->columns(1), ]); @@ -88,25 +88,25 @@ class RoleResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('name') - ->label('Rol') + ->label(__('Rol')) ->formatStateUsing(fn ($state) => Permissions::roleLabels()[$state] ?? $state) ->searchable() ->sortable(), Tables\Columns\TextColumn::make('name') - ->label('Slug') + ->label(__('Slug')) ->copyable() ->color('gray'), Tables\Columns\TextColumn::make('permissions_count') ->counts('permissions') - ->label('Drepturi') + ->label(__('Drepturi')) ->badge(), Tables\Columns\TextColumn::make('users_count') ->counts('users') - ->label('Utilizatori') + ->label(__('Utilizatori')) ->badge(), ]) ->actions([ - Actions\EditAction::make()->label('Editează drepturi'), + Actions\EditAction::make()->label(__('Editează drepturi')), Actions\DeleteAction::make() ->hidden(fn ($record) => in_array($record->name, array_keys(Permissions::roleMatrix()), true)), ]) diff --git a/app/Filament/Tenant/Resources/ServiceTemplateResource.php b/app/Filament/Tenant/Resources/ServiceTemplateResource.php index 0751445..e6ac211 100644 --- a/app/Filament/Tenant/Resources/ServiceTemplateResource.php +++ b/app/Filament/Tenant/Resources/ServiceTemplateResource.php @@ -30,9 +30,19 @@ class ServiceTemplateResource extends Resource return __('nav.group.Service'); } - protected static ?string $modelLabel = 'șablon'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'șabloane servicii'; + public static function getModelLabel(): string + { + return __('șablon'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('șabloane servicii'); + } protected static ?int $navigationSort = 33; @@ -42,14 +52,14 @@ class ServiceTemplateResource extends Resource Schemas\Components\Section::make() ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Denumire')->required() - ->placeholder('ex: Revizie completă 15.000 km')->columnSpanFull(), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required() + ->placeholder(__('ex: Revizie completă 15.000 km'))->columnSpanFull(), Forms\Components\Select::make('category') - ->label('Categorie') + ->label(__('Categorie')) ->options(array_combine(Labor::CATEGORIES, Labor::CATEGORIES)) ->searchable(), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]), ]); } @@ -60,18 +70,18 @@ class ServiceTemplateResource extends Resource ->columns([ Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('category')->badge()->placeholder('—'), - Tables\Columns\TextColumn::make('items_count')->counts('items')->label('Linii')->alignRight(), - Tables\Columns\IconColumn::make('is_active')->label('Activ')->boolean(), + Tables\Columns\TextColumn::make('items_count')->counts('items')->label(__('Linii'))->alignRight(), + Tables\Columns\IconColumn::make('is_active')->label(__('Activ'))->boolean(), ]) ->filters([ - Tables\Filters\TernaryFilter::make('is_active')->label('Active'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Active')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun șablon') - ->emptyStateDescription('Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet") și aplică-l pe o fișă cu un click.') + ->emptyStateHeading(__('Niciun șablon')) + ->emptyStateDescription(__('Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet") și aplică-l pe o fișă cu un click.')) ->emptyStateIcon('heroicon-o-clipboard-document-list') ->defaultSort('name'); } diff --git a/app/Filament/Tenant/Resources/ServiceTemplateResource/RelationManagers/ItemsRelationManager.php b/app/Filament/Tenant/Resources/ServiceTemplateResource/RelationManagers/ItemsRelationManager.php index 96b455d..f42a703 100644 --- a/app/Filament/Tenant/Resources/ServiceTemplateResource/RelationManagers/ItemsRelationManager.php +++ b/app/Filament/Tenant/Resources/ServiceTemplateResource/RelationManagers/ItemsRelationManager.php @@ -18,19 +18,24 @@ class ItemsRelationManager extends RelationManager { protected static string $relationship = 'items'; - protected static ?string $title = 'Conținut șablon'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Conținut șablon'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('kind') - ->label('Tip') + ->label(__('Tip')) ->options(ServiceTemplateItem::KINDS) ->default('labor') ->live() ->required(), Forms\Components\Select::make('labor_id') - ->label('Manoperă') + ->label(__('Manoperă')) ->options(fn () => Labor::where('is_active', true)->pluck('name_ro', 'id')) ->searchable() ->visible(fn (Get $get) => $get('kind') === 'labor') @@ -42,7 +47,7 @@ class ItemsRelationManager extends RelationManager } }), Forms\Components\Select::make('part_id') - ->label('Piesă') + ->label(__('Piesă')) ->options(fn () => Part::where('is_active', true) ->get()->mapWithKeys(fn ($p) => [$p->id => "{$p->name} " . ($p->article ? "[{$p->article}]" : '')])->toArray()) ->searchable() @@ -51,10 +56,10 @@ class ItemsRelationManager extends RelationManager ->afterStateUpdated(function ($state, Set $set) { if ($state && $p = Part::find($state)) $set('name', $p->name); }), - Forms\Components\TextInput::make('name')->label('Denumire')->required()->columnSpanFull(), - Forms\Components\TextInput::make('hours')->label('Ore')->numeric() + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->columnSpanFull(), + Forms\Components\TextInput::make('hours')->label(__('Ore'))->numeric() ->visible(fn (Get $get) => $get('kind') === 'labor'), - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->default(1) + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1) ->visible(fn (Get $get) => $get('kind') === 'part'), ]); } @@ -65,13 +70,13 @@ class ItemsRelationManager extends RelationManager ->recordTitleAttribute('name') ->columns([ Tables\Columns\TextColumn::make('kind') - ->label('Tip') + ->label(__('Tip')) ->formatStateUsing(fn ($s) => ServiceTemplateItem::KINDS[$s] ?? $s) ->badge() ->color(fn ($s) => $s === 'labor' ? 'info' : 'gray'), Tables\Columns\TextColumn::make('name')->wrap(), - Tables\Columns\TextColumn::make('hours')->label('Ore')->placeholder('—')->alignRight(), - Tables\Columns\TextColumn::make('qty')->label('Cant.')->placeholder('—')->alignRight(), + Tables\Columns\TextColumn::make('hours')->label(__('Ore'))->placeholder('—')->alignRight(), + Tables\Columns\TextColumn::make('qty')->label(__('Cant.'))->placeholder('—')->alignRight(), ]) ->headerActions([Actions\CreateAction::make()]) ->actions([Actions\EditAction::make(), Actions\DeleteAction::make()]); diff --git a/app/Filament/Tenant/Resources/ShopCustomerResource.php b/app/Filament/Tenant/Resources/ShopCustomerResource.php index 5e72398..e92eb32 100644 --- a/app/Filament/Tenant/Resources/ShopCustomerResource.php +++ b/app/Filament/Tenant/Resources/ShopCustomerResource.php @@ -32,24 +32,20 @@ class ShopCustomerResource extends Resource return __('nav.group.Magazin'); } - protected static ?string $modelLabel = 'client magazin'; - - protected static ?string $pluralModelLabel = 'clienți magazin'; - protected static ?int $navigationSort = 52; public static function form(Schema $schema): Schema { return $schema->components([ Schemas\Components\Section::make()->columns(2)->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(160), - Forms\Components\TextInput::make('phone')->label('Telefon')->required()->maxLength(40), - Forms\Components\TextInput::make('email')->label('Email')->email()->maxLength(160), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(160), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->required()->maxLength(40), + Forms\Components\TextInput::make('email')->label(__('Email'))->email()->maxLength(160), Forms\Components\Select::make('client_id') - ->label('Client legat (CRM)') + ->label(__('Client legat (CRM)')) ->options(fn () => \App\Models\Tenant\Client::pluck('name', 'id')) ->searchable() - ->helperText('Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.'), + ->helperText(__('Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.')), ]), ]); } @@ -61,19 +57,19 @@ class ShopCustomerResource extends Resource Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('phone')->copyable()->searchable(), Tables\Columns\TextColumn::make('email')->placeholder('—')->copyable()->toggleable(), - Tables\Columns\TextColumn::make('client.name')->label('Client CRM')->placeholder('—')->toggleable(), - Tables\Columns\TextColumn::make('orders_count')->counts('orders')->label('Comenzi')->alignRight(), - Tables\Columns\TextColumn::make('last_login_at')->label('Ultim login')->since()->placeholder('Niciodată'), - Tables\Columns\TextColumn::make('created_at')->label('Înregistrat')->date('d.m.Y')->toggleable(), + Tables\Columns\TextColumn::make('client.name')->label(__('Client CRM'))->placeholder('—')->toggleable(), + Tables\Columns\TextColumn::make('orders_count')->counts('orders')->label(__('Comenzi'))->alignRight(), + Tables\Columns\TextColumn::make('last_login_at')->label(__('Ultim login'))->since()->placeholder(__('Niciodată')), + Tables\Columns\TextColumn::make('created_at')->label(__('Înregistrat'))->date('d.m.Y')->toggleable(), ]) ->actions([ Actions\Action::make('reset_password') - ->label('Trimite reset parolă') + ->label(__('Trimite reset parolă')) ->icon('heroicon-m-key') ->color('warning') ->visible(fn (ShopCustomer $r) => ! empty($r->email)) ->requiresConfirmation() - ->modalDescription('Trimite emailul standard de resetare a parolei către clientul magazinului.') + ->modalDescription(__('Trimite emailul standard de resetare a parolei către clientul magazinului.')) ->action(function (ShopCustomer $r) { $status = Password::broker('shop_customers')->sendResetLink(['email' => $r->email]); Notification::make() @@ -86,8 +82,8 @@ class ShopCustomerResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun client magazin') - ->emptyStateDescription('Aici apar clienții care și-au creat cont în magazinul online (/shop/register).') + ->emptyStateHeading(__('Niciun client magazin')) + ->emptyStateDescription(__('Aici apar clienții care și-au creat cont în magazinul online (/shop/register).')) ->emptyStateIcon('heroicon-o-user-circle') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/ShopCustomerResource/RelationManagers/OrdersRelationManager.php b/app/Filament/Tenant/Resources/ShopCustomerResource/RelationManagers/OrdersRelationManager.php index 25ebbfd..82e93b1 100644 --- a/app/Filament/Tenant/Resources/ShopCustomerResource/RelationManagers/OrdersRelationManager.php +++ b/app/Filament/Tenant/Resources/ShopCustomerResource/RelationManagers/OrdersRelationManager.php @@ -11,15 +11,20 @@ class OrdersRelationManager extends RelationManager { protected static string $relationship = 'orders'; - protected static ?string $title = 'Comenzi'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Comenzi'); + } public function table(Table $table): Table { return $table ->recordTitleAttribute('number') ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.'), - Tables\Columns\TextColumn::make('created_at')->label('Data')->dateTime('d.m.Y H:i'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.')), + Tables\Columns\TextColumn::make('created_at')->label(__('Data'))->dateTime('d.m.Y H:i'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => OnlineOrder::STATUSES[$s] ?? $s) ->badge() @@ -33,6 +38,6 @@ class OrdersRelationManager extends RelationManager Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight(), ]) ->defaultSort('created_at', 'desc') - ->emptyStateHeading('Nicio comandă încă'); + ->emptyStateHeading(__('Nicio comandă încă')); } } diff --git a/app/Filament/Tenant/Resources/SubcontractJobResource.php b/app/Filament/Tenant/Resources/SubcontractJobResource.php index 5ee7808..9fe977c 100644 --- a/app/Filament/Tenant/Resources/SubcontractJobResource.php +++ b/app/Filament/Tenant/Resources/SubcontractJobResource.php @@ -30,10 +30,6 @@ class SubcontractJobResource extends Resource return __('nav.group.Subcontractare'); } - protected static ?string $modelLabel = 'lucrare terți'; - - protected static ?string $pluralModelLabel = 'lucrări terți'; - protected static ?int $navigationSort = 71; public static function getNavigationBadge(): ?string @@ -45,43 +41,43 @@ class SubcontractJobResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Lucrare') + Schemas\Components\Section::make(__('Lucrare')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('number')->label('Nr.')->disabled()->dehydrated(false)->placeholder('Generat automat'), + Forms\Components\TextInput::make('number')->label(__('Nr.'))->disabled()->dehydrated(false)->placeholder(__('Generat automat')), Forms\Components\Select::make('status')->options(SubcontractJob::STATUSES)->default('sent')->required(), Forms\Components\Select::make('subcontractor_id') - ->label('Subcontractor') + ->label(__('Subcontractor')) ->options(fn () => Subcontractor::where('is_active', true)->pluck('name', 'id')) ->searchable(), Forms\Components\Select::make('work_order_id') - ->label('Fișă asociată') + ->label(__('Fișă asociată')) ->options(fn () => WorkOrder::whereNotIn('status', ['done', 'cancelled']) ->get()->mapWithKeys(fn ($w) => [$w->id => "#{$w->number} · " . ($w->vehicle?->plate ?? '')])->toArray()) ->searchable(), Forms\Components\Select::make('category') - ->label('Categorie') + ->label(__('Categorie')) ->options(array_combine(Subcontractor::SPECIALTIES, Subcontractor::SPECIALTIES)) ->searchable(), - Forms\Components\Textarea::make('description')->label('Descriere')->rows(2)->columnSpanFull(), + Forms\Components\Textarea::make('description')->label(__('Descriere'))->rows(2)->columnSpanFull(), ]), - Schemas\Components\Section::make('Cost & marjă') + Schemas\Components\Section::make(__('Cost & marjă')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('cost')->label('Cost (de la terț)')->numeric()->default(0)->required(), - Forms\Components\TextInput::make('markup_pct')->label('Markup %')->numeric()->default(0) - ->helperText('> 0 calculează automat prețul client.'), - Forms\Components\TextInput::make('client_price')->label('Preț client')->numeric()->default(0) - ->helperText('Setat manual dacă markup = 0.'), - Forms\Components\Toggle::make('paid_to_sub')->label('Plătit către terț'), + Forms\Components\TextInput::make('cost')->label(__('Cost (de la terț)'))->numeric()->default(0)->required(), + Forms\Components\TextInput::make('markup_pct')->label(__('Markup %'))->numeric()->default(0) + ->helperText(__('> 0 calculează automat prețul client.')), + Forms\Components\TextInput::make('client_price')->label(__('Preț client'))->numeric()->default(0) + ->helperText(__('Setat manual dacă markup = 0.')), + Forms\Components\Toggle::make('paid_to_sub')->label(__('Plătit către terț')), ]), - Schemas\Components\Section::make('Termene') + Schemas\Components\Section::make(__('Termene')) ->columns(3) ->schema([ - Forms\Components\DatePicker::make('sent_at')->label('Trimis')->default(today()), - Forms\Components\DatePicker::make('eta')->label('ETA'), - Forms\Components\DatePicker::make('returned_at')->label('Returnat'), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\DatePicker::make('sent_at')->label(__('Trimis'))->default(today()), + Forms\Components\DatePicker::make('eta')->label(__('ETA')), + Forms\Components\DatePicker::make('returned_at')->label(__('Returnat')), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]), ]); } @@ -90,14 +86,14 @@ class SubcontractJobResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), - Tables\Columns\TextColumn::make('subcontractor.name')->label('Terț')->placeholder('—'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('subcontractor.name')->label(__('Terț'))->placeholder('—'), Tables\Columns\TextColumn::make('category')->badge()->placeholder('—'), - Tables\Columns\TextColumn::make('workOrder.number')->label('Fișă')->placeholder('—'), - Tables\Columns\TextColumn::make('cost')->label('Cost')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('client_price')->label('Preț client')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('workOrder.number')->label(__('Fișă'))->placeholder('—'), + Tables\Columns\TextColumn::make('cost')->label(__('Cost'))->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('client_price')->label(__('Preț client'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('margin') - ->label('Marjă') + ->label(__('Marjă')) ->state(fn (SubcontractJob $r) => $r->margin()) ->money('MDL') ->alignRight() @@ -110,20 +106,20 @@ class SubcontractJobResource extends Resource 'success' => ['done', 'returned'], 'danger' => ['cancelled'], ]), - Tables\Columns\IconColumn::make('paid_to_sub')->label('Plătit terț')->boolean()->toggleable(), + Tables\Columns\IconColumn::make('paid_to_sub')->label(__('Plătit terț'))->boolean()->toggleable(), ]) ->filters([ Tables\Filters\SelectFilter::make('status')->options(SubcontractJob::STATUSES), Tables\Filters\SelectFilter::make('subcontractor_id') - ->label('Subcontractor') + ->label(__('Subcontractor')) ->options(fn () => Subcontractor::pluck('name', 'id')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Nicio lucrare la terți') - ->emptyStateDescription('Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.') + ->emptyStateHeading(__('Nicio lucrare la terți')) + ->emptyStateDescription(__('Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.')) ->emptyStateIcon('heroicon-o-arrow-top-right-on-square') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/SubcontractorResource.php b/app/Filament/Tenant/Resources/SubcontractorResource.php index 5b62a8e..84326c0 100644 --- a/app/Filament/Tenant/Resources/SubcontractorResource.php +++ b/app/Filament/Tenant/Resources/SubcontractorResource.php @@ -38,19 +38,19 @@ class SubcontractorResource extends Resource { return $schema->components([ Schemas\Components\Section::make()->columns(2)->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(160), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(160), Forms\Components\Select::make('specialty') - ->label('Specialitate') + ->label(__('Specialitate')) ->options(array_combine(Subcontractor::SPECIALTIES, Subcontractor::SPECIALTIES)) ->searchable(), - Forms\Components\TextInput::make('phone')->label('Telefon')->tel()->maxLength(40), + Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), Forms\Components\Select::make('rating') - ->label('Rating') + ->label(__('Rating')) ->options([1 => '★', 2 => '★★', 3 => '★★★', 4 => '★★★★', 5 => '★★★★★']) ->default(3), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]), ]); } @@ -63,18 +63,18 @@ class SubcontractorResource extends Resource Tables\Columns\TextColumn::make('specialty')->badge()->placeholder('—'), Tables\Columns\TextColumn::make('phone')->copyable()->placeholder('—'), Tables\Columns\TextColumn::make('rating')->formatStateUsing(fn ($s) => str_repeat('★', (int) $s)), - Tables\Columns\TextColumn::make('jobs_count')->counts('jobs')->label('Lucrări')->alignRight(), + Tables\Columns\TextColumn::make('jobs_count')->counts('jobs')->label(__('Lucrări'))->alignRight(), Tables\Columns\IconColumn::make('is_active')->boolean(), ]) ->filters([ - Tables\Filters\TernaryFilter::make('is_active')->label('Activi'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Activi')), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun subcontractor') - ->emptyStateDescription('Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.') + ->emptyStateHeading(__('Niciun subcontractor')) + ->emptyStateDescription(__('Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.')) ->emptyStateIcon('heroicon-o-user-group') ->defaultSort('name'); } diff --git a/app/Filament/Tenant/Resources/SupplierResource.php b/app/Filament/Tenant/Resources/SupplierResource.php index 7936b78..ae49e2d 100644 --- a/app/Filament/Tenant/Resources/SupplierResource.php +++ b/app/Filament/Tenant/Resources/SupplierResource.php @@ -37,30 +37,30 @@ class SupplierResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(160), - Forms\Components\TextInput::make('contact_name')->label('Persoană contact')->maxLength(120), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(160), + Forms\Components\TextInput::make('contact_name')->label(__('Persoană contact'))->maxLength(120), Forms\Components\TextInput::make('phone')->tel()->maxLength(40), Forms\Components\TextInput::make('email')->email()->maxLength(120), Forms\Components\TextInput::make('website')->url()->maxLength(160), ]), - Schemas\Components\Section::make('Comercial') + Schemas\Components\Section::make(__('Comercial')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('pay_terms')->label('Termeni plată')->placeholder('Net 30 / Avans')->maxLength(60), - Forms\Components\TextInput::make('delivery_days')->label('Zile livrare')->numeric()->default(0), + Forms\Components\TextInput::make('pay_terms')->label(__('Termeni plată'))->placeholder(__('Net 30 / Avans'))->maxLength(60), + Forms\Components\TextInput::make('delivery_days')->label(__('Zile livrare'))->numeric()->default(0), Forms\Components\Select::make('rating') - ->label('Rating') + ->label(__('Rating')) ->options([1 => '★', 2 => '★★', 3 => '★★★', 4 => '★★★★', 5 => '★★★★★']) ->default(3) ->required(), - Forms\Components\TextInput::make('discount_pct')->label('Discount %')->numeric()->default(0), - Forms\Components\TagsInput::make('categories')->label('Categorii')->placeholder('Frâne, Ulei, ...')->columnSpan(2), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\TextInput::make('discount_pct')->label(__('Discount %'))->numeric()->default(0), + Forms\Components\TagsInput::make('categories')->label(__('Categorii'))->placeholder(__('Frâne, Ulei, ...'))->columnSpan(2), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -69,41 +69,41 @@ class SupplierResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('name')->searchable()->sortable(), - Tables\Columns\TextColumn::make('contact_name')->label('Contact')->placeholder('—'), + Tables\Columns\TextColumn::make('contact_name')->label(__('Contact'))->placeholder('—'), Tables\Columns\TextColumn::make('phone')->copyable()->placeholder('—'), Tables\Columns\TextColumn::make('rating') - ->label('Rating') + ->label(__('Rating')) ->formatStateUsing(fn ($s) => str_repeat('★', (int) $s)), Tables\Columns\TextColumn::make('on_time_pct') - ->label('La timp 90d') + ->label(__('La timp 90d')) ->state(fn (Supplier $r) => app(\App\Services\Warehouse\SupplierAnalytics::class)->onTimeRate($r)) ->formatStateUsing(fn ($s) => $s === null ? '—' : "{$s}%") ->color(fn ($s) => $s === null ? 'gray' : ($s >= 90 ? 'success' : ($s >= 70 ? 'warning' : 'danger'))) ->alignRight() ->toggleable(), Tables\Columns\TextColumn::make('avg_delivery_days') - ->label('Avg zile') + ->label(__('Avg zile')) ->state(fn (Supplier $r) => app(\App\Services\Warehouse\SupplierAnalytics::class)->avgDeliveryDays($r)) ->formatStateUsing(fn ($s) => $s === null ? '—' : (string) $s) ->alignRight() ->toggleable(), Tables\Columns\TextColumn::make('spend_90d') - ->label('Cheltuit 90d') + ->label(__('Cheltuit 90d')) ->state(fn (Supplier $r) => app(\App\Services\Warehouse\SupplierAnalytics::class)->spend($r)) ->money('MDL') ->alignRight() ->toggleable(), - Tables\Columns\TextColumn::make('delivery_days')->label('Livrare (zile)')->alignRight()->toggleable(), - Tables\Columns\TextColumn::make('discount_pct')->label('Discount') + Tables\Columns\TextColumn::make('delivery_days')->label(__('Livrare (zile)'))->alignRight()->toggleable(), + Tables\Columns\TextColumn::make('discount_pct')->label(__('Discount')) ->formatStateUsing(fn ($s) => $s . '%')->alignRight()->toggleable(), Tables\Columns\IconColumn::make('is_active')->boolean(), ]) ->filters([ - Tables\Filters\TernaryFilter::make('is_active')->label('Activi'), + Tables\Filters\TernaryFilter::make('is_active')->label(__('Activi')), ]) ->actions([ Actions\Action::make('rate') - ->label('Rerating') + ->label(__('Rerating')) ->icon('heroicon-m-arrow-path') ->color('gray') ->action(function (Supplier $r) { @@ -111,8 +111,8 @@ class SupplierResource extends Resource ->computedRating($r); if ($score === null) { \Filament\Notifications\Notification::make() - ->title('Date insuficiente') - ->body('Necesită cel puțin 2 recepții complete cu data așteptată setată.') + ->title(__('Date insuficiente')) + ->body(__('Necesită cel puțin 2 recepții complete cu data așteptată setată.')) ->warning() ->send(); return; diff --git a/app/Filament/Tenant/Resources/TireSetResource.php b/app/Filament/Tenant/Resources/TireSetResource.php index 9e5f901..5d8e495 100644 --- a/app/Filament/Tenant/Resources/TireSetResource.php +++ b/app/Filament/Tenant/Resources/TireSetResource.php @@ -32,10 +32,6 @@ class TireSetResource extends Resource return __('nav.group.Anvelope'); } - protected static ?string $modelLabel = 'set anvelope'; - - protected static ?string $pluralModelLabel = 'seturi anvelope'; - protected static ?int $navigationSort = 60; public static function getNavigationBadge(): ?string @@ -52,53 +48,53 @@ class TireSetResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Proprietar') + Schemas\Components\Section::make(__('Proprietar')) ->columns(2) ->schema([ Forms\Components\Select::make('client_id') - ->label('Client') + ->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->live() ->required(), Forms\Components\Select::make('vehicle_id') - ->label('Auto') + ->label(__('Auto')) ->options(fn (Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id'))->get() ->mapWithKeys(fn ($v) => [$v->id => "{$v->make} {$v->model} {$v->plate}"])->toArray() : []) ->searchable(), - Forms\Components\TextInput::make('label')->label('Etichetă')->placeholder('ex: Iarnă Michelin'), - Forms\Components\Select::make('season')->label('Sezon')->options(TireSet::SEASONS)->default('winter')->required(), + Forms\Components\TextInput::make('label')->label(__('Etichetă'))->placeholder(__('ex: Iarnă Michelin')), + Forms\Components\Select::make('season')->label(__('Sezon'))->options(TireSet::SEASONS)->default('winter')->required(), ]), - Schemas\Components\Section::make('Specificații') + Schemas\Components\Section::make(__('Specificații')) ->columns(3) ->schema([ - Forms\Components\TextInput::make('width')->label('Lățime')->numeric()->placeholder('205'), - Forms\Components\TextInput::make('profile')->label('Profil')->numeric()->placeholder('55'), - Forms\Components\TextInput::make('diameter')->label('Diametru R')->numeric()->placeholder('16'), + Forms\Components\TextInput::make('width')->label(__('Lățime'))->numeric()->placeholder('205'), + Forms\Components\TextInput::make('profile')->label(__('Profil'))->numeric()->placeholder('55'), + Forms\Components\TextInput::make('diameter')->label(__('Diametru R'))->numeric()->placeholder('16'), Forms\Components\TextInput::make('brand')->maxLength(64), Forms\Components\TextInput::make('model')->maxLength(64), - Forms\Components\TextInput::make('dot_year')->label('DOT')->maxLength(8)->placeholder('3621'), - Forms\Components\Toggle::make('has_rims')->label('Cu jante'), - Forms\Components\Select::make('rim_type')->label('Tip jante')->options(['steel' => 'Tablă', 'alloy' => 'Aliaj']), - Forms\Components\Select::make('condition')->label('Stare')->options(TireSet::CONDITIONS), + Forms\Components\TextInput::make('dot_year')->label(__('DOT'))->maxLength(8)->placeholder('3621'), + Forms\Components\Toggle::make('has_rims')->label(__('Cu jante')), + Forms\Components\Select::make('rim_type')->label(__('Tip jante'))->options(['steel' => 'Tablă', 'alloy' => 'Aliaj']), + Forms\Components\Select::make('condition')->label(__('Stare'))->options(TireSet::CONDITIONS), ]), - Schemas\Components\Section::make('Uzură (mm) per poziție') + Schemas\Components\Section::make(__('Uzură (mm) per poziție')) ->columns(4) ->schema([ - Forms\Components\TextInput::make('tread.fl')->label('Față-Stânga')->numeric(), - Forms\Components\TextInput::make('tread.fr')->label('Față-Dreapta')->numeric(), - Forms\Components\TextInput::make('tread.rl')->label('Spate-Stânga')->numeric(), - Forms\Components\TextInput::make('tread.rr')->label('Spate-Dreapta')->numeric(), + Forms\Components\TextInput::make('tread.fl')->label(__('Față-Stânga'))->numeric(), + Forms\Components\TextInput::make('tread.fr')->label(__('Față-Dreapta'))->numeric(), + Forms\Components\TextInput::make('tread.rl')->label(__('Spate-Stânga'))->numeric(), + Forms\Components\TextInput::make('tread.rr')->label(__('Spate-Dreapta'))->numeric(), ]), - Schemas\Components\Section::make('TPMS & foto') + Schemas\Components\Section::make(__('TPMS & foto')) ->columns(2) ->schema([ - Forms\Components\Toggle::make('tpms')->label('Senzori TPMS'), - Forms\Components\TextInput::make('notes')->label('Observații'), + Forms\Components\Toggle::make('tpms')->label(__('Senzori TPMS')), + Forms\Components\TextInput::make('notes')->label(__('Observații')), \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('photos') - ->label('Fotografii') + ->label(__('Fotografii')) ->collection('photos') ->multiple() ->image() @@ -112,23 +108,23 @@ class TireSetResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable()->sortable(), - Tables\Columns\TextColumn::make('label')->label('Etichetă')->placeholder('—'), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('label')->label(__('Etichetă'))->placeholder('—'), Tables\Columns\TextColumn::make('size') - ->label('Dimensiune') + ->label(__('Dimensiune')) ->state(fn (TireSet $r) => $r->sizeLabel()), Tables\Columns\TextColumn::make('season') - ->label('Sezon') + ->label(__('Sezon')) ->formatStateUsing(fn ($s) => TireSet::SEASONS[$s] ?? $s) ->badge() ->colors(['warning' => ['summer'], 'info' => ['winter'], 'gray' => ['allseason']]), - Tables\Columns\TextColumn::make('tread_min')->label('Uzură min') + Tables\Columns\TextColumn::make('tread_min')->label(__('Uzură min')) ->formatStateUsing(fn ($s) => $s ? $s . ' mm' : '—') ->color(fn ($s) => $s !== null && (float) $s < 3 ? 'danger' : null) ->alignRight(), - Tables\Columns\IconColumn::make('tpms')->label('TPMS')->boolean()->toggleable(), + Tables\Columns\IconColumn::make('tpms')->label(__('TPMS'))->boolean()->toggleable(), Tables\Columns\TextColumn::make('storage_status') - ->label('Depozit') + ->label(__('Depozit')) ->state(fn (TireSet $r) => $r->isStored() ? ($r->currentStorage()?->location ?? 'da') : '—') ->badge() ->color(fn ($state) => $state === '—' ? 'gray' : 'success'), @@ -136,19 +132,19 @@ class TireSetResource extends Resource ->filters([ Tables\Filters\SelectFilter::make('season')->options(TireSet::SEASONS), Tables\Filters\Filter::make('stored') - ->label('În depozit') + ->label(__('În depozit')) ->query(fn ($q) => $q->whereHas('storage', fn ($s) => $s->where('status', 'stored'))), ]) ->actions([ Actions\Action::make('check_in') - ->label('Check-in depozit') + ->label(__('Check-in depozit')) ->icon('heroicon-m-arrow-down-on-square') ->color('success') ->visible(fn (TireSet $r) => ! $r->isStored()) ->schema([ - Forms\Components\TextInput::make('location')->label('Locație (raft)')->required()->placeholder('A1-03'), - Forms\Components\TextInput::make('season_label')->label('Perioadă')->placeholder('Iarnă 2025-2026'), - Forms\Components\TextInput::make('fee')->label('Taxă depozitare')->numeric()->default(0), + Forms\Components\TextInput::make('location')->label(__('Locație (raft)'))->required()->placeholder(__('A1-03')), + Forms\Components\TextInput::make('season_label')->label(__('Perioadă'))->placeholder(__('Iarnă 2025-2026')), + Forms\Components\TextInput::make('fee')->label(__('Taxă depozitare'))->numeric()->default(0), ]) ->action(function (TireSet $r, array $data) { \App\Models\Tenant\TireStorage::create([ @@ -159,27 +155,27 @@ class TireSetResource extends Resource 'status' => 'stored', 'checked_in_at' => now(), ]); - \Filament\Notifications\Notification::make()->title('Set primit în depozit')->success()->send(); + \Filament\Notifications\Notification::make()->title(__('Set primit în depozit'))->success()->send(); }), Actions\Action::make('check_out') - ->label('Eliberează') + ->label(__('Eliberează')) ->icon('heroicon-m-arrow-up-on-square') ->color('warning') ->visible(fn (TireSet $r) => $r->isStored()) ->requiresConfirmation() - ->modalDescription('Marchează setul ca ridicat de client.') + ->modalDescription(__('Marchează setul ca ridicat de client.')) ->action(function (TireSet $r) { $storage = $r->currentStorage(); if ($storage) { $storage->update(['status' => 'retrieved', 'checked_out_at' => now()]); } - \Filament\Notifications\Notification::make()->title('Set eliberat din depozit')->success()->send(); + \Filament\Notifications\Notification::make()->title(__('Set eliberat din depozit'))->success()->send(); }), Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun set de anvelope') - ->emptyStateDescription('Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.') + ->emptyStateHeading(__('Niciun set de anvelope')) + ->emptyStateDescription(__('Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.')) ->emptyStateIcon('heroicon-o-lifebuoy') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/TireSetResource/RelationManagers/StorageRelationManager.php b/app/Filament/Tenant/Resources/TireSetResource/RelationManagers/StorageRelationManager.php index 8afee07..cf9b4de 100644 --- a/app/Filament/Tenant/Resources/TireSetResource/RelationManagers/StorageRelationManager.php +++ b/app/Filament/Tenant/Resources/TireSetResource/RelationManagers/StorageRelationManager.php @@ -11,25 +11,30 @@ class StorageRelationManager extends RelationManager { protected static string $relationship = 'storage'; - protected static ?string $title = 'Istoric depozitare'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Istoric depozitare'); + } public function table(Table $table): Table { return $table ->columns([ - Tables\Columns\TextColumn::make('season_label')->label('Perioadă')->placeholder('—'), - Tables\Columns\TextColumn::make('location')->label('Locație')->placeholder('—'), - Tables\Columns\TextColumn::make('checked_in_at')->label('Primit')->dateTime('d.m.Y'), - Tables\Columns\TextColumn::make('checked_out_at')->label('Ridicat')->dateTime('d.m.Y')->placeholder('—'), + Tables\Columns\TextColumn::make('season_label')->label(__('Perioadă'))->placeholder('—'), + Tables\Columns\TextColumn::make('location')->label(__('Locație'))->placeholder('—'), + Tables\Columns\TextColumn::make('checked_in_at')->label(__('Primit'))->dateTime('d.m.Y'), + Tables\Columns\TextColumn::make('checked_out_at')->label(__('Ridicat'))->dateTime('d.m.Y')->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => TireStorage::STATUSES[$s] ?? $s) ->badge() ->colors(['success' => ['stored'], 'gray' => ['retrieved']]), Tables\Columns\TextColumn::make('fee')->money('MDL')->alignRight(), - Tables\Columns\IconColumn::make('paid')->label('Plătit')->boolean(), + Tables\Columns\IconColumn::make('paid')->label(__('Plătit'))->boolean(), ]) ->defaultSort('checked_in_at', 'desc') - ->emptyStateHeading('Niciun istoric') - ->emptyStateDescription('Folosește „Check-in depozit" pe set pentru a înregistra prima depozitare.'); + ->emptyStateHeading(__('Niciun istoric')) + ->emptyStateDescription(__('Folosește „Check-in depozit" pe set pentru a înregistra prima depozitare.')); } } diff --git a/app/Filament/Tenant/Resources/UserResource.php b/app/Filament/Tenant/Resources/UserResource.php index ae4098a..f818520 100644 --- a/app/Filament/Tenant/Resources/UserResource.php +++ b/app/Filament/Tenant/Resources/UserResource.php @@ -54,39 +54,39 @@ class UserResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identitate') + Schemas\Components\Section::make(__('Identitate')) ->columns(2) ->schema([ - Forms\Components\TextInput::make('name')->label('Nume')->required()->maxLength(120), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->maxLength(120), Forms\Components\TextInput::make('email')->email()->required()->maxLength(120), Forms\Components\TextInput::make('phone')->tel()->maxLength(40), Forms\Components\Select::make('locale') ->options(['ro' => 'Română', 'ru' => 'Русский', 'en' => 'English']) ->default('ro'), ]), - Schemas\Components\Section::make('Acces') + Schemas\Components\Section::make(__('Acces')) ->columns(2) ->schema([ Forms\Components\Select::make('role') - ->label('Rol primar') + ->label(__('Rol primar')) ->options(\App\Auth\Permissions::roleLabels()) ->required() ->default('mechanic') - ->helperText('Rolul principal — sincronizat automat cu drepturile RBAC.'), + ->helperText(__('Rolul principal — sincronizat automat cu drepturile RBAC.')), Forms\Components\Select::make('status') ->options(['active' => 'Activ', 'inactive' => 'Inactiv', 'blocked' => 'Blocat']) ->default('active') ->required(), Forms\Components\TextInput::make('password') - ->label('Parolă') + ->label(__('Parolă')) ->password() ->required(fn (string $context) => $context === 'create') ->dehydrated(fn ($state) => filled($state)) ->dehydrateStateUsing(fn ($state) => Hash::make($state)) ->minLength(6) - ->helperText('La editare lasă gol pentru a păstra parola actuală.'), + ->helperText(__('La editare lasă gol pentru a păstra parola actuală.')), Forms\Components\Select::make('roles_picked') - ->label('Roluri suplimentare') + ->label(__('Roluri suplimentare')) ->multiple() ->options(\App\Auth\Permissions::roleLabels()) ->afterStateHydrated(function ($component, $record) { @@ -94,35 +94,35 @@ class UserResource extends Resource }) ->dehydrated(false) ->columnSpanFull() - ->helperText('Roluri suplimentare peste rolul primar — drepturile se cumulează.'), + ->helperText(__('Roluri suplimentare peste rolul primar — drepturile se cumulează.')), ]), - Schemas\Components\Section::make('Salariu & marjă') - ->description('Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.') + Schemas\Components\Section::make(__('Salariu & marjă')) + ->description(__('Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.')) ->columns(2) ->visible(fn () => auth()->user()?->canDo(\App\Auth\Permissions::FINANCE_VIEW_INTERNAL_MARGIN) ?? false) ->schema([ Forms\Components\TextInput::make('hourly_rate') - ->label('Tarif orar (MDL)') + ->label(__('Tarif orar (MDL)')) ->numeric() ->step(0.01) - ->placeholder('Ex: 100'), + ->placeholder(__('Ex: 100')), Forms\Components\TextInput::make('internal_margin_pct') - ->label('Marjă internă (%)') + ->label(__('Marjă internă (%)')) ->numeric() ->step(0.01) ->minValue(0) ->maxValue(90) - ->placeholder('Ex: 20 pentru +20%') - ->helperText('Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.'), + ->placeholder(__('Ex: 20 pentru +20%')) + ->helperText(__('Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.')), ]), - Schemas\Components\Section::make('Securitate') + Schemas\Components\Section::make(__('Securitate')) ->columns(2) ->schema([ Forms\Components\Placeholder::make('mfa_status') - ->label('Autentificare 2FA') + ->label(__('Autentificare 2FA')) ->content(fn ($record) => $record && $record->hasTwoFactorEnabled() ? '✓ Activat (TOTP)' : '✗ Dezactivat'), Forms\Components\Placeholder::make('last_login') - ->label('Ultima autentificare') + ->label(__('Ultima autentificare')) ->content(fn ($record) => $record?->last_login_at?->diffForHumans() ?? '—'), ]), ]); @@ -139,7 +139,7 @@ class UserResource extends Resource ->formatStateUsing(fn ($state) => \App\Auth\Permissions::roleLabels()[$state] ?? $state) ->badge(), Tables\Columns\IconColumn::make('app_authentication_secret') - ->label('2FA') + ->label(__('2FA')) ->boolean() ->getStateUsing(fn ($record) => $record->hasTwoFactorEnabled()) ->trueIcon('heroicon-o-shield-check') @@ -147,14 +147,14 @@ class UserResource extends Resource ->falseIcon('heroicon-o-shield-exclamation') ->falseColor('warning'), Tables\Columns\TextColumn::make('active_sessions') - ->label('Sesiuni') + ->label(__('Sesiuni')) ->getStateUsing(fn ($record) => \Illuminate\Support\Facades\DB::table('sessions')->where('user_id', $record->id)->count()) ->badge() ->color(fn ($state) => $state > 0 ? 'success' : 'gray') ->toggleable(), Tables\Columns\TextColumn::make('permission_overrides_count') ->counts('permissionOverrides') - ->label('Excepții') + ->label(__('Excepții')) ->badge() ->color('warning') ->toggleable(isToggledHiddenByDefault: true), @@ -180,7 +180,7 @@ class UserResource extends Resource ->actions([ Actions\EditAction::make(), Actions\Action::make('force_logout') - ->label('Force logout') + ->label(__('Force logout')) ->icon('heroicon-o-arrow-right-on-rectangle') ->color('warning') ->visible(fn ($record) => \Illuminate\Support\Facades\DB::table('sessions')->where('user_id', $record->id)->exists()) @@ -191,16 +191,16 @@ class UserResource extends Resource \Filament\Notifications\Notification::make()->title("$n sesiuni revoke-uite")->success()->send(); }), Actions\Action::make('reset_2fa') - ->label('Resetează 2FA') + ->label(__('Resetează 2FA')) ->icon('heroicon-o-shield-exclamation') ->color('warning') ->visible(fn ($record) => $record && $record->hasTwoFactorEnabled()) ->requiresConfirmation() - ->modalDescription('Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.') + ->modalDescription(__('Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.')) ->action(function ($record) { $record->saveAppAuthenticationSecret(null); $record->saveAppAuthenticationRecoveryCodes(null); - \Filament\Notifications\Notification::make()->title('2FA resetat')->success()->send(); + \Filament\Notifications\Notification::make()->title(__('2FA resetat'))->success()->send(); }), Actions\DeleteAction::make(), ]) diff --git a/app/Filament/Tenant/Resources/UserResource/RelationManagers/PermissionOverridesRelationManager.php b/app/Filament/Tenant/Resources/UserResource/RelationManagers/PermissionOverridesRelationManager.php index 15340f2..33c8dee 100644 --- a/app/Filament/Tenant/Resources/UserResource/RelationManagers/PermissionOverridesRelationManager.php +++ b/app/Filament/Tenant/Resources/UserResource/RelationManagers/PermissionOverridesRelationManager.php @@ -16,18 +16,23 @@ class PermissionOverridesRelationManager extends RelationManager { protected static string $relationship = 'permissionOverrides'; - protected static ?string $title = 'Excepții drepturi'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Excepții drepturi'); + } protected static string|\BackedEnum|null $icon = 'heroicon-o-shield-exclamation'; public function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Excepție') + Schemas\Components\Section::make(__('Excepție')) ->columns(2) ->schema([ Forms\Components\Select::make('permission_id') - ->label('Drept') + ->label(__('Drept')) ->required() ->searchable() ->options(fn () => Permission::orderBy('name')->pluck('name', 'id')) @@ -37,12 +42,12 @@ class PermissionOverridesRelationManager extends RelationManager ->options(['grant' => 'GRANT — adaugă dreptul', 'deny' => 'DENY — interzice dreptul']) ->default('grant'), Forms\Components\DatePicker::make('expires_at') - ->label('Expiră la (opțional)') + ->label(__('Expiră la (opțional)')) ->minDate(now()), Forms\Components\Textarea::make('reason') - ->label('Motiv') + ->label(__('Motiv')) ->columnSpanFull() - ->placeholder('Ex: lockdown temporar; acces pentru audit; etc.') + ->placeholder(__('Ex: lockdown temporar; acces pentru audit; etc.')) ->rows(2), ]), ]); @@ -54,7 +59,7 @@ class PermissionOverridesRelationManager extends RelationManager ->recordTitleAttribute('mode') ->columns([ Tables\Columns\TextColumn::make('permission.name') - ->label('Drept') + ->label(__('Drept')) ->searchable() ->sortable(), Tables\Columns\TextColumn::make('mode') @@ -65,12 +70,12 @@ class PermissionOverridesRelationManager extends RelationManager ->limit(40) ->placeholder('—'), Tables\Columns\TextColumn::make('expires_at') - ->label('Expiră') + ->label(__('Expiră')) ->date() - ->placeholder('niciodată') + ->placeholder(__('niciodată')) ->color(fn ($record) => $record?->isExpired() ? 'danger' : 'gray'), Tables\Columns\TextColumn::make('grantedBy.name') - ->label('Acordat de') + ->label(__('Acordat de')) ->placeholder('—') ->toggleable(), ]) diff --git a/app/Filament/Tenant/Resources/VehicleResource.php b/app/Filament/Tenant/Resources/VehicleResource.php index 9ad7820..2bf22dd 100644 --- a/app/Filament/Tenant/Resources/VehicleResource.php +++ b/app/Filament/Tenant/Resources/VehicleResource.php @@ -24,9 +24,19 @@ class VehicleResource extends Resource return __('nav.label.Automobile'); } - protected static ?string $modelLabel = 'mașină'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'mașini'; + public static function getModelLabel(): string + { + return __('mașină'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('mașini'); + } protected static ?int $navigationSort = 20; @@ -51,21 +61,21 @@ class VehicleResource extends Resource public static function form(Schema $schema): Schema { return $schema->components([ - Schemas\Components\Section::make('Identificare') + Schemas\Components\Section::make(__('Identificare')) ->columns(2) ->schema([ Forms\Components\Select::make('client_id') - ->label('Proprietar') + ->label(__('Proprietar')) ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->required(), - Forms\Components\TextInput::make('plate')->label('Nr. înmatriculare')->maxLength(16), - Forms\Components\TextInput::make('make')->label('Marca')->required()->maxLength(60), + Forms\Components\TextInput::make('plate')->label(__('Nr. înmatriculare'))->maxLength(16), + Forms\Components\TextInput::make('make')->label(__('Marca'))->required()->maxLength(60), Forms\Components\TextInput::make('model')->required()->maxLength(60), Forms\Components\TextInput::make('year')->numeric()->minValue(1950)->maxValue(2100), Forms\Components\TextInput::make('vin')->maxLength(32), ]), - Schemas\Components\Section::make('Tehnice') + Schemas\Components\Section::make(__('Tehnice')) ->columns(2) ->schema([ Forms\Components\TextInput::make('engine')->maxLength(60), @@ -76,13 +86,13 @@ class VehicleResource extends Resource 'EV' => 'Electric', 'GPL' => 'GPL', 'GNC' => 'GNC', ]), Forms\Components\Select::make('vehicle_class') - ->label('Clasă (pentru pricing)') + ->label(__('Clasă (pentru pricing)')) ->options(\App\Models\Tenant\PricingCoefficient::VEHICLE_CLASSES) - ->helperText('Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.'), - Forms\Components\TextInput::make('mileage')->label('Kilometraj')->numeric()->default(0), + ->helperText(__('Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.')), + Forms\Components\TextInput::make('mileage')->label(__('Kilometraj'))->numeric()->default(0), Forms\Components\TextInput::make('color')->maxLength(40), ]), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(3), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(3), ]); } @@ -90,18 +100,18 @@ class VehicleResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('plate')->label('Nr.')->searchable(), + Tables\Columns\TextColumn::make('plate')->label(__('Nr.'))->searchable(), Tables\Columns\TextColumn::make('make')->sortable(), Tables\Columns\TextColumn::make('model'), Tables\Columns\TextColumn::make('year'), - Tables\Columns\TextColumn::make('client.name')->label('Proprietar')->searchable(), + Tables\Columns\TextColumn::make('client.name')->label(__('Proprietar'))->searchable(), Tables\Columns\TextColumn::make('vin')->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('mileage')->label('Km')->numeric(), + Tables\Columns\TextColumn::make('mileage')->label(__('Km'))->numeric(), Tables\Columns\TextColumn::make('created_at')->date()->sortable(), ]) ->actions([ Actions\Action::make('decode_vin') - ->label('Decode VIN') + ->label(__('Decode VIN')) ->icon('heroicon-m-cpu-chip') ->color('gray') ->visible(fn (\App\Models\Tenant\Vehicle $r) => ! empty($r->vin) && strlen($r->vin) === 17) @@ -113,11 +123,11 @@ class VehicleResource extends Resource return view('filament.tenant.vin-decode', ['info' => $info, 'vehicle' => $r]); }), Actions\Action::make('ai_recommend') - ->label('AI: recomandări') + ->label(__('AI: recomandări')) ->icon('heroicon-m-sparkles') ->color('primary') ->visible(fn (\App\Models\Tenant\Vehicle $r) => ! empty($r->vin)) - ->modalHeading('Recomandări AI') + ->modalHeading(__('Recomandări AI')) ->modalSubmitAction(false) ->modalCancelActionLabel('Închide') ->modalContent(function (\App\Models\Tenant\Vehicle $r) { @@ -128,8 +138,8 @@ class VehicleResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Nicio mașină încă') - ->emptyStateDescription('Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.') + ->emptyStateHeading(__('Nicio mașină încă')) + ->emptyStateDescription(__('Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.')) ->emptyStateIcon('heroicon-o-truck') ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/VehicleResource/Pages/ListVehicles.php b/app/Filament/Tenant/Resources/VehicleResource/Pages/ListVehicles.php index 539220b..6980f18 100644 --- a/app/Filament/Tenant/Resources/VehicleResource/Pages/ListVehicles.php +++ b/app/Filament/Tenant/Resources/VehicleResource/Pages/ListVehicles.php @@ -17,16 +17,16 @@ class ListVehicles extends ListRecords { return [ Actions\Action::make('export') - ->label('Export CSV') + ->label(__('Export CSV')) ->icon('heroicon-m-arrow-down-tray') ->color('gray') ->action(fn () => app(CsvImportExport::class)->exportVehicles()), Actions\Action::make('import') - ->label('Import CSV') + ->label(__('Import CSV')) ->icon('heroicon-m-arrow-up-tray') ->color('gray') - ->modalHeading('Import mașini din CSV') - ->modalDescription('CSV cu header: ' . implode(', ', CsvImportExport::VEHICLE_COLUMNS) . '. Coloana client_phone trebuie să existe deja la clienți.') + ->modalHeading(__('Import mașini din CSV')) + ->modalDescription(__('CSV cu header: ' . implode(', ', CsvImportExport::VEHICLE_COLUMNS) . '. Coloana client_phone trebuie să existe deja la clienți.')) ->schema([ Forms\Components\FileUpload::make('file') ->required() diff --git a/app/Filament/Tenant/Resources/WarehouseResource.php b/app/Filament/Tenant/Resources/WarehouseResource.php index 596b324..4358478 100644 --- a/app/Filament/Tenant/Resources/WarehouseResource.php +++ b/app/Filament/Tenant/Resources/WarehouseResource.php @@ -38,11 +38,11 @@ class WarehouseResource extends Resource { return $schema->components([ Schemas\Components\Section::make()->columns(2)->schema([ - Forms\Components\TextInput::make('code')->label('Cod')->required()->maxLength(32), - Forms\Components\TextInput::make('name')->label('Denumire')->required()->maxLength(120), - Forms\Components\TextInput::make('address')->label('Adresă')->columnSpanFull()->maxLength(200), - Forms\Components\Toggle::make('is_default')->label('Depozit implicit'), - Forms\Components\Toggle::make('is_active')->label('Activ')->default(true), + Forms\Components\TextInput::make('code')->label(__('Cod'))->required()->maxLength(32), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->maxLength(120), + Forms\Components\TextInput::make('address')->label(__('Adresă'))->columnSpanFull()->maxLength(200), + Forms\Components\Toggle::make('is_default')->label(__('Depozit implicit')), + Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), ]), ]); } @@ -54,19 +54,19 @@ class WarehouseResource extends Resource Tables\Columns\TextColumn::make('code')->searchable()->sortable(), Tables\Columns\TextColumn::make('name')->searchable()->sortable(), Tables\Columns\TextColumn::make('address')->placeholder('—')->toggleable(), - Tables\Columns\IconColumn::make('is_default')->label('Implicit')->boolean(), - Tables\Columns\IconColumn::make('is_active')->label('Activ')->boolean(), + Tables\Columns\IconColumn::make('is_default')->label(__('Implicit'))->boolean(), + Tables\Columns\IconColumn::make('is_active')->label(__('Activ'))->boolean(), Tables\Columns\TextColumn::make('batches_count') ->counts('batches') - ->label('Loturi') + ->label(__('Loturi')) ->alignRight(), ]) ->actions([ Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Niciun depozit') - ->emptyStateDescription('Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).') + ->emptyStateHeading(__('Niciun depozit')) + ->emptyStateDescription(__('Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).')) ->emptyStateIcon('heroicon-o-building-storefront') ->defaultSort('code'); } diff --git a/app/Filament/Tenant/Resources/WorkOrderResource.php b/app/Filament/Tenant/Resources/WorkOrderResource.php index 50fec98..d1a67c0 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource.php @@ -34,9 +34,19 @@ class WorkOrderResource extends Resource return __('nav.group.Service'); } - protected static ?string $modelLabel = 'fișă'; + protected static ?string $modelLabel = null; - protected static ?string $pluralModelLabel = 'fișe lucru'; + public static function getModelLabel(): string + { + return __('fișă'); + } + + protected static ?string $pluralModelLabel = null; + + public static function getPluralModelLabel(): string + { + return __('fișe lucru'); + } protected static ?int $navigationSort = 30; @@ -63,76 +73,76 @@ class WorkOrderResource extends Resource { return $schema->components([ // Antet compact — o singură secțiune densă cu doar esențialul, mereu deschisă. - Schemas\Components\Section::make('Antet fișă') - ->description('Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.') + Schemas\Components\Section::make(__('Antet fișă')) + ->description(__('Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.')) ->compact() ->columns(4) ->schema([ - Forms\Components\TextInput::make('number')->label('Nr.')->disabled()->dehydrated(false)->placeholder('auto'), - Forms\Components\DatePicker::make('opened_at')->label('Deschis')->default(today())->required(), + Forms\Components\TextInput::make('number')->label(__('Nr.'))->disabled()->dehydrated(false)->placeholder('auto'), + Forms\Components\DatePicker::make('opened_at')->label(__('Deschis'))->default(today())->required(), Forms\Components\Select::make('status')->options(WorkOrder::STATUSES)->default('new')->required(), - Forms\Components\Select::make('urgency')->label('Urgență') + Forms\Components\Select::make('urgency')->label(__('Urgență')) ->options(\App\Models\Tenant\PricingCoefficient::URGENCY)->default('normal')->required(), - Forms\Components\Select::make('client_id')->label('Client') + Forms\Components\Select::make('client_id')->label(__('Client')) ->options(fn () => Client::pluck('name', 'id')) ->searchable()->live()->required()->columnSpan(2), - Forms\Components\Select::make('vehicle_id')->label('Auto') + Forms\Components\Select::make('vehicle_id')->label(__('Auto')) ->options(fn (Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id'))->get() ->mapWithKeys(fn ($v) => [$v->id => "{$v->make} {$v->model} {$v->plate}"])->toArray() : []) ->searchable()->columnSpan(2), - Forms\Components\Select::make('master_id')->label('Maistru') + Forms\Components\Select::make('master_id')->label(__('Maistru')) ->options(fn () => User::where('status', 'active')->pluck('name', 'id')) ->searchable()->columnSpan(2), - Forms\Components\TextInput::make('mileage_in')->label('Km intrare')->numeric(), - Forms\Components\TextInput::make('mileage_out')->label('Km ieșire')->numeric(), + Forms\Components\TextInput::make('mileage_in')->label(__('Km intrare'))->numeric(), + Forms\Components\TextInput::make('mileage_out')->label(__('Km ieșire'))->numeric(), ]), // Toate secțiunile de detaliu — collapsible și collapsed by default - Schemas\Components\Section::make('Diagnostic') + Schemas\Components\Section::make(__('Diagnostic')) ->collapsible()->collapsed()->compact() ->schema([ - Forms\Components\Textarea::make('complaint')->label('Plângere client')->rows(2)->columnSpanFull(), - Forms\Components\Textarea::make('diagnosis')->label('Diagnostic')->rows(3)->columnSpanFull(), - Forms\Components\Textarea::make('recommendations')->label('Recomandări')->rows(2)->columnSpanFull(), + Forms\Components\Textarea::make('complaint')->label(__('Plângere client'))->rows(2)->columnSpanFull(), + Forms\Components\Textarea::make('diagnosis')->label(__('Diagnostic'))->rows(3)->columnSpanFull(), + Forms\Components\Textarea::make('recommendations')->label(__('Recomandări'))->rows(2)->columnSpanFull(), ]), - Schemas\Components\Section::make('Foto') + Schemas\Components\Section::make(__('Foto')) ->collapsible()->collapsed()->compact() ->schema([ \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('photos') - ->label('Fotografii')->collection('photos') + ->label(__('Fotografii'))->collection('photos') ->multiple()->reorderable()->image()->imageEditor()->maxFiles(20) ->columnSpanFull(), ]), - Schemas\Components\Section::make('Tracking & ETA') + Schemas\Components\Section::make(__('Tracking & ETA')) ->columns(3)->collapsible()->collapsed()->compact() ->schema([ - Forms\Components\DateTimePicker::make('eta_at')->label('Gata estimat (ETA)')->seconds(false), - Forms\Components\TextInput::make('tracking_token')->label('Token public') + Forms\Components\DateTimePicker::make('eta_at')->label(__('Gata estimat (ETA)'))->seconds(false), + Forms\Components\TextInput::make('tracking_token')->label(__('Token public')) ->disabled()->dehydrated(false)->columnSpan(2) ->helperText(fn (?WorkOrder $record) => $record?->tracking_token ? 'Link client: ' . $record->trackingUrl() : 'Se generează la salvare'), ]), - Schemas\Components\Section::make('Plată & total') + Schemas\Components\Section::make(__('Plată & total')) ->columns(3)->collapsible()->collapsed()->compact() ->schema([ Forms\Components\Select::make('pay_status')->options(WorkOrder::PAY_STATUSES)->default('unpaid')->required(), - Forms\Components\TextInput::make('discount_pct')->label('Discount %')->numeric()->default(0), + Forms\Components\TextInput::make('discount_pct')->label(__('Discount %'))->numeric()->default(0), Forms\Components\Toggle::make('apply_margin') - ->label('Aplică marjă internă') + ->label(__('Aplică marjă internă')) ->default(true) - ->helperText('On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).') + ->helperText(__('On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).')) ->visible(fn () => auth()->user()?->canDo(\App\Auth\Permissions::FINANCE_VIEW_INTERNAL_MARGIN) ?? false), - Forms\Components\TextInput::make('override_margin_pct')->label('Marjă internă (%) — override') + Forms\Components\TextInput::make('override_margin_pct')->label(__('Marjă internă (%) — override')) ->numeric()->step(0.01)->minValue(0)->maxValue(90) - ->placeholder('Ex: 25') - ->helperText('Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.') + ->placeholder(__('Ex: 25')) + ->helperText(__('Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.')) ->visible(fn () => auth()->user()?->canDo(\App\Auth\Permissions::FINANCE_VIEW_INTERNAL_MARGIN) ?? false), - Forms\Components\TextInput::make('total')->label('Total')->numeric()->disabled()->dehydrated(false), - Forms\Components\Toggle::make('approved')->label('Aprobat de client'), - Forms\Components\DatePicker::make('closed_at')->label('Închis'), + Forms\Components\TextInput::make('total')->label(__('Total'))->numeric()->disabled()->dehydrated(false), + Forms\Components\Toggle::make('approved')->label(__('Aprobat de client')), + Forms\Components\DatePicker::make('closed_at')->label(__('Închis')), ]), ]); } @@ -141,11 +151,11 @@ class WorkOrderResource extends Resource { return $table ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), - Tables\Columns\TextColumn::make('opened_at')->label('Deschis')->date('d.m.Y')->sortable(), - Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable(), - Tables\Columns\TextColumn::make('vehicle.plate')->label('Auto')->placeholder('—'), - Tables\Columns\TextColumn::make('master.name')->label('Maistru')->placeholder('—'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.'))->searchable()->sortable(), + Tables\Columns\TextColumn::make('opened_at')->label(__('Deschis'))->date('d.m.Y')->sortable(), + Tables\Columns\TextColumn::make('client.name')->label(__('Client'))->searchable(), + Tables\Columns\TextColumn::make('vehicle.plate')->label(__('Auto'))->placeholder('—'), + Tables\Columns\TextColumn::make('master.name')->label(__('Maistru'))->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($state) => WorkOrder::STATUSES[$state] ?? $state) ->badge() @@ -170,12 +180,12 @@ class WorkOrderResource extends Resource Tables\Filters\SelectFilter::make('status')->options(WorkOrder::STATUSES), Tables\Filters\SelectFilter::make('pay_status')->options(WorkOrder::PAY_STATUSES), Tables\Filters\SelectFilter::make('master_id') - ->label('Maistru') + ->label(__('Maistru')) ->options(fn () => User::pluck('name', 'id')), ]) ->actions([ Actions\Action::make('pdf') - ->label('PDF') + ->label(__('PDF')) ->icon('heroicon-m-document-arrow-down') ->color('gray') ->action(function (WorkOrder $r) { @@ -190,8 +200,8 @@ class WorkOrderResource extends Resource Actions\EditAction::make(), Actions\DeleteAction::make(), ]) - ->emptyStateHeading('Nicio fișă de lucru') - ->emptyStateDescription('Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.') + ->emptyStateHeading(__('Nicio fișă de lucru')) + ->emptyStateDescription(__('Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.')) ->emptyStateIcon('heroicon-o-wrench-screwdriver') ->defaultSort('opened_at', 'desc'); } diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php b/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php index ba614f3..c1f6a28 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php @@ -33,12 +33,12 @@ class EditWorkOrder extends EditRecord { return [ Actions\Action::make('apply_template') - ->label('Aplică șablon') + ->label(__('Aplică șablon')) ->icon('heroicon-m-clipboard-document-list') ->color('gray') ->schema([ \Filament\Forms\Components\Select::make('template_id') - ->label('Șablon serviciu') + ->label(__('Șablon serviciu')) ->options(fn () => \App\Models\Tenant\ServiceTemplate::where('is_active', true)->pluck('name', 'id')) ->searchable() ->required(), @@ -53,11 +53,11 @@ class EditWorkOrder extends EditRecord ->success()->send(); }), Actions\Action::make('ai_diagnose') - ->label('AI: sugerează diagnostic') + ->label(__('AI: sugerează diagnostic')) ->icon('heroicon-m-sparkles') ->color('primary') ->visible(fn () => ! empty($this->record->complaint)) - ->modalHeading('Diagnostic AI bazat pe plângerea clientului') + ->modalHeading(__('Diagnostic AI bazat pe plângerea clientului')) ->modalSubmitAction(false) ->modalCancelActionLabel('Închide') ->modalContent(function () { @@ -66,7 +66,7 @@ class EditWorkOrder extends EditRecord return view('filament.tenant.ai-reply', ['reply' => $reply, 'meta' => $meta]); }), Actions\Action::make('tracking') - ->label('Link client (QR)') + ->label(__('Link client (QR)')) ->icon('heroicon-m-qr-code') ->color('primary') ->modalHeading(fn () => 'Tracking client — WO #' . $this->record->number) @@ -76,7 +76,7 @@ class EditWorkOrder extends EditRecord 'wo' => $this->record, ])), Actions\Action::make('pdf') - ->label('Descarcă PDF') + ->label(__('Descarcă PDF')) ->icon('heroicon-m-document-arrow-down') ->color('gray') ->action(function () { diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php index d634e2c..4175dff 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php @@ -19,13 +19,18 @@ class PartsRelationManager extends RelationManager { protected static string $relationship = 'parts'; - protected static ?string $title = 'Piese'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Piese'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('part_id') - ->label('Din catalog (lasă gol pentru text liber)') + ->label(__('Din catalog (lasă gol pentru text liber)')) ->options(fn () => Part::where('is_active', true) ->get() ->mapWithKeys(fn ($p) => [$p->id => "{$p->name} " . ($p->article ? "[{$p->article}] " : '') . "(stoc: {$p->qty})"]) @@ -43,20 +48,20 @@ class PartsRelationManager extends RelationManager } }) ->columnSpanFull(), - Forms\Components\TextInput::make('name')->label('Denumire')->required()->columnSpanFull(), - Forms\Components\TextInput::make('article')->label('Cod articol')->maxLength(64), + Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->columnSpanFull(), + Forms\Components\TextInput::make('article')->label(__('Cod articol'))->maxLength(64), Forms\Components\TextInput::make('brand')->maxLength(64), - Forms\Components\TextInput::make('qty')->label('Cantitate')->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label('UM')->maxLength(16)->default('buc'), - Forms\Components\TextInput::make('buy_price')->label('Preț achiziție')->numeric()->default(0), - Forms\Components\TextInput::make('sell_price')->label('Preț vânzare')->numeric()->required(), - Forms\Components\TextInput::make('discount_pct')->label('Discount %')->numeric()->default(0), + Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), + Forms\Components\TextInput::make('unit')->label(__('UM'))->maxLength(16)->default('buc'), + Forms\Components\TextInput::make('buy_price')->label(__('Preț achiziție'))->numeric()->default(0), + Forms\Components\TextInput::make('sell_price')->label(__('Preț vânzare'))->numeric()->required(), + Forms\Components\TextInput::make('discount_pct')->label(__('Discount %'))->numeric()->default(0), Forms\Components\Select::make('status') ->options(WorkOrderPart::STATUSES) ->default('needed') ->required() - ->helperText('La trecere pe „Montată" se scade automat din stoc.'), - Forms\Components\Textarea::make('notes')->label('Observații')->columnSpanFull()->rows(2), + ->helperText(__('La trecere pe „Montată" se scade automat din stoc.')), + Forms\Components\Textarea::make('notes')->label(__('Observații'))->columnSpanFull()->rows(2), ]); } @@ -65,11 +70,11 @@ class PartsRelationManager extends RelationManager return $table ->recordTitleAttribute('name') ->columns([ - Tables\Columns\TextColumn::make('name')->label('Piesă')->wrap(), - Tables\Columns\TextColumn::make('article')->label('Cod')->placeholder('—'), + Tables\Columns\TextColumn::make('name')->label(__('Piesă'))->wrap(), + Tables\Columns\TextColumn::make('article')->label(__('Cod'))->placeholder('—'), Tables\Columns\TextColumn::make('brand')->placeholder('—'), - Tables\Columns\TextColumn::make('qty')->label('Cant.')->alignRight(), - Tables\Columns\TextColumn::make('sell_price')->label('Preț')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('qty')->label(__('Cant.'))->alignRight(), + Tables\Columns\TextColumn::make('sell_price')->label(__('Preț'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => WorkOrderPart::STATUSES[$s] ?? $s) @@ -86,11 +91,11 @@ class PartsRelationManager extends RelationManager ]) ->actions([ Actions\Action::make('smart_price') - ->label('Preț inteligent') + ->label(__('Preț inteligent')) ->icon('heroicon-m-sparkles') ->color('primary') ->visible(fn (WorkOrderPart $r) => (bool) $r->part_id) - ->modalHeading('Preț contextual') + ->modalHeading(__('Preț contextual')) ->modalSubmitActionLabel('Aplică prețul') ->modalContent(function (WorkOrderPart $r) { $wo = $r->workOrder; @@ -112,7 +117,7 @@ class PartsRelationManager extends RelationManager ->success()->send(); }), Actions\Action::make('issue_now') - ->label('Eliberează') + ->label(__('Eliberează')) ->icon('heroicon-m-arrow-up-on-square') ->color('warning') ->visible(fn (WorkOrderPart $r) => $r->part_id @@ -128,7 +133,7 @@ class PartsRelationManager extends RelationManager ->success()->send(); }), Actions\Action::make('return_part') - ->label('Restituire') + ->label(__('Restituire')) ->icon('heroicon-m-arrow-uturn-left') ->color('gray') ->visible(fn (WorkOrderPart $r) => $r->part_id @@ -137,12 +142,12 @@ class PartsRelationManager extends RelationManager ->exists()) ->schema([ Forms\Components\TextInput::make('qty') - ->label('Cantitate restituită') + ->label(__('Cantitate restituită')) ->numeric() ->required() ->minValue(0.001) ->default(fn (WorkOrderPart $r) => (float) $r->qty), - Forms\Components\Textarea::make('notes')->rows(2)->label('Observații'), + Forms\Components\Textarea::make('notes')->rows(2)->label(__('Observații')), ]) ->action(function (WorkOrderPart $r, array $data) { $batch = app(WarehouseService::class)->returnPart( diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PaymentsRelationManager.php b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PaymentsRelationManager.php index c6b455c..5abf322 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PaymentsRelationManager.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PaymentsRelationManager.php @@ -14,19 +14,24 @@ class PaymentsRelationManager extends RelationManager { protected static string $relationship = 'payments'; - protected static ?string $title = 'Plăți'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Plăți'); + } public function form(Schema $schema): Schema { return $schema->components([ - Forms\Components\DatePicker::make('paid_at')->label('Data')->default(today())->required(), - Forms\Components\TextInput::make('amount')->label('Sumă')->numeric()->required(), + Forms\Components\DatePicker::make('paid_at')->label(__('Data'))->default(today())->required(), + Forms\Components\TextInput::make('amount')->label(__('Sumă'))->numeric()->required(), Forms\Components\Select::make('method') ->options(Payment::METHODS) ->default('cash') ->required(), - Forms\Components\TextInput::make('reference')->label('Referință')->maxLength(64), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\TextInput::make('reference')->label(__('Referință'))->maxLength(64), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -35,9 +40,9 @@ class PaymentsRelationManager extends RelationManager return $table ->recordTitleAttribute('amount') ->columns([ - Tables\Columns\TextColumn::make('paid_at')->label('Data')->date('d.m.Y'), + Tables\Columns\TextColumn::make('paid_at')->label(__('Data'))->date('d.m.Y'), Tables\Columns\TextColumn::make('amount')->money('MDL')->alignRight() - ->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label('Plătit')), + ->summarize(Tables\Columns\Summarizers\Sum::make()->money('MDL')->label(__('Plătit'))), Tables\Columns\TextColumn::make('method') ->formatStateUsing(fn ($s) => Payment::METHODS[$s] ?? $s) ->badge(), diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/SubcontractJobsRelationManager.php b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/SubcontractJobsRelationManager.php index 770da7e..4ede31a 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/SubcontractJobsRelationManager.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/SubcontractJobsRelationManager.php @@ -15,27 +15,32 @@ class SubcontractJobsRelationManager extends RelationManager { protected static string $relationship = 'subcontractJobs'; - protected static ?string $title = 'Lucrări la terți'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Lucrări la terți'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('subcontractor_id') - ->label('Subcontractor') + ->label(__('Subcontractor')) ->options(fn () => Subcontractor::where('is_active', true)->pluck('name', 'id')) ->searchable() ->columnSpanFull(), Forms\Components\Select::make('category') - ->label('Categorie') + ->label(__('Categorie')) ->options(array_combine(Subcontractor::SPECIALTIES, Subcontractor::SPECIALTIES)) ->searchable(), Forms\Components\Select::make('status')->options(SubcontractJob::STATUSES)->default('sent')->required(), - Forms\Components\Textarea::make('description')->label('Descriere')->rows(2)->columnSpanFull(), - Forms\Components\TextInput::make('cost')->label('Cost (terț)')->numeric()->default(0)->required(), - Forms\Components\TextInput::make('markup_pct')->label('Markup %')->numeric()->default(0), - Forms\Components\TextInput::make('client_price')->label('Preț client')->numeric()->default(0) - ->helperText('Folosit dacă markup = 0.'), - Forms\Components\DatePicker::make('eta')->label('ETA'), + Forms\Components\Textarea::make('description')->label(__('Descriere'))->rows(2)->columnSpanFull(), + Forms\Components\TextInput::make('cost')->label(__('Cost (terț)'))->numeric()->default(0)->required(), + Forms\Components\TextInput::make('markup_pct')->label(__('Markup %'))->numeric()->default(0), + Forms\Components\TextInput::make('client_price')->label(__('Preț client'))->numeric()->default(0) + ->helperText(__('Folosit dacă markup = 0.')), + Forms\Components\DatePicker::make('eta')->label(__('ETA')), ]); } @@ -44,13 +49,13 @@ class SubcontractJobsRelationManager extends RelationManager return $table ->recordTitleAttribute('number') ->columns([ - Tables\Columns\TextColumn::make('number')->label('Nr.'), - Tables\Columns\TextColumn::make('subcontractor.name')->label('Terț')->placeholder('—'), + Tables\Columns\TextColumn::make('number')->label(__('Nr.')), + Tables\Columns\TextColumn::make('subcontractor.name')->label(__('Terț'))->placeholder('—'), Tables\Columns\TextColumn::make('category')->badge()->placeholder('—'), Tables\Columns\TextColumn::make('cost')->money('MDL')->alignRight(), - Tables\Columns\TextColumn::make('client_price')->label('Preț client')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('client_price')->label(__('Preț client'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('margin') - ->label('Marjă') + ->label(__('Marjă')) ->state(fn (SubcontractJob $r) => $r->margin()) ->money('MDL')->alignRight() ->color(fn ($s) => (float) $s > 0 ? 'success' : ((float) $s < 0 ? 'danger' : 'gray')), diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/WorksRelationManager.php b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/WorksRelationManager.php index d4ba58c..1d6b57d 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/WorksRelationManager.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/WorksRelationManager.php @@ -17,13 +17,18 @@ class WorksRelationManager extends RelationManager { protected static string $relationship = 'works'; - protected static ?string $title = 'Manopere'; + protected static ?string $title = null; + + public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string + { + return __('Manopere'); + } public function form(Schema $schema): Schema { return $schema->components([ Forms\Components\Select::make('labor_id') - ->label('Catalog manoperă') + ->label(__('Catalog manoperă')) ->options(fn () => Labor::where('is_active', true) ->get() ->mapWithKeys(fn ($l) => [$l->id => "[{$l->category}] {$l->name_ro} ({$l->hours}h)"]) @@ -38,18 +43,18 @@ class WorksRelationManager extends RelationManager } }) ->columnSpanFull(), - Forms\Components\TextInput::make('name')->label('Nume')->required()->columnSpanFull(), - Forms\Components\TextInput::make('hours')->label('Ore')->numeric()->default(1)->required(), - Forms\Components\TextInput::make('price_per_hour')->label('Preț/h')->numeric()->required(), + Forms\Components\TextInput::make('name')->label(__('Nume'))->required()->columnSpanFull(), + Forms\Components\TextInput::make('hours')->label(__('Ore'))->numeric()->default(1)->required(), + Forms\Components\TextInput::make('price_per_hour')->label(__('Preț/h'))->numeric()->required(), Forms\Components\Select::make('master_id') - ->label('Maistru') + ->label(__('Maistru')) ->options(fn () => User::pluck('name', 'id')) ->searchable(), Forms\Components\Select::make('status') ->options(WorkOrderWork::STATUSES) ->default('todo') ->required(), - Forms\Components\Textarea::make('notes')->label('Notițe')->columnSpanFull()->rows(2), + Forms\Components\Textarea::make('notes')->label(__('Notițe'))->columnSpanFull()->rows(2), ]); } @@ -68,11 +73,11 @@ class WorksRelationManager extends RelationManager return $table ->recordTitleAttribute('name') ->columns([ - Tables\Columns\TextColumn::make('name')->label('Manoperă')->wrap(), - Tables\Columns\TextColumn::make('hours')->label('Ore')->numeric(decimalPlaces: 2)->alignRight(), - Tables\Columns\TextColumn::make('price_per_hour')->label('Preț/h')->money('MDL')->alignRight(), + Tables\Columns\TextColumn::make('name')->label(__('Manoperă'))->wrap(), + Tables\Columns\TextColumn::make('hours')->label(__('Ore'))->numeric(decimalPlaces: 2)->alignRight(), + Tables\Columns\TextColumn::make('price_per_hour')->label(__('Preț/h'))->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('total') - ->label('Total') + ->label(__('Total')) ->money('MDL') ->alignRight() ->description(function ($record) { @@ -82,7 +87,7 @@ class WorksRelationManager extends RelationManager if ((float) $record->applied_margin_pct === 0.0) return null; return 'Bază salariu: ' . number_format((float) $record->salary_base, 2) . ' MDL · marjă ' . rtrim(rtrim(number_format((float) $record->applied_margin_pct, 2), '0'), '.') . '%'; }), - Tables\Columns\TextColumn::make('master.name')->label('Maistru')->placeholder('—'), + Tables\Columns\TextColumn::make('master.name')->label(__('Maistru'))->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($s) => WorkOrderWork::STATUSES[$s] ?? $s) ->badge() diff --git a/app/Filament/Tenant/Widgets/LowStockTable.php b/app/Filament/Tenant/Widgets/LowStockTable.php index c73c761..04ea94d 100644 --- a/app/Filament/Tenant/Widgets/LowStockTable.php +++ b/app/Filament/Tenant/Widgets/LowStockTable.php @@ -25,13 +25,13 @@ class LowStockTable extends BaseWidget ->orderBy('qty') ) ->columns([ - Tables\Columns\TextColumn::make('name')->label('Piesă')->wrap()->searchable(), - Tables\Columns\TextColumn::make('article')->label('Cod')->placeholder('—'), - Tables\Columns\TextColumn::make('qty')->label('Stoc')->alignRight() + Tables\Columns\TextColumn::make('name')->label(__('Piesă'))->wrap()->searchable(), + Tables\Columns\TextColumn::make('article')->label(__('Cod'))->placeholder('—'), + Tables\Columns\TextColumn::make('qty')->label(__('Stoc'))->alignRight() ->color(fn ($state) => $state <= 0 ? 'danger' : 'warning') ->weight('bold'), - Tables\Columns\TextColumn::make('min_qty')->label('Min.')->alignRight(), - Tables\Columns\TextColumn::make('preferredSupplier.name')->label('Furnizor')->placeholder('—'), + Tables\Columns\TextColumn::make('min_qty')->label(__('Min.'))->alignRight(), + Tables\Columns\TextColumn::make('preferredSupplier.name')->label(__('Furnizor'))->placeholder('—'), ]) ->paginated(false) ->defaultSort('qty'); diff --git a/app/Filament/Tenant/Widgets/StatsOverview.php b/app/Filament/Tenant/Widgets/StatsOverview.php index 8e08126..c0b5319 100644 --- a/app/Filament/Tenant/Widgets/StatsOverview.php +++ b/app/Filament/Tenant/Widgets/StatsOverview.php @@ -22,22 +22,22 @@ class StatsOverview extends BaseWidget return [ Stat::make('Clienți', Client::count()) - ->description('Total în baza de date') + ->description(__('Total în baza de date')) ->icon('heroicon-o-users') ->color('primary'), Stat::make('Mașini', Vehicle::count()) - ->description('Total înregistrate') + ->description(__('Total înregistrate')) ->icon('heroicon-o-truck') ->color('info'), Stat::make('Cereri noi', $newLeads) - ->description('De procesat') + ->description(__('De procesat')) ->icon('heroicon-o-inbox-arrow-down') ->color($newLeads > 0 ? 'warning' : 'success'), Stat::make('Deal-uri active', $openDeals) - ->description('În pipeline') + ->description(__('În pipeline')) ->icon('heroicon-o-funnel') ->color('primary'), diff --git a/lang/en.json b/lang/en.json index 76fc6fe..35cb09e 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,76 +1,1424 @@ { - "Dashboard": "Dashboard", - "Save": "Save", - "Cancel": "Cancel", - "Delete": "Delete", - "Edit": "Edit", - "Create": "Create", - "Search": "Search", - "Filters": "Filters", - "Reset": "Reset", - "Yes": "Yes", - "No": "No", - "Loading": "Loading...", - "Empty": "No results", - "Settings": "Settings", - "Profile": "Profile", - "Logout": "Logout", - "Welcome": "Welcome", - "Total": "Total", - "Date": "Date", - "Status": "Status", - "Actions": "Actions", - "Notifications": "Notifications", - "Clienți": "Clients", - "Mașini": "Vehicles", - "Cereri": "Leads", - "Pâlnie": "Pipeline", - "Calendar": "Calendar", - "Programări": "Appointments", - "Fișe lucru": "Work orders", - "Kanban": "Kanban", - "Norme-ore": "Labor times", - "Depozit": "Inventory", - "Furnizori": "Suppliers", - "Achiziții": "Purchases", - "Plăți": "Payments", - "Cheltuieli": "Expenses", - "Salarii": "Payroll", - "Tehnicieni": "Technicians", - "Marketing": "Marketing", - "Mesaje": "Messages", - "Rapoarte": "Reports", - "Recomandări": "Recommendations", - "Încărcare STO": "Workshop load", - "Procentaj": "Markup", - "VIN-căutare": "VIN search", - "Integrări": "Integrations", - "Backup": "Backup", - "Asistent AI": "AI Assistant", - "Setări companie": "Company settings", - "Utilizatori": "Users", - "Jurnal": "Audit log", - "Telefonie": "Calls", - "Finanțe": "Finance", - "Site PSauto": "Public site", - "Seturi anvelope": "Tire sets", - "Anvelope": "Tires", - "set anvelope": "tire set", - "seturi anvelope": "tire sets", - "Tinichigerie / Detailing": "Body / Detailing", - "Tinichigerie": "Body shop", - "lucrare caroserie": "body job", - "lucrări caroserie": "body jobs", - "Subcontractori": "Subcontractors", - "Subcontractare": "Subcontracting", - "subcontractor": "subcontractor", - "Lucrări terți": "Outsourced jobs", - "lucrare terți": "outsourced job", - "lucrări terți": "outsourced jobs", - "Coeficienți preț": "Pricing coefficients", - "coeficient": "coefficient", - "Magazin": "Shop", - "Clienți magazin": "Shop customers", - "client magazin": "shop customer", - "clienți magazin": "shop customers" -} + "$n sesiuni revoke-uite": "$n sesiuni revoke-uite", + "% Manopere": "% Manopere", + "% Piese": "% Piese", + "% din marja (sell-buy) pieselor montate.": "% din marja (sell-buy) pieselor montate.", + "% din venitul manoperelor finalizate.": "% din venitul manoperelor finalizate.", + "% manopere": "% manopere", + "% marja piese": "% marja piese", + "% marjă implicit": "Default margin %", + "% piese": "% piese", + "+1 {$item->article} — {$item->qty_received}/{$item->qty}": "+1 {$item->article} — {$item->qty_received}/{$item->qty}", + "0 = dezactivat": "0 = dezactivat", + "1.15 = +15% peste prețul de bază. 0.95 = -5%.": "1.15 = +15% peste prețul de bază. 0.95 = -5%.", + "123456:ABC-XYZ...": "123456:ABC-XYZ...", + "2FA": "2FA", + "2FA dezactivat.": "2FA dezactivat.", + "2FA resetat": "2FA resetat", + "> 0 calculează automat prețul client.": "> 0 calculează automat prețul client.", + "A1-03": "A1-03", + "AGRNMD2X": "AGRNMD2X", + "AI: preț recomandat": "AI: preț recomandat", + "AI: recomandări": "AI: recomandări", + "AI: sugerează diagnostic": "AI: sugerează diagnostic", + "API Tokens": "API Tokens", + "AUTOCRM_001": "AUTOCRM_001", + "AYS...": "AYS...", + "Abonament": "Abonament", + "Abonament până la": "Abonament până la", + "Abonați": "Abonați", + "Absent": "Absent", + "Accept": "Accept", + "Acces": "Acces", + "Acces în aplicație (opțional)": "Acces în aplicație (opțional)", + "Această acțiune este ireversibilă": "This action is irreversible", + "Achitat": "Paid", + "Achiziții": "Purchases", + "Acord": "Agreement", + "Acordat de": "Acordat de", + "Actions": "Actions", + "Activ": "Active", + "Activare": "Activation", + "Active": "Active", + "Activează": "Activate", + "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.": "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.", + "Activează PayPal": "Activează PayPal", + "Activează Paynet": "Activează Paynet", + "Activează Stripe": "Activează Stripe", + "Activează coeficienții de preț VIP pe fișele acestui client.": "Activează coeficienții de preț VIP pe fișele acestui client.", + "Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat\".": "Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat\".", + "Activează plata prin transfer": "Activează plata prin transfer", + "Activi": "Active", + "Activă": "Active", + "Actualizat la": "Updated at", + "Actualizează": "Update", + "Acțiune": "Action", + "Acțiuni în bulk": "Bulk actions", + "Acțiuni în masă": "Bulk actions", + "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.": "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.", + "Adaugă auto": "Add vehicle", + "Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.": "Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.", + "Adaugă cheltuială": "Add expense", + "Adaugă client": "Add client", + "Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.": "Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.", + "Adaugă comentariu": "Add comment", + "Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.": "Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.", + "Adaugă furnizor": "Add supplier", + "Adaugă imagine": "Add image", + "Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.": "Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.", + "Adaugă notă": "Add note", + "Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.": "Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.", + "Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.": "Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.", + "Adaugă piesă": "Add part", + "Adaugă plată": "Add payment", + "Adaugă primul client SaaS folosind butonul „Adaugă companie\" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.": "Adaugă primul client SaaS folosind butonul „Adaugă companie\" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.", + "Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.": "Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.", + "Adaugă produs": "Add product", + "Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.": "Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.", + "Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.": "Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.", + "Adaugă rol": "Add role", + "Adaugă rând": "Add row", + "Adaugă sarcină": "Add task", + "Adaugă serviciu": "Add service", + "Adaugă utilizator": "Add user", + "Adaugă vehicul": "Add vehicle", + "Admin tenant (la creare)": "Admin tenant (la creare)", + "Adresă": "Address", + "Adresă IP": "IP address", + "Adresă email": "Email address", + "Afișează detalii marjă la procesele calculate": "Afișează detalii marjă la procesele calculate", + "Ai deja cont?": "Already have an account?", + "Ai uitat parola?": "Forgot password?", + "Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește\".": "Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește\".", + "Aici apar clienții care și-au creat cont în magazinul online (/shop/register).": "Aici apar clienții care și-au creat cont în magazinul online (/shop/register).", + "Aleatoriu": "Random", + "Alege categorie": "Choose category", + "Alege client": "Choose client", + "Alege dată": "Choose date", + "Alege limbă": "Choose language", + "Alege maistru": "Choose master", + "Alege mecanic": "Choose mechanic", + "Alege metodă plată": "Choose payment method", + "Alege monedă": "Choose currency", + "Alege oră": "Choose time", + "Alege pod": "Choose bay", + "Alege stare": "Choose status", + "Alege tip": "Choose type", + "Alege vehicul": "Choose vehicle", + "Alfabetic": "Alphabetically", + "Alte cheltuieli": "Other expenses", + "An": "Year", + "An fabricație": "Model year", + "Angajat din": "Angajat din", + "Ani": "Years", + "Antet": "Header", + "Antet fișă": "Order header", + "Anual": "Anual", + "Anul curent": "Current year", + "Anul trecut": "Last year", + "Anulat": "Cancelled", + "Anulează fișa": "Cancel order", + "Anvelope": "Tires", + "Apare în magazinul online public.": "Apare în magazinul online public.", + "Apasă „Recepție\" pe lista de piese pentru a înregistra prima intrare în depozit.": "Apasă „Recepție\" pe lista de piese pentru a înregistra prima intrare în depozit.", + "Apel": "Apel", + "Aplicat": "Aplicat", + "Aplică": "Apply", + "Aplică filtru": "Apply filter", + "Aplică marjă internă": "Apply internal margin", + "Aplică prețul": "Aplică prețul", + "Aplică toate regulile la stoc": "Aplică toate regulile la stoc", + "Aplică șablon": "Apply template", + "Aprilie": "April", + "Aprobare": "Approval", + "Aprobat": "Approved", + "Aprobat de": "Approved by", + "Aprobat de client": "Approved by client", + "Aprobă": "Approve", + "Arată": "Show", + "Arhivat": "Archived", + "Arhivează": "Archive", + "Arhivă": "Archive", + "Articol nu se potrivește comenzii": "Articol nu se potrivește comenzii", + "Articole": "Articole", + "Ascunde": "Hide", + "Ascuns": "Hidden", + "Asig.": "Asig.", + "Asigurare": "Asigurare", + "Asigurător": "Asigurător", + "Asistent AI": "AI Assistant", + "Astăzi": "Today", + "Atelierul meu": "Atelierul meu", + "Atenție": "Warning", + "August": "August", + "Autentificare": "Login", + "Autentificare 2FA": "2FA authentication", + "Auto": "Vehicle", + "AutoCRM SRL": "AutoCRM SRL", + "Avans": "Advance", + "Avg zile": "Avg zile", + "Așteptată": "Expected", + "BIC / SWIFT": "BIC / SWIFT", + "Backup": "Backup", + "Backup & Export date": "Backup & Export date", + "Balance": "Balance", + "Balanță": "Balance", + "Banca": "Banca", + "Bază": "Base", + "Beneficiar": "Beneficiar", + "Bifează ce poate face acest rol. Modificările au efect imediat.": "Bifează ce poate face acest rol. Modificările au efect imediat.", + "Bifă": "Check", + "Bine ai venit": "Welcome", + "Blocată": "Blocked", + "Blochează": "Block", + "Boală": "Sick", + "Bon": "Receipt", + "Bonuri": "Receipts", + "Bot OK: @{$name}": "Bot OK: @{$name}", + "Bot Telegram nu răspunde": "Bot Telegram nu răspunde", + "Bot token": "Bot token", + "Brand": "Brand", + "Brand & contact": "Brand & contact", + "Buget": "Budget", + "Buget & rezultate (luna curentă)": "Buget & rezultate (luna curentă)", + "Bun venit": "Welcome", + "Bun venit în AutoCRM!": "Bun venit în AutoCRM!", + "Bună ziua": "Hello", + "CSV": "CSV", + "Calcul": "Calcul", + "Calculat salariul {$period} pentru {$count} utilizatori": "Calculat salariul {$period} pentru {$count} utilizatori", + "Calculează luna curentă": "Calculează luna curentă", + "Calendar": "Calendar", + "Calendar vizual": "Calendar vizual", + "Canal marketing": "Canal marketing", + "Cancel": "Cancel", + "Cant.": "Qty", + "Cantitate": "Quantity", + "Cantitate recepționată": "Cantitate recepționată", + "Cantitate restituită": "Cantitate restituită", + "Capacitate motor": "Engine size", + "Capacitatea zilnică în ore": "Capacitatea zilnică în ore", + "Caroserie": "Body type", + "Catalog manoperă": "Catalog manoperă", + "Categorie": "Category", + "Categorie implicită": "Default category", + "Categorii": "Categorii", + "Caută": "Search", + "Caută în tabel": "Search in table", + "Caz asigurare": "Caz asigurare", + "Caz de asigurare": "Caz de asigurare", + "Cereri": "Leads", + "Check-in depozit": "Check-in depozit", + "Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.": "Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.", + "Cheie": "Cheie", + "Cheltuială": "Expense", + "Cheltuieli": "Expenses", + "Cheltuit": "Spent", + "Cheltuit 90d": "Cheltuit 90d", + "Chitanțe": "Receipts", + "Chitanță": "Receipt", + "Ciornă": "Draft", + "Clase auto": "Vehicle classes", + "Clasă (pentru pricing)": "Clasă (pentru pricing)", + "Claude API Key": "Claude API Key", + "Client": "Client", + "Client & Auto": "Client & Auto", + "Client CRM": "Client CRM", + "Client ID": "Client ID", + "Client VIP": "Client VIP", + "Client existent": "Existing client", + "Client legat (CRM)": "Client legat (CRM)", + "Client nou": "New client", + "Clienți": "Clients", + "Clienți magazin": "Shop customers", + "Cod": "Code", + "Cod articol": "Article code", + "Cod bare": "Cod bare", + "Cod echivalent": "Cod echivalent", + "Cod necunoscut": "Cod necunoscut", + "Codul produsului": "Product code", + "Coduri cross (OEM/echivalente)": "Coduri cross (OEM/echivalente)", + "Coeficient": "Coeficient", + "Coeficienți preț": "Pricing coefficients", + "Coloane": "Columns", + "Coloană": "Column", + "Comandat": "Comandat", + "Comandată": "Comandată", + "Comandă": "Comandă", + "Comandă nouă #": "Comandă nouă #", + "Combustibil": "Fuel", + "Comenzi": "Orders", + "Comercial": "Comercial", + "Comision manopere": "Comision manopere", + "Comision piese": "Comision piese", + "Companie": "Company", + "Companie & plan": "Companie & plan", + "Companie creată: {$result['company']->name}": "Companie creată: {$result['company']->name}", + "Complet": "Full", + "Concediu": "Vacation", + "Condiții (toate trebuie îndeplinite)": "Condiții (toate trebuie îndeplinite)", + "Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.": "Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.", + "Configurări": "Configuration", + "Configurări avansate": "Advanced settings", + "Confirmare plată primită": "Confirmare plată primită", + "Confirmă că mecanicul ia fizic piesa din depozit. Stocul scade acum, fără să aștepți închiderea fișei.": "Confirmă că mecanicul ia fizic piesa din depozit. Stocul scade acum, fără să aștepți închiderea fișei.", + "Confirmă emailul": "Verify email", + "Confirmă ștergerea": "Confirm deletion", + "Contact": "Contact", + "Contactare": "Contact", + "Contacte": "Contacte", + "Contactează": "Contact", + "Convertește": "Convertește", + "Convertite": "Converted", + "Conținut": "Conținut", + "Conținut șablon": "Conținut șablon", + "Copiat": "Copied", + "Copiază": "Copy", + "Copiază token-ul ACUM — nu va mai fi afișat.": "Copiază token-ul ACUM — nu va mai fi afișat.", + "Cost": "Cost", + "Cost & marjă": "Cost & marjă", + "Cost (de la terț)": "Cost (de la terț)", + "Cost (terț)": "Cost (terț)", + "Cost/lead": "Cost/lead", + "Costuri": "Costs", + "Creat": "Creat", + "Creat de": "Created by", + "Creat la": "Created at", + "Create": "Create", + "Crează manual prima factură sau folosește butonul „Generează factură\" din pagina Companiei.": "Crează manual prima factură sau folosește butonul „Generează factură\" din pagina Companiei.", + "Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.": "Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.", + "Credit": "Credit", + "Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.": "Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.", + "Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook\". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.": "Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook\". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.", + "Crescător": "Ascending", + "Critic": "Critical", + "Cronologic": "Chronologically", + "Cu TVA": "With VAT", + "Cu jante": "Cu jante", + "Culoare": "Color", + "Culoare brand": "Brand color", + "Culoare în calendar": "Culoare în calendar", + "Cumul.": "Cumul.", + "Cumulabil": "Cumulabil", + "Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.": "Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.", + "Curăță": "Clear", + "Cutie": "Gearbox", + "Cutie de viteze": "Transmission", + "Câmp obligatoriu": "Required field", + "Când": "Când", + "Când & unde": "Când & unde", + "DOT": "DOT", + "Da, confirmă": "Yes, confirm", + "Da, șterge": "Yes, delete", + "Dacă e gol, generăm 10 caractere random.": "Dacă e gol, generăm 10 caractere random.", + "Dashboard": "Dashboard", + "Data": "Date", + "Data & ora": "Data & ora", + "Data actualizării": "Updated at", + "Data comandă": "Data comandă", + "Data creării": "Created at", + "Data factură": "Invoice date", + "Data ieșire": "Delivery date", + "Data intrare": "Intake date", + "Data plății": "Payment date", + "Data sfârșit": "End date", + "Data început": "Start date", + "Date": "Date", + "Date generale": "Date generale", + "Date insuficiente": "Date insuficiente", + "Date legale (apar pe facturi)": "Date legale (apar pe facturi)", + "Date personale": "Date personale", + "Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.": "Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.", + "Datorie": "Debt", + "Daune": "Daune", + "De către": "De către", + "De la": "From", + "De la (preț achiziție)": "De la (preț achiziție)", + "De la data": "From date", + "De la ora": "From time", + "De obicei 10× preț lunar (2 luni gratis).": "De obicei 10× preț lunar (2 luni gratis).", + "De procesat": "De procesat", + "Deal": "Deal", + "Deals": "Deals", + "Debit": "Debit", + "Deblochează": "Unblock", + "Decembrie": "December", + "Decode VIN": "Decode VIN", + "Deconectare": "Logout", + "Delete": "Delete", + "Demo (pentru demonstrații sales)": "Demo (pentru demonstrații sales)", + "Denumire": "Name", + "Denumire afișată": "Display name", + "Denumire companie": "Company name", + "Denumire produs": "Product name", + "Depozit": "Warehouse", + "Depozit gol": "Depozit gol", + "Depozit implicit": "Default warehouse", + "Depozit țintă": "Target warehouse", + "Depublică": "Unpublish", + "Descarcă PDF": "Download PDF", + "Descarcă factura": "Download invoice", + "Descarcă template": "Download template", + "Deschide": "Open", + "Deschide tenantul": "Deschide tenantul", + "Deschis": "Opened", + "Deschis la": "Opened at", + "Descrescător": "Descending", + "Descriere": "Description", + "Descriere produs": "Product description", + "Detalii": "Details", + "Detalii auto": "Vehicle details", + "Detalii client": "Client details", + "Detalii comandă": "Order details", + "Deviz": "Deviz", + "Dezactivare": "Deactivation", + "Dezactivează": "Deactivate", + "Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.": "Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.", + "Diagnostic": "Diagnostic", + "Diagnostic AI bazat pe plângerea clientului": "Diagnostic AI bazat pe plângerea clientului", + "Diagnosticare": "Diagnostics", + "Diagnoza": "Diagnosis", + "Diagnoză": "Diagnosis", + "Diametru R": "Diametru R", + "Dimensiune": "Dimensiune", + "Din catalog (lasă gol pentru text liber)": "Din catalog (lasă gol pentru text liber)", + "Discount": "Discount", + "Discount %": "Discount %", + "Distribuie": "Distribute", + "Doar active": "Active only", + "Doar arhivate": "Archived only", + "Doar clienți VIP": "VIP clients only", + "Doar clienții mei": "My clients only", + "Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.": "Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.", + "Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.": "Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.", + "Doar tu ești aici": "Doar tu ești aici", + "Doar utilizatorii mei": "My users only", + "Document": "Document", + "Documente": "Documents", + "Downgrade": "Downgrade", + "Drept": "Permission", + "Drepturi": "Permissions", + "Duminică": "Sunday", + "Duplică": "Duplicate", + "Duplică rând": "Duplicate row", + "După": "După", + "Durată": "Duration", + "Durată (sec)": "Durată (sec)", + "Durată estimată": "Estimated duration", + "Durată reală": "Actual duration", + "EJk...": "EJk...", + "ETA": "ETA", + "Edit": "Edit", + "Editează": "Edit", + "Editează drepturi": "Editează drepturi", + "Editează produs": "Edit product", + "Electrică": "Electrical", + "Eliberat: {$n} rezervări consumate": "Eliberat: {$n} rezervări consumate", + "Eliberează": "Issue", + "Email": "Email", + "Email admin": "Email admin", + "Empty": "No results", + "Endpoint webhook: https://service.mir.md/payments/stripe/webhook": "Endpoint webhook: https://service.mir.md/payments/stripe/webhook", + "Eroare": "Error", + "Eroare la recepție": "Eroare la recepție", + "Etape": "Stages", + "Etapă": "Stage", + "Etichetă": "Etichetă", + "Ex 30 → preț vânzare = preț achiziție × 1.30": "Ex 30 → preț vânzare = preț achiziție × 1.30", + "Ex: 100": "Ex: 100", + "Ex: 20": "Ex: 20", + "Ex: 20 pentru +20%": "Ex: 20 pentru +20%", + "Ex: 25": "Ex: 25", + "Ex: Pod 1, Curte 1, Atelier electric": "Ex: Pod 1, Curte 1, Atelier electric", + "Ex: cu lift, fără lift, doar diagnoză...": "Ex: cu lift, fără lift, doar diagnoză...", + "Ex: lockdown temporar; acces pentru audit; etc.": "Ex: lockdown temporar; acces pentru audit; etc.", + "Excepție": "Excepție", + "Excepții": "Overrides", + "Excepții drepturi": "Permission overrides", + "Expiră": "Expiră", + "Expiră la (opțional)": "Expiră la (opțional)", + "Export CSV": "Export CSV", + "Extras": "Statement", + "Ești sigur?": "Are you sure?", + "Facturi": "Invoices", + "Facturi pending / overdue": "Facturi pending / overdue", + "Factură": "Invoice", + "Factură importată": "Factură importată", + "Factură nouă": "New invoice", + "Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.": "Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.", + "Favicon": "Favicon", + "Față-Dreapta": "Față-Dreapta", + "Față-Stânga": "Față-Stânga", + "Februarie": "February", + "Filters": "Filters", + "Filtre": "Filters", + "Filtru": "Filter", + "Finalizat": "Completed", + "Financiar": "Financiar", + "Finanțe": "Finanțe", + "Fișe": "Orders", + "Fișe active": "Active orders", + "Fișe lucru": "Work orders", + "Fișe închise": "Closed orders", + "Fișier": "File", + "Fișiere": "Files", + "Fișă": "Order", + "Fișă asociată": "Fișă asociată", + "Fișă lucru": "Fișă lucru", + "Folosește „Check-in depozit\" pe set pentru a înregistra prima depozitare.": "Folosește „Check-in depozit\" pe set pentru a înregistra prima depozitare.", + "Folosit dacă markup = 0.": "Folosit dacă markup = 0.", + "Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.": "Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.", + "Force logout": "Force logout", + "Foto": "Photo", + "Foto factură": "Foto factură", + "Foto piesă": "Foto piesă", + "Foto înainte / după": "Foto înainte / după", + "Fotografii": "Fotografii", + "Frâne": "Brakes", + "Frâne, Ulei, ...": "Frâne, Ulei, ...", + "Funcție": "Funcție", + "Funcționalități": "Funcționalități", + "Funcționalități incluse": "Funcționalități incluse", + "Furnizor": "Supplier", + "Furnizor (opțional)": "Furnizor (opțional)", + "Furnizor factură": "Invoice supplier", + "Furnizor implicit": "Default supplier", + "Furnizor preferat": "Furnizor preferat", + "Furnizori": "Suppliers", + "Fără TVA": "Without VAT", + "Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.": "Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.", + "Gata estimat (ETA)": "Gata estimat (ETA)", + "Gemini API Key": "Gemini API Key", + "General": "General", + "Generat automat": "Auto-generated", + "Generează factură": "Generează factură", + "Generează raport": "Generate report", + "Generează token": "Generează token", + "Global": "Global", + "Grafic": "Chart", + "Gravitate": "Gravitate", + "Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet\") și aplică-l pe o fișă cu un click.": "Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet\") și aplică-l pe o fișă cu un click.", + "Hartă daune": "Hartă daune", + "IBAN": "IBAN", + "ID": "ID", + "IDNO / CUI": "IDNO / CUI", + "Ianuarie": "January", + "Iarnă 2025-2026": "Iarnă 2025-2026", + "Iconiță (emoji)": "Iconiță (emoji)", + "Identificare": "Identification", + "Identitate": "Identitate", + "Ieri": "Yesterday", + "Ieșire": "Logout", + "Imagine": "Image", + "Imagine principală": "Main image", + "Imagini": "Images", + "Implicit": "Default", + "Import": "Import", + "Import CSV": "Import CSV", + "Import clienți din CSV": "Import clienți din CSV", + "Import factură (OCR)": "Import factură (OCR)", + "Import factură Excel/CSV": "Import factură Excel/CSV", + "Import factură via OCR": "Import factură via OCR", + "Import mașini din CSV": "Import mașini din CSV", + "Import reușit — Purchase {$purchase->number}": "Import reușit — Purchase {$purchase->number}", + "Import: {$r['imported']} adăugate, {$r['skipped']} ignorate": "Import: {$r['imported']} adăugate, {$r['skipped']} ignorate", + "Import: {$r['imported']} adăugați, {$r['skipped']} ignorați": "Import: {$r['imported']} adăugați, {$r['skipped']} ignorați", + "Importă din CSV": "Import from CSV", + "Importă din Excel": "Import from Excel", + "Inactiv": "Inactive", + "Infopanel": "Dashboard", + "Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.": "Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.", + "Informație": "Info", + "Instrucțiuni adiționale": "Instrucțiuni adiționale", + "Integrări": "Integrations", + "Integrări de plată": "Integrări de plată", + "Integrări externe": "Integrări externe", + "Integrări salvate": "Integrări salvate", + "Interval": "Range", + "Interval dată": "Date range", + "Interval orar": "Time range", + "Intrat": "Intrat", + "Invitație": "Invitation", + "Invitații": "Invitations", + "Istoric depozitare": "Istoric depozitare", + "Istoric prețuri furnizori": "Istoric prețuri furnizori", + "Iulie": "July", + "Iunie": "June", + "JSON": "JSON", + "Joi": "Thursday", + "Jurnal": "Audit log", + "KPI mecanici": "KPI mecanici", + "Kanban": "Kanban", + "Kilometraj": "Kilometraj", + "Km": "Km", + "Km curent": "Current mileage", + "Km ieșire": "Km out", + "Km intrare": "Km in", + "Km parcurși": "Km driven", + "La editare lasă gol pentru a păstra parola actuală.": "La editare lasă gol pentru a păstra parola actuală.", + "La revedere": "Goodbye", + "La timp 90d": "La timp 90d", + "La trecere pe „Montată\" se scade automat din stoc.": "La trecere pe „Montată\" se scade automat din stoc.", + "Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.": "Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.", + "Lead-uri": "Lead-uri", + "Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.": "Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.", + "Limbă": "Language", + "Limbă default": "Default language", + "Limite": "Limite", + "Linii": "Linii", + "Link client (QR)": "Link client (QR)", + "Lipsă": "Lipsă", + "Liste configurabile": "Configurable lists", + "Livrare": "Livrare", + "Livrare (zile)": "Livrare (zile)", + "Livrare gratuită peste": "Livrare gratuită peste", + "Loading": "Loading...", + "Loc.": "Loc.", + "Local": "Local", + "Localizare & monedă": "Localization & currency", + "Locație": "Locație", + "Locație (raft)": "Locație (raft)", + "Locație rack/bin": "Locație rack/bin", + "Login": "Login", + "Logo": "Logo", + "Logo & favicon": "Logo & favicon", + "Logo (opțional)": "Logo (opțional)", + "Logo actualizat": "Logo actualizat", + "Logout": "Logout", + "Loturi": "Loturi", + "Loturi (FIFO)": "Loturi (FIFO)", + "Lucrare": "Lucrare", + "Lucrare blocată": "Work blocked", + "Lucrări": "Lucrări", + "Lucrări la terți": "Lucrări la terți", + "Lucrări terți": "Outsourced jobs", + "Luna curentă": "Current month", + "Luna trecută": "Last month", + "Lunar": "Lunar", + "Luni": "Months", + "Luni (zi)": "Monday", + "Lățime": "Lățime", + "MD00 AG00 0000 ...": "MD00 AG00 0000 ...", + "MRR realizat": "MRR realizat", + "Magazin": "Magazin", + "Magazin activ": "Magazin activ", + "Magazin online": "Online shop", + "Mai": "May", + "Mai mic = aplicat primul.": "Mai mic = aplicat primul.", + "Maistru": "Master", + "Maistru / Mecanic": "Maistru / Mecanic", + "Manopere": "Labor lines", + "Manoperă": "Labor", + "Marca": "Marca", + "Marchează acest tenant ca demo (datele pot fi resetate periodic).": "Marchează acest tenant ca demo (datele pot fi resetate periodic).", + "Marchează anulat": "Mark as cancelled", + "Marchează plătit": "Mark as paid", + "Marchează setul ca ridicat de client.": "Marchează setul ca ridicat de client.", + "Marchează terminat": "Mark as done", + "Marcă": "Brand", + "Marcă implicită": "Default brand", + "Marja": "Margin", + "Marja piese": "Marja piese", + "Marja profitului": "Profit margin", + "Marjă": "Margin", + "Marjă client": "Client margin", + "Marjă internă (%)": "Internal margin (%)", + "Marjă internă (%) — override": "Marjă internă (%) — override", + "Marjă internă (nu TVA)": "Internal margin (not VAT)", + "Marketing": "Marketing", + "Marketing (UTM)": "Marketing (UTM)", + "Markup": "Markup", + "Markup %": "Markup %", + "Martie": "March", + "Marți": "Tuesday", + "Max clienți": "Max clienți", + "Max fișe/lună": "Max fișe/lună", + "Max mașini": "Max mașini", + "Max useri": "Max useri", + "Mașina e gata de ridicat": "Mașina e gata de ridicat", + "Mașini": "Mașini", + "Mecanic implicit": "Default mechanic", + "Mediu": "Medium", + "Merchant Code": "Merchant Code", + "Mesaj": "Mesaj", + "Mesaj client": "Mesaj client", + "Mesaje": "Messages", + "Mesaje AI/lună": "Mesaje AI/lună", + "Metode de livrare": "Metode de livrare", + "Metode plată": "Payment methods", + "Metodă plată": "Payment method", + "Miercuri": "Wednesday", + "Min.": "Min.", + "Minim": "Minim", + "Minute": "Minutes", + "Mod": "Mode", + "Mod tarifare": "Mod tarifare", + "Model": "Model", + "Model Claude": "Model Claude", + "Model Gemini": "Model Gemini", + "Model OpenAI": "Model OpenAI", + "Model implicit": "Default model", + "Modele": "Models", + "Modificat de": "Modified by", + "Moldova-Agroindbank": "Moldova-Agroindbank", + "Monedă": "Currency", + "Monedă platformă": "Monedă platformă", + "Motiv": "Reason", + "Motor": "Engine", + "Motor / Frâne / Electrică ...": "Motor / Frâne / Electrică ...", + "Motorizare": "Engine", + "Multiplicator": "Multiplier", + "Mulțumim": "Thank you", + "Mâine": "Tomorrow", + "Mărci auto suportate (separate prin virgulă)": "Mărci auto suportate (separate prin virgulă)", + "Necesită cel puțin 2 recepții complete cu data așteptată setată.": "Necesită cel puțin 2 recepții complete cu data așteptată setată.", + "Necompletat": "Empty", + "Necunoscut": "Unknown", + "Neplătit": "Unpaid", + "Nepublicat": "Unpublished", + "Nesemnat": "Unsigned", + "Nesincronizat": "Not synced", + "Net 30 / Avans": "Net 30 / Avans", + "Neverificat": "Not verified", + "Nicio comandă încă": "Nicio comandă încă", + "Nicio daună marcată": "Nicio daună marcată", + "Nicio factură generată": "Nicio factură generată", + "Nicio fișă de lucru": "Nicio fișă de lucru", + "Nicio lucrare de caroserie": "Nicio lucrare de caroserie", + "Nicio lucrare la terți": "Nicio lucrare la terți", + "Nicio mașină încă": "Nicio mașină încă", + "Nicio piesă implicită": "Nicio piesă implicită", + "Niciodată": "Niciodată", + "Niciun client magazin": "Niciun client magazin", + "Niciun client încă": "Niciun client încă", + "Niciun cod cross": "Niciun cod cross", + "Niciun coeficient": "Niciun coeficient", + "Niciun depozit": "Niciun depozit", + "Niciun istoric": "Niciun istoric", + "Niciun lot înregistrat": "Niciun lot înregistrat", + "Niciun mecanic implicit": "Niciun mecanic implicit", + "Niciun plan definit": "Niciun plan definit", + "Niciun preț înregistrat": "Niciun preț înregistrat", + "Niciun set de anvelope": "Niciun set de anvelope", + "Niciun subcontractor": "Niciun subcontractor", + "Niciun tenant încă": "Niciun tenant încă", + "Niciun șablon": "Niciun șablon", + "No": "No", + "Noiembrie": "November", + "Normal": "Normal", + "Norme-ore": "Labor times", + "Note interne": "Note interne", + "Notificare": "Notification", + "Notifications": "Notifications", + "Notificări": "Notifications", + "Notițe": "Notes", + "Notițe interne": "Notițe interne", + "Nou": "New", + "Nou tehnician": "Nou tehnician", + "Nouă": "New", + "Nr.": "No.", + "Nr. dosar daună": "Nr. dosar daună", + "Nr. factură": "Invoice no.", + "Nr. fișă": "Order no.", + "Nr. poliță": "Nr. poliță", + "Nr. telefon": "Phone number", + "Nr. înmatriculare": "License plate", + "Nu ai cont?": "Don't have an account?", + "Nu am găsit linii valide — verifică maparea coloanelor": "Nu am găsit linii valide — verifică maparea coloanelor", + "Nu există date": "No data", + "Nu există rezultate": "No results", + "Nu re-trimite mai des de X zile": "Nu re-trimite mai des de X zile", + "Nu te poți șterge pe tine!": "Nu te poți șterge pe tine!", + "Nu, renunță": "No, cancel", + "Nume": "Name", + "Nume (RO)": "Nume (RO)", + "Nume (RU)": "Nume (RU)", + "Nume (descriere)": "Nume (descriere)", + "Nume admin": "Nume admin", + "Nume canal": "Nume canal", + "Nume template": "Nume template", + "Numele este obligatoriu": "Numele este obligatoriu", + "Numărul de zile gratis după ce un tenant alege acest plan.": "Numărul de zile gratis după ce un tenant alege acest plan.", + "OCR eșuat": "OCR eșuat", + "Observații": "Notes", + "Octombrie": "October", + "On = arată „Bază salariu: X · marjă Y%\" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).": "On = arată „Bază salariu: X · marjă Y%\" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).", + "On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).": "On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).", + "Onorat": "Onorat", + "Onorează (scade stoc)": "Onorează (scade stoc)", + "OpenAI API Key": "OpenAI API Key", + "Ora": "Hour", + "Ora deschidere": "Opened at", + "Ora ieșire": "Delivery time", + "Ora intrare": "Intake time", + "Ora sfârșit": "End time", + "Ora începere": "Start time", + "Ora închidere": "Closed at", + "Oraș": "City", + "Ordine": "Order", + "Ordonare": "Sort", + "Ordonat": "Sorted", + "Ordonează după": "Sort by", + "Ore": "Hours", + "Ore (normă)": "Ore (normă)", + "Ore disponibile / zi": "Available hours / day", + "Ore lucrate": "Hours worked", + "Ore/zi": "Hours/day", + "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.": "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.", + "PDF": "PDF", + "PNG/ICO, max 512 KB.": "PNG/ICO, max 512 KB.", + "PNG/SVG, max 2 MB. Apare în sidebar.": "PNG/SVG, max 2 MB. Apare în sidebar.", + "PO": "PO", + "Pagina": "Page", + "Pagini": "Pages", + "Panou": "Panel", + "Panou control": "Control panel", + "Parolă": "Password", + "Parolă (lasă gol pentru a nu schimba)": "Parolă (lasă gol pentru a nu schimba)", + "Parolă (lasă gol pentru auto-generat)": "Parolă (lasă gol pentru auto-generat)", + "Parolă API": "Parolă API", + "Parolă resetată.": "Parolă resetată.", + "Parțial": "Partial", + "Pas 1 — Datele afacerii": "Pas 1 — Datele afacerii", + "Pas 2 — Brand & limbă": "Pas 2 — Brand & limbă", + "Pas 3 — Tarif & terminat": "Pas 3 — Tarif & terminat", + "Pauză": "Pause", + "Penalizări": "Penalizări", + "Perioada (YYYY-MM)": "Perioada (YYYY-MM)", + "Perioadă": "Period", + "Perioadă (YYYY-MM)": "Perioadă (YYYY-MM)", + "Perioadă raport": "Report period", + "Perioadă trial (zile)": "Perioadă trial (zile)", + "Permisiuni": "Permisiuni", + "Persoană contact": "Persoană contact", + "Personalizat": "Custom", + "Pierdute": "Pierdute", + "Piese": "Parts", + "Piese impl.": "Piese impl.", + "Piese implicite": "Piese implicite", + "Piesă": "Part", + "Piesă din catalog": "Piesă din catalog", + "Pipeline": "Pipeline", + "Plan": "Plan", + "Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.": "Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.", + "Plata stabilește automat starea fișei (unpaid/partial/paid)": "Plata stabilește automat starea fișei (unpaid/partial/paid)", + "Plată": "Payment", + "Plată & total": "Payment & total", + "Plângere client": "Plângere client", + "Plătit": "Paid", + "Plătit către terț": "Plătit către terț", + "Plătit de": "Paid by", + "Plătit la": "Plătit la", + "Plătit terț": "Plătit terț", + "Plătită": "Plătită", + "Plăți": "Payments", + "Plăți internaționale. Mai potrivit pentru clienți din afara MD.": "Plăți internaționale. Mai potrivit pentru clienți din afara MD.", + "Pod": "Bay", + "Pod / Spațiu lucru": "Bay / Work space", + "Politica de confidențialitate": "Privacy policy", + "Politici": "Policies", + "Pontaj": "Timesheet", + "Populare": "Popular", + "Post actualizat": "Post actualizat", + "Poze": "Photos", + "Pozele auto": "Vehicle photos", + "Prefix facturi": "Prefix facturi", + "Preview": "Preview", + "Preview PDF": "Preview PDF", + "Previzualizare": "Preview", + "Prezent": "Present", + "Prezențe": "Attendances", + "Prezență": "Attendance", + "Preț": "Price", + "Preț achiziție": "Purchase price", + "Preț anual": "Preț anual", + "Preț client": "Client price", + "Preț contextual": "Preț contextual", + "Preț fix": "Preț fix", + "Preț fix (MDL)": "Preț fix (MDL)", + "Preț implicit": "Default price", + "Preț inteligent": "Smart price", + "Preț lunar": "Preț lunar", + "Preț orientativ (MDL)": "Preț orientativ (MDL)", + "Preț unit.": "Preț unit.", + "Preț unitar": "Preț unitar", + "Preț vz.": "Preț vz.", + "Preț vânzare": "Sale price", + "Preț/h": "Preț/h", + "Prețuri": "Prețuri", + "Prețurile se înregistrează automat la fiecare recepție de PO.": "Prețurile se înregistrează automat la fiecare recepție de PO.", + "Primit": "Primit", + "Print": "Print", + "Print factură": "Print invoice", + "Print fișă": "Print order", + "Prio": "Prio", + "Prioritate": "Priority", + "Privat": "Private", + "Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin\".": "Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin\".", + "Procentaj": "Markup", + "Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.": "Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.", + "Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.": "Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.", + "Procesează": "Process", + "Produs": "Product", + "Produse": "Products", + "Profesie": "Profesie", + "Profil": "Profil", + "Profile": "Profile", + "Profit": "Profit", + "Programare adăugată": "Programare adăugată", + "Programare confirmată": "Programare confirmată", + "Programare mutată": "Programare mutată", + "Programare ștearsă": "Programare ștearsă", + "Programează întâlnire": "Schedule meeting", + "Programări": "Appointments", + "Progres": "Progres", + "Proprietar": "Owner", + "Provider implicit": "Provider implicit", + "Public": "Public", + "Public (afișat la signup)": "Public (afișat la signup)", + "Publicat": "Published", + "Publicat în magazin": "Publicat în magazin", + "Publică": "Publish", + "Publică în magazin": "Publică în magazin", + "Publishable key": "Publishable key", + "Putere": "Power", + "Pâlnie": "Pipeline", + "Până la": "Until", + "Până la (gol = ∞)": "Până la (gol = ∞)", + "Până la data": "To date", + "Până la ora": "To time", + "QR": "QR", + "ROI": "ROI", + "Rapoarte": "Reports", + "Raport": "Report", + "Rating": "Rating", + "Rating actualizat → {$score}★": "Rating actualizat → {$score}★", + "Reactivează": "Reactivate", + "Recalculat preț pentru {$count} piese": "Recalculat preț pentru {$count} piese", + "Recalculează": "Recalculează", + "Recent": "Recent", + "Recente": "Recent", + "Recepție": "Recepție", + "Recepție completă — batch-uri create": "Recepție completă — batch-uri create", + "Recepție totală": "Recepție totală", + "Recepționat": "Recepționat", + "Recepționat — batch creat": "Recepționat — batch creat", + "Recepționată": "Recepționată", + "Recepționează": "Recepționează", + "Recomandat": "Recommended", + "Recomandate": "Recommended", + "Recomandă": "Recommend", + "Recomandări": "Recomandări", + "Recomandări AI": "Recomandări AI", + "Recomandări — clienți de urmărit": "Recomandări — clienți de urmărit", + "Redeschide fișa": "Reopen order", + "Ref.": "Ref.", + "Ref. lot/factură": "Ref. lot/factură", + "Referință": "Referință", + "Referință (Stripe id, transfer)": "Referință (Stripe id, transfer)", + "Referință (chitanță / tranzacție)": "Referință (chitanță / tranzacție)", + "Regenerează": "Regenerate", + "Register": "Register", + "Regulă": "Rule", + "Reia": "Resume", + "Reminder ITP / revizie": "Reminder ITP / revizie", + "Reminder service auto": "Reminder service auto", + "Renunță": "Cancel", + "Reordonează": "Reorder", + "Reparație": "Repair", + "Reparații": "Repairs", + "Rerating": "Rerating", + "Reset": "Reset", + "Reset parolă": "Reset parolă", + "Resetează": "Reset", + "Resetează 2FA": "Reset 2FA", + "Resetează filtre": "Reset filters", + "Resetează parola": "Reset password", + "Respinge": "Reject", + "Respins": "Rejected", + "Respins de": "Rejected by", + "Responsabil": "Assignee", + "Rest de plată": "Balance due", + "Restanță": "Restanță", + "Restaurează": "Restore", + "Restituire": "Restituire", + "Returnat": "Returnat", + "Revizie": "Service", + "Revizii": "Services", + "Rezervat": "Rezervat", + "Rezultat": "Result", + "Rezultate": "Results", + "Rezumat": "Summary", + "Reîmprospătează": "Refresh", + "Ridicat": "High", + "Rol": "Role", + "Rol primar": "Primary role", + "Rolul principal — sincronizat automat cu drepturile RBAC.": "Rolul principal — sincronizat automat cu drepturile RBAC.", + "Roluri suplimentare": "Additional roles", + "Roluri suplimentare peste rolul primar — drepturile se cumulează.": "Roluri suplimentare peste rolul primar — drepturile se cumulează.", + "Rolurile sistem (owner/admin/etc.) au numele blocat": "Rolurile sistem (owner/admin/etc.) au numele blocat", + "Rotire": "Rotate", + "Rulează `php artisan push:vapid` și adaugă cheile în .env.": "Rulează `php artisan push:vapid` și adaugă cheile în .env.", + "Rând": "Row", + "Rânduri": "Rows", + "Rânduri per pagină": "Rows per page", + "Rămas": "Rămas", + "Salarii": "Payroll", + "Salariu & comisioane": "Salariu & comisioane", + "Salariu & marjă": "Salary & margin", + "Salariu bază": "Salariu bază", + "Salut": "Hi", + "Salvează": "Save", + "Salvează schimbările": "Save changes", + "Salvează și adaugă altul": "Save and add another", + "Salvează și continuă": "Save and continue", + "Salvează și închide": "Save and close", + "Sarcini": "Tasks", + "Sarcini restante": "Overdue tasks", + "Sarcină": "Task", + "Save": "Save", + "Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.": "Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.", + "Scadent": "Scadent", + "Scadent la": "Scadent la", + "Scadență": "Due date", + "Scaner cod QR / Bare": "Scaner cod QR / Bare", + "Scoate din magazin": "Scoate din magazin", + "Scăzut": "Low", + "Se completează automat când clientul scrie la bot": "Se completează automat când clientul scrie la bot", + "Se procesează...": "Processing...", + "Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.": "Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.", + "Search": "Search", + "Secret": "Secret", + "Secret key": "Secret key", + "Secret semnătură": "Secret semnătură", + "Secunde": "Seconds", + "Securitate": "Security", + "Selectat": "Selected", + "Selectate": "Selected", + "Selectează": "Select", + "Selectează dată": "Select date", + "Selectează fișier": "Select file", + "Selectează furnizorul": "Selectează furnizorul", + "Selectează interval": "Select range", + "Selectează oră": "Select time", + "Selectează perioadă": "Select period", + "Semnat": "Signed", + "Semnat de": "Signed by", + "Semnează": "Sign", + "Semnătură": "Signature", + "Semnătură digitală": "Digital signature", + "Senzori TPMS": "Senzori TPMS", + "Septembrie": "September", + "Serie factură": "Invoice series", + "Service ID": "Service ID", + "Servicii": "Services", + "Servicii & tarif": "Services & rate", + "Servicii oferite (separate prin virgulă)": "Servicii oferite (separate prin virgulă)", + "Servicii terțe": "Third-party services", + "Sesiuni": "Sessions", + "Set eliberat din depozit": "Set eliberat din depozit", + "Set primit în depozit": "Set primit în depozit", + "Setare implicită": "Default", + "Setat manual dacă markup = 0.": "Setat manual dacă markup = 0.", + "Setează webhook": "Setează webhook", + "Settings": "Settings", + "Seturi anvelope": "Tire sets", + "Setări companie": "Company settings", + "Setări plăți salvate": "Setări plăți salvate", + "Setări salvate": "Setări salvate", + "Setările au fost salvate. Hai să adăugăm primul client.": "Setările au fost salvate. Hai să adăugăm primul client.", + "Sezon": "Sezon", + "Sfat": "Tip", + "Sign in": "Sign in", + "Sign up": "Sign up", + "Sincronizat": "Synced", + "Sincronizează": "Sync", + "Sistem": "Sistem", + "Site PSauto": "Public site", + "Slug": "Slug", + "Sold": "Balance", + "Spate-Dreapta": "Spate-Dreapta", + "Spate-Stânga": "Spate-Stânga", + "Spațiu de lucru adăugat": "Spațiu de lucru adăugat", + "Specialitate": "Specialitate", + "Specializare": "Specializare", + "Specificații": "Specificații", + "Standard": "Standard", + "Stare": "Stare", + "Status": "Status", + "Status dosar": "Status dosar", + "Status implicit": "Default status", + "Stoc": "Stock", + "Stoc adăugat": "Stoc adăugat", + "Stoc minim": "Stoc minim", + "Storage (MB)": "Storage (MB)", + "Subcontractare": "Subcontracting", + "Subcontractor": "Subcontractor", + "Subcontractori": "Subcontractors", + "Subdomeniul: .service.mir.md": "Subdomeniul: .service.mir.md", + "Subiect": "Subject", + "Subiect (email)": "Subiect (email)", + "Subiect și data sunt obligatorii": "Subiect și data sunt obligatorii", + "Subtotal": "Subtotal", + "Succes": "Success", + "Sumă": "Amount", + "Sunet": "Sound", + "Sunt de acord": "I agree", + "Sunteți sigur?": "Are you sure?", + "Sursă": "Source", + "Sursă & Atribuire": "Sursă & Atribuire", + "Suspendat": "Suspended", + "Suspendă": "Suspendă", + "Suspensie": "Suspension", + "Sâmbătă": "Saturday", + "Săpt. lucrătoare": "Working days", + "Săptămâna curentă": "Current week", + "Săptămâna trecută": "Last week", + "Săptămâni": "Weeks", + "TPMS": "TPMS", + "TPMS & foto": "TPMS & foto", + "TVA": "VAT", + "Tarif normo-oră": "Hour rate", + "Tarif normo-oră (poți schimba oricând)": "Tarif normo-oră (poți schimba oricând)", + "Tarif orar (MDL)": "Hourly rate (MDL)", + "Tarif/h": "Tarif/h", + "Tarif/oră": "Tarif/oră", + "Tarifare": "Tarifare", + "Taxă depozitare": "Taxă depozitare", + "Taxă livrare": "Taxă livrare", + "Tehnice": "Tehnice", + "Tehnicieni": "Technicians", + "Telefon": "Phone", + "Telefon alternativ": "Telefon alternativ", + "Telefon principal": "Telefon principal", + "Telefonie": "Calls", + "Telegram bot": "Telegram bot", + "Telegram chat ID": "Telegram chat ID", + "Telegram va trimite mesajele primite la URL-ul webhook de mai jos.": "Telegram va trimite mesajele primite la URL-ul webhook de mai jos.", + "Tenant activat.": "Tenant activat.", + "Tenant demo": "Tenant demo", + "Tenant not resolved": "Tenant not resolved", + "Tenant suspendat.": "Tenant suspendat.", + "Tenants recenți": "Tenants recenți", + "Termen": "Due date", + "Termen limită": "Deadline", + "Termene": "Termene", + "Termeni": "Terms", + "Termeni de plată": "Termeni de plată", + "Termeni plată": "Termeni plată", + "Termeni și condiții": "Terms & conditions", + "Termină": "Finish", + "Terț": "Terț", + "Test notificare push": "Test notificare push", + "Testează bot Telegram": "Testează bot Telegram", + "Timp lucrat": "Time worked", + "Timp mediu": "Avg time", + "Timp total": "Total time", + "Tinichigerie": "Body shop", + "Tinichigerie / Detailing": "Body / Detailing", + "Tip": "Type", + "Tip daună": "Tip daună", + "Tip implicit": "Default type", + "Tip jante": "Tip jante", + "Tipărește": "Print", + "Tipărește etichete QR": "Tipărește etichete QR", + "Toate atelierele": "All workshops", + "Toate autovehiculele": "All vehicles", + "Toate categoriile": "All categories", + "Toate cererile": "All leads", + "Toate cheltuielile": "All expenses", + "Toate comenzile": "All orders", + "Toate companiile": "Toate companiile", + "Toate câmpurile": "All fields", + "Toate limbile": "All languages", + "Toate plățile": "All payments", + "Toate produsele": "All products", + "Toate rolurile": "All roles", + "Token generat!": "Token generat!", + "Token public": "Token public", + "Token revocat": "Token revocat", + "Total": "Total", + "Total general": "Grand total", + "Total net": "Total net", + "Total plătit": "Total paid", + "Total rezultate": "Total results", + "Total în baza de date": "Total în baza de date", + "Total înregistrate": "Total înregistrate", + "Toți clienții": "All clients", + "Toți utilizatorii": "All users", + "Tracking & ETA": "Tracking & ETA", + "Trage fișierul aici": "Drop file here", + "Trial expiră la": "Trial expiră la", + "Trimis": "Sent", + "Trimite": "Send", + "Trimite SMS": "Send SMS", + "Trimite email": "Send email", + "Trimite emailul standard de resetare a parolei către clientul magazinului.": "Trimite emailul standard de resetare a parolei către clientul magazinului.", + "Trimite factură": "Send invoice", + "Trimite invitație": "Send invitation", + "Trimite link resetare": "Send reset link", + "Trimite mesaj": "Send message", + "Trimite notificare": "Send notification", + "Trimite reminder după X zile fără vizită": "Trimite reminder după X zile fără vizită", + "Trimite reset parolă": "Trimite reset parolă", + "UM": "Unit", + "Ulei": "Oil", + "Ultim login": "Ultim login", + "Ultima actualizare": "Last updated", + "Ultima autentificare": "Last login", + "Ultima modificare": "Last modified", + "Ultima sincronizare": "Last synced", + "Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).": "Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).", + "Upgrade": "Upgrade", + "Urgent": "Urgent", + "Urgență": "Urgency", + "User API": "User API", + "Useri": "Useri", + "Utilizator": "User", + "Utilizatori": "Users", + "Uzură (mm) per poziție": "Uzură (mm) per poziție", + "Uzură min": "Uzură min", + "VIN": "VIN", + "VIN căutare & piese": "VIN căutare & piese", + "VIN-căutare": "VIN search", + "Va deconecta utilizatorul pe toate device-urile.": "Va deconecta utilizatorul pe toate device-urile.", + "Va fi pre-completat când creezi o programare pentru acest pod": "Va fi pre-completat când creezi o programare pentru acest pod", + "Va recalcula sell_price pentru TOATE piesele active. Continui?": "Va recalcula sell_price pentru TOATE piesele active. Continui?", + "Val.": "Val.", + "Valoare": "Valoare", + "Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}": "Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}", + "Vehicul": "Vehicle", + "Vehicul existent": "Existing vehicle", + "Vehicul nou": "New vehicle", + "Venit": "Revenue", + "Venit generat": "Venit generat", + "Venit manopere": "Venit manopere", + "Venituri": "Revenues", + "Verificare": "Verification", + "Verificat": "Verified", + "Verifică emailul": "Check your email", + "Vezi imagine": "View image", + "Vezi mai mult": "View more", + "Vezi tot": "View all", + "Viitoare": "Viitoare", + "Vineri": "Friday", + "Vizibil": "Visible", + "Vizual": "Visual", + "Vizualizează": "View", + "Vizualizări": "Views", + "Vârstă max (ani)": "Max age (years)", + "Vârstă min (ani)": "Min age (years)", + "Web Push neconfigurat": "Web Push neconfigurat", + "Webhook": "Webhook", + "Webhook (Notify URL)": "Webhook (Notify URL)", + "Webhook eșuat": "Webhook eșuat", + "Webhook secret": "Webhook secret", + "Webhook setat — botul e gata": "Webhook setat — botul e gata", + "Weekend": "Weekend", + "Welcome": "Welcome", + "Widget": "Widget", + "XLS": "XLS", + "Yes": "Yes", + "ZIP": "ZIP", + "Zile": "Days", + "Zile libere": "Days off", + "Zile livrare": "Zile livrare", + "Zile lucrate": "Days worked", + "Zonă": "Zonă", + "Zoom in": "Zoom in", + "Zoom out": "Zoom out", + "achiziție": "achiziție", + "achiziții": "achiziții", + "angajați": "angajați", + "apasă": "click", + "auto-generat": "auto-generat", + "bonusuri/avansuri": "bonusuri/avansuri", + "canale marketing": "canale marketing", + "cheltuială": "cheltuială", + "client magazin": "client magazin", + "clienți": "clienți", + "clienți magazin": "clienți magazin", + "coeficient": "coefficient", + "coeficienți preț": "coeficienți preț", + "comandă": "comandă", + "comenzi online": "comenzi online", + "deal-uri": "deal-uri", + "ex: Iarnă Michelin": "ex: Iarnă Michelin", + "ex: Mașină veche, Client VIP, Express": "ex: Mașină veche, Client VIP, Express", + "ex: Revizie completă 15.000 km": "ex: Revizie completă 15.000 km", + "ex: app-mobil, integrare-erp": "ex: app-mobil, integrare-erp", + "factură": "factură", + "fișe lucru": "fișe lucru", + "fișă": "fișă", + "lucrare caroserie": "lucrare caroserie", + "lucrare terți": "lucrare terți", + "lucrări caroserie": "lucrări caroserie", + "lucrări terți": "lucrări terți", + "mașini": "mașini", + "mașină": "mașină", + "niciodată": "niciodată", + "norme-ore": "norme-ore", + "normă": "normă", + "null = fără trial": "null = fără trial", + "pentru a alege": "to select", + "piesă": "piesă", + "pk_test_...": "pk_test_...", + "plată": "plată", + "plăți": "plăți", + "portal.common.currency_mdl": "portal.common.currency_mdl", + "portal.common.email": "portal.common.email", + "portal.common.name": "portal.common.name", + "portal.common.password": "portal.common.password", + "portal.common.powered_by": "portal.common.powered_by", + "portal.common.search": "portal.common.search", + "portal.common.total": "portal.common.total", + "portal.invitation.activate_account": "portal.invitation.activate_account", + "portal.invitation.confirm_password": "portal.invitation.confirm_password", + "portal.invitation.expired_body": "portal.invitation.expired_body", + "portal.invitation.expired_title": "portal.invitation.expired_title", + "portal.invitation.invalid_body": "portal.invitation.invalid_body", + "portal.invitation.invalid_title": "portal.invitation.invalid_title", + "portal.invitation.new_password": "portal.invitation.new_password", + "portal.invitation.title_accept": "portal.invitation.title_accept", + "portal.shop.add_to_cart": "portal.shop.add_to_cart", + "portal.shop.address_delivery": "portal.shop.address_delivery", + "portal.shop.all_categories": "portal.shop.all_categories", + "portal.shop.back_to_catalog": "portal.shop.back_to_catalog", + "portal.shop.back_to_login": "portal.shop.back_to_login", + "portal.shop.brand_label": "portal.shop.brand_label", + "portal.shop.cart": "portal.shop.cart", + "portal.shop.cart_empty": "portal.shop.cart_empty", + "portal.shop.cart_title": "portal.shop.cart_title", + "portal.shop.catalog": "portal.shop.catalog", + "portal.shop.catalog_title": "portal.shop.catalog_title", + "portal.shop.checkout_arrow": "portal.shop.checkout_arrow", + "portal.shop.checkout_title": "portal.shop.checkout_title", + "portal.shop.code_label": "portal.shop.code_label", + "portal.shop.contact_data": "portal.shop.contact_data", + "portal.shop.continue_shopping": "portal.shop.continue_shopping", + "portal.shop.cross_refs_title": "portal.shop.cross_refs_title", + "portal.shop.delivery": "portal.shop.delivery", + "portal.shop.delivery_data": "portal.shop.delivery_data", + "portal.shop.delivery_fee_note": "portal.shop.delivery_fee_note", + "portal.shop.delivery_req": "portal.shop.delivery_req", + "portal.shop.description": "portal.shop.description", + "portal.shop.forgot_intro": "portal.shop.forgot_intro", + "portal.shop.forgot_password": "portal.shop.forgot_password", + "portal.shop.forgot_title": "portal.shop.forgot_title", + "portal.shop.full_name": "portal.shop.full_name", + "portal.shop.have_account": "portal.shop.have_account", + "portal.shop.in_stock": "portal.shop.in_stock", + "portal.shop.in_stock_only": "portal.shop.in_stock_only", + "portal.shop.login": "portal.shop.login", + "portal.shop.logout": "portal.shop.logout", + "portal.shop.my_account": "portal.shop.my_account", + "portal.shop.no_account": "portal.shop.no_account", + "portal.shop.no_orders_yet": "portal.shop.no_orders_yet", + "portal.shop.no_parts_found": "portal.shop.no_parts_found", + "portal.shop.notes": "portal.shop.notes", + "portal.shop.on_order": "portal.shop.on_order", + "portal.shop.order_date": "portal.shop.order_date", + "portal.shop.order_details_arrow": "portal.shop.order_details_arrow", + "portal.shop.order_items": "portal.shop.order_items", + "portal.shop.order_nr": "portal.shop.order_nr", + "portal.shop.order_number": "portal.shop.order_number", + "portal.shop.part": "portal.shop.part", + "portal.shop.phone_req": "portal.shop.phone_req", + "portal.shop.place_order": "portal.shop.place_order", + "portal.shop.price": "portal.shop.price", + "portal.shop.products": "portal.shop.products", + "portal.shop.qty": "portal.shop.qty", + "portal.shop.register": "portal.shop.register", + "portal.shop.register_button": "portal.shop.register_button", + "portal.shop.register_title": "portal.shop.register_title", + "portal.shop.reset_button": "portal.shop.reset_button", + "portal.shop.reset_title": "portal.shop.reset_title", + "portal.shop.search_by_vin": "portal.shop.search_by_vin", + "portal.shop.search_placeholder": "portal.shop.search_placeholder", + "portal.shop.send_reset_link": "portal.shop.send_reset_link", + "portal.shop.signin_button": "portal.shop.signin_button", + "portal.shop.signin_title": "portal.shop.signin_title", + "portal.shop.status": "portal.shop.status", + "portal.shop.subtotal": "portal.shop.subtotal", + "portal.shop.summary": "portal.shop.summary", + "portal.shop.this_car": "portal.shop.this_car", + "portal.shop.update_cart": "portal.shop.update_cart", + "portal.shop.view_catalog": "portal.shop.view_catalog", + "portal.shop.vin_contact_note": "portal.shop.vin_contact_note", + "portal.shop.vin_country": "portal.shop.vin_country", + "portal.shop.vin_decode": "portal.shop.vin_decode", + "portal.shop.vin_identified": "portal.shop.vin_identified", + "portal.shop.vin_intro": "portal.shop.vin_intro", + "portal.shop.vin_invalid": "portal.shop.vin_invalid", + "portal.shop.vin_manufacturer": "portal.shop.vin_manufacturer", + "portal.shop.vin_region": "portal.shop.vin_region", + "portal.shop.vin_title": "portal.shop.vin_title", + "portal.shop.vin_year": "portal.shop.vin_year", + "portal.tracking.approval_needed_body": "portal.tracking.approval_needed_body", + "portal.tracking.approval_needed_title": "portal.tracking.approval_needed_title", + "portal.tracking.approve": "portal.tracking.approve", + "portal.tracking.auto": "portal.tracking.auto", + "portal.tracking.decline": "portal.tracking.decline", + "portal.tracking.details": "portal.tracking.details", + "portal.tracking.hours": "portal.tracking.hours", + "portal.tracking.master": "portal.tracking.master", + "portal.tracking.mileage": "portal.tracking.mileage", + "portal.tracking.opened": "portal.tracking.opened", + "portal.tracking.photos": "portal.tracking.photos", + "portal.tracking.ready_estimated": "portal.tracking.ready_estimated", + "portal.tracking.recommendations": "portal.tracking.recommendations", + "portal.tracking.stages": "portal.tracking.stages", + "portal.tracking.unit_pcs": "portal.tracking.unit_pcs", + "portal.tracking.what_asked": "portal.tracking.what_asked", + "posturi de lucru": "posturi de lucru", + "programări": "programări", + "reguli markup": "reguli markup", + "regulă": "regulă", + "sau": "or", + "set anvelope": "set anvelope", + "seturi anvelope": "seturi anvelope", + "subcontractor": "subcontractor", + "subcontractori": "subcontractori", + "template-uri": "template-uri", + "Împărtășește": "Share", + "În curs": "In progress", + "În depozit": "In warehouse", + "În pipeline": "In pipeline", + "Înainte": "Back", + "Încarcă fișier": "Upload file", + "Încarcă fișierul Excel sau CSV": "Încarcă fișierul Excel sau CSV", + "Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.": "Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.", + "Începe": "Start", + "Închide": "Close", + "Închide fișa": "Close order", + "Închis": "Closed", + "Închis la": "Closed at", + "Încărcare STO": "Workshop load", + "Încărcare service": "Workshop load", + "Înregistrare": "Register", + "Înregistrat": "Registered", + "Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.": "Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.", + "Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.": "Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.", + "Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.": "Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.", + "Șabloane servicii": "Service templates", + "Șablon aplicat: {$r['labor']} manopere, {$r['parts']} piese": "Șablon aplicat: {$r['labor']} manopere, {$r['parts']} piese", + "Șablon serviciu": "Service template", + "Șterge imagine": "Delete image", + "Șterge produs": "Delete product", + "Șterge rând": "Delete row", + "Șterge selectate": "Delete selected", + "Șterge tot": "Delete all", + "Șters": "Deleted", + "Șters de": "Deleted by", + "Șters la": "Deleted at", + "șabloane servicii": "șabloane servicii", + "șablon": "șablon", + "Ține-mă minte": "Remember me", + "✓ Pe abonament plătit": "✓ Pe abonament plătit", + "🅿️ PayPal": "🅿️ PayPal", + "🇲🇩 Paynet (Moldova)": "🇲🇩 Paynet (Moldova)", + "🎉 Bun venit în AutoCRM!": "🎉 Bun venit în AutoCRM!", + "🎉 Toate facturile sunt plătite": "🎉 Toate facturile sunt plătite", + "🏦 Transfer bancar (manual)": "🏦 Transfer bancar (manual)", + "💳 Stripe": "💳 Stripe" +} \ No newline at end of file diff --git a/lang/ru.json b/lang/ru.json index df684a4..cadbdf0 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -1,76 +1,1424 @@ { - "Dashboard": "Панель", - "Save": "Сохранить", - "Cancel": "Отмена", - "Delete": "Удалить", - "Edit": "Изменить", - "Create": "Создать", - "Search": "Поиск", - "Filters": "Фильтры", - "Reset": "Сбросить", - "Yes": "Да", - "No": "Нет", - "Loading": "Загрузка...", - "Empty": "Нет результатов", - "Settings": "Настройки", - "Profile": "Профиль", - "Logout": "Выход", - "Welcome": "Добро пожаловать", - "Total": "Итого", - "Date": "Дата", - "Status": "Статус", - "Actions": "Действия", - "Notifications": "Уведомления", - "Clienți": "Клиенты", - "Mașini": "Машины", - "Cereri": "Заявки", - "Pâlnie": "Воронка", - "Calendar": "Календарь", - "Programări": "Записи", - "Fișe lucru": "Рабочие листы", - "Kanban": "Канбан", - "Norme-ore": "Нормо-часы", - "Depozit": "Склад", - "Furnizori": "Поставщики", - "Achiziții": "Закупки", - "Plăți": "Платежи", - "Cheltuieli": "Расходы", - "Salarii": "Зарплаты", - "Tehnicieni": "Техники", - "Marketing": "Маркетинг", - "Mesaje": "Сообщения", - "Rapoarte": "Отчёты", - "Recomandări": "Рекомендации", - "Încărcare STO": "Загрузка СТО", - "Procentaj": "Наценка", - "VIN-căutare": "VIN-поиск", - "Integrări": "Интеграции", - "Backup": "Бэкап", - "Asistent AI": "AI Ассистент", - "Setări companie": "Настройки компании", - "Utilizatori": "Пользователи", - "Jurnal": "Журнал", - "Telefonie": "Телефония", - "Finanțe": "Финансы", - "Site PSauto": "Сайт", - "Seturi anvelope": "Шины (комплекты)", - "Anvelope": "Шины", - "set anvelope": "комплект шин", - "seturi anvelope": "комплекты шин", - "Tinichigerie / Detailing": "Кузов / Детейлинг", - "Tinichigerie": "Кузовной цех", - "lucrare caroserie": "кузовная работа", - "lucrări caroserie": "кузовные работы", - "Subcontractori": "Субподрядчики", - "Subcontractare": "Субподряд", - "subcontractor": "субподрядчик", - "Lucrări terți": "Работы у третьих лиц", - "lucrare terți": "работа у третьих лиц", - "lucrări terți": "работы у третьих лиц", - "Coeficienți preț": "Ценовые коэффициенты", - "coeficient": "коэффициент", - "Magazin": "Магазин", - "Clienți magazin": "Клиенты магазина", - "client magazin": "клиент магазина", - "clienți magazin": "клиенты магазина" -} + "$n sesiuni revoke-uite": "$n sesiuni revoke-uite", + "% Manopere": "% Manopere", + "% Piese": "% Piese", + "% din marja (sell-buy) pieselor montate.": "% din marja (sell-buy) pieselor montate.", + "% din venitul manoperelor finalizate.": "% din venitul manoperelor finalizate.", + "% manopere": "% manopere", + "% marja piese": "% marja piese", + "% marjă implicit": "% маржи по умолчанию", + "% piese": "% piese", + "+1 {$item->article} — {$item->qty_received}/{$item->qty}": "+1 {$item->article} — {$item->qty_received}/{$item->qty}", + "0 = dezactivat": "0 = dezactivat", + "1.15 = +15% peste prețul de bază. 0.95 = -5%.": "1.15 = +15% peste prețul de bază. 0.95 = -5%.", + "123456:ABC-XYZ...": "123456:ABC-XYZ...", + "2FA": "2FA", + "2FA dezactivat.": "2FA dezactivat.", + "2FA resetat": "2FA resetat", + "> 0 calculează automat prețul client.": "> 0 calculează automat prețul client.", + "A1-03": "A1-03", + "AGRNMD2X": "AGRNMD2X", + "AI: preț recomandat": "AI: preț recomandat", + "AI: recomandări": "AI: recomandări", + "AI: sugerează diagnostic": "AI: sugerează diagnostic", + "API Tokens": "API Tokens", + "AUTOCRM_001": "AUTOCRM_001", + "AYS...": "AYS...", + "Abonament": "Abonament", + "Abonament până la": "Abonament până la", + "Abonați": "Abonați", + "Absent": "Отсутствует", + "Accept": "Принимаю", + "Acces": "Acces", + "Acces în aplicație (opțional)": "Acces în aplicație (opțional)", + "Această acțiune este ireversibilă": "Это действие необратимо", + "Achitat": "Оплачено", + "Achiziții": "Закупки", + "Acord": "Соглашение", + "Acordat de": "Acordat de", + "Actions": "Действия", + "Activ": "Активный", + "Activare": "Активация", + "Active": "Активные", + "Activează": "Активировать", + "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.": "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.", + "Activează PayPal": "Activează PayPal", + "Activează Paynet": "Activează Paynet", + "Activează Stripe": "Activează Stripe", + "Activează coeficienții de preț VIP pe fișele acestui client.": "Activează coeficienții de preț VIP pe fișele acestui client.", + "Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat\".": "Activează magazinul public la .service.mir.md/shop. Piesele apar doar dacă sunt marcate „Publicat\".", + "Activează plata prin transfer": "Activează plata prin transfer", + "Activi": "Активные", + "Activă": "Активна", + "Actualizat la": "Обновлено", + "Actualizează": "Обновить", + "Acțiune": "Действие", + "Acțiuni în bulk": "Массовые действия", + "Acțiuni în masă": "Массовые действия", + "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.": "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.", + "Adaugă auto": "Добавить авто", + "Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.": "Adaugă chei API ca să activezi asistentul. Cheile rămân la voi — nu sunt partajate.", + "Adaugă cheltuială": "Добавить расход", + "Adaugă client": "Добавить клиента", + "Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.": "Adaugă coduri echivalente OEM/aftermarket ca să fie găsite în căutarea din magazin.", + "Adaugă comentariu": "Добавить комментарий", + "Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.": "Adaugă echipa ta — colegi de la suport, sales, finance — fiecare cu rolul lui.", + "Adaugă furnizor": "Добавить поставщика", + "Adaugă imagine": "Добавить изображение", + "Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.": "Adaugă mașini manual sau importă din CSV. Folosește VIN-căutare pentru decoder rapid și completare automată brand/model/an.", + "Adaugă notă": "Добавить заметку", + "Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.": "Adaugă piese manual, sau folosește Achiziții ca să le adaugi prin recepție de la furnizor (cu prețuri și stoc auto). Procentaj poate seta automat prețul de vânzare.", + "Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.": "Adaugă piesele care se montează de obicei la această manoperă — se adaugă automat în fișă când selectezi manopera.", + "Adaugă piesă": "Добавить запчасть", + "Adaugă plată": "Добавить платёж", + "Adaugă primul client SaaS folosind butonul „Adaugă companie\" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.": "Adaugă primul client SaaS folosind butonul „Adaugă companie\" — se va crea un tenant nou cu admin pe `.service.mir.md/app`.", + "Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.": "Adaugă primul tău client manual sau importă din CSV. Toate mașinile, fișele și plățile se vor lega automat de el.", + "Adaugă produs": "Добавить товар", + "Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.": "Adaugă punctele de daună pe zone (capotă, ușă, aripă) cu tip și gravitate — formează harta de daune a mașinii.", + "Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.": "Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.", + "Adaugă rol": "Добавить роль", + "Adaugă rând": "Добавить строку", + "Adaugă sarcină": "Добавить задачу", + "Adaugă serviciu": "Добавить услугу", + "Adaugă utilizator": "Добавить пользователя", + "Adaugă vehicul": "Добавить транспорт", + "Admin tenant (la creare)": "Admin tenant (la creare)", + "Adresă": "Адрес", + "Adresă IP": "IP-адрес", + "Adresă email": "Адрес email", + "Afișează detalii marjă la procesele calculate": "Afișează detalii marjă la procesele calculate", + "Ai deja cont?": "Уже есть аккаунт?", + "Ai uitat parola?": "Забыли пароль?", + "Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește\".": "Aici apar cererile clienților potențiali. Convertește-le în deal-uri sau direct în programări de la butonul „Convertește\".", + "Aici apar clienții care și-au creat cont în magazinul online (/shop/register).": "Aici apar clienții care și-au creat cont în magazinul online (/shop/register).", + "Aleatoriu": "Случайно", + "Alege categorie": "Выбрать категорию", + "Alege client": "Выбрать клиента", + "Alege dată": "Выбрать дату", + "Alege limbă": "Выбрать язык", + "Alege maistru": "Выбрать мастера", + "Alege mecanic": "Выбрать механика", + "Alege metodă plată": "Выбрать способ оплаты", + "Alege monedă": "Выбрать валюту", + "Alege oră": "Выбрать время", + "Alege pod": "Выбрать пост", + "Alege stare": "Выбрать статус", + "Alege tip": "Выбрать тип", + "Alege vehicul": "Выбрать транспорт", + "Alfabetic": "По алфавиту", + "Alte cheltuieli": "Прочие расходы", + "An": "Год", + "An fabricație": "Год выпуска", + "Angajat din": "Angajat din", + "Ani": "Годы", + "Antet": "Шапка", + "Antet fișă": "Шапка наряда", + "Anual": "Anual", + "Anul curent": "Текущий год", + "Anul trecut": "Прошлый год", + "Anulat": "Отменено", + "Anulează fișa": "Отменить наряд", + "Anvelope": "Шины", + "Apare în magazinul online public.": "Apare în magazinul online public.", + "Apasă „Recepție\" pe lista de piese pentru a înregistra prima intrare în depozit.": "Apasă „Recepție\" pe lista de piese pentru a înregistra prima intrare în depozit.", + "Apel": "Apel", + "Aplicat": "Aplicat", + "Aplică": "Применить", + "Aplică filtru": "Применить фильтр", + "Aplică marjă internă": "Применить внутреннюю маржу", + "Aplică prețul": "Aplică prețul", + "Aplică toate regulile la stoc": "Aplică toate regulile la stoc", + "Aplică șablon": "Применить шаблон", + "Aprilie": "Апрель", + "Aprobare": "Утверждение", + "Aprobat": "Утверждено", + "Aprobat de": "Утверждено", + "Aprobat de client": "Утверждено клиентом", + "Aprobă": "Утвердить", + "Arată": "Показать", + "Arhivat": "В архиве", + "Arhivează": "В архив", + "Arhivă": "Архив", + "Articol nu se potrivește comenzii": "Articol nu se potrivește comenzii", + "Articole": "Articole", + "Ascunde": "Скрыть", + "Ascuns": "Скрыто", + "Asig.": "Asig.", + "Asigurare": "Asigurare", + "Asigurător": "Asigurător", + "Asistent AI": "AI-ассистент", + "Astăzi": "Сегодня", + "Atelierul meu": "Atelierul meu", + "Atenție": "Внимание", + "August": "Август", + "Autentificare": "Аутентификация", + "Autentificare 2FA": "2FA аутентификация", + "Auto": "Авто", + "AutoCRM SRL": "AutoCRM SRL", + "Avans": "Аванс", + "Avg zile": "Avg zile", + "Așteptată": "Ожидается", + "BIC / SWIFT": "BIC / SWIFT", + "Backup": "Бэкап", + "Backup & Export date": "Backup & Export date", + "Balance": "Баланс", + "Balanță": "Баланс", + "Banca": "Banca", + "Bază": "База", + "Beneficiar": "Beneficiar", + "Bifează ce poate face acest rol. Modificările au efect imediat.": "Bifează ce poate face acest rol. Modificările au efect imediat.", + "Bifă": "Флажок", + "Bine ai venit": "Добро пожаловать", + "Blocată": "Заблокирована", + "Blochează": "Заблокировать", + "Boală": "Больничный", + "Bon": "Чек", + "Bonuri": "Чеки", + "Bot OK: @{$name}": "Bot OK: @{$name}", + "Bot Telegram nu răspunde": "Bot Telegram nu răspunde", + "Bot token": "Bot token", + "Brand": "Brand", + "Brand & contact": "Бренд и контакты", + "Buget": "Бюджет", + "Buget & rezultate (luna curentă)": "Buget & rezultate (luna curentă)", + "Bun venit": "Добро пожаловать", + "Bun venit în AutoCRM!": "Bun venit în AutoCRM!", + "Bună ziua": "Здравствуйте", + "CSV": "CSV", + "Calcul": "Calcul", + "Calculat salariul {$period} pentru {$count} utilizatori": "Calculat salariul {$period} pentru {$count} utilizatori", + "Calculează luna curentă": "Calculează luna curentă", + "Calendar": "Календарь", + "Calendar vizual": "Calendar vizual", + "Canal marketing": "Canal marketing", + "Cancel": "Отмена", + "Cant.": "Кол-во", + "Cantitate": "Количество", + "Cantitate recepționată": "Cantitate recepționată", + "Cantitate restituită": "Cantitate restituită", + "Capacitate motor": "Объём двигателя", + "Capacitatea zilnică în ore": "Capacitatea zilnică în ore", + "Caroserie": "Кузов", + "Catalog manoperă": "Catalog manoperă", + "Categorie": "Категория", + "Categorie implicită": "Категория по умолчанию", + "Categorii": "Categorii", + "Caută": "Поиск", + "Caută în tabel": "Поиск в таблице", + "Caz asigurare": "Caz asigurare", + "Caz de asigurare": "Caz de asigurare", + "Cereri": "Заявки", + "Check-in depozit": "Check-in depozit", + "Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.": "Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.", + "Cheie": "Cheie", + "Cheltuială": "Расход", + "Cheltuieli": "Расходы", + "Cheltuit": "Потрачено", + "Cheltuit 90d": "Cheltuit 90d", + "Chitanțe": "Квитанции", + "Chitanță": "Квитанция", + "Ciornă": "Черновик", + "Clase auto": "Классы авто", + "Clasă (pentru pricing)": "Clasă (pentru pricing)", + "Claude API Key": "Claude API Key", + "Client": "Клиент", + "Client & Auto": "Client & Auto", + "Client CRM": "Client CRM", + "Client ID": "Client ID", + "Client VIP": "Client VIP", + "Client existent": "Существующий клиент", + "Client legat (CRM)": "Client legat (CRM)", + "Client nou": "Новый клиент", + "Clienți": "Клиенты", + "Clienți magazin": "Клиенты магазина", + "Cod": "Код", + "Cod articol": "Артикул", + "Cod bare": "Cod bare", + "Cod echivalent": "Cod echivalent", + "Cod necunoscut": "Cod necunoscut", + "Codul produsului": "Код товара", + "Coduri cross (OEM/echivalente)": "Coduri cross (OEM/echivalente)", + "Coeficient": "Coeficient", + "Coeficienți preț": "Ценовые коэффициенты", + "Coloane": "Колонки", + "Coloană": "Колонка", + "Comandat": "Comandat", + "Comandată": "Comandată", + "Comandă": "Comandă", + "Comandă nouă #": "Comandă nouă #", + "Combustibil": "Топливо", + "Comenzi": "Заказы", + "Comercial": "Comercial", + "Comision manopere": "Comision manopere", + "Comision piese": "Comision piese", + "Companie": "Компания", + "Companie & plan": "Companie & plan", + "Companie creată: {$result['company']->name}": "Companie creată: {$result['company']->name}", + "Complet": "Полностью", + "Concediu": "Отпуск", + "Condiții (toate trebuie îndeplinite)": "Condiții (toate trebuie îndeplinite)", + "Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.": "Configurează procentele pentru calcul salariu. Marja internă (nu TVA) se scade din prețul de manoperă pentru a determina baza salariului.", + "Configurări": "Конфигурация", + "Configurări avansate": "Расширенная конфигурация", + "Confirmare plată primită": "Confirmare plată primită", + "Confirmă că mecanicul ia fizic piesa din depozit. Stocul scade acum, fără să aștepți închiderea fișei.": "Confirmă că mecanicul ia fizic piesa din depozit. Stocul scade acum, fără să aștepți închiderea fișei.", + "Confirmă emailul": "Подтвердите email", + "Confirmă ștergerea": "Подтвердите удаление", + "Contact": "Контакт", + "Contactare": "Связь", + "Contacte": "Contacte", + "Contactează": "Связаться", + "Convertește": "Convertește", + "Convertite": "Конвертированы", + "Conținut": "Conținut", + "Conținut șablon": "Conținut șablon", + "Copiat": "Скопировано", + "Copiază": "Копировать", + "Copiază token-ul ACUM — nu va mai fi afișat.": "Copiază token-ul ACUM — nu va mai fi afișat.", + "Cost": "Стоимость", + "Cost & marjă": "Cost & marjă", + "Cost (de la terț)": "Cost (de la terț)", + "Cost (terț)": "Cost (terț)", + "Cost/lead": "Cost/lead", + "Costuri": "Стоимости", + "Creat": "Creat", + "Creat de": "Создано", + "Creat la": "Создано", + "Create": "Создать", + "Crează manual prima factură sau folosește butonul „Generează factură\" din pagina Companiei.": "Crează manual prima factură sau folosește butonul „Generează factură\" din pagina Companiei.", + "Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.": "Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.", + "Credit": "Кредит", + "Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.": "Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.", + "Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook\". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.": "Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook\". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.", + "Crescător": "По возрастанию", + "Critic": "Критический", + "Cronologic": "Хронологически", + "Cu TVA": "С НДС", + "Cu jante": "Cu jante", + "Culoare": "Цвет", + "Culoare brand": "Цвет бренда", + "Culoare în calendar": "Culoare în calendar", + "Cumul.": "Cumul.", + "Cumulabil": "Cumulabil", + "Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.": "Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.", + "Curăță": "Очистить", + "Cutie": "Коробка", + "Cutie de viteze": "КПП", + "Câmp obligatoriu": "Обязательное поле", + "Când": "Când", + "Când & unde": "Când & unde", + "DOT": "DOT", + "Da, confirmă": "Да, подтвердить", + "Da, șterge": "Да, удалить", + "Dacă e gol, generăm 10 caractere random.": "Dacă e gol, generăm 10 caractere random.", + "Dashboard": "Панель", + "Data": "Дата", + "Data & ora": "Data & ora", + "Data actualizării": "Дата обновления", + "Data comandă": "Data comandă", + "Data creării": "Дата создания", + "Data factură": "Дата счёта", + "Data ieșire": "Дата выдачи", + "Data intrare": "Дата приёма", + "Data plății": "Дата платежа", + "Data sfârșit": "Дата окончания", + "Data început": "Дата начала", + "Date": "Дата", + "Date generale": "Date generale", + "Date insuficiente": "Date insuficiente", + "Date legale (apar pe facturi)": "Date legale (apar pe facturi)", + "Date personale": "Date personale", + "Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.": "Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.", + "Datorie": "Долг", + "Daune": "Daune", + "De către": "De către", + "De la": "От", + "De la (preț achiziție)": "De la (preț achiziție)", + "De la data": "С даты", + "De la ora": "С", + "De obicei 10× preț lunar (2 luni gratis).": "De obicei 10× preț lunar (2 luni gratis).", + "De procesat": "De procesat", + "Deal": "Сделка", + "Deals": "Сделки", + "Debit": "Дебет", + "Deblochează": "Разблокировать", + "Decembrie": "Декабрь", + "Decode VIN": "Decode VIN", + "Deconectare": "Отключение", + "Delete": "Удалить", + "Demo (pentru demonstrații sales)": "Demo (pentru demonstrații sales)", + "Denumire": "Наименование", + "Denumire afișată": "Отображаемое название", + "Denumire companie": "Название компании", + "Denumire produs": "Название товара", + "Depozit": "Склад", + "Depozit gol": "Depozit gol", + "Depozit implicit": "Склад по умолчанию", + "Depozit țintă": "Целевой склад", + "Depublică": "Снять с публикации", + "Descarcă PDF": "Скачать PDF", + "Descarcă factura": "Скачать счёт", + "Descarcă template": "Скачать шаблон", + "Deschide": "Открыть", + "Deschide tenantul": "Deschide tenantul", + "Deschis": "Открыт", + "Deschis la": "Открыт", + "Descrescător": "По убыванию", + "Descriere": "Описание", + "Descriere produs": "Описание товара", + "Detalii": "Подробности", + "Detalii auto": "Данные авто", + "Detalii client": "Данные клиента", + "Detalii comandă": "Данные заказа", + "Deviz": "Deviz", + "Dezactivare": "Деактивация", + "Dezactivează": "Деактивировать", + "Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.": "Dezactivează 2FA pentru acest utilizator. Va trebui să re-configureze TOTP la următoarea autentificare.", + "Diagnostic": "Диагностика", + "Diagnostic AI bazat pe plângerea clientului": "Diagnostic AI bazat pe plângerea clientului", + "Diagnosticare": "Диагностика", + "Diagnoza": "Диагностика", + "Diagnoză": "Диагностика", + "Diametru R": "Diametru R", + "Dimensiune": "Dimensiune", + "Din catalog (lasă gol pentru text liber)": "Din catalog (lasă gol pentru text liber)", + "Discount": "Скидка", + "Discount %": "Скидка %", + "Distribuie": "Распределить", + "Doar active": "Только активные", + "Doar arhivate": "Только архивные", + "Doar clienți VIP": "Только VIP клиенты", + "Doar clienții mei": "Только мои клиенты", + "Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.": "Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.", + "Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.": "Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.", + "Doar tu ești aici": "Doar tu ești aici", + "Doar utilizatorii mei": "Только мои пользователи", + "Document": "Документ", + "Documente": "Документы", + "Downgrade": "Понижение", + "Drept": "Право", + "Drepturi": "Права", + "Duminică": "Воскресенье", + "Duplică": "Дублировать", + "Duplică rând": "Дублировать строку", + "După": "După", + "Durată": "Длительность", + "Durată (sec)": "Durată (sec)", + "Durată estimată": "Расчётная длительность", + "Durată reală": "Фактическая длительность", + "EJk...": "EJk...", + "ETA": "ETA", + "Edit": "Изменить", + "Editează": "Изменить", + "Editează drepturi": "Editează drepturi", + "Editează produs": "Изменить товар", + "Electrică": "Электрика", + "Eliberat: {$n} rezervări consumate": "Eliberat: {$n} rezervări consumate", + "Eliberează": "Выдать", + "Email": "Email", + "Email admin": "Email admin", + "Empty": "Нет результатов", + "Endpoint webhook: https://service.mir.md/payments/stripe/webhook": "Endpoint webhook: https://service.mir.md/payments/stripe/webhook", + "Eroare": "Ошибка", + "Eroare la recepție": "Eroare la recepție", + "Etape": "Этапы", + "Etapă": "Этап", + "Etichetă": "Etichetă", + "Ex 30 → preț vânzare = preț achiziție × 1.30": "Ex 30 → preț vânzare = preț achiziție × 1.30", + "Ex: 100": "Ex: 100", + "Ex: 20": "Ex: 20", + "Ex: 20 pentru +20%": "Ex: 20 pentru +20%", + "Ex: 25": "Ex: 25", + "Ex: Pod 1, Curte 1, Atelier electric": "Ex: Pod 1, Curte 1, Atelier electric", + "Ex: cu lift, fără lift, doar diagnoză...": "Ex: cu lift, fără lift, doar diagnoză...", + "Ex: lockdown temporar; acces pentru audit; etc.": "Ex: lockdown temporar; acces pentru audit; etc.", + "Excepție": "Excepție", + "Excepții": "Исключения", + "Excepții drepturi": "Исключения прав", + "Expiră": "Expiră", + "Expiră la (opțional)": "Expiră la (opțional)", + "Export CSV": "Экспорт CSV", + "Extras": "Выписка", + "Ești sigur?": "Ты уверен?", + "Facturi": "Счета", + "Facturi pending / overdue": "Facturi pending / overdue", + "Factură": "Счёт", + "Factură importată": "Factură importată", + "Factură nouă": "Новый счёт", + "Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.": "Fallback când mecanicul nu are marja proprie setată. Lasă gol = 0%.", + "Favicon": "Favicon", + "Față-Dreapta": "Față-Dreapta", + "Față-Stânga": "Față-Stânga", + "Februarie": "Февраль", + "Filters": "Фильтры", + "Filtre": "Фильтры", + "Filtru": "Фильтр", + "Finalizat": "Завершено", + "Financiar": "Financiar", + "Finanțe": "Finanțe", + "Fișe": "Наряды", + "Fișe active": "Активные наряды", + "Fișe lucru": "Рабочие листы", + "Fișe închise": "Закрытые наряды", + "Fișier": "Файл", + "Fișiere": "Файлы", + "Fișă": "Наряд", + "Fișă asociată": "Fișă asociată", + "Fișă lucru": "Fișă lucru", + "Folosește „Check-in depozit\" pe set pentru a înregistra prima depozitare.": "Folosește „Check-in depozit\" pe set pentru a înregistra prima depozitare.", + "Folosit dacă markup = 0.": "Folosit dacă markup = 0.", + "Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.": "Folosită de coeficienții de preț. Hibrid/EV se deduc și din combustibil.", + "Force logout": "Выйти со всех устройств", + "Foto": "Фото", + "Foto factură": "Foto factură", + "Foto piesă": "Foto piesă", + "Foto înainte / după": "Foto înainte / după", + "Fotografii": "Fotografii", + "Frâne": "Тормоза", + "Frâne, Ulei, ...": "Frâne, Ulei, ...", + "Funcție": "Funcție", + "Funcționalități": "Funcționalități", + "Funcționalități incluse": "Funcționalități incluse", + "Furnizor": "Поставщик", + "Furnizor (opțional)": "Furnizor (opțional)", + "Furnizor factură": "Поставщик счёта", + "Furnizor implicit": "Поставщик по умолчанию", + "Furnizor preferat": "Furnizor preferat", + "Furnizori": "Поставщики", + "Fără TVA": "Без НДС", + "Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.": "Galerie de până la 8 imagini. Prima e afișată în catalog. Max 2 MB / imagine.", + "Gata estimat (ETA)": "Gata estimat (ETA)", + "Gemini API Key": "Gemini API Key", + "General": "Общее", + "Generat automat": "Генерируется автоматически", + "Generează factură": "Generează factură", + "Generează raport": "Сгенерировать отчёт", + "Generează token": "Generează token", + "Global": "Глобально", + "Grafic": "График", + "Gravitate": "Gravitate", + "Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet\") și aplică-l pe o fișă cu un click.": "Grupează manopere + piese frecvente într-un șablon (ex: „Schimb ulei complet\") și aplică-l pe o fișă cu un click.", + "Hartă daune": "Hartă daune", + "IBAN": "IBAN", + "ID": "ID", + "IDNO / CUI": "IDNO / CUI", + "Ianuarie": "Январь", + "Iarnă 2025-2026": "Iarnă 2025-2026", + "Iconiță (emoji)": "Iconiță (emoji)", + "Identificare": "Идентификация", + "Identitate": "Identitate", + "Ieri": "Вчера", + "Ieșire": "Выход", + "Imagine": "Изображение", + "Imagine principală": "Главное изображение", + "Imagini": "Изображения", + "Implicit": "По умолчанию", + "Import": "Импорт", + "Import CSV": "Импорт CSV", + "Import clienți din CSV": "Import clienți din CSV", + "Import factură (OCR)": "Import factură (OCR)", + "Import factură Excel/CSV": "Import factură Excel/CSV", + "Import factură via OCR": "Import factură via OCR", + "Import mașini din CSV": "Import mașini din CSV", + "Import reușit — Purchase {$purchase->number}": "Import reușit — Purchase {$purchase->number}", + "Import: {$r['imported']} adăugate, {$r['skipped']} ignorate": "Import: {$r['imported']} adăugate, {$r['skipped']} ignorate", + "Import: {$r['imported']} adăugați, {$r['skipped']} ignorați": "Import: {$r['imported']} adăugați, {$r['skipped']} ignorați", + "Importă din CSV": "Импорт из CSV", + "Importă din Excel": "Импорт из Excel", + "Inactiv": "Неактивно", + "Infopanel": "Инфопанель", + "Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.": "Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.", + "Informație": "Информация", + "Instrucțiuni adiționale": "Instrucțiuni adiționale", + "Integrări": "Интеграции", + "Integrări de plată": "Integrări de plată", + "Integrări externe": "Integrări externe", + "Integrări salvate": "Integrări salvate", + "Interval": "Интервал", + "Interval dată": "Диапазон дат", + "Interval orar": "Часовой интервал", + "Intrat": "Intrat", + "Invitație": "Приглашение", + "Invitații": "Приглашения", + "Istoric depozitare": "Istoric depozitare", + "Istoric prețuri furnizori": "Istoric prețuri furnizori", + "Iulie": "Июль", + "Iunie": "Июнь", + "JSON": "JSON", + "Joi": "Четверг", + "Jurnal": "Журнал", + "KPI mecanici": "KPI mecanici", + "Kanban": "Канбан", + "Kilometraj": "Kilometraj", + "Km": "Км", + "Km curent": "Текущий пробег", + "Km ieșire": "Пробег выход", + "Km intrare": "Пробег вход", + "Km parcurși": "Пройдено км", + "La editare lasă gol pentru a păstra parola actuală.": "La editare lasă gol pentru a păstra parola actuală.", + "La revedere": "До свидания", + "La timp 90d": "La timp 90d", + "La trecere pe „Montată\" se scade automat din stoc.": "La trecere pe „Montată\" se scade automat din stoc.", + "Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.": "Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.", + "Lead-uri": "Lead-uri", + "Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.": "Legătura cu fișa CRM (opțional). Auto-matched la înregistrare după telefon.", + "Limbă": "Язык", + "Limbă default": "Язык по умолчанию", + "Limite": "Limite", + "Linii": "Linii", + "Link client (QR)": "Link client (QR)", + "Lipsă": "Lipsă", + "Liste configurabile": "Настраиваемые списки", + "Livrare": "Livrare", + "Livrare (zile)": "Livrare (zile)", + "Livrare gratuită peste": "Livrare gratuită peste", + "Loading": "Загрузка...", + "Loc.": "Loc.", + "Local": "Локально", + "Localizare & monedă": "Локализация и валюта", + "Locație": "Locație", + "Locație (raft)": "Locație (raft)", + "Locație rack/bin": "Locație rack/bin", + "Login": "Вход", + "Logo": "Logo", + "Logo & favicon": "Logo & favicon", + "Logo (opțional)": "Logo (opțional)", + "Logo actualizat": "Logo actualizat", + "Logout": "Выход", + "Loturi": "Loturi", + "Loturi (FIFO)": "Loturi (FIFO)", + "Lucrare": "Lucrare", + "Lucrare blocată": "Работа заблокирована", + "Lucrări": "Lucrări", + "Lucrări la terți": "Lucrări la terți", + "Lucrări terți": "Работы у третьих лиц", + "Luna curentă": "Текущий месяц", + "Luna trecută": "Прошлый месяц", + "Lunar": "Lunar", + "Luni": "Месяцы", + "Luni (zi)": "Понедельник", + "Lățime": "Lățime", + "MD00 AG00 0000 ...": "MD00 AG00 0000 ...", + "MRR realizat": "MRR realizat", + "Magazin": "Magazin", + "Magazin activ": "Magazin activ", + "Magazin online": "Онлайн-магазин", + "Mai": "Май", + "Mai mic = aplicat primul.": "Mai mic = aplicat primul.", + "Maistru": "Мастер", + "Maistru / Mecanic": "Maistru / Mecanic", + "Manopere": "Работы", + "Manoperă": "Работа", + "Marca": "Marca", + "Marchează acest tenant ca demo (datele pot fi resetate periodic).": "Marchează acest tenant ca demo (datele pot fi resetate periodic).", + "Marchează anulat": "Отметить как отменено", + "Marchează plătit": "Отметить как оплачено", + "Marchează setul ca ridicat de client.": "Marchează setul ca ridicat de client.", + "Marchează terminat": "Отметить как готовое", + "Marcă": "Марка", + "Marcă implicită": "Марка по умолчанию", + "Marja": "Маржа", + "Marja piese": "Marja piese", + "Marja profitului": "Маржа прибыли", + "Marjă": "Маржа", + "Marjă client": "Маржа клиента", + "Marjă internă (%)": "Внутренняя маржа (%)", + "Marjă internă (%) — override": "Marjă internă (%) — override", + "Marjă internă (nu TVA)": "Внутренняя маржа (не НДС)", + "Marketing": "Marketing", + "Marketing (UTM)": "Marketing (UTM)", + "Markup": "Markup", + "Markup %": "Наценка %", + "Martie": "Март", + "Marți": "Вторник", + "Max clienți": "Max clienți", + "Max fișe/lună": "Max fișe/lună", + "Max mașini": "Max mașini", + "Max useri": "Max useri", + "Mașina e gata de ridicat": "Mașina e gata de ridicat", + "Mașini": "Mașini", + "Mecanic implicit": "Механик по умолчанию", + "Mediu": "Средний", + "Merchant Code": "Merchant Code", + "Mesaj": "Mesaj", + "Mesaj client": "Mesaj client", + "Mesaje": "Сообщения", + "Mesaje AI/lună": "Mesaje AI/lună", + "Metode de livrare": "Metode de livrare", + "Metode plată": "Способы оплаты", + "Metodă plată": "Способ оплаты", + "Miercuri": "Среда", + "Min.": "Min.", + "Minim": "Minim", + "Minute": "Минуты", + "Mod": "Режим", + "Mod tarifare": "Mod tarifare", + "Model": "Модель", + "Model Claude": "Model Claude", + "Model Gemini": "Model Gemini", + "Model OpenAI": "Model OpenAI", + "Model implicit": "Модель по умолчанию", + "Modele": "Модели", + "Modificat de": "Изменено", + "Moldova-Agroindbank": "Moldova-Agroindbank", + "Monedă": "Валюта", + "Monedă platformă": "Monedă platformă", + "Motiv": "Причина", + "Motor": "Двигатель", + "Motor / Frâne / Electrică ...": "Motor / Frâne / Electrică ...", + "Motorizare": "Двигатель", + "Multiplicator": "Множитель", + "Mulțumim": "Спасибо", + "Mâine": "Завтра", + "Mărci auto suportate (separate prin virgulă)": "Mărci auto suportate (separate prin virgulă)", + "Necesită cel puțin 2 recepții complete cu data așteptată setată.": "Necesită cel puțin 2 recepții complete cu data așteptată setată.", + "Necompletat": "Не заполнено", + "Necunoscut": "Неизвестно", + "Neplătit": "Не оплачено", + "Nepublicat": "Не опубликовано", + "Nesemnat": "Не подписано", + "Nesincronizat": "Не синхронизировано", + "Net 30 / Avans": "Net 30 / Avans", + "Neverificat": "Не проверено", + "Nicio comandă încă": "Nicio comandă încă", + "Nicio daună marcată": "Nicio daună marcată", + "Nicio factură generată": "Nicio factură generată", + "Nicio fișă de lucru": "Nicio fișă de lucru", + "Nicio lucrare de caroserie": "Nicio lucrare de caroserie", + "Nicio lucrare la terți": "Nicio lucrare la terți", + "Nicio mașină încă": "Nicio mașină încă", + "Nicio piesă implicită": "Nicio piesă implicită", + "Niciodată": "Niciodată", + "Niciun client magazin": "Niciun client magazin", + "Niciun client încă": "Niciun client încă", + "Niciun cod cross": "Niciun cod cross", + "Niciun coeficient": "Niciun coeficient", + "Niciun depozit": "Niciun depozit", + "Niciun istoric": "Niciun istoric", + "Niciun lot înregistrat": "Niciun lot înregistrat", + "Niciun mecanic implicit": "Niciun mecanic implicit", + "Niciun plan definit": "Niciun plan definit", + "Niciun preț înregistrat": "Niciun preț înregistrat", + "Niciun set de anvelope": "Niciun set de anvelope", + "Niciun subcontractor": "Niciun subcontractor", + "Niciun tenant încă": "Niciun tenant încă", + "Niciun șablon": "Niciun șablon", + "No": "Нет", + "Noiembrie": "Ноябрь", + "Normal": "Обычный", + "Norme-ore": "Нормо-часы", + "Note interne": "Note interne", + "Notificare": "Уведомление", + "Notifications": "Уведомления", + "Notificări": "Уведомления", + "Notițe": "Заметки", + "Notițe interne": "Notițe interne", + "Nou": "Новый", + "Nou tehnician": "Nou tehnician", + "Nouă": "Новая", + "Nr.": "№", + "Nr. dosar daună": "Nr. dosar daună", + "Nr. factură": "№ счёта", + "Nr. fișă": "№ наряда", + "Nr. poliță": "Nr. poliță", + "Nr. telefon": "Тел. номер", + "Nr. înmatriculare": "Гос. номер", + "Nu ai cont?": "Нет аккаунта?", + "Nu am găsit linii valide — verifică maparea coloanelor": "Nu am găsit linii valide — verifică maparea coloanelor", + "Nu există date": "Нет данных", + "Nu există rezultate": "Нет результатов", + "Nu re-trimite mai des de X zile": "Nu re-trimite mai des de X zile", + "Nu te poți șterge pe tine!": "Nu te poți șterge pe tine!", + "Nu, renunță": "Нет, отменить", + "Nume": "Имя", + "Nume (RO)": "Nume (RO)", + "Nume (RU)": "Nume (RU)", + "Nume (descriere)": "Nume (descriere)", + "Nume admin": "Nume admin", + "Nume canal": "Nume canal", + "Nume template": "Nume template", + "Numele este obligatoriu": "Numele este obligatoriu", + "Numărul de zile gratis după ce un tenant alege acest plan.": "Numărul de zile gratis după ce un tenant alege acest plan.", + "OCR eșuat": "OCR eșuat", + "Observații": "Комментарии", + "Octombrie": "Октябрь", + "On = arată „Bază salariu: X · marjă Y%\" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).": "On = arată „Bază salariu: X · marjă Y%\" sub Total în tabelul Manopere. Off = doar Total (util când review-uiești o Fișă cu clientul de față).", + "On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).": "On = din prețul manoperelor se scade marja pentru salariu. Off = manopere la cost (salariu pe Total integral).", + "Onorat": "Onorat", + "Onorează (scade stoc)": "Onorează (scade stoc)", + "OpenAI API Key": "OpenAI API Key", + "Ora": "Час", + "Ora deschidere": "Время открытия", + "Ora ieșire": "Время выдачи", + "Ora intrare": "Время приёма", + "Ora sfârșit": "Конец", + "Ora începere": "Начало", + "Ora închidere": "Время закрытия", + "Oraș": "Город", + "Ordine": "Порядок", + "Ordonare": "Сортировка", + "Ordonat": "Отсортировано", + "Ordonează după": "Сортировать по", + "Ore": "Часы", + "Ore (normă)": "Ore (normă)", + "Ore disponibile / zi": "Доступные часы / день", + "Ore lucrate": "Отработанные часы", + "Ore/zi": "Часов/день", + "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.": "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.", + "PDF": "PDF", + "PNG/ICO, max 512 KB.": "PNG/ICO, max 512 KB.", + "PNG/SVG, max 2 MB. Apare în sidebar.": "PNG/SVG, max 2 MB. Apare în sidebar.", + "PO": "PO", + "Pagina": "Страница", + "Pagini": "Страницы", + "Panou": "Панель", + "Panou control": "Панель управления", + "Parolă": "Пароль", + "Parolă (lasă gol pentru a nu schimba)": "Parolă (lasă gol pentru a nu schimba)", + "Parolă (lasă gol pentru auto-generat)": "Parolă (lasă gol pentru auto-generat)", + "Parolă API": "Parolă API", + "Parolă resetată.": "Parolă resetată.", + "Parțial": "Частично", + "Pas 1 — Datele afacerii": "Pas 1 — Datele afacerii", + "Pas 2 — Brand & limbă": "Pas 2 — Brand & limbă", + "Pas 3 — Tarif & terminat": "Pas 3 — Tarif & terminat", + "Pauză": "Пауза", + "Penalizări": "Penalizări", + "Perioada (YYYY-MM)": "Perioada (YYYY-MM)", + "Perioadă": "Период", + "Perioadă (YYYY-MM)": "Perioadă (YYYY-MM)", + "Perioadă raport": "Период отчёта", + "Perioadă trial (zile)": "Perioadă trial (zile)", + "Permisiuni": "Permisiuni", + "Persoană contact": "Persoană contact", + "Personalizat": "Персонализировано", + "Pierdute": "Pierdute", + "Piese": "Запчасти", + "Piese impl.": "Piese impl.", + "Piese implicite": "Piese implicite", + "Piesă": "Запчасть", + "Piesă din catalog": "Piesă din catalog", + "Pipeline": "Воронка", + "Plan": "План", + "Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.": "Plan ascuns public, folosit pentru tenant-i demo cu features deblocate.", + "Plata stabilește automat starea fișei (unpaid/partial/paid)": "Plata stabilește automat starea fișei (unpaid/partial/paid)", + "Plată": "Платёж", + "Plată & total": "Оплата и итого", + "Plângere client": "Plângere client", + "Plătit": "Оплачено", + "Plătit către terț": "Plătit către terț", + "Plătit de": "Оплачено", + "Plătit la": "Plătit la", + "Plătit terț": "Plătit terț", + "Plătită": "Plătită", + "Plăți": "Платежи", + "Plăți internaționale. Mai potrivit pentru clienți din afara MD.": "Plăți internaționale. Mai potrivit pentru clienți din afara MD.", + "Pod": "Пост", + "Pod / Spațiu lucru": "Пост / рабочее место", + "Politica de confidențialitate": "Политика конфиденциальности", + "Politici": "Политики", + "Pontaj": "Табель", + "Populare": "Популярные", + "Post actualizat": "Post actualizat", + "Poze": "Фото", + "Pozele auto": "Фото авто", + "Prefix facturi": "Prefix facturi", + "Preview": "Предпросмотр", + "Preview PDF": "Предпросмотр PDF", + "Previzualizare": "Предпросмотр", + "Prezent": "Присутствует", + "Prezențe": "Присутствия", + "Prezență": "Присутствие", + "Preț": "Цена", + "Preț achiziție": "Цена закупки", + "Preț anual": "Preț anual", + "Preț client": "Цена клиенту", + "Preț contextual": "Preț contextual", + "Preț fix": "Preț fix", + "Preț fix (MDL)": "Preț fix (MDL)", + "Preț implicit": "Цена по умолчанию", + "Preț inteligent": "Умная цена", + "Preț lunar": "Preț lunar", + "Preț orientativ (MDL)": "Preț orientativ (MDL)", + "Preț unit.": "Preț unit.", + "Preț unitar": "Preț unitar", + "Preț vz.": "Preț vz.", + "Preț vânzare": "Цена продажи", + "Preț/h": "Preț/h", + "Prețuri": "Prețuri", + "Prețurile se înregistrează automat la fiecare recepție de PO.": "Prețurile se înregistrează automat la fiecare recepție de PO.", + "Primit": "Primit", + "Print": "Печать", + "Print factură": "Печать счёта", + "Print fișă": "Печать наряда", + "Prio": "Prio", + "Prioritate": "Приоритет", + "Privat": "Приватно", + "Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin\".": "Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin\".", + "Procentaj": "Наценка", + "Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.": "Procesare carduri online. Funcționează în 30+ țări. Comision ~2.9% + 0.30€/tranzacție.", + "Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.": "Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.", + "Procesează": "Обработать", + "Produs": "Товар", + "Produse": "Товары", + "Profesie": "Profesie", + "Profil": "Profil", + "Profile": "Профиль", + "Profit": "Прибыль", + "Programare adăugată": "Programare adăugată", + "Programare confirmată": "Programare confirmată", + "Programare mutată": "Programare mutată", + "Programare ștearsă": "Programare ștearsă", + "Programează întâlnire": "Запланировать встречу", + "Programări": "Записи", + "Progres": "Progres", + "Proprietar": "Владелец", + "Provider implicit": "Provider implicit", + "Public": "Публично", + "Public (afișat la signup)": "Public (afișat la signup)", + "Publicat": "Опубликовано", + "Publicat în magazin": "Publicat în magazin", + "Publică": "Опубликовать", + "Publică în magazin": "Publică în magazin", + "Publishable key": "Publishable key", + "Putere": "Мощность", + "Pâlnie": "Воронка", + "Până la": "До", + "Până la (gol = ∞)": "Până la (gol = ∞)", + "Până la data": "До даты", + "Până la ora": "По", + "QR": "QR", + "ROI": "ROI", + "Rapoarte": "Отчёты", + "Raport": "Отчёт", + "Rating": "Рейтинг", + "Rating actualizat → {$score}★": "Rating actualizat → {$score}★", + "Reactivează": "Реактивировать", + "Recalculat preț pentru {$count} piese": "Recalculat preț pentru {$count} piese", + "Recalculează": "Recalculează", + "Recent": "Недавно", + "Recente": "Недавние", + "Recepție": "Recepție", + "Recepție completă — batch-uri create": "Recepție completă — batch-uri create", + "Recepție totală": "Recepție totală", + "Recepționat": "Recepționat", + "Recepționat — batch creat": "Recepționat — batch creat", + "Recepționată": "Recepționată", + "Recepționează": "Recepționează", + "Recomandat": "Рекомендуется", + "Recomandate": "Рекомендуемые", + "Recomandă": "Рекомендуется", + "Recomandări": "Recomandări", + "Recomandări AI": "Recomandări AI", + "Recomandări — clienți de urmărit": "Recomandări — clienți de urmărit", + "Redeschide fișa": "Переоткрыть наряд", + "Ref.": "Реф.", + "Ref. lot/factură": "Ref. lot/factură", + "Referință": "Referință", + "Referință (Stripe id, transfer)": "Referință (Stripe id, transfer)", + "Referință (chitanță / tranzacție)": "Referință (chitanță / tranzacție)", + "Regenerează": "Перегенерировать", + "Register": "Регистрация", + "Regulă": "Правило", + "Reia": "Продолжить", + "Reminder ITP / revizie": "Reminder ITP / revizie", + "Reminder service auto": "Reminder service auto", + "Renunță": "Отменить", + "Reordonează": "Изменить порядок", + "Reparație": "Ремонт", + "Reparații": "Ремонты", + "Rerating": "Rerating", + "Reset": "Сбросить", + "Reset parolă": "Reset parolă", + "Resetează": "Сбросить", + "Resetează 2FA": "Сбросить 2FA", + "Resetează filtre": "Сбросить фильтры", + "Resetează parola": "Сбросить пароль", + "Respinge": "Отклонить", + "Respins": "Отклонено", + "Respins de": "Отклонено", + "Responsabil": "Ответственный", + "Rest de plată": "Осталось к оплате", + "Restanță": "Restanță", + "Restaurează": "Восстановить", + "Restituire": "Restituire", + "Returnat": "Returnat", + "Revizie": "ТО", + "Revizii": "ТО", + "Rezervat": "Rezervat", + "Rezultat": "Результат", + "Rezultate": "Результаты", + "Rezumat": "Резюме", + "Reîmprospătează": "Обновить", + "Ridicat": "Высокий", + "Rol": "Роль", + "Rol primar": "Основная роль", + "Rolul principal — sincronizat automat cu drepturile RBAC.": "Rolul principal — sincronizat automat cu drepturile RBAC.", + "Roluri suplimentare": "Доп. роли", + "Roluri suplimentare peste rolul primar — drepturile se cumulează.": "Roluri suplimentare peste rolul primar — drepturile se cumulează.", + "Rolurile sistem (owner/admin/etc.) au numele blocat": "Rolurile sistem (owner/admin/etc.) au numele blocat", + "Rotire": "Поворот", + "Rulează `php artisan push:vapid` și adaugă cheile în .env.": "Rulează `php artisan push:vapid` și adaugă cheile în .env.", + "Rând": "Строка", + "Rânduri": "Строки", + "Rânduri per pagină": "Строк на странице", + "Rămas": "Rămas", + "Salarii": "Зарплаты", + "Salariu & comisioane": "Salariu & comisioane", + "Salariu & marjă": "Зарплата и маржа", + "Salariu bază": "Salariu bază", + "Salut": "Привет", + "Salvează": "Сохранить", + "Salvează schimbările": "Сохранить изменения", + "Salvează și adaugă altul": "Сохранить и добавить другой", + "Salvează și continuă": "Сохранить и продолжить", + "Salvează și închide": "Сохранить и закрыть", + "Sarcini": "Задачи", + "Sarcini restante": "Просроченные задачи", + "Sarcină": "Задача", + "Save": "Сохранить", + "Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.": "Scade din stoc piesele legate de catalog (FIFO) și marchează comanda confirmată.", + "Scadent": "Scadent", + "Scadent la": "Scadent la", + "Scadență": "Срок оплаты", + "Scaner cod QR / Bare": "Scaner cod QR / Bare", + "Scoate din magazin": "Scoate din magazin", + "Scăzut": "Низкий", + "Se completează automat când clientul scrie la bot": "Se completează automat când clientul scrie la bot", + "Se procesează...": "Обработка...", + "Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.": "Se vor crea batch-uri pentru toate restanțele rămase în depozitul țintă.", + "Search": "Поиск", + "Secret": "Secret", + "Secret key": "Secret key", + "Secret semnătură": "Secret semnătură", + "Secunde": "Секунды", + "Securitate": "Безопасность", + "Selectat": "Выбрано", + "Selectate": "Выбранные", + "Selectează": "Выбрать", + "Selectează dată": "Выберите дату", + "Selectează fișier": "Выберите файл", + "Selectează furnizorul": "Selectează furnizorul", + "Selectează interval": "Выберите интервал", + "Selectează oră": "Выберите время", + "Selectează perioadă": "Выберите период", + "Semnat": "Подписано", + "Semnat de": "Подписано", + "Semnează": "Подписать", + "Semnătură": "Подпись", + "Semnătură digitală": "Цифровая подпись", + "Senzori TPMS": "Senzori TPMS", + "Septembrie": "Сентябрь", + "Serie factură": "Серия счёта", + "Service ID": "Service ID", + "Servicii": "Услуги", + "Servicii & tarif": "Услуги и тариф", + "Servicii oferite (separate prin virgulă)": "Servicii oferite (separate prin virgulă)", + "Servicii terțe": "Сторонние услуги", + "Sesiuni": "Сессии", + "Set eliberat din depozit": "Set eliberat din depozit", + "Set primit în depozit": "Set primit în depozit", + "Setare implicită": "По умолчанию", + "Setat manual dacă markup = 0.": "Setat manual dacă markup = 0.", + "Setează webhook": "Setează webhook", + "Settings": "Настройки", + "Seturi anvelope": "Шины (комплекты)", + "Setări companie": "Настройки компании", + "Setări plăți salvate": "Setări plăți salvate", + "Setări salvate": "Setări salvate", + "Setările au fost salvate. Hai să adăugăm primul client.": "Setările au fost salvate. Hai să adăugăm primul client.", + "Sezon": "Sezon", + "Sfat": "Совет", + "Sign in": "Войти", + "Sign up": "Регистрация", + "Sincronizat": "Синхронизировано", + "Sincronizează": "Синхронизировать", + "Sistem": "Sistem", + "Site PSauto": "Сайт", + "Slug": "Slug", + "Sold": "Баланс", + "Spate-Dreapta": "Spate-Dreapta", + "Spate-Stânga": "Spate-Stânga", + "Spațiu de lucru adăugat": "Spațiu de lucru adăugat", + "Specialitate": "Specialitate", + "Specializare": "Specializare", + "Specificații": "Specificații", + "Standard": "Стандартно", + "Stare": "Stare", + "Status": "Статус", + "Status dosar": "Status dosar", + "Status implicit": "Статус по умолчанию", + "Stoc": "Остаток", + "Stoc adăugat": "Stoc adăugat", + "Stoc minim": "Stoc minim", + "Storage (MB)": "Storage (MB)", + "Subcontractare": "Субподряд", + "Subcontractor": "Субподрядчик", + "Subcontractori": "Субподрядчики", + "Subdomeniul: .service.mir.md": "Subdomeniul: .service.mir.md", + "Subiect": "Тема", + "Subiect (email)": "Subiect (email)", + "Subiect și data sunt obligatorii": "Subiect și data sunt obligatorii", + "Subtotal": "Промежуточный итог", + "Succes": "Успех", + "Sumă": "Сумма", + "Sunet": "Звук", + "Sunt de acord": "Я согласен", + "Sunteți sigur?": "Вы уверены?", + "Sursă": "Источник", + "Sursă & Atribuire": "Sursă & Atribuire", + "Suspendat": "Приостановлено", + "Suspendă": "Suspendă", + "Suspensie": "Подвеска", + "Sâmbătă": "Суббота", + "Săpt. lucrătoare": "Раб. дни", + "Săptămâna curentă": "Текущая неделя", + "Săptămâna trecută": "Прошлая неделя", + "Săptămâni": "Недели", + "TPMS": "TPMS", + "TPMS & foto": "TPMS & foto", + "TVA": "НДС", + "Tarif normo-oră": "Тариф нормо-часа", + "Tarif normo-oră (poți schimba oricând)": "Tarif normo-oră (poți schimba oricând)", + "Tarif orar (MDL)": "Часовой тариф (MDL)", + "Tarif/h": "Tarif/h", + "Tarif/oră": "Tarif/oră", + "Tarifare": "Tarifare", + "Taxă depozitare": "Taxă depozitare", + "Taxă livrare": "Taxă livrare", + "Tehnice": "Tehnice", + "Tehnicieni": "Техники", + "Telefon": "Телефон", + "Telefon alternativ": "Telefon alternativ", + "Telefon principal": "Telefon principal", + "Telefonie": "Телефония", + "Telegram bot": "Telegram bot", + "Telegram chat ID": "Telegram chat ID", + "Telegram va trimite mesajele primite la URL-ul webhook de mai jos.": "Telegram va trimite mesajele primite la URL-ul webhook de mai jos.", + "Tenant activat.": "Tenant activat.", + "Tenant demo": "Tenant demo", + "Tenant not resolved": "Tenant not resolved", + "Tenant suspendat.": "Tenant suspendat.", + "Tenants recenți": "Tenants recenți", + "Termen": "Срок", + "Termen limită": "Крайний срок", + "Termene": "Termene", + "Termeni": "Условия", + "Termeni de plată": "Termeni de plată", + "Termeni plată": "Termeni plată", + "Termeni și condiții": "Условия и положения", + "Termină": "Завершить", + "Terț": "Terț", + "Test notificare push": "Test notificare push", + "Testează bot Telegram": "Testează bot Telegram", + "Timp lucrat": "Отработано", + "Timp mediu": "Среднее время", + "Timp total": "Общее время", + "Tinichigerie": "Кузовной цех", + "Tinichigerie / Detailing": "Кузов / Детейлинг", + "Tip": "Тип", + "Tip daună": "Tip daună", + "Tip implicit": "Тип по умолчанию", + "Tip jante": "Tip jante", + "Tipărește": "Печать", + "Tipărește etichete QR": "Tipărește etichete QR", + "Toate atelierele": "Все мастерские", + "Toate autovehiculele": "Все авто", + "Toate categoriile": "Все категории", + "Toate cererile": "Все заявки", + "Toate cheltuielile": "Все расходы", + "Toate comenzile": "Все заказы", + "Toate companiile": "Toate companiile", + "Toate câmpurile": "Все поля", + "Toate limbile": "Все языки", + "Toate plățile": "Все платежи", + "Toate produsele": "Все товары", + "Toate rolurile": "Все роли", + "Token generat!": "Token generat!", + "Token public": "Token public", + "Token revocat": "Token revocat", + "Total": "Итого", + "Total general": "Общий итог", + "Total net": "Total net", + "Total plătit": "Всего оплачено", + "Total rezultate": "Всего результатов", + "Total în baza de date": "Total în baza de date", + "Total înregistrate": "Total înregistrate", + "Toți clienții": "Все клиенты", + "Toți utilizatorii": "Все пользователи", + "Tracking & ETA": "Отслеживание и ETA", + "Trage fișierul aici": "Перетащите файл сюда", + "Trial expiră la": "Trial expiră la", + "Trimis": "Отправлено", + "Trimite": "Отправить", + "Trimite SMS": "Отправить SMS", + "Trimite email": "Отправить email", + "Trimite emailul standard de resetare a parolei către clientul magazinului.": "Trimite emailul standard de resetare a parolei către clientul magazinului.", + "Trimite factură": "Отправить счёт", + "Trimite invitație": "Отправить приглашение", + "Trimite link resetare": "Отправить ссылку сброса", + "Trimite mesaj": "Отправить сообщение", + "Trimite notificare": "Отправить уведомление", + "Trimite reminder după X zile fără vizită": "Trimite reminder după X zile fără vizită", + "Trimite reset parolă": "Trimite reset parolă", + "UM": "Ед.", + "Ulei": "Масло", + "Ultim login": "Ultim login", + "Ultima actualizare": "Последнее обновление", + "Ultima autentificare": "Последний вход", + "Ultima modificare": "Последнее изменение", + "Ultima sincronizare": "Последняя синхронизация", + "Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).": "Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).", + "Upgrade": "Обновление", + "Urgent": "Срочный", + "Urgență": "Срочность", + "User API": "User API", + "Useri": "Useri", + "Utilizator": "Пользователь", + "Utilizatori": "Пользователи", + "Uzură (mm) per poziție": "Uzură (mm) per poziție", + "Uzură min": "Uzură min", + "VIN": "VIN", + "VIN căutare & piese": "VIN căutare & piese", + "VIN-căutare": "VIN-поиск", + "Va deconecta utilizatorul pe toate device-urile.": "Va deconecta utilizatorul pe toate device-urile.", + "Va fi pre-completat când creezi o programare pentru acest pod": "Va fi pre-completat când creezi o programare pentru acest pod", + "Va recalcula sell_price pentru TOATE piesele active. Continui?": "Va recalcula sell_price pentru TOATE piesele active. Continui?", + "Val.": "Val.", + "Valoare": "Valoare", + "Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}": "Variabile disponibile: {name}, {car}, {date}, {time}, {amount}, {service}, {mileage}", + "Vehicul": "Транспорт", + "Vehicul existent": "Существующий транспорт", + "Vehicul nou": "Новый транспорт", + "Venit": "Доход", + "Venit generat": "Venit generat", + "Venit manopere": "Venit manopere", + "Venituri": "Доходы", + "Verificare": "Проверка", + "Verificat": "Проверено", + "Verifică emailul": "Проверьте email", + "Vezi imagine": "Просмотр изображения", + "Vezi mai mult": "Смотреть больше", + "Vezi tot": "Смотреть все", + "Viitoare": "Viitoare", + "Vineri": "Пятница", + "Vizibil": "Видимо", + "Vizual": "Визуально", + "Vizualizează": "Просмотр", + "Vizualizări": "Просмотры", + "Vârstă max (ani)": "Макс. возраст (лет)", + "Vârstă min (ani)": "Мин. возраст (лет)", + "Web Push neconfigurat": "Web Push neconfigurat", + "Webhook": "Webhook", + "Webhook (Notify URL)": "Webhook (Notify URL)", + "Webhook eșuat": "Webhook eșuat", + "Webhook secret": "Webhook secret", + "Webhook setat — botul e gata": "Webhook setat — botul e gata", + "Weekend": "Выходные", + "Welcome": "Добро пожаловать", + "Widget": "Виджет", + "XLS": "XLS", + "Yes": "Да", + "ZIP": "ZIP", + "Zile": "Дни", + "Zile libere": "Выходные", + "Zile livrare": "Zile livrare", + "Zile lucrate": "Отработанные дни", + "Zonă": "Zonă", + "Zoom in": "Приблизить", + "Zoom out": "Отдалить", + "achiziție": "achiziție", + "achiziții": "achiziții", + "angajați": "angajați", + "apasă": "нажмите", + "auto-generat": "auto-generat", + "bonusuri/avansuri": "bonusuri/avansuri", + "canale marketing": "canale marketing", + "cheltuială": "cheltuială", + "client magazin": "client magazin", + "clienți": "clienți", + "clienți magazin": "clienți magazin", + "coeficient": "коэффициент", + "coeficienți preț": "coeficienți preț", + "comandă": "comandă", + "comenzi online": "comenzi online", + "deal-uri": "deal-uri", + "ex: Iarnă Michelin": "ex: Iarnă Michelin", + "ex: Mașină veche, Client VIP, Express": "ex: Mașină veche, Client VIP, Express", + "ex: Revizie completă 15.000 km": "ex: Revizie completă 15.000 km", + "ex: app-mobil, integrare-erp": "ex: app-mobil, integrare-erp", + "factură": "factură", + "fișe lucru": "fișe lucru", + "fișă": "fișă", + "lucrare caroserie": "lucrare caroserie", + "lucrare terți": "lucrare terți", + "lucrări caroserie": "lucrări caroserie", + "lucrări terți": "lucrări terți", + "mașini": "mașini", + "mașină": "mașină", + "niciodată": "niciodată", + "norme-ore": "norme-ore", + "normă": "normă", + "null = fără trial": "null = fără trial", + "pentru a alege": "чтобы выбрать", + "piesă": "piesă", + "pk_test_...": "pk_test_...", + "plată": "plată", + "plăți": "plăți", + "portal.common.currency_mdl": "portal.common.currency_mdl", + "portal.common.email": "portal.common.email", + "portal.common.name": "portal.common.name", + "portal.common.password": "portal.common.password", + "portal.common.powered_by": "portal.common.powered_by", + "portal.common.search": "portal.common.search", + "portal.common.total": "portal.common.total", + "portal.invitation.activate_account": "portal.invitation.activate_account", + "portal.invitation.confirm_password": "portal.invitation.confirm_password", + "portal.invitation.expired_body": "portal.invitation.expired_body", + "portal.invitation.expired_title": "portal.invitation.expired_title", + "portal.invitation.invalid_body": "portal.invitation.invalid_body", + "portal.invitation.invalid_title": "portal.invitation.invalid_title", + "portal.invitation.new_password": "portal.invitation.new_password", + "portal.invitation.title_accept": "portal.invitation.title_accept", + "portal.shop.add_to_cart": "portal.shop.add_to_cart", + "portal.shop.address_delivery": "portal.shop.address_delivery", + "portal.shop.all_categories": "portal.shop.all_categories", + "portal.shop.back_to_catalog": "portal.shop.back_to_catalog", + "portal.shop.back_to_login": "portal.shop.back_to_login", + "portal.shop.brand_label": "portal.shop.brand_label", + "portal.shop.cart": "portal.shop.cart", + "portal.shop.cart_empty": "portal.shop.cart_empty", + "portal.shop.cart_title": "portal.shop.cart_title", + "portal.shop.catalog": "portal.shop.catalog", + "portal.shop.catalog_title": "portal.shop.catalog_title", + "portal.shop.checkout_arrow": "portal.shop.checkout_arrow", + "portal.shop.checkout_title": "portal.shop.checkout_title", + "portal.shop.code_label": "portal.shop.code_label", + "portal.shop.contact_data": "portal.shop.contact_data", + "portal.shop.continue_shopping": "portal.shop.continue_shopping", + "portal.shop.cross_refs_title": "portal.shop.cross_refs_title", + "portal.shop.delivery": "portal.shop.delivery", + "portal.shop.delivery_data": "portal.shop.delivery_data", + "portal.shop.delivery_fee_note": "portal.shop.delivery_fee_note", + "portal.shop.delivery_req": "portal.shop.delivery_req", + "portal.shop.description": "portal.shop.description", + "portal.shop.forgot_intro": "portal.shop.forgot_intro", + "portal.shop.forgot_password": "portal.shop.forgot_password", + "portal.shop.forgot_title": "portal.shop.forgot_title", + "portal.shop.full_name": "portal.shop.full_name", + "portal.shop.have_account": "portal.shop.have_account", + "portal.shop.in_stock": "portal.shop.in_stock", + "portal.shop.in_stock_only": "portal.shop.in_stock_only", + "portal.shop.login": "portal.shop.login", + "portal.shop.logout": "portal.shop.logout", + "portal.shop.my_account": "portal.shop.my_account", + "portal.shop.no_account": "portal.shop.no_account", + "portal.shop.no_orders_yet": "portal.shop.no_orders_yet", + "portal.shop.no_parts_found": "portal.shop.no_parts_found", + "portal.shop.notes": "portal.shop.notes", + "portal.shop.on_order": "portal.shop.on_order", + "portal.shop.order_date": "portal.shop.order_date", + "portal.shop.order_details_arrow": "portal.shop.order_details_arrow", + "portal.shop.order_items": "portal.shop.order_items", + "portal.shop.order_nr": "portal.shop.order_nr", + "portal.shop.order_number": "portal.shop.order_number", + "portal.shop.part": "portal.shop.part", + "portal.shop.phone_req": "portal.shop.phone_req", + "portal.shop.place_order": "portal.shop.place_order", + "portal.shop.price": "portal.shop.price", + "portal.shop.products": "portal.shop.products", + "portal.shop.qty": "portal.shop.qty", + "portal.shop.register": "portal.shop.register", + "portal.shop.register_button": "portal.shop.register_button", + "portal.shop.register_title": "portal.shop.register_title", + "portal.shop.reset_button": "portal.shop.reset_button", + "portal.shop.reset_title": "portal.shop.reset_title", + "portal.shop.search_by_vin": "portal.shop.search_by_vin", + "portal.shop.search_placeholder": "portal.shop.search_placeholder", + "portal.shop.send_reset_link": "portal.shop.send_reset_link", + "portal.shop.signin_button": "portal.shop.signin_button", + "portal.shop.signin_title": "portal.shop.signin_title", + "portal.shop.status": "portal.shop.status", + "portal.shop.subtotal": "portal.shop.subtotal", + "portal.shop.summary": "portal.shop.summary", + "portal.shop.this_car": "portal.shop.this_car", + "portal.shop.update_cart": "portal.shop.update_cart", + "portal.shop.view_catalog": "portal.shop.view_catalog", + "portal.shop.vin_contact_note": "portal.shop.vin_contact_note", + "portal.shop.vin_country": "portal.shop.vin_country", + "portal.shop.vin_decode": "portal.shop.vin_decode", + "portal.shop.vin_identified": "portal.shop.vin_identified", + "portal.shop.vin_intro": "portal.shop.vin_intro", + "portal.shop.vin_invalid": "portal.shop.vin_invalid", + "portal.shop.vin_manufacturer": "portal.shop.vin_manufacturer", + "portal.shop.vin_region": "portal.shop.vin_region", + "portal.shop.vin_title": "portal.shop.vin_title", + "portal.shop.vin_year": "portal.shop.vin_year", + "portal.tracking.approval_needed_body": "portal.tracking.approval_needed_body", + "portal.tracking.approval_needed_title": "portal.tracking.approval_needed_title", + "portal.tracking.approve": "portal.tracking.approve", + "portal.tracking.auto": "portal.tracking.auto", + "portal.tracking.decline": "portal.tracking.decline", + "portal.tracking.details": "portal.tracking.details", + "portal.tracking.hours": "portal.tracking.hours", + "portal.tracking.master": "portal.tracking.master", + "portal.tracking.mileage": "portal.tracking.mileage", + "portal.tracking.opened": "portal.tracking.opened", + "portal.tracking.photos": "portal.tracking.photos", + "portal.tracking.ready_estimated": "portal.tracking.ready_estimated", + "portal.tracking.recommendations": "portal.tracking.recommendations", + "portal.tracking.stages": "portal.tracking.stages", + "portal.tracking.unit_pcs": "portal.tracking.unit_pcs", + "portal.tracking.what_asked": "portal.tracking.what_asked", + "posturi de lucru": "posturi de lucru", + "programări": "programări", + "reguli markup": "reguli markup", + "regulă": "regulă", + "sau": "или", + "set anvelope": "set anvelope", + "seturi anvelope": "seturi anvelope", + "subcontractor": "субподрядчик", + "subcontractori": "subcontractori", + "template-uri": "template-uri", + "Împărtășește": "Поделиться", + "În curs": "В процессе", + "În depozit": "На складе", + "În pipeline": "В воронке", + "Înainte": "Назад", + "Încarcă fișier": "Загрузить файл", + "Încarcă fișierul Excel sau CSV": "Încarcă fișierul Excel sau CSV", + "Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.": "Încarcă o poză cu factura. AI-ul extrage furnizorul, data și liniile. Verifici și salvezi.", + "Începe": "Начать", + "Închide": "Закрыть", + "Închide fișa": "Закрыть наряд", + "Închis": "Закрыт", + "Închis la": "Закрыт", + "Încărcare STO": "Загрузка СТО", + "Încărcare service": "Загрузка сервиса", + "Înregistrare": "Регистрация", + "Înregistrat": "Зарегистрирован", + "Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.": "Înregistrează lucrări de tinichigerie, vopsitorie, PDR, detailing, ceramică, PPF sau polish. Hartă daune, dosar asigurare și arhivă foto înainte/după.", + "Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.": "Înregistrează lucrările trimise la ateliere externe (turbo, cutii, vopsitorie). Costul terțului + markup intră automat în totalul fișei asociate.", + "Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.": "Înregistrează seturile de anvelope ale clienților și gestionează depozitarea sezonieră (tire hotel). Urmărește uzura, TPMS și locația în depozit.", + "Șabloane servicii": "Шаблоны услуг", + "Șablon aplicat: {$r['labor']} manopere, {$r['parts']} piese": "Șablon aplicat: {$r['labor']} manopere, {$r['parts']} piese", + "Șablon serviciu": "Шаблон услуги", + "Șterge imagine": "Удалить изображение", + "Șterge produs": "Удалить товар", + "Șterge rând": "Удалить строку", + "Șterge selectate": "Удалить выбранные", + "Șterge tot": "Удалить все", + "Șters": "Удалено", + "Șters de": "Удалено", + "Șters la": "Удалено", + "șabloane servicii": "șabloane servicii", + "șablon": "șablon", + "Ține-mă minte": "Запомнить меня", + "✓ Pe abonament plătit": "✓ Pe abonament plătit", + "🅿️ PayPal": "🅿️ PayPal", + "🇲🇩 Paynet (Moldova)": "🇲🇩 Paynet (Moldova)", + "🎉 Bun venit în AutoCRM!": "🎉 Bun venit în AutoCRM!", + "🎉 Toate facturile sunt plătite": "🎉 Toate facturile sunt plătite", + "🏦 Transfer bancar (manual)": "🏦 Transfer bancar (manual)", + "💳 Stripe": "💳 Stripe" +} \ No newline at end of file