Files
landing-api/app/Filament/Support/TranslationStatusColumn.php
Josep Piguillem 3897113313 Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:55:27 +02:00

91 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Support;
use App\Support\TranslationFreshness;
use Filament\Tables\Columns\TextColumn;
use Illuminate\Database\Eloquent\Model;
/**
* Shows at a glance which languages a record has been translated into.
*
* Each supported locale renders as a badge — solid when a translation exists,
* muted when it is still missing — so an editor can spot untranslated content
* without opening every record.
*
* A third state matters as much as the first two and is invisible everywhere
* else: a translation that exists and has fallen behind the language it was
* made from. Nothing else in the panel would ever mention it, because by every
* other measure that record is finished.
*/
class TranslationStatusColumn extends TextColumn
{
/**
* The translatable attribute whose presence stands in for "translated".
* Defaults to the model's first translatable attribute.
*/
protected ?string $trackedAttribute = null;
protected function setUp(): void
{
parent::setUp();
$this->badge();
$this->sortable(false);
$this->searchable(false);
$this->state(function (Model $record): array {
$attribute = $this->resolveTrackedAttribute($record);
$translations = $record->getTranslations($attribute);
$stale = TranslationFreshness::for($record);
$states = [];
foreach (array_keys(config('locales.supported')) as $locale) {
if (blank($translations[$locale] ?? null)) {
$states[] = strtolower($locale);
continue;
}
// Translated, but the language it was translated from has
// moved on since. The badge has to say something other than
// "done", because as far as everything else is concerned this
// record is finished.
$states[] = isset($stale[$locale])
? strtoupper($locale).'!'
: strtoupper($locale);
}
return $states;
});
/*
* Uppercase = translated, lowercase = missing, and a "!" for a
* translation the source has since moved past. Colour carries the same
* three states for anyone who does not notice the casing.
*/
$this->color(fn (string $state): string => match (true) {
str_ends_with($state, '!') => 'warning',
$state === strtoupper($state) => 'success',
default => 'gray',
});
}
public function trackedAttribute(string $attribute): static
{
$this->trackedAttribute = $attribute;
return $this;
}
protected function resolveTrackedAttribute(Model $record): string
{
return $this->trackedAttribute
?? $record->getTranslatableAttributes()[0]
?? 'title';
}
}