From badb4dc46faa10dd8b37200fe429d100b5d70a04 Mon Sep 17 00:00:00 2001 From: josh Date: Tue, 16 Jun 2026 14:42:05 +1000 Subject: [PATCH] Updated Map View --- bootstrap/app.php | 76 +++++++++++++++++-- .../js/Components/FlightsGoneBy/FlightMap.vue | 42 +++++++++- .../Components/FlightsGoneBy/MainHeader.vue | 18 ++--- resources/js/Pages/Error.vue | 23 ++++++ 4 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 resources/js/Pages/Error.vue diff --git a/bootstrap/app.php b/bootstrap/app.php index 7271f0d..33a18db 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,8 +1,17 @@ withRouting( @@ -13,16 +22,71 @@ return Application::configure(basePath: dirname(__DIR__)) ) ->withMiddleware(function (Middleware $middleware): void { $middleware->web(append: [ - \App\Http\Middleware\HandleInertiaRequests::class, - \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class, + HandleInertiaRequests::class, + AddLinkHeadersForPreloadedAssets::class, ]); $middleware->alias([ - 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, - 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, - 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, + 'role' => RoleMiddleware::class, + 'permission' => PermissionMiddleware::class, + 'role_or_permission' => RoleOrPermissionMiddleware::class, ]); // }) ->withExceptions(function (Exceptions $exceptions): void { - // + $exceptions->respond(function (Response $response, Throwable $e, Request $request) { + $status = $response->getStatusCode(); + + $errors = [ + 403 => [ + 'title' => "The Cockpit is Off Limits", + 'message' => 'You don\'t have permission to access this page.', + ], + 404 => [ + 'title' => 'You Flight Has Been Cancelled', + 'message' => 'The page you are looking for doesn\'t exist or has been moved.', + ], + 419 => [ + 'title' => 'Page Expired', + 'message' => 'Your session has expired. Please refresh the page and try again.', + ], + 429 => [ + 'title' => 'Too Many Requests', + 'message' => 'You\'re making too many requests. Please slow down and try again.', + ], + 500 => [ + 'title' => 'This Plane Has Made An Emergency Landing', + 'message' => 'Something went wrong on our end. Please try again later.', + ], + 503 => [ + 'title' => 'Service Unavailable', + 'message' => 'We\'re down for maintenance. Please check back soon.', + ], + ]; + + $isLocal = app()->environment(['local', 'testing']); + $handled = array_keys($errors); + $friendlyErrorsOnLocal = [404, 403]; + + // In local/testing, only handle 404. In production, handle all. + $shouldHandle = isset($errors[$status]) && ( + !$isLocal || in_array($status, $friendlyErrorsOnLocal) + ); + + if (!$shouldHandle) { + return $response; + } + + return Inertia::render('Error', [ + 'statusCode' => $status, + 'statusTitle' => $errors[$status]['title'], + 'statusMessage' => $errors[$status]['message'], + 'auth' => [ + 'user' => $request->user(), + 'roles' => $request->user()?->getRoleNames() ?? [], + 'permissions' => $request->user()?->getAllPermissions()->pluck('name') ?? [], + ], + ]) + ->toResponse($request) + ->setStatusCode($status); + }); })->create(); diff --git a/resources/js/Components/FlightsGoneBy/FlightMap.vue b/resources/js/Components/FlightsGoneBy/FlightMap.vue index 71031d8..f396f59 100644 --- a/resources/js/Components/FlightsGoneBy/FlightMap.vue +++ b/resources/js/Components/FlightsGoneBy/FlightMap.vue @@ -2,9 +2,12 @@
- +

No flight data available

@@ -266,6 +269,15 @@ export default defineComponent({ const legendOpen = ref(true) + const isGlobe = ref(false) + + const toggleProjection = (): void => { + if (!map?.isStyleLoaded()) return + isGlobe.value = !isGlobe.value + map.setProjection({ type: isGlobe.value ? 'globe' : 'mercator' }) + } + + const legendItems = [ { color: '#f97316', label: '5+ flights' }, { color: '#eab308', label: '3–4 flights' }, @@ -670,6 +682,11 @@ export default defineComponent({ map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right') map.addControl(new maplibregl.FullscreenControl(), 'top-right') + + map.on('style.load', () => { + map?.setProjection({ type: 'mercator' }) + }) + map.on('load', () => { addLayers() fitBounds() @@ -718,7 +735,7 @@ export default defineComponent({ if (map) { map.remove(); map = null } }) - return { mapContainer, mapReady, exportMapBasic, legendOpen, legendItems } + return { mapContainer, mapReady, exportMapBasic, legendOpen, legendItems, isGlobe, toggleProjection } }, }) @@ -901,4 +918,25 @@ export default defineComponent({ letter-spacing: 0.06em; color: #c8cdd8; } + +.projection-btn { + position: absolute; + bottom: 48px; + right: 12px; + z-index: 10; + background: rgba(10,14,22,0.85); + border: 1px solid rgba(255,255,255,0.08); + color: #a0b4c8; + width: 30px; + height: 30px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + transition: color 0.15s, background 0.15s; +} +.projection-btn:hover { background: rgba(77,166,255,0.15); color: #4da6ff; } +.projection-btn.globe-active { color: #4da6ff; border-color: rgba(77,166,255,0.35); } diff --git a/resources/js/Components/FlightsGoneBy/MainHeader.vue b/resources/js/Components/FlightsGoneBy/MainHeader.vue index 38b83ed..5086657 100644 --- a/resources/js/Components/FlightsGoneBy/MainHeader.vue +++ b/resources/js/Components/FlightsGoneBy/MainHeader.vue @@ -5,7 +5,7 @@ import type { SharedProps } from '@/Types/types' import { ref, onMounted, onUnmounted } from 'vue' import NotificationMenu from "@/Components/FlightsGoneBy/NotificationMenu.vue"; -const props = usePage().props +const page = usePage() const menuOpen = ref(false) const dropdownOpen = ref(false) const dropdownRef = ref(null) @@ -27,23 +27,23 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside))
FlightsGoneBy - +