Added Notifications

This commit is contained in:
2026-06-14 16:04:01 +10:00
parent e24d3ceaec
commit a753bffaf8
49 changed files with 1118 additions and 381 deletions
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\DTOs;
readonly class MissingLivery
{
public function __construct(
public readonly string $airline_name,
public readonly string $aircraft_display_name,
public readonly string $filename,
public readonly string $clipboard_text,
) {}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Models\IgnoredMissingLivery;
use App\Models\User;
use App\Models\UserFlight;
use App\Services\AdminService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Inertia\Inertia;
class AdminController extends Controller
{
public function __construct(private readonly AdminService $adminService){}
function staticAdminData(){
return [
'missingLiveryCount' => $this->adminService->getMissingLiveries()->count(),
];
}
function dashboard(){
return inertia('Admin/Dashboard', [
'title' => 'Admin Control Panel',
'userCount' => User::count(),
'oneWeekUserGrowth' => User::where('created_at', '>=', Carbon::now()->subWeek())->count(),
'flightCount' => UserFlight::count(),
'oneWeekFlightGrowth' => UserFlight::where('created_at', '>=', Carbon::now()->subWeek())->count(),
'latestUser' => User::latest()->first()->name,
...$this->staticAdminData(),
]);
}
function reconcileMissingLiveries(){
$missingLiveries = $this->adminService->getMissingLiveries();
return Inertia::render('Admin/MissingLiveries', [
'title' => $missingLiveries->count() . ' Missing Liveries',
'missingLiveries' => $missingLiveries,
...$this->staticAdminData(),
]);
}
public function ignoreMissingLivery(Request $request)
{
$validated = $request->validate([
'filename' => 'required|string',
]);
IgnoredMissingLivery::createOrFirst($validated);
return back();
}
}
+40 -25
View File
@@ -126,6 +126,18 @@ class FlightImportController extends Controller
return $airlines;
}
public function showFr24Import()
{
if (Auth::user()->importedFlights()->exists()) {
return to_route('reconcile');
}
return Inertia::render('Fr24Import');
}
// FlightImportController.php
public function reconcile(Request $request)
{
$user = Auth::user();
@@ -133,35 +145,38 @@ class FlightImportController extends Controller
$flightToReconcile = ImportedFlight::where('user_id', $user->id)->orderBy('date', 'asc')->first();
if (!$flightToReconcile) {
return null;
return to_route('import.fr24');
}
$date = null;
if ($flightToReconcile->date) {
$date = Carbon::createFromFormat('Y-m-d', $flightToReconcile->date)->format('Y-m-d');
}
$date = $flightToReconcile->date
? Carbon::createFromFormat('Y-m-d', $flightToReconcile->date)->format('Y-m-d')
: null;
return [
'imported_flight_id' => $flightToReconcile->id,
'flight_classes' => $this->selectOptions(FlightClass::class),
'flight_reasons' => $this->selectOptions(FlightReason::class),
'seat_types' => $this->selectOptions(SeatType::class),
'flight_number' => $flightToReconcile->flight_number ?? '',
'date' => $date ?? '',
'dep_time' => $this->formatTime($flightToReconcile->dep_time),
'arr_time' => $this->formatTime($flightToReconcile->arr_time),
'duration' => $this->formatTime($flightToReconcile->duration),
'registration' => $flightToReconcile->registration ?? '',
'note' => $flightToReconcile->note ?? '',
'flight_class' => $flightToReconcile->flight_class !== null ? (int) $flightToReconcile->flight_class : null,
'seat_type' => $flightToReconcile->seat_type !== null ? (int) $flightToReconcile->seat_type : null,
'flight_reason' => $flightToReconcile->flight_reason !== null ? (int) $flightToReconcile->flight_reason : null,
'airline_options' => $this->getPossibleAirlines($flightToReconcile->airline ?? ''),
'to_options' => $this->getPossibleAirports($flightToReconcile->to ?? ''),
'from_options' => $this->getPossibleAirports($flightToReconcile->from ?? ''),
'aircraft_options' => $this->getPossibleAircraft($flightToReconcile->aircraft ?? ''),
$flight = [
'imported_flight_id' => $flightToReconcile->id,
'flight_classes' => $this->selectOptions(FlightClass::class),
'flight_reasons' => $this->selectOptions(FlightReason::class),
'seat_types' => $this->selectOptions(SeatType::class),
'flight_number' => $flightToReconcile->flight_number ?? '',
'date' => $date ?? '',
'dep_time' => $this->formatTime($flightToReconcile->dep_time),
'arr_time' => $this->formatTime($flightToReconcile->arr_time),
'duration' => $this->formatTime($flightToReconcile->duration),
'registration' => $flightToReconcile->registration ?? '',
'note' => $flightToReconcile->note ?? '',
'flight_class' => $flightToReconcile->flight_class !== null ? (int) $flightToReconcile->flight_class : null,
'seat_type' => $flightToReconcile->seat_type !== null ? (int) $flightToReconcile->seat_type : null,
'flight_reason' => $flightToReconcile->flight_reason !== null ? (int) $flightToReconcile->flight_reason : null,
'airline_options' => $this->getPossibleAirlines($flightToReconcile->airline ?? ''),
'to_options' => $this->getPossibleAirports($flightToReconcile->to ?? ''),
'from_options' => $this->getPossibleAirports($flightToReconcile->from ?? ''),
'aircraft_options' => $this->getPossibleAircraft($flightToReconcile->aircraft ?? ''),
];
return Inertia::render('ReconcileFlight', [
'flight' => $flight,
'key' => $flight['imported_flight_id'],
]);
}
public function save(Request $request)
@@ -17,7 +17,7 @@ class FlightProfileController extends Controller
public function profileData(User $user, string $view, ?int $selectedFlightId = null) : array {
return [
'user' => $user,
'canEdit' => auth()->check() && auth()->id() === $user->id,
'canEdit' => (auth()->check() && auth()->id() === $user->id) || auth()->user()->hasRole('admin'),
'initialView' => $view,
'selectedFlightId' => $selectedFlightId,
'flight_api_url' => self::getUserFlightApiURL($user),
@@ -35,6 +35,8 @@ class HandleInertiaRequests extends Middleware
'logo_api_url' => config('app.logo_api_url'),
'auth' => [
'user' => $request->user(),
'roles' => $request->user()?->getRoleNames() ?? [],
'permissions' => $request->user()?->getAllPermissions()->pluck('name') ?? [],
],
'achievement_notifications' => fn() => $request->user()
? $request->user()
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class IgnoredMissingLivery extends Model
{
protected $table = 'ignored_missing_liveries';
public $timestamps = false;
protected $fillable = ['filename'];
}
+2 -4
View File
@@ -10,8 +10,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Traits\HasAchievements;
use App\Models\Notification;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
#[Fillable(['name', 'email', 'password', 'distance_unit'])]
#[Hidden(['password', 'remember_token'])]
@@ -19,9 +19,7 @@ class User extends Authenticatable
{
/** @use HasFactory<UserFactory> */
use HasFactory, HasAchievements, HasApiTokens;
use HasFactory, HasAchievements, HasApiTokens, HasRoles;
/**
* Get the attributes that should be cast.
+1 -1
View File
@@ -37,7 +37,7 @@ class UserFlightPolicy
*/
public function update(User $user, UserFlight $userFlight): bool
{
return $user->id === $userFlight->user_id;
return $user->id === $userFlight->user_id || $user->hasRole('admin');
}
/**
@@ -1,26 +1,29 @@
<?php
namespace App\Http\Controllers;
namespace App\Services;
use Illuminate\Http\Request;
use App\DTOs\MissingLivery;
use App\Models\IgnoredMissingLivery;
use App\Models\UserFlight;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Collection;
class AdminToolsController extends Controller
class AdminService
{
function missingLiveries(){
/** @return Collection<int, MissingLivery> */
function getMissingLiveries(): Collection
{
/* $existingFiles = collect(glob(public_path('img/liveries/generated/*')))
->map(fn ($path) => pathinfo($path, PATHINFO_FILENAME))
->toArray();*/
/* $existingFiles = collect(glob(public_path('img/liveries/generated/*')))
->map(fn ($path) => pathinfo($path, PATHINFO_FILENAME))
->toArray();*/
$existingFiles = collect(glob(Storage::disk('local')->path('images/liveries').'/*.png'))
->map(fn ($path) => pathinfo($path, PATHINFO_FILENAME))
->toArray();
$combos = \App\Models\UserFlight::with(['aircraft', 'airline'])
$combos = UserFlight::with(['aircraft', 'airline'])
->select('airline_id', 'aircraft_id')
->whereNotNull('airline_id')
->whereNotNull('aircraft_id')
@@ -31,14 +34,15 @@ class AdminToolsController extends Controller
'airline_name' => $flight->airline->name,
'aircraft_display_name' => $flight->aircraft->display_name,
'filename' => $flight->airline->internal_name . '_' . $flight->aircraft->designator,
'clipboard_text' => $flight->airline->name . ' ' . $flight->aircraft->display_name_short,
])
->filter(fn ($combo) => !in_array($combo['filename'], $existingFiles))
->filter(fn ($combo) => !in_array($combo['filename'], $existingFiles));
$ignoredFiles = IgnoredMissingLivery::whereIn('filename', $combos->pluck('filename'))->pluck('filename')->toArray();
return $combos
->filter(fn ($combo) => !in_array($combo['filename'], $ignoredFiles))
->sortBy('airline_name')
->values();
return response()->json([
'count' => $combos->count(),
'liveries' => $combos,
]);
}
}