Converted to TS, add import

This commit is contained in:
2026-04-03 18:14:42 +10:00
parent 89135a554a
commit 063a393168
23 changed files with 644 additions and 90 deletions
+30
View File
@@ -1,3 +1,33 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--bg: #0a0f1c; /* deep night sky */
--surface: #111827; /* panels */
--surface-alt: #0f172a; /* slight variation */
--text: #e6edf3; /* crisp readable */
--muted: #94a3b8; /* secondary text */
--accent: #38bdf8; /* sky blue */
--accent-soft: #0ea5e9; /* deeper blue */
--accent-glow: rgba(56, 189, 248, 0.15);
--border: #1f2937;
}
.glass {
background: rgba(17, 24, 39, 0.2); /* --surface at 60% */
backdrop-filter: blur(12px) saturate(180%);
-webkit-backdrop-filter: blur(12px) saturate(180%);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(56, 189, 248, 0.05);
}
.glass-border {
border: 1px solid rgba(56, 189, 248, 0.15); /* --accent at low opacity */
}
body {
background-color: var(--bg);
background-image:
radial-gradient(ellipse at 20% 50%, rgba(56, 189, 248, 0.06) 0%, transparent 60%),
radial-gradient(ellipse at 80% 20%, rgba(14, 165, 233, 0.05) 0%, transparent 50%);
}
@@ -0,0 +1,22 @@
<script setup lang="ts">
</script>
<template>
<div class="glass-box glass glass-border">
<slot />
</div>
</template>
<style scoped>
.glass-box {
width: 50%;
height: 50dvh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap:1em;
padding: 2em;
}
</style>
@@ -0,0 +1,20 @@
<script setup lang="ts">
</script>
<template>
<footer class="glass">
&copy; FlightsGoneBy. All rights reserved.
</footer>
</template>
<style scoped>
/* Footer = ~5dvh */
footer {
flex: 0 0 5dvh;
min-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
</style>
@@ -0,0 +1,54 @@
<script setup lang="ts">
import {Link} from "@inertiajs/vue3";
import { usePage } from '@inertiajs/vue3'
import type { SharedProps } from '@/Types/types'
const props = usePage<SharedProps>().props
</script>
<template>
<header class="glass">
<h1>FlightsGoneBy</h1>
<nav>
<Link v-if="props.canLogin && !props.auth.user" :href="route('login')">Log In</Link>
<Link v-if="props.canRegister && !props.auth.user" :href="route('register')">Register</Link>
<Link v-if="props.auth.user">Welcome {{props.auth.user.name}}</Link>
</nav>
</header>
</template>
<style scoped>
header {
display:flex;
align-content: center;
justify-content: center;
flex: 0 0 5dvh;
min-height: 40px; /* prevents it getting too tiny */
align-items: center;
padding: 0 1rem;
}
header h1 {
flex-basis: 25%;
margin: 0;
font-size: 1.5rem;
letter-spacing: 0.08em;
color: var(--accent);
cursor: pointer;
}
header nav{
flex-basis: 75%;
display: flex;
align-content: center;
justify-content: flex-end;
padding: 0 1em;
gap: 1rem;
}
header nav a {
height: 100%;
}
</style>
@@ -0,0 +1,132 @@
<script setup lang="ts">
</script>
<!-- RadarBackground.vue -->
<template>
<div class="radar-bg">
<svg class="radar-svg" viewBox="0 0 800 420"
preserveAspectRatio="xMidYMid slice"
xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="bgGlow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#38bdf8" stop-opacity="0.04"/>
<stop offset="100%" stop-color="#0a0f1c" stop-opacity="0"/>
</radialGradient>
<clipPath id="radarClip">
<circle cx="400" cy="210" r="208"/>
</clipPath>
</defs>
<!-- Ambient glow -->
<ellipse cx="400" cy="210" rx="320" ry="220" fill="url(#bgGlow)"/>
<!-- Rings -->
<circle class="ring" cx="400" cy="210" r="52"/>
<circle class="ring" cx="400" cy="210" r="104"/>
<circle class="ring" cx="400" cy="210" r="156"/>
<circle class="ring" cx="400" cy="210" r="208"/>
<circle cx="400" cy="210" r="208" fill="none"
stroke="rgba(56,189,248,0.25)" stroke-width="1"/>
<!-- Crosshairs -->
<line class="crosshair" x1="192" y1="210" x2="608" y2="210"/>
<line class="crosshair" x1="400" y1="2" x2="400" y2="418"/>
<line class="crosshair" x1="253" y1="63" x2="547" y2="357" transform="rotate(45,400,210)"/>
<line class="crosshair" x1="253" y1="63" x2="547" y2="357" transform="rotate(-45,400,210)"/>
<!-- Sweep trail + line -->
<g clip-path="url(#radarClip)">
<g class="trail">
<path d="M400,210 L400,2 A208,208 0 0,1 547,63 Z" fill="rgba(56,189,248,0.08)"/>
<path d="M400,210 L400,2 A208,208 0 0,1 493,42 Z" fill="rgba(56,189,248,0.10)"/>
<path d="M400,210 L400,2 A208,208 0 0,1 435,4 Z" fill="rgba(56,189,248,0.13)"/>
</g>
<g class="sweep">
<line x1="400" y1="210" x2="400" y2="3"
stroke="#38bdf8" stroke-width="1.5" stroke-opacity="0.9"/>
</g>
</g>
<!-- Blip glows -->
<g clip-path="url(#radarClip)">
<circle class="blip-glow" cx="470" cy="148" r="8"/>
<circle class="blip-glow" cx="340" cy="270" r="8"/>
<circle class="blip-glow" cx="510" cy="230" r="8"/>
<circle class="blip-glow" cx="378" cy="130" r="8"/>
<circle class="blip-glow" cx="430" cy="310" r="8"/>
<circle class="blip-glow" cx="290" cy="185" r="8"/>
</g>
<!-- Blips -->
<g clip-path="url(#radarClip)">
<circle class="blip" cx="470" cy="148" r="2.5"/>
<circle class="blip" cx="340" cy="270" r="2.5"/>
<circle class="blip" cx="510" cy="230" r="2.5"/>
<circle class="blip" cx="378" cy="130" r="2.5"/>
<circle class="blip" cx="430" cy="310" r="2.5"/>
<circle class="blip" cx="290" cy="185" r="2.5"/>
</g>
</svg>
<slot/>
</div>
</template>
<style scoped>
/* In your component <style> or global CSS */
.radar-bg {
position: fixed;
inset: 0;
background: var(--bg);
overflow: hidden;
z-index: 0;
}
.radar-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
opacity:0.1
}
.ring { fill: none; stroke: rgba(56,189,248,0.12); stroke-width: 1; }
.crosshair { stroke: rgba(56,189,248,0.1); stroke-width: 0.5; }
.range-label { font-size: 9px; fill: rgba(56,189,248,0.35); font-family: monospace; }
/* Sweep */
.sweep, .trail {
transform-origin: 400px 210px;
animation: radar-spin 10s linear infinite;
}
@keyframes radar-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Blips */
.blip, .blip-glow {
animation: blip-fade 4s linear infinite;
}
.blip { fill: #38bdf8; }
.blip-glow { fill: rgba(56,189,248,0.2); }
@keyframes blip-fade {
0% { opacity: 0; }
2% { opacity: 1; }
60% { opacity: 0.5; }
100%{ opacity: 0; }
}
/* Stagger each blip so they don't all pulse together */
.blip:nth-child(2), .blip-glow:nth-child(2) { animation-delay: -1.1s; }
.blip:nth-child(3), .blip-glow:nth-child(3) { animation-delay: -2.3s; }
.blip:nth-child(4), .blip-glow:nth-child(4) { animation-delay: -0.5s; }
.blip:nth-child(5), .blip-glow:nth-child(5) { animation-delay: -3.1s; }
.blip:nth-child(6), .blip-glow:nth-child(6) { animation-delay: -1.8s; }
</style>
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
import {Link} from "@inertiajs/vue3";
import MainHeader from "@/Components/FlightsGoneBy/MainHeader.vue";
import MainFooter from "@/Components/FlightsGoneBy/MainFooter.vue";
import Radar from "@/Components/FlightsGoneBy/Radar.vue";
</script>
<template>
<Radar>
<div class="layoutContainer">
<MainHeader />
<main id="pageContainer">
<slot />
</main>
<MainFooter />
</div>
</Radar>
</template>
<style scoped>
.layoutContainer {
display: flex;
flex-direction: column;
min-height: 100dvh;
background: var(--bg);
color: var(--text);
}
main {
flex: 1 0 90dvh; /* THIS is the key */
min-height: 0;
padding: 1rem;
background: var(--surface-alt);
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="ts">
import MainLayout from "@/Layouts/MainLayout.vue";
import GlassBox from "@/Components/FlightsGoneBy/GlassBox.vue";
import {Head} from "@inertiajs/vue3";
defineOptions({
layout: MainLayout
})
</script>
<template>
<Head title="Import" />
<GlassBox>
<h2>Import Your Flights</h2>
<p>
Import a CSV export from MyFlightRadar24. You will then be guided to reconcile any data mismatches.
</p>
<v-file-input style="width:100%; flex:0" label="Select CSV File" accept=".csv" />
</GlassBox>
</template>
<style scoped>
h2{
font-size: 2rem;
}
p{
text-align: center;
width: 80%;
}
</style>
+15
View File
@@ -0,0 +1,15 @@
<script setup>
import MainLayout from "@/Layouts/MainLayout.vue";
import { Head } from '@inertiajs/vue3';
defineOptions({
layout: MainLayout
})
</script>
<template>
<Head title="Home" />
</template>
<style scoped>
</style>
+27
View File
@@ -0,0 +1,27 @@
declare global {
const route: typeof import('ziggy-js')['route']
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
route: typeof import('ziggy-js')['route']
}
}
export interface User {
id: number
name: string
email: string
email_verified_at: string | null
}
export type SharedProps = import('@inertiajs/core').PageProps & {
auth: {
user: User | null
isLoggedIn: boolean
}
}
declare module '@inertiajs/vue3' {
interface PageProps extends SharedProps {}
}
+5 -3
View File
@@ -3,22 +3,24 @@ import './bootstrap';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { createApp, h } from 'vue';
import { ZiggyVue } from '../../vendor/tightenco/ziggy';
import { createApp, h, DefineComponent } from 'vue';
import vuetify from './plugins/vuetify';
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
title: (title) => `${title} | ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob('./Pages/**/*.vue'),
import.meta.glob<DefineComponent>('./Pages/**/*.vue'),
),
setup({ el, App, props, plugin }) {
return createApp({ render: () => h(App, props) })
.use(plugin)
.use(ZiggyVue)
.use(vuetify)
.mount(el);
},
progress: {
+8
View File
@@ -0,0 +1,8 @@
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
defaultTheme: 'dark',
},
})
+3
View File
@@ -0,0 +1,3 @@
/// <reference types="vite/client" />
declare module '*.css'
+49
View File
@@ -0,0 +1,49 @@
/* This file is generated by Ziggy. */
declare module 'ziggy-js' {
interface RouteList {
"dashboard": [],
"profile.edit": [],
"profile.update": [],
"profile.destroy": [],
"register": [],
"login": [],
"password.request": [],
"password.email": [],
"password.reset": [
{
"name": "token",
"required": true
}
],
"password.store": [],
"verification.notice": [],
"verification.verify": [
{
"name": "id",
"required": true
},
{
"name": "hash",
"required": true
}
],
"verification.send": [],
"password.confirm": [],
"password.update": [],
"logout": [],
"sanctum.csrf-cookie": [],
"storage.local": [
{
"name": "path",
"required": true
}
],
"storage.local.upload": [
{
"name": "path",
"required": true
}
]
}
}
export {};
+5
View File
@@ -0,0 +1,5 @@
const Ziggy = {"url":"http:\/\/localhost:8000","port":8000,"defaults":{},"routes":{"dashboard":{"uri":"dashboard","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"profile.edit":{"uri":"profile","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"profile.update":{"uri":"profile","methods":["PATCH"],"domain":"flightsgoneby.test"},"profile.destroy":{"uri":"profile","methods":["DELETE"],"domain":"flightsgoneby.test"},"register":{"uri":"register","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"login":{"uri":"login","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"password.email":{"uri":"forgot-password","methods":["POST"],"domain":"flightsgoneby.test"},"password.reset":{"uri":"reset-password\/{token}","methods":["GET","HEAD"],"domain":"flightsgoneby.test","parameters":["token"]},"password.store":{"uri":"reset-password","methods":["POST"],"domain":"flightsgoneby.test"},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"verification.verify":{"uri":"verify-email\/{id}\/{hash}","methods":["GET","HEAD"],"domain":"flightsgoneby.test","parameters":["id","hash"]},"verification.send":{"uri":"email\/verification-notification","methods":["POST"],"domain":"flightsgoneby.test"},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"],"domain":"flightsgoneby.test"},"password.update":{"uri":"password","methods":["PUT"],"domain":"flightsgoneby.test"},"logout":{"uri":"logout","methods":["POST"],"domain":"flightsgoneby.test"},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"storage.local":{"uri":"storage\/{path}","methods":["GET","HEAD"],"wheres":{"path":".*"},"parameters":["path"]},"storage.local.upload":{"uri":"storage\/{path}","methods":["PUT"],"wheres":{"path":".*"},"parameters":["path"]}}};
if (typeof window !== 'undefined' && typeof window.Ziggy !== 'undefined') {
Object.assign(Ziggy.routes, window.Ziggy.routes);
}
export { Ziggy };
+1 -1
View File
@@ -12,7 +12,7 @@
<!-- Scripts -->
@routes
@vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"])
@vite(['resources/js/app.ts', "resources/js/Pages/{$page['component']}.vue"])
@inertiaHead
</head>
<body class="font-sans antialiased">