Working on drawer

This commit is contained in:
2025-04-20 20:51:10 -04:00
parent 19ed8ef2ca
commit 06f9a96498
11 changed files with 109 additions and 427 deletions

331
api/Cargo.lock generated
View File

@@ -239,21 +239,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "actix-web-httpauth"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456348ed9dcd72a13a1f4a660449fafdecee9ac8205552e286809eb5b0b29bd3"
dependencies = [
"actix-utils",
"actix-web",
"base64",
"futures-core",
"futures-util",
"log",
"pin-project-lite",
]
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.24.2" version = "0.24.2"
@@ -371,17 +356,13 @@ dependencies = [
"actix-cors", "actix-cors",
"actix-multipart", "actix-multipart",
"actix-web", "actix-web",
"actix-web-httpauth",
"argon2", "argon2",
"byteorder",
"chrono", "chrono",
"dotenv", "dotenv",
"env_logger", "env_logger",
"futures", "futures",
"futures-util", "futures-util",
"geo-types",
"log", "log",
"moka",
"rand 0.9.0", "rand 0.9.0",
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"redis", "redis",
@@ -395,15 +376,6 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.7.1" version = "1.7.1"
@@ -422,17 +394,6 @@ dependencies = [
"password-hash", "password-hash",
] ]
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.87" version = "0.1.87"
@@ -769,24 +730,6 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.3.12" version = "0.3.12"
@@ -1031,16 +974,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@@ -1204,19 +1137,6 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "generator"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
dependencies = [
"cfg-if",
"libc",
"log",
"rustversion",
"windows",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@@ -1227,17 +1147,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "geo-types"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bd1157f0f936bf0cd68dec91e8f7c311afe60295574d62b70d4861a1bfdf2d9"
dependencies = [
"approx",
"num-traits",
"serde",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@@ -1557,7 +1466,7 @@ dependencies = [
"iana-time-zone-haiku", "iana-time-zone-haiku",
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.52.0", "windows-core",
] ]
[[package]] [[package]]
@@ -1873,28 +1782,6 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "loom"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]] [[package]]
name = "maybe-async" name = "maybe-async"
version = "0.2.10" version = "0.2.10"
@@ -1964,28 +1851,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "moka"
version = "0.12.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926"
dependencies = [
"async-lock",
"crossbeam-channel",
"crossbeam-epoch",
"crossbeam-utils",
"event-listener",
"futures-util",
"loom",
"parking_lot",
"portable-atomic",
"rustc_version",
"smallvec",
"tagptr",
"thiserror 1.0.69",
"uuid",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.14" version = "0.2.14"
@@ -2003,16 +1868,6 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.6"
@@ -2145,12 +2000,6 @@ dependencies = [
"hashbrown 0.14.5", "hashbrown 0.14.5",
] ]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "parking" name = "parking"
version = "2.2.1" version = "2.2.1"
@@ -2430,17 +2279,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata 0.4.9", "regex-automata",
"regex-syntax 0.8.5", "regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
] ]
[[package]] [[package]]
@@ -2451,7 +2291,7 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax 0.8.5", "regex-syntax",
] ]
[[package]] [[package]]
@@ -2460,12 +2300,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.5" version = "0.8.5"
@@ -2712,12 +2546,6 @@ dependencies = [
"parking_lot", "parking_lot",
] ]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@@ -2834,15 +2662,6 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@@ -3208,12 +3027,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.18.0" version = "3.18.0"
@@ -3268,16 +3081,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.39" version = "0.3.39"
@@ -3473,36 +3276,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
] ]
[[package]] [[package]]
@@ -3601,12 +3374,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@@ -3740,38 +3507,6 @@ dependencies = [
"wasite", "wasite",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.52.0"
@@ -3781,41 +3516,6 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.1.0" version = "0.1.0"
@@ -3828,20 +3528,11 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [ dependencies = [
"windows-result 0.3.1", "windows-result",
"windows-strings 0.3.1", "windows-strings",
"windows-targets 0.53.0", "windows-targets 0.53.0",
] ]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.1" version = "0.3.1"
@@ -3851,16 +3542,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.3.1" version = "0.3.1"

View File

@@ -12,7 +12,6 @@ license = "GPL-3.0-or-later"
[dependencies] [dependencies]
actix-web = "4.10.2" actix-web = "4.10.2"
actix-cors = "0.7.1" actix-cors = "0.7.1"
actix-web-httpauth = "0.8.2"
actix-multipart = "0.7.2" actix-multipart = "0.7.2"
chrono = { version = "0.4.40", features = ["serde"] } chrono = { version = "0.4.40", features = ["serde"] }
dotenv = "0.15.0" dotenv = "0.15.0"
@@ -31,7 +30,4 @@ futures-util = "0.3.31"
rust-s3 = "0.35.1" rust-s3 = "0.35.1"
rand = "0.9.0" rand = "0.9.0"
rand_chacha = "0.9.0" rand_chacha = "0.9.0"
geo-types = "0.7.15"
byteorder = "1.5.0"
futures = "0.3.31" futures = "0.3.31"
moka = { version = "0.12.10", features = ["future"] }

View File

@@ -27,7 +27,7 @@ impl MetarCheck {
status, status,
updated_at: Utc::now(), updated_at: Utc::now(),
last_metar: None, last_metar: None,
} },
} }
} }

View File

@@ -993,10 +993,7 @@ impl Metar {
}) })
} }
pub async fn find_all_distinct( pub async fn find_all_distinct(client: &Client, icao_list: &Vec<String>) -> ApiResult<Vec<Self>> {
client: &Client,
icao_list: &Vec<String>
) -> ApiResult<Vec<Self>> {
if icao_list.is_empty() { if icao_list.is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
} }

25
ui/package-lock.json generated
View File

@@ -17,6 +17,7 @@
"d3": "^7.9.0", "d3": "^7.9.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lodash.debounce": "^4.0.8",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
@@ -27,6 +28,7 @@
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/leaflet": "^1.9.16", "@types/leaflet": "^1.9.16",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^22.13.10", "@types/node": "^22.13.10",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
@@ -1881,6 +1883,23 @@
"@types/geojson": "*" "@types/geojson": "*"
} }
}, },
"node_modules/@types/lodash": {
"version": "4.17.16",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
"integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash.debounce": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz",
"integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.13.10", "version": "22.13.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
@@ -3741,6 +3760,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",

View File

@@ -20,6 +20,7 @@
"d3": "^7.9.0", "d3": "^7.9.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lodash.debounce": "^4.0.8",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
@@ -30,6 +31,7 @@
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/leaflet": "^1.9.16", "@types/leaflet": "^1.9.16",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^22.13.10", "@types/node": "^22.13.10",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",

View File

@@ -10,7 +10,7 @@ import { Header } from '@components/Header';
import AirportLayer from '@components/AirportLayer.tsx'; import AirportLayer from '@components/AirportLayer.tsx';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Airport } from '@lib/airport.types.ts'; import { Airport } from '@lib/airport.types.ts';
import AirportDrawer from '@components/AirportDrawer.tsx'; import Index from '@components/AirportDrawer';
import { getWeatherMapUrl } from '@lib/rainViewer.ts'; import { getWeatherMapUrl } from '@lib/rainViewer.ts';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
// import { createRoot } from 'react-dom/client'; // import { createRoot } from 'react-dom/client';
@@ -150,7 +150,7 @@ function App() {
<div className='App'> <div className='App'>
<Header /> <Header />
<div className='map-wrapper'> <div className='map-wrapper'>
<AirportDrawer airport={airport} setAirport={setAirport} /> <Index airport={airport} setAirport={setAirport} />
<MapContainer <MapContainer
className='leaflet-container' className='leaflet-container'
attributionControl={false} attributionControl={false}

View File

@@ -0,0 +1,3 @@
.drawer {
background: #32495f;
}

View File

@@ -1,11 +1,12 @@
import { Badge, Box, Divider, Drawer, Group, Tabs, TabsList, Text, Tooltip } from '@mantine/core'; import { Badge, Box, Divider, Drawer, Group, Tabs, TabsList, Text, Tooltip, UnstyledButton } from '@mantine/core';
import { Airport, AirportCategory } from '@lib/airport.types.ts'; import { Airport, AirportCategory } from '@lib/airport.types.ts';
import { getMarkerColor, Metar } from '@lib/metar.types.ts'; import { getMarkerColor, Metar } from '@lib/metar.types.ts';
import { forwardRef, ReactNode, useEffect, useState } from 'react'; import { CSSProperties, forwardRef, ReactNode, useEffect, useState } from 'react';
import { getMetars } from '@lib/metar.ts'; import { getMetars } from '@lib/metar.ts';
import { useMediaQuery } from '@mantine/hooks'; import { useMediaQuery } from '@mantine/hooks';
import { IconViewfinder } from '@tabler/icons-react';
export default function AirportDrawer({ export default function Index({
airport, airport,
setAirport setAirport
}: { }: {
@@ -48,7 +49,7 @@ export default function AirportDrawer({
onClose={() => setAirport(null)} onClose={() => setAirport(null)}
withinPortal withinPortal
zIndex={1000} zIndex={1000}
styles={{ root: { padding: 0, margin: 0, width: 0, height: 0 } }} styles={{ root: { padding: 0, margin: 0, width: 0, height: 0, backgroundColor: 'red' } }}
padding='md' padding='md'
size={isMobile ? '100%' : 'md'} size={isMobile ? '100%' : 'md'}
position='left' position='left'
@@ -68,7 +69,7 @@ export default function AirportDrawer({
justify='space-between' justify='space-between'
mb='md' mb='md'
style={{ style={{
backgroundColor: '#272f38', backgroundColor: '#32495f',
borderTop: '1px solid #1a242f', borderTop: '1px solid #1a242f',
borderBottom: '1px solid #1a242f', borderBottom: '1px solid #1a242f',
padding: '10px' padding: '10px'
@@ -102,21 +103,22 @@ export default function AirportDrawer({
); );
} }
function AirportInfoSlot({ title, value, units }: { title: string; value: string | number; units?: string }) { function AirportInfoSlot({ title, style, children }: { title?: string; style?: CSSProperties; children?: ReactNode }) {
return ( return (
<div> <div style={{ ...style }}>
<Text size='xs' color='dimmed'> {title && (
{title} <Text size='xs' color='dimmed'>
</Text> {title}
<Text fw={500} size='sm'> </Text>
{value} )}
{units} <Box fw={500} size='sm'>
</Text> {children}
</Box>
</div> </div>
); );
} }
function AirportInfoRow({ children }: { children: ReactNode }) { function AirportInfoRow({ style, children }: { style?: CSSProperties; children: ReactNode }) {
return ( return (
<div <div
style={{ style={{
@@ -124,7 +126,8 @@ function AirportInfoRow({ children }: { children: ReactNode }) {
justifyContent: 'space-between', justifyContent: 'space-between',
alignContent: 'center', alignContent: 'center',
padding: 'var(--mantine-spacing-sm) var(--mantine-spacing-lg)', padding: 'var(--mantine-spacing-sm) var(--mantine-spacing-lg)',
borderTop: '1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5))' borderTop: '1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5))',
...style
}} }}
> >
{children} {children}
@@ -136,16 +139,21 @@ function AirportInfo({ airport }: { airport: Airport }) {
return ( return (
<div> <div>
<AirportInfoRow> <AirportInfoRow>
<AirportInfoSlot title={'ICAO'} value={airport.icao} /> <AirportInfoSlot title={'ICAO'} children={airport.icao} />
<AirportInfoSlot title={'IATA'} value={airport.iata} /> <AirportInfoSlot title={'IATA'} children={airport.iata} />
<AirportInfoSlot title={'LOCAL'} value={airport.local} /> <AirportInfoSlot title={'LOCAL'} children={airport.local} />
<AirportInfoSlot title={'Category'} value={airportCategoryToText(airport.category)} /> <AirportInfoSlot title={'Category'} children={airportCategoryToText(airport.category)} />
</AirportInfoRow> </AirportInfoRow>
<AirportInfoRow> <AirportInfoRow style={{ justifyContent: 'flex-start' }}>
<AirportInfoSlot title={'Latitude'} value={airport.latitude} units={'} /> <AirportInfoSlot title={'Location'}>
<AirportInfoSlot title={'Longitude'} value={airport.longitude} units={'°'} /> {airport.latitude}°, {airport.longitude}°
<AirportInfoSlot title={'Elevation'} value={airport.elevation_ft} units={' ft'} /> </AirportInfoSlot>
Zoom To <AirportInfoSlot title={'Elevation'} style={{ paddingLeft: '1rem' }} children={`${airport.elevation_ft} ft`} />
<AirportInfoSlot style={{ marginLeft: 'auto', paddingLeft: '1rem', paddingTop: '0.5rem' }}>
<UnstyledButton>
<IconViewfinder />
</UnstyledButton>
</AirportInfoSlot>
</AirportInfoRow> </AirportInfoRow>
<Divider /> <Divider />
</div> </div>

View File

@@ -1,15 +1,12 @@
import { useEffect, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Airport, AirportCategory } from '@lib/airport.types.ts'; import { Airport, AirportCategory } from '@lib/airport.types.ts';
import { useMapEvents } from 'react-leaflet'; import { useMapEvents } from 'react-leaflet';
import debounce from 'lodash.debounce';
import { getAirports } from '@lib/airport.ts'; import { getAirports } from '@lib/airport.ts';
import AirportMarker from '@components/AirportMarker.tsx'; import AirportMarker from '@components/AirportMarker.tsx';
import { LeafletEvent } from 'leaflet';
import { LayerInfo } from '@/App.tsx'; import { LayerInfo } from '@/App.tsx';
interface Bounds { const EXPANSION_FACTOR = 0.5;
northEast: { lat: number; lon: number };
southWest: { lat: number; lon: number };
}
export default function AirportLayer({ export default function AirportLayer({
setAirport, setAirport,
@@ -21,76 +18,48 @@ export default function AirportLayer({
selectedLayer: LayerInfo; selectedLayer: LayerInfo;
}) { }) {
const [airports, setAirports] = useState<Airport[]>([]); const [airports, setAirports] = useState<Airport[]>([]);
const lastBoundsRef = useRef<{ ne: any; sw: any } | null>(null);
function loadAirports(event: LeafletEvent) { const debouncedLoad = useRef(
const map = event.target; debounce(async (map: any) => {
const bounds = map.getBounds(); const b = map.getBounds();
const north = b.getNorth(),
south = b.getSouth();
const east = b.getEast(),
west = b.getWest();
const latDelta = (north - south) * EXPANSION_FACTOR;
const lonDelta = (east - west) * EXPANSION_FACTOR;
const boundsParam: Bounds = { // expanded bbox
northEast: { const ne = { lat: north + latDelta, lon: east + lonDelta };
lat: bounds.getNorth(), const sw = { lat: south - latDelta, lon: west - lonDelta };
lon: bounds.getEast() lastBoundsRef.current = { ne, sw };
},
southWest: {
lat: bounds.getSouth(),
lon: bounds.getWest()
}
};
getAirports({ try {
bounds: boundsParam, const resp = await getAirports({
metars: true, bounds: { northEast: ne, southWest: sw },
categories: [AirportCategory.HELIPORT, AirportCategory.SMALL, AirportCategory.MEDIUM, AirportCategory.LARGE] metars: true,
}) categories: [AirportCategory.HELIPORT, AirportCategory.SMALL, AirportCategory.MEDIUM, AirportCategory.LARGE]
.then((response) => { });
setAirports(response.data); setAirports(resp.data);
}) } catch (err) {
.catch((error) => { console.error('fetch error', err);
console.error('Error fetching airports:', error);
setAirports([]); setAirports([]);
}); }
} }, 300)
).current;
const map = useMapEvents({ const map = useMapEvents({
moveend: loadAirports move: () => debouncedLoad(map)
}); });
useEffect(() => { useEffect(() => {
if (map) { if (map) debouncedLoad(map);
loadAirports({ target: map } as LeafletEvent); return () => {
} debouncedLoad.cancel();
};
}, [map]); }, [map]);
// const categoryOrder: { [key in AirportCategory]?: number } = {
// [AirportCategory.LARGE]: 3,
// [AirportCategory.MEDIUM]: 2,
// [AirportCategory.SMALL]: 1,
// [AirportCategory.HELIPORT]: 0
// };
// const sortedAirports = airports.slice().sort((a, b) => {
// // Compare by airport category first.
// const categoryA = categoryOrder[a.category] ?? 4;
// const categoryB = categoryOrder[b.category] ?? 4;
// if (categoryA !== categoryB) {
// return categoryA - categoryB;
// }
//
// // Then compare by flight category if available.
// // Assuming that latest_metar.flight_category is a string and "UNKN" needs to come last.
// const fcA = a.latest_metar?.flight_category ?? 'UNKN';
// const fcB = b.latest_metar?.flight_category ?? 'UNKN';
//
// if (fcA === 'UNKN' && fcB !== 'UNKN') return 1;
// if (fcB === 'UNKN' && fcA !== 'UNKN') return -1;
//
// // If both flight categories are not "UNKN", do a simple alphabetical comparison.
// // (You may wish to customize this logic based on the actual flight category values.)
// if (fcA < fcB) return -1;
// if (fcA > fcB) return 1;
// return 0;
// });
return ( return (
<> <>
{airports.map((airport, index) => ( {airports.map((airport, index) => (

View File

@@ -1,7 +1,8 @@
.header { .header {
height: 56px; height: 56px;
padding: 0 16px 0 16px; padding: 0 16px 0 16px;
background-color: var(--mantine-color-body); /*background-color: var(--mantine-color-body);*/
background: #32495f;
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
} }