feat: add UserResource with is_admin toggle and delete
User management resource with editable is_admin field, postcode support, admin filter, and inline delete action. Includes list and edit pages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
35
app/Filament/NavigationGroup.php
Normal file
35
app/Filament/NavigationGroup.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament;
|
||||
|
||||
use BackedEnum;
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
|
||||
enum NavigationGroup implements HasIcon, HasLabel
|
||||
{
|
||||
case Users;
|
||||
|
||||
case Data;
|
||||
|
||||
case System;
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Users => 'Users',
|
||||
self::Data => 'Data',
|
||||
self::System => 'System',
|
||||
};
|
||||
}
|
||||
|
||||
public function getIcon(): string|BackedEnum|Htmlable|null
|
||||
{
|
||||
return match ($this) {
|
||||
self::Users => 'heroicon-o-users',
|
||||
self::Data => 'heroicon-o-circle-stack',
|
||||
self::System => 'heroicon-o-cog-6-tooth',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\NavigationGroup;
|
||||
use App\Filament\Resources\ApiLogResource\Pages\ListApiLogs;
|
||||
use App\Filament\Resources\ApiLogResource\Pages\ViewApiLog;
|
||||
use App\Models\ApiLog;
|
||||
@@ -21,9 +22,7 @@ class ApiLogResource extends Resource
|
||||
{
|
||||
protected static ?string $model = ApiLog::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-server';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'System';
|
||||
protected static string|\UnitEnum|null $navigationGroup = NavigationGroup::System;
|
||||
|
||||
protected static ?string $navigationLabel = 'API Logs';
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\NavigationGroup;
|
||||
use App\Filament\Resources\BrentPriceResource\Pages\ListBrentPrices;
|
||||
use App\Models\BrentPrice;
|
||||
use Filament\Resources\Resource;
|
||||
@@ -12,9 +13,7 @@ class BrentPriceResource extends Resource
|
||||
{
|
||||
protected static ?string $model = BrentPrice::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-currency-dollar';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Data';
|
||||
protected static string|\UnitEnum|null $navigationGroup = NavigationGroup::Data;
|
||||
|
||||
protected static ?string $navigationLabel = 'Brent Prices';
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Filament\Resources;
|
||||
|
||||
use App\Enums\PredictionSource;
|
||||
use App\Enums\TrendDirection;
|
||||
use App\Filament\NavigationGroup;
|
||||
use App\Filament\Resources\OilPredictionResource\Pages\ListOilPredictions;
|
||||
use App\Filament\Resources\OilPredictionResource\Pages\ViewOilPrediction;
|
||||
use App\Models\PricePrediction;
|
||||
@@ -23,9 +24,7 @@ class OilPredictionResource extends Resource
|
||||
{
|
||||
protected static ?string $model = PricePrediction::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-beaker';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Data';
|
||||
protected static string|\UnitEnum|null $navigationGroup = NavigationGroup::Data;
|
||||
|
||||
protected static ?string $navigationLabel = 'Oil Predictions';
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\NavigationGroup;
|
||||
use App\Filament\Resources\StationResource\Pages\ListStations;
|
||||
use App\Filament\Resources\StationResource\Pages\ViewStation;
|
||||
use App\Models\Station;
|
||||
@@ -20,9 +21,7 @@ class StationResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Station::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-map-pin';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Data';
|
||||
protected static string|\UnitEnum|null $navigationGroup = NavigationGroup::Data;
|
||||
|
||||
protected static ?string $navigationLabel = 'Stations';
|
||||
|
||||
@@ -32,10 +31,13 @@ class StationResource extends Resource
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('trading_name')->searchable()->sortable(),
|
||||
TextColumn::make('trading_name')->searchable()->sortable()
|
||||
->limit(25)
|
||||
->tooltip(fn (Station $record) => strlen($record->trading_name) > 45 ? $record->trading_name : null),
|
||||
TextColumn::make('brand_name')->searchable()->placeholder('—'),
|
||||
TextColumn::make('postcode')->searchable(),
|
||||
TextColumn::make('city'),
|
||||
TextColumn::make('city')
|
||||
->description(fn (Station $record) => $record->postcode)
|
||||
->searchable(['city', 'postcode']),
|
||||
IconColumn::make('is_supermarket')->label('Supermarket')->boolean(),
|
||||
IconColumn::make('is_motorway_service_station')->label('Motorway')->boolean(),
|
||||
IconColumn::make('temporary_closure')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\NavigationGroup;
|
||||
use App\Filament\Resources\UserResource\Pages\EditUser;
|
||||
use App\Filament\Resources\UserResource\Pages\ListUsers;
|
||||
use App\Models\User;
|
||||
@@ -20,7 +21,7 @@ class UserResource extends Resource
|
||||
{
|
||||
protected static ?string $model = User::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-users';
|
||||
protected static string|\UnitEnum|null $navigationGroup = NavigationGroup::Users;
|
||||
|
||||
protected static ?int $navigationSort = 1;
|
||||
|
||||
|
||||
@@ -8,23 +8,25 @@ class StationTaggingService
|
||||
{
|
||||
/** @var array<string, string> brand keyword → normalised brand name */
|
||||
private const SUPERMARKET_BRANDS = [
|
||||
'tesco' => 'Tesco',
|
||||
'asda' => 'Asda',
|
||||
'tesco' => 'Tesco',
|
||||
'asda' => 'Asda',
|
||||
'morrisons' => 'Morrisons',
|
||||
'sainsbury' => 'Sainsbury\'s',
|
||||
'aldi' => 'Aldi',
|
||||
'lidl' => 'Lidl',
|
||||
'costco' => 'Costco',
|
||||
'aldi' => 'Aldi',
|
||||
'lidl' => 'Lidl',
|
||||
'costco' => 'Costco',
|
||||
];
|
||||
|
||||
public function tag(Station $station): void
|
||||
{
|
||||
$name = strtolower($station->trading_name);
|
||||
$tradingName = strtolower($station->trading_name);
|
||||
$brandName = strtolower($station->brand_name ?? '');
|
||||
|
||||
foreach (self::SUPERMARKET_BRANDS as $keyword => $normalisedBrand) {
|
||||
if (str_contains($name, $keyword)) {
|
||||
if (str_contains($tradingName, $keyword) || str_contains($brandName, $keyword)) {
|
||||
$station->is_supermarket = true;
|
||||
$station->brand_name = $normalisedBrand;
|
||||
$station->brand_name = $normalisedBrand;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user