78ff8d4b43
User screenshots showed the tenant admin panel (Filament) had sidebar
labels stuck in Romanian even when switching to Russian: 'Cereri',
'Calendar vizual', 'Atelierul meu', 'KPI mecanici', 'Fișe lucru',
'Norme-ore', 'Tehnicieni', 'Șabloane servicii', 'Depozite', 'Scaner',
'Depozit', 'VIN-căutare', 'Furnizori', 'Achiziții', 'Procentaj',
'Coeficienți preț', plus all group headers.
Root cause: every Filament Resource and Page had static properties
'protected static ?string $navigationLabel = "Fișe lucru"' — string
literals baked into class definitions. Static properties don't run
through the translation layer.
Fix in two parts:
1. New translation files with 52 label keys + 12 group keys:
- lang/ro/nav.php — Romanian (identity)
- lang/ru/nav.php — full Russian translations (Заказ-наряды,
Автомобили, Клиенты, Календарь, Моя мастерская, Механики KPI,
Настройки, etc.)
- lang/en/nav.php — English translations (Work orders, Vehicles,
Clients, Calendar, My workshop, Mechanic KPI, Settings, etc.)
Keyed by the Romanian original so lookups map 1:1 —
'nav.label.Fișe lucru' returns 'Заказ-наряды' in RU, 'Work orders'
in EN, 'Fișe lucru' in RO.
2. Python transformer converted 54 files:
- 33 Filament Tenant Resources
- 15 Filament Tenant Pages
- 4 Filament Central Resources
- 1 Filament Central Page
- 1 Widget
Each 'protected static ?string $navigationLabel = "X";' became
'public static function getNavigationLabel(): string { return
__("nav.label.X"); }'. Same treatment for $navigationGroup.
Cleanup: 6 resources already had manually-added getNavigationLabel
methods from an earlier partial effort — those used flat JSON keys
(__("Cereri")) that never resolved. Deduped so only the nav.label.*
version remains.
Untouched (intentional):
- $modelLabel / $pluralModelLabel (used in breadcrumbs and headings —
still hardcoded, next tier of work)
- Section titles, column headers, form field labels (medium priority)
- $navigationSort (numeric, no translation needed)
- $navigationIcon (icon reference)
Suite: 306 passed (853 assertions). Unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
101 lines
3.5 KiB
PHP
101 lines
3.5 KiB
PHP
<?php
|
||
|
||
namespace App\Filament\Tenant\Pages;
|
||
|
||
use App\Models\Tenant\Appointment;
|
||
use App\Models\Tenant\Post;
|
||
use Carbon\Carbon;
|
||
use Filament\Pages\Page;
|
||
|
||
class Workload extends Page
|
||
{
|
||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-fire';
|
||
|
||
public static function getNavigationLabel(): string
|
||
{
|
||
return __('nav.label.Încărcare STO');
|
||
}
|
||
|
||
public static function getNavigationGroup(): ?string
|
||
{
|
||
return __('nav.group.Analiză');
|
||
}
|
||
|
||
protected static ?int $navigationSort = 73;
|
||
|
||
protected static ?string $title = 'Încărcare service';
|
||
|
||
protected string $view = 'filament.tenant.pages.workload';
|
||
|
||
public string $weekStart;
|
||
|
||
public function mount(): void
|
||
{
|
||
$this->weekStart = Carbon::now()->startOfWeek()->toDateString();
|
||
}
|
||
|
||
public function setWeek(int $delta): void
|
||
{
|
||
$this->weekStart = Carbon::parse($this->weekStart)->addWeeks($delta)->toDateString();
|
||
}
|
||
|
||
public function data(): array
|
||
{
|
||
$start = Carbon::parse($this->weekStart);
|
||
$end = (clone $start)->addDays(6);
|
||
|
||
$posts = Post::where('is_active', true)->orderBy('sort_order')->get();
|
||
$days = collect(range(0, 6))->map(fn ($i) => $start->copy()->addDays($i));
|
||
|
||
// Appointments grouped by post + day.
|
||
$appts = Appointment::whereBetween('date', [$start, $end])
|
||
->whereNotIn('status', ['cancelled', 'no_show'])
|
||
->get()
|
||
->groupBy(fn ($a) => $a->post_id . '|' . $a->date->format('Y-m-d'));
|
||
|
||
// Compute total hours per cell.
|
||
$matrix = [];
|
||
foreach ($posts as $post) {
|
||
$matrix[$post->id] = ['post' => $post, 'days' => []];
|
||
foreach ($days as $d) {
|
||
$key = $post->id . '|' . $d->format('Y-m-d');
|
||
$items = $appts->get($key, collect());
|
||
$hours = $items->sum(function ($a) {
|
||
$start = strtotime("1970-01-01 {$a->time_start}");
|
||
$end = strtotime("1970-01-01 {$a->time_end}");
|
||
return max(0, ($end - $start) / 3600);
|
||
});
|
||
$matrix[$post->id]['days'][$d->format('Y-m-d')] = [
|
||
'date' => $d,
|
||
'hours' => $hours,
|
||
'count' => $items->count(),
|
||
];
|
||
}
|
||
}
|
||
|
||
// Same for appointments without post (drop them on a virtual "—" row).
|
||
$unposted = $appts->keys()->filter(fn ($k) => str_starts_with($k, '|'));
|
||
if ($unposted->isNotEmpty()) {
|
||
$matrix[0] = ['post' => (object) ['id' => 0, 'name' => '— fără pod —', 'color' => '#9ca3af'], 'days' => []];
|
||
foreach ($days as $d) {
|
||
$key = '|' . $d->format('Y-m-d');
|
||
$items = $appts->get($key, collect());
|
||
$hours = $items->sum(function ($a) {
|
||
$start = strtotime("1970-01-01 {$a->time_start}");
|
||
$end = strtotime("1970-01-01 {$a->time_end}");
|
||
return max(0, ($end - $start) / 3600);
|
||
});
|
||
$matrix[0]['days'][$d->format('Y-m-d')] = ['date' => $d, 'hours' => $hours, 'count' => $items->count()];
|
||
}
|
||
}
|
||
|
||
return [
|
||
'matrix' => $matrix,
|
||
'days' => $days,
|
||
'weekLabel' => $start->format('d.m') . ' – ' . $end->format('d.m.Y'),
|
||
// Working day = 10h. Color scale: 0..10h → light to red.
|
||
'capacity' => 10,
|
||
];
|
||
}
|
||
}
|