Stage 11 — Tire Service: tire hotel + wheel sets

Schema:
- tire_sets (client/vehicle, season, size width/profile/diameter, brand/DOT,
  rims, tread JSON per position + tread_min cache, TPMS + sensor ids, photos)
- tire_storage (location, season_label, stored/retrieved, check-in/out, fee)

Models:
- TireSet (HasMedia): sizeLabel, isStored, currentStorage, auto tread_min
- TireStorage: durationDays, isActive

Filament (new "Anvelope" nav group):
- TireSetResource: specs form + per-position tread + TPMS + photo upload;
  table with size, season badge, min tread (red < 3mm), storage status
- Check-in (location + period + fee → stored) / Check-out (→ retrieved)
- StorageRelationManager (stay history); nav badge = sets currently stored

Tests (6 new):
- sizeLabel formatting; tread_min from positions; check-in active storage;
  check-out retrieved + duration; multiple stays per set; tenant isolation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 06:33:00 +00:00
parent a1be01b0d5
commit 94938f24d7
9 changed files with 602 additions and 0 deletions
@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Tenant\Resources\TireSetResource\Pages;
use App\Filament\Tenant\Resources\TireSetResource;
use Filament\Resources\Pages\CreateRecord;
class CreateTireSet extends CreateRecord
{
protected static string $resource = TireSetResource::class;
}
@@ -0,0 +1,17 @@
<?php
namespace App\Filament\Tenant\Resources\TireSetResource\Pages;
use App\Filament\Tenant\Resources\TireSetResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditTireSet extends EditRecord
{
protected static string $resource = TireSetResource::class;
protected function getHeaderActions(): array
{
return [Actions\DeleteAction::make()];
}
}
@@ -0,0 +1,17 @@
<?php
namespace App\Filament\Tenant\Resources\TireSetResource\Pages;
use App\Filament\Tenant\Resources\TireSetResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListTireSets extends ListRecords
{
protected static string $resource = TireSetResource::class;
protected function getHeaderActions(): array
{
return [Actions\CreateAction::make()];
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Filament\Tenant\Resources\TireSetResource\RelationManagers;
use App\Models\Tenant\TireStorage;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
class StorageRelationManager extends RelationManager
{
protected static string $relationship = 'storage';
protected static ?string $title = '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('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(),
])
->defaultSort('checked_in_at', 'desc')
->emptyStateHeading('Niciun istoric')
->emptyStateDescription('Folosește „Check-in depozit" pe set pentru a înregistra prima depozitare.');
}
}