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
+1 -1
View File
@@ -1,7 +1,7 @@
.env
vendor/
.git/
storage/app/private/images
storage/app/private
node_modules
public/hot
public/build
@@ -33,6 +33,7 @@ class HandleInertiaRequests extends Middleware
...parent::share($request),
'auth' => [
'user' => $request->user(),
'isLoggedIn' => $request->user() !== null,
],
];
}
-10
View File
@@ -1,10 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["resources/js/*"],
"ziggy-js": ["./vendor/tightenco/ziggy"]
}
},
"exclude": ["node_modules", "public"]
}
+149 -64
View File
@@ -4,10 +4,14 @@
"requires": true,
"packages": {
"": {
"dependencies": {
"vuetify": "^4.0.5"
},
"devDependencies": {
"@inertiajs/vue3": "^2.0.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/vite": "^4.0.0",
"@types/node": "^25.5.0",
"@vitejs/plugin-vue": "^6.0.0",
"autoprefixer": "^10.4.12",
"axios": "^1.11.0",
@@ -16,7 +20,9 @@
"laravel-vite-plugin": "^3.0.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.2.1",
"typescript": "^6.0.2",
"vite": "^8.0.0",
"vite-plugin-vuetify": "^2.1.3",
"vue": "^3.4.0"
}
},
@@ -37,7 +43,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -47,7 +52,6 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -57,7 +61,6 @@
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.0"
@@ -73,7 +76,6 @@
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -87,7 +89,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -160,7 +161,6 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -178,7 +178,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz",
"integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -235,7 +234,7 @@
"version": "0.122.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz",
"integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
@@ -248,7 +247,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -265,7 +263,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -282,7 +279,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -299,7 +295,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -316,7 +311,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -333,7 +327,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -350,7 +343,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -367,7 +359,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -384,7 +375,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -401,7 +391,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -418,7 +407,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -435,7 +423,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -452,7 +439,6 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -469,7 +455,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -486,7 +471,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -500,7 +484,7 @@
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz",
"integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/@tailwindcss/forms": {
@@ -806,7 +790,6 @@
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -830,6 +813,17 @@
"@types/lodash": "*"
}
},
"node_modules/@types/node": {
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.18.0"
}
},
"node_modules/@vitejs/plugin-vue": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz",
@@ -858,7 +852,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz",
"integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.2",
@@ -872,7 +865,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz",
"integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.31",
@@ -883,7 +875,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz",
"integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.2",
@@ -901,7 +892,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz",
"integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.31",
@@ -912,7 +902,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz",
"integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.31"
@@ -922,7 +911,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz",
"integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.31",
@@ -933,7 +921,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz",
"integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.31",
@@ -946,7 +933,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz",
"integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.31",
@@ -960,9 +946,22 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz",
"integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==",
"dev": true,
"license": "MIT"
},
"node_modules/@vuetify/loader-shared": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.1.2.tgz",
"integrity": "sha512-X+1jBLmXHkpQEnC0vyOb4rtX2QSkBiFhaFXz8yhQqN2A4vQ6k2nChxN4Ol7VAY5KoqMdFoRMnmNdp/1qYXDQig==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"upath": "^2.0.1"
},
"peerDependencies": {
"vue": "^3.0.0",
"vuetify": ">=3"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -2418,7 +2417,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -2458,7 +2456,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -2525,7 +2523,6 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -2607,7 +2604,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/esutils": {
@@ -2673,7 +2669,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -2756,7 +2752,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -3064,7 +3059,7 @@
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"devOptional": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -3133,7 +3128,7 @@
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"dev": true,
"devOptional": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
@@ -3166,7 +3161,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3187,7 +3181,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3208,7 +3201,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3229,7 +3221,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3250,7 +3241,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3271,7 +3261,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3292,7 +3281,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3313,7 +3301,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3334,7 +3321,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3355,7 +3341,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3376,7 +3361,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3452,7 +3436,6 @@
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -3597,7 +3580,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
@@ -3703,14 +3685,13 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"devOptional": true,
"license": "MIT",
"peer": true,
"engines": {
@@ -3744,7 +3725,6 @@
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -4130,7 +4110,7 @@
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz",
"integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.122.0",
@@ -4330,7 +4310,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -4516,7 +4495,7 @@
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -4583,9 +4562,42 @@
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"devOptional": true,
"license": "0BSD"
},
"node_modules/typescript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"devOptional": true,
"license": "MIT"
},
"node_modules/upath": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
"integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4",
"yarn": "*"
}
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -4628,7 +4640,7 @@
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz",
"integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==",
"dev": true,
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -4727,11 +4739,56 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vite-plugin-vuetify": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.3.tgz",
"integrity": "sha512-Q4SC/4TqbNvaZIFb9YsfBqkGlYHbJJJ6uU3CnRBZqLUF3s5eCMVZAaV4GkTbehIH/bhSj42lMXztOwc71u6rVw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vuetify/loader-shared": "^2.1.2",
"debug": "^4.3.3",
"upath": "^2.0.1"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"vite": ">=5",
"vue": "^3.0.0",
"vuetify": ">=3"
}
},
"node_modules/vite-plugin-vuetify/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/vite-plugin-vuetify/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/vue": {
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz",
"integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -4750,6 +4807,34 @@
}
}
},
"node_modules/vuetify": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.5.tgz",
"integrity": "sha512-pFysKOHuY3dROTVh9PdlhVz50ZR0E5/goY5ecTXc8F8tajUA2ee3xZ8Lqs1WtEw/X3w93wx/LogyjgaQCAL/Ig==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/johnleider"
},
"peerDependencies": {
"typescript": ">=4.7",
"vite-plugin-vuetify": ">=2.1.0",
"vue": "^3.5.0",
"webpack-plugin-vuetify": ">=3.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
},
"vite-plugin-vuetify": {
"optional": true
},
"webpack-plugin-vuetify": {
"optional": true
}
}
},
"node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+6
View File
@@ -11,6 +11,7 @@
"@inertiajs/vue3": "^2.0.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/vite": "^4.0.0",
"@types/node": "^25.5.0",
"@vitejs/plugin-vue": "^6.0.0",
"autoprefixer": "^10.4.12",
"axios": "^1.11.0",
@@ -19,7 +20,12 @@
"laravel-vite-plugin": "^3.0.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.2.1",
"typescript": "^6.0.2",
"vite": "^8.0.0",
"vite-plugin-vuetify": "^2.1.3",
"vue": "^3.4.0"
},
"dependencies": {
"vuetify": "^4.0.5"
}
}
+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">
+12 -10
View File
@@ -6,16 +6,17 @@ use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
/**
* App Routes
*/
Route::domain(config('app.domain'))->group(
function() {
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
return Inertia::render('Index');
});
Route::get('/import/fr24', function () {
return Inertia::render('Fr24Import');
});
Route::get('/dashboard', function () {
@@ -33,6 +34,10 @@ Route::domain(config('app.domain'))->group(
}
);
/**
* API Routes
*/
Route::domain(config('app.api_domain'))->group(function () {
Route::get('/', function () {
return response()->json(['message' => 'Welcome to the FlightsGoneBy API']);
@@ -40,6 +45,3 @@ Route::domain(config('app.api_domain'))->group(function () {
Route::get('airlines/logos/tail/{code}', [LogoController::class, 'getLogoByCode'])
->where('code', '[A-Za-z0-9]{2,3}');
});
+22
View File
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"allowJs": true,
"checkJs": false,
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"@/*": ["resources/js/*"],
"ziggy-js": ["./vendor/tightenco/ziggy"]
},
},
"include": [
"resources/js/**/*",
"resources/js/ziggy.d.ts",
"resources/js/Types/types.d.ts"
],
"exclude": ["node_modules", "public"]
}
+9 -1
View File
@@ -1,11 +1,13 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
import path from 'path';
import vuetify from "vite-plugin-vuetify";
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
input: 'resources/js/app.ts', // ← rename app.ts to app.ts
refresh: true,
}),
vue({
@@ -16,5 +18,11 @@ export default defineConfig({
},
},
}),
vuetify({autoImport: true})
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'resources/js'), // ← mirrors tsconfig paths
},
},
});