From f1a0778e968b1f25a10fe163bed25851c7ef0699 Mon Sep 17 00:00:00 2001 From: Vladimir Simakhin Date: Fri, 14 Feb 2025 16:10:52 +0100 Subject: [PATCH] add first part of airports page and some optimization --- app/handlers_airport.go | 36 +- app/routes.go | 3 +- app/ui/package-lock.json | 352 +++++++++--------- app/ui/package.json | 4 +- .../components/Aircrafts/AircraftsTable.jsx | 10 +- .../Aircrafts/CSVAircraftExportButton.jsx | 39 -- .../Aircrafts/CSVCategoriesExportButton.jsx | 38 -- .../components/Aircrafts/CategoriesTable.jsx | 10 +- app/ui/src/components/Airports/Airports.jsx | 35 ++ .../Airports/StandardAirportsTable.jsx | 102 +++++ .../components/Licensing/CSVExportButton.jsx | 43 --- .../components/Licensing/LicensingTable.jsx | 12 +- .../src/components/Logbook/LogbookTable.jsx | 4 +- app/ui/src/components/Logbook/helpers.jsx | 2 +- .../CSVExportButton.jsx | 63 +++- app/ui/src/constants/constants.jsx | 10 +- app/ui/src/main.jsx | 2 + app/ui/src/pages/AirportsPage.jsx | 10 + app/ui/src/util/http/airport.jsx | 12 +- 19 files changed, 427 insertions(+), 360 deletions(-) delete mode 100644 app/ui/src/components/Aircrafts/CSVAircraftExportButton.jsx delete mode 100644 app/ui/src/components/Aircrafts/CSVCategoriesExportButton.jsx create mode 100644 app/ui/src/components/Airports/Airports.jsx create mode 100644 app/ui/src/components/Airports/StandardAirportsTable.jsx delete mode 100644 app/ui/src/components/Licensing/CSVExportButton.jsx rename app/ui/src/components/{Logbook => UIElements}/CSVExportButton.jsx (50%) create mode 100644 app/ui/src/pages/AirportsPage.jsx diff --git a/app/handlers_airport.go b/app/handlers_airport.go index 7316ddc..d47df26 100644 --- a/app/handlers_airport.go +++ b/app/handlers_airport.go @@ -33,6 +33,17 @@ func (app *application) HandlerAirportByID(w http.ResponseWriter, r *http.Reques app.writeJSON(w, http.StatusOK, airport) } +// HandlerApiAirportList returns a list of standard airports +func (app *application) HandlerApiAirportList(w http.ResponseWriter, r *http.Request) { + airports, err := app.db.GetStandardAirports() + if err != nil { + app.handleError(w, err) + return + } + + app.writeJSON(w, http.StatusOK, airports) +} + ///////////////////////////////////////////////// // for futher review ///////////////////////////////////////////////// @@ -262,28 +273,3 @@ func (app *application) HandlerAirportCustomData(w http.ResponseWriter, r *http. } // HandlerAirportDBData generates data for the standard airports table -func (app *application) HandlerAirportDBData(w http.ResponseWriter, r *http.Request) { - - type TableData struct { - Data [][]string `json:"data"` - } - - var tableData TableData - - airports, err := app.db.GetStandardAirports() - if err != nil { - app.errorLog.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - for _, item := range airports { - tableRow := []string{item.ICAO, item.IATA, item.Name, item.City, item.Country, - fmt.Sprintf("%d", item.Elevation), fmt.Sprintf("%f", item.Lat), fmt.Sprintf("%f", item.Lon), "", - } - - tableData.Data = append(tableData.Data, tableRow) - } - - app.writeJSON(w, http.StatusOK, tableData) -} diff --git a/app/routes.go b/app/routes.go index 7bac93c..52a7534 100644 --- a/app/routes.go +++ b/app/routes.go @@ -168,6 +168,7 @@ func (app *application) routes() *chi.Mux { // airports r.Route("/airport", func(r chi.Router) { + r.With(middleware.Compress(5)).Get("/standard-list", app.HandlerApiAirportList) r.Get("/{id}", app.HandlerAirportByID) }) @@ -200,7 +201,7 @@ func (app *application) routes() *chi.Mux { // airports r.Get(APIAirportID, app.HandlerAirportByID) r.Get(APIAirportUpdate, app.HandlerAirportUpdate) - r.Get(APIAirportStandardData, app.HandlerAirportDBData) + // r.Get(APIAirportStandardData, app.HandlerAirportDBData) r.Get(APIAirportCustomData, app.HandlerAirportCustomData) r.Post(APIAirportAddCustom, app.HandlerAirportAddCustom) r.Post(APIAirportDeleteCustom, app.HandlerAirportDeleteCustom) diff --git a/app/ui/package-lock.json b/app/ui/package-lock.json index 0b353a6..f04612b 100644 --- a/app/ui/package-lock.json +++ b/app/ui/package-lock.json @@ -20,7 +20,7 @@ "dayjs": "^1.11.13", "export-to-csv": "^1.4.0", "file-type": "^20.0.0", - "material-react-table": "^3.0.3", + "material-react-table": "3.0.3", "ol": "^10.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -77,22 +77,21 @@ } }, "node_modules/@babel/core": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz", - "integrity": "sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.8", + "@babel/generator": "^7.26.9", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.8", - "@babel/template": "^7.26.8", - "@babel/traverse": "^7.26.8", - "@babel/types": "^7.26.8", - "@types/gensync": "^1.0.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -114,13 +113,13 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", - "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -212,25 +211,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", - "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", - "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.8" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -270,9 +269,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -282,30 +281,30 @@ } }, "node_modules/@babel/template": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", - "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8" + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", - "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.8", - "@babel/parser": "^7.26.8", - "@babel/template": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -323,9 +322,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", - "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1227,9 +1226,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.3.tgz", - "integrity": "sha512-hlyOzo2ObarllAOeT1ZSAusADE5NZNencUeIvXrdQ1Na+FL1lcznhbxfV5He1KqGiuR8Az3xtCUcYKwMVGFdzg==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.4.tgz", + "integrity": "sha512-r+J0EditrekkTtO2CnCBCOGpNaDYwJqz8lH4rj6o/anDcskZFJodBlG8aCJkS8DL/CF/9EHS+Gz53EbmYEnQbw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1237,9 +1236,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.3.tgz", - "integrity": "sha512-3IY9LpjkwIJVgL/SkZQKKCUcumdHdQEsJaIavvsQze2QEztBt0HJ17naToN0DBBdhKdtwX5xXrfD6ZFUeWWk8g==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.4.tgz", + "integrity": "sha512-uF1chGaoFmYdRUomK6f8kgJfWosk9A3HXWiVD0vQm+2mE7f25eTQ1E8RRO11LXpnUBqu8Rbv/uGlpnjT/u1Ksg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1252,7 +1251,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.3", + "@mui/material": "^6.4.4", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1308,13 +1307,13 @@ } }, "node_modules/@mui/material": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.3.tgz", - "integrity": "sha512-ubtQjplbWneIEU8Y+4b2VA0CDBlyH5I3AmVFGmsLyDe/bf0ubxav5t11c8Afem6rkSFWPlZA2DilxmGka1xiKQ==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.4.tgz", + "integrity": "sha512-ISVPrIsPQsxnwvS40C4u03AuNSPigFeS2+n1qpuEZ94hDsdMi19dQM2JcC9CHEhXecSIQjP1RTyY0mPiSpSrFQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.3", + "@mui/core-downloads-tracker": "^6.4.4", "@mui/system": "^6.4.3", "@mui/types": "^7.2.21", "@mui/utils": "^6.4.3", @@ -1613,9 +1612,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", - "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz", + "integrity": "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw==", "cpu": [ "arm" ], @@ -1626,9 +1625,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", - "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz", + "integrity": "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg==", "cpu": [ "arm64" ], @@ -1639,9 +1638,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", - "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz", + "integrity": "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA==", "cpu": [ "arm64" ], @@ -1652,9 +1651,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", - "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz", + "integrity": "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA==", "cpu": [ "x64" ], @@ -1665,9 +1664,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", - "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz", + "integrity": "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA==", "cpu": [ "arm64" ], @@ -1678,9 +1677,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", - "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz", + "integrity": "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q==", "cpu": [ "x64" ], @@ -1691,9 +1690,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", - "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz", + "integrity": "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ==", "cpu": [ "arm" ], @@ -1704,9 +1703,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", - "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz", + "integrity": "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw==", "cpu": [ "arm" ], @@ -1717,9 +1716,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", - "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz", + "integrity": "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA==", "cpu": [ "arm64" ], @@ -1730,9 +1729,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", - "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz", + "integrity": "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw==", "cpu": [ "arm64" ], @@ -1743,9 +1742,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", - "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz", + "integrity": "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw==", "cpu": [ "loong64" ], @@ -1756,9 +1755,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", - "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz", + "integrity": "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w==", "cpu": [ "ppc64" ], @@ -1769,9 +1768,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", - "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz", + "integrity": "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw==", "cpu": [ "riscv64" ], @@ -1782,9 +1781,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", - "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz", + "integrity": "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw==", "cpu": [ "s390x" ], @@ -1795,9 +1794,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", - "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz", + "integrity": "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA==", "cpu": [ "x64" ], @@ -1808,9 +1807,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", - "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz", + "integrity": "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ==", "cpu": [ "x64" ], @@ -1821,9 +1820,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", - "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz", + "integrity": "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw==", "cpu": [ "arm64" ], @@ -1834,9 +1833,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", - "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz", + "integrity": "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg==", "cpu": [ "ia32" ], @@ -1847,9 +1846,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", - "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz", + "integrity": "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w==", "cpu": [ "x64" ], @@ -1902,9 +1901,9 @@ } }, "node_modules/@tanstack/react-table": { - "version": "8.20.6", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.6.tgz", - "integrity": "sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ==", + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz", + "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==", "license": "MIT", "dependencies": { "@tanstack/table-core": "8.20.5" @@ -1922,20 +1921,20 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz", - "integrity": "sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==", + "version": "3.10.9", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz", + "integrity": "sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==", "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.11.2" + "@tanstack/virtual-core": "3.10.9" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/@tanstack/table-core": { @@ -1952,9 +1951,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz", - "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==", + "version": "3.10.9", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.9.tgz", + "integrity": "sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==", "license": "MIT", "funding": { "type": "github", @@ -2120,12 +2119,6 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, - "node_modules/@types/gensync": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz", - "integrity": "sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA==", - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2522,9 +2515,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2904,9 +2897,9 @@ "license": "ISC" }, "node_modules/electron-to-chromium": { - "version": "1.5.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", - "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", + "version": "1.5.100", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.100.tgz", + "integrity": "sha512-u1z9VuzDXV86X2r3vAns0/5ojfXBue9o0+JDUDBKYqGLjxLkSqsSUoPU/6kW0gx76V44frHaf6Zo+QF74TQCMg==", "license": "ISC" }, "node_modules/error-ex": { @@ -3062,13 +3055,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -3151,9 +3147,9 @@ } }, "node_modules/eslint": { - "version": "9.20.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.0.tgz", - "integrity": "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { @@ -3519,9 +3515,9 @@ "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { @@ -3699,9 +3695,9 @@ } }, "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", "engines": { @@ -4593,14 +4589,14 @@ } }, "node_modules/material-react-table": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/material-react-table/-/material-react-table-3.1.0.tgz", - "integrity": "sha512-/zPn38QhxQE7mkwLex4CojX3UP2+/+/u7NVq7CHS5d6P8LdTteJPVYXVzym/uhaXzAzFB1ojsbP7zI/y6iUdtQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/material-react-table/-/material-react-table-3.0.3.tgz", + "integrity": "sha512-XeMRM6DiZgkbm2luuuAmz6ULWM4HDGOT9JgwcQWAAYCgUnbRoV3HjfnC8AgAoK/Oa91+tfYQzQLcDabiZaxWqw==", "license": "MIT", "dependencies": { "@tanstack/match-sorter-utils": "8.19.4", - "@tanstack/react-table": "8.20.6", - "@tanstack/react-virtual": "3.11.2", + "@tanstack/react-table": "8.20.5", + "@tanstack/react-virtual": "3.10.9", "highlight-words": "2.0.0" }, "engines": { @@ -5069,9 +5065,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "funding": [ { "type": "opencollective", @@ -5364,9 +5360,9 @@ } }, "node_modules/rollup": { - "version": "4.34.6", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", - "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz", + "integrity": "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -5379,25 +5375,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.6", - "@rollup/rollup-android-arm64": "4.34.6", - "@rollup/rollup-darwin-arm64": "4.34.6", - "@rollup/rollup-darwin-x64": "4.34.6", - "@rollup/rollup-freebsd-arm64": "4.34.6", - "@rollup/rollup-freebsd-x64": "4.34.6", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", - "@rollup/rollup-linux-arm-musleabihf": "4.34.6", - "@rollup/rollup-linux-arm64-gnu": "4.34.6", - "@rollup/rollup-linux-arm64-musl": "4.34.6", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", - "@rollup/rollup-linux-riscv64-gnu": "4.34.6", - "@rollup/rollup-linux-s390x-gnu": "4.34.6", - "@rollup/rollup-linux-x64-gnu": "4.34.6", - "@rollup/rollup-linux-x64-musl": "4.34.6", - "@rollup/rollup-win32-arm64-msvc": "4.34.6", - "@rollup/rollup-win32-ia32-msvc": "4.34.6", - "@rollup/rollup-win32-x64-msvc": "4.34.6", + "@rollup/rollup-android-arm-eabi": "4.34.7", + "@rollup/rollup-android-arm64": "4.34.7", + "@rollup/rollup-darwin-arm64": "4.34.7", + "@rollup/rollup-darwin-x64": "4.34.7", + "@rollup/rollup-freebsd-arm64": "4.34.7", + "@rollup/rollup-freebsd-x64": "4.34.7", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.7", + "@rollup/rollup-linux-arm-musleabihf": "4.34.7", + "@rollup/rollup-linux-arm64-gnu": "4.34.7", + "@rollup/rollup-linux-arm64-musl": "4.34.7", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.7", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7", + "@rollup/rollup-linux-riscv64-gnu": "4.34.7", + "@rollup/rollup-linux-s390x-gnu": "4.34.7", + "@rollup/rollup-linux-x64-gnu": "4.34.7", + "@rollup/rollup-linux-x64-musl": "4.34.7", + "@rollup/rollup-win32-arm64-msvc": "4.34.7", + "@rollup/rollup-win32-ia32-msvc": "4.34.7", + "@rollup/rollup-win32-x64-msvc": "4.34.7", "fsevents": "~2.3.2" } }, diff --git a/app/ui/package.json b/app/ui/package.json index 8a6ff20..a328b88 100644 --- a/app/ui/package.json +++ b/app/ui/package.json @@ -22,7 +22,7 @@ "dayjs": "^1.11.13", "export-to-csv": "^1.4.0", "file-type": "^20.0.0", - "material-react-table": "^3.0.3", + "material-react-table": "3.0.3", "ol": "^10.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -41,4 +41,4 @@ "globals": "^15.12.0", "vite": "^6.0.1" } -} +} \ No newline at end of file diff --git a/app/ui/src/components/Aircrafts/AircraftsTable.jsx b/app/ui/src/components/Aircrafts/AircraftsTable.jsx index 840f97e..f58b2e0 100644 --- a/app/ui/src/components/Aircrafts/AircraftsTable.jsx +++ b/app/ui/src/components/Aircrafts/AircraftsTable.jsx @@ -8,11 +8,11 @@ import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; import LinearProgress from '@mui/material/LinearProgress'; // Custom components and libraries -import CSVAircraftExportButton from './CSVAircraftExportButton'; -import { tableJSONCodec } from '../../constants/constants'; +import { defaultColumnFilterTextFieldProps, tableJSONCodec } from '../../constants/constants'; import { fetchAircrafts } from "../../util/http/aircraft"; import { useErrorNotification } from "../../hooks/useAppNotifications"; import { dateFilterFn } from '../../util/helpers'; +import CSVExportButton from '../UIElements/CSVExportButton'; const paginationKey = 'aircrafts-table-page-size'; const columnVisibilityKey = 'aircrafts-table-column-visibility'; @@ -67,14 +67,12 @@ export const AircraftsTable = ({ ...props }) => { onColumnVisibilityChange: setColumnVisibility, renderTopToolbarCustomActions: ({ table }) => ( - + ), onPaginationChange: setPagination, state: { pagination, columnFilters: columnFilters, columnVisibility }, - defaultColumn: { - muiFilterTextFieldProps: ({ column }) => ({ label: `Filter by ${column.columnDef.header}` }), - }, + defaultColumn: { muiFilterTextFieldProps: defaultColumnFilterTextFieldProps }, ...tableOptions }); diff --git a/app/ui/src/components/Aircrafts/CSVAircraftExportButton.jsx b/app/ui/src/components/Aircrafts/CSVAircraftExportButton.jsx deleted file mode 100644 index 9b7f2ba..0000000 --- a/app/ui/src/components/Aircrafts/CSVAircraftExportButton.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useCallback } from 'react'; -// MUI UI elements -import Tooltip from '@mui/material/Tooltip'; -import IconButton from '@mui/material/IconButton'; -// MUI Icons -import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; - -import { mkConfig, generateCsv, download } from 'export-to-csv'; - -const csvConfig = mkConfig({ - fieldSeparator: ',', - decimalSeparator: '.', - useKeysAsHeaders: true, - filename: 'aircrafts', -}); - -const handleExportRows = (rows) => { - const rowData = rows.map((row) => ({ - "Registration": row.original.reg, - "Model": row.original.model, - "Category": row.original.category, - })); - const csv = generateCsv(csvConfig)(rowData); - download(csvConfig)(csv); -}; - -export const CSVAircraftExportButton = ({ table }) => { - const handleCSVExport = useCallback((table) => { - handleExportRows(table.getPrePaginationRowModel().rows); - }, []); - - return ( - - handleCSVExport(table)} size="small"> - - ) -} - -export default CSVAircraftExportButton; \ No newline at end of file diff --git a/app/ui/src/components/Aircrafts/CSVCategoriesExportButton.jsx b/app/ui/src/components/Aircrafts/CSVCategoriesExportButton.jsx deleted file mode 100644 index bc9b8fd..0000000 --- a/app/ui/src/components/Aircrafts/CSVCategoriesExportButton.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useCallback } from 'react'; -// MUI UI elements -import Tooltip from '@mui/material/Tooltip'; -import IconButton from '@mui/material/IconButton'; -// MUI Icons -import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; - -import { mkConfig, generateCsv, download } from 'export-to-csv'; - -const csvConfig = mkConfig({ - fieldSeparator: ',', - decimalSeparator: '.', - useKeysAsHeaders: true, - filename: 'categories', -}); - -const handleExportRows = (rows) => { - const rowData = rows.map((row) => ({ - "Type": row.original.model, - "Category": row.original.category, - })); - const csv = generateCsv(csvConfig)(rowData); - download(csvConfig)(csv); -}; - -export const CSVCategoriesExportButton = ({ table }) => { - const handleCSVExport = useCallback((table) => { - handleExportRows(table.getPrePaginationRowModel().rows); - }, []); - - return ( - - handleCSVExport(table)} size="small"> - - ) -} - -export default CSVCategoriesExportButton; \ No newline at end of file diff --git a/app/ui/src/components/Aircrafts/CategoriesTable.jsx b/app/ui/src/components/Aircrafts/CategoriesTable.jsx index d4aadf3..e8f925a 100644 --- a/app/ui/src/components/Aircrafts/CategoriesTable.jsx +++ b/app/ui/src/components/Aircrafts/CategoriesTable.jsx @@ -11,13 +11,13 @@ import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; import LinearProgress from '@mui/material/LinearProgress'; // Custom components and libraries -import { tableJSONCodec } from '../../constants/constants'; +import { defaultColumnFilterTextFieldProps, tableJSONCodec } from '../../constants/constants'; import { useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { fetchAircraftModelsCategories } from '../../util/http/aircraft'; import { useErrorNotification } from '../../hooks/useAppNotifications'; import EditCategoriesModal from './EditCategoriesModal'; -import CSVCategoriesExportButton from './CSVCategoriesExportButton'; +import CSVExportButton from '../UIElements/CSVExportButton'; const paginationKey = 'categories-table-page-size'; const columnVisibilityKey = 'categories-table-column-visibility'; @@ -82,14 +82,12 @@ export const CategoriesTable = ({ ...props }) => { onColumnVisibilityChange: setColumnVisibility, renderTopToolbarCustomActions: ({ table }) => ( - + ), onPaginationChange: setPagination, state: { pagination, columnFilters: columnFilters, columnVisibility }, - defaultColumn: { - muiFilterTextFieldProps: ({ column }) => ({ label: `Filter by ${column.columnDef.header}` }), - }, + defaultColumn: { muiFilterTextFieldProps: defaultColumnFilterTextFieldProps }, renderRowActions: renderRowActions, ...tableOptions }); diff --git a/app/ui/src/components/Airports/Airports.jsx b/app/ui/src/components/Airports/Airports.jsx new file mode 100644 index 0000000..92ed7e8 --- /dev/null +++ b/app/ui/src/components/Airports/Airports.jsx @@ -0,0 +1,35 @@ +// MUI +import Grid from "@mui/material/Grid2"; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +// Custom +import CardHeader from "../UIElements/CardHeader"; +import StandardAirportsTable from "./StandardAirportsTable"; + +export const Airports = () => { + + return ( + <> + + + + + + + + + + + + + + + + + + + + ); +} + +export default Airports; \ No newline at end of file diff --git a/app/ui/src/components/Airports/StandardAirportsTable.jsx b/app/ui/src/components/Airports/StandardAirportsTable.jsx new file mode 100644 index 0000000..ca4f790 --- /dev/null +++ b/app/ui/src/components/Airports/StandardAirportsTable.jsx @@ -0,0 +1,102 @@ +import { MaterialReactTable, useMaterialReactTable, MRT_TableHeadCellFilterContainer, MRT_ExpandAllButton } from 'material-react-table'; +import { useMemo, useState } from 'react'; +import { useLocalStorageState } from '@toolpad/core/useLocalStorageState'; +import { useNavigate } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +// MUI UI elements +import Box from '@mui/material/Box'; +import Drawer from '@mui/material/Drawer'; +import LinearProgress from '@mui/material/LinearProgress'; +// Custom components and libraries +import { defaultColumnFilterTextFieldProps, tableJSONCodec } from '../../constants/constants'; +import { useErrorNotification } from "../../hooks/useAppNotifications"; +import { fetchAirports } from '../../util/http/airport'; +import CSVExportButton from '../UIElements/CSVExportButton'; + +const paginationKey = 'standard-airports-table-page-size'; +const columnVisibilityKey = 'standard-airports-table-column-visibility'; + +const tableOptions = { + initialState: { density: 'compact' }, + positionToolbarAlertBanner: 'bottom', + groupedColumnMode: 'remove', + enableColumnResizing: true, + enableGlobalFilterModes: true, + enableColumnFilters: true, + enableColumnDragging: false, + enableColumnPinning: false, + enableGrouping: true, + enableDensityToggle: false, + columnResizeMode: 'onEnd', + muiTablePaperProps: { variant: 'outlined', elevation: 0 }, + columnFilterDisplayMode: 'custom', + enableFacetedValues: true, + enableSorting: true, + enableColumnActions: true, +} + +export const StandardAirportsTable = ({ ...props }) => { + const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false); + const [columnFilters, setColumnFilters] = useState([]); + const [columnVisibility, setColumnVisibility] = useLocalStorageState(columnVisibilityKey, {}, { codec: tableJSONCodec }); + const [pagination, setPagination] = useLocalStorageState(paginationKey, { pageIndex: 0, pageSize: 15 }, { codec: tableJSONCodec }); + + const navigate = useNavigate(); + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['airports'], + queryFn: ({ signal }) => fetchAirports({ signal, navigate }), + }); + useErrorNotification({ isError, error, fallbackMessage: 'Failed to load airports' }); + + const columns = useMemo(() => [ + { accessorKey: "icao", header: "ICAO", size: 100 }, + { accessorKey: "iata", header: "IATA", size: 100 }, + { accessorKey: "name", header: "Name", grow: true }, + { accessorKey: "city", header: "City", size: 120 }, + { accessorKey: "country", header: "Country", size: 70 }, + { accessorKey: "elevation", header: "Elevation", size: 70 }, + { accessorKey: "lat", header: "Lat", size: 70 }, + { accessorKey: "lon", header: "Lon", size: 70 }, + ], []); + + const table = useMaterialReactTable({ + isLoading: isLoading, + columns: columns, + data: data ?? [], + onShowColumnFiltersChange: () => (setIsFilterDrawerOpen(true)), + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + renderTopToolbarCustomActions: ({ table }) => ( + + + + ), + onPaginationChange: setPagination, + state: { pagination, columnFilters: columnFilters, columnVisibility }, + defaultColumn: { muiFilterTextFieldProps: defaultColumnFilterTextFieldProps }, + ...tableOptions + }); + + return ( + <> + {isLoading && } + + setIsFilterDrawerOpen(false)} sx={{ + '& .MuiDrawer-paper': { + marginTop: '64px', + height: 'calc(100% - 64px)', + }, + }}> + + {table.getLeafHeaders().map((header) => { + if (header.id.startsWith('mrt-') || header.id.startsWith('center_1_')) return null; + return < MRT_TableHeadCellFilterContainer key={header.id} header={header} table={table} in /> + })} + + + + ); +} + +export default StandardAirportsTable; \ No newline at end of file diff --git a/app/ui/src/components/Licensing/CSVExportButton.jsx b/app/ui/src/components/Licensing/CSVExportButton.jsx deleted file mode 100644 index 0d8403a..0000000 --- a/app/ui/src/components/Licensing/CSVExportButton.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useCallback } from 'react'; -// MUI UI elements -import Tooltip from '@mui/material/Tooltip'; -import IconButton from '@mui/material/IconButton'; -// MUI Icons -import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; - -import { mkConfig, generateCsv, download } from 'export-to-csv'; - -const csvConfig = mkConfig({ - fieldSeparator: ',', - decimalSeparator: '.', - useKeysAsHeaders: true, - filename: 'licensing', -}); - -const handleExportRows = (rows) => { - const rowData = rows.map((row) => ({ - "Category": row.original.category, - "Name": row.original.name, - "Number": row.original.number, - "Issued": row.original.issued, - "Valid From": row.original.valid_from, - "Valid Until": row.original.valid_until, - "Remarks": row.original.remarks, - })); - const csv = generateCsv(csvConfig)(rowData); - download(csvConfig)(csv); -}; - -export const CSVExportButton = ({ table }) => { - const handleCSVExport = useCallback((table) => { - handleExportRows(table.getPrePaginationRowModel().rows); - }, []); - - return ( - - handleCSVExport(table)} size="small"> - - ) -} - -export default CSVExportButton; \ No newline at end of file diff --git a/app/ui/src/components/Licensing/LicensingTable.jsx b/app/ui/src/components/Licensing/LicensingTable.jsx index d117fca..23e6701 100644 --- a/app/ui/src/components/Licensing/LicensingTable.jsx +++ b/app/ui/src/components/Licensing/LicensingTable.jsx @@ -7,11 +7,11 @@ import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; import Typography from "@mui/material/Typography"; // Custom components and libraries -import CSVExportButton from './CSVExportButton'; import NewLicenseRecordButton from './NewLicenseRecordButton'; import { calculateExpiry, createDateColumn, getExpireColor } from "./helpers"; -import { tableJSONCodec } from '../../constants/constants'; +import { defaultColumnFilterTextFieldProps, tableJSONCodec } from '../../constants/constants'; import { dateFilterFn } from '../../util/helpers'; +import CSVExportButton from '../UIElements/CSVExportButton'; const paginationKey = 'licensing-table-page-size'; const columnVisibilityKey = 'licensing-table-column-visibility'; @@ -93,14 +93,12 @@ export const LisencingTable = ({ data, isLoading, ...props }) => { renderTopToolbarCustomActions: ({ table }) => ( - + ), onPaginationChange: setPagination, state: { pagination, columnFilters: columnFilters, columnVisibility }, - defaultColumn: { - muiFilterTextFieldProps: ({ column }) => ({ label: `Filter by ${column.columnDef.header}` }), - }, + defaultColumn: { muiFilterTextFieldProps: defaultColumnFilterTextFieldProps }, ...tableOptions }); @@ -116,7 +114,7 @@ export const LisencingTable = ({ data, isLoading, ...props }) => { {table.getLeafHeaders().map((header) => { if (header.id.startsWith('mrt-') || header.id.startsWith('Expire') || header.id.startsWith('center_1_')) return null; - return < MRT_TableHeadCellFilterContainer key={header.id} header={header} table={table} in /> + return })} diff --git a/app/ui/src/components/Logbook/LogbookTable.jsx b/app/ui/src/components/Logbook/LogbookTable.jsx index 6cbc0d5..beb3f8e 100644 --- a/app/ui/src/components/Logbook/LogbookTable.jsx +++ b/app/ui/src/components/Logbook/LogbookTable.jsx @@ -7,11 +7,11 @@ import Drawer from '@mui/material/Drawer'; // Custom components and libraries import { getFilterLabel, landingFilterFn, timeFilterFn } from './helpers'; import PDFExportButton from './PDFExportButton'; -import CSVExportButton from './CSVExportButton'; import NewFlightRecordButton from './NewFlightRecordButton'; import { tableJSONCodec } from '../../constants/constants'; import { createColumn, createDateColumn, createLandingColumn, createTimeColumn, renderHeader, renderProps, renderTextProps, renderTotalFooter } from "./helpers"; import { dateFilterFn } from '../../util/helpers'; +import CSVExportButton from '../UIElements/CSVExportButton'; const paginationKey = 'logbook-table-page-size'; const columnVisibilityKey = 'logbook-table-column-visibility'; @@ -133,7 +133,7 @@ export const LogbookTable = ({ data, isLoading, ...props }) => { renderTopToolbarCustomActions: ({ table }) => ( - + ), diff --git a/app/ui/src/components/Logbook/helpers.jsx b/app/ui/src/components/Logbook/helpers.jsx index ecac915..b583dc3 100644 --- a/app/ui/src/components/Logbook/helpers.jsx +++ b/app/ui/src/components/Logbook/helpers.jsx @@ -127,7 +127,7 @@ export const getFilterLabel = (column) => { fieldName = `${header} Time`; } - return { label: `Filter by ${fieldName}` }; + return { label: `Filter by ${fieldName}`, placeholder: '', InputLabelProps: { shrink: true } }; }; // custom filter function for time range diff --git a/app/ui/src/components/Logbook/CSVExportButton.jsx b/app/ui/src/components/UIElements/CSVExportButton.jsx similarity index 50% rename from app/ui/src/components/Logbook/CSVExportButton.jsx rename to app/ui/src/components/UIElements/CSVExportButton.jsx index 4771b94..a9bf49f 100644 --- a/app/ui/src/components/Logbook/CSVExportButton.jsx +++ b/app/ui/src/components/UIElements/CSVExportButton.jsx @@ -7,15 +7,48 @@ import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; import { mkConfig, generateCsv, download } from 'export-to-csv'; -const csvConfig = mkConfig({ +const defaultConfig = { fieldSeparator: ',', decimalSeparator: '.', useKeysAsHeaders: true, - filename: 'logbook', -}); + filename: 'export', +}; + +// Separate mappers for different exports +const exportMappers = { + aircrafts: (rows) => rows.map((row) => ({ + "Registration": row.original.reg, + "Model": row.original.model, + "Category": row.original.category, + })), + + categories: (rows) => rows.map((row) => ({ + "Type": row.original.model, + "Category": row.original.category, + })), -const handleExportRows = (rows) => { - const rowData = rows.map((row) => ({ + airports: (rows) => rows.map((row) => ({ + "ICAO": row.original.icao, + "IATA": row.original.iata, + "Name": row.original.name, + "City": row.original.city, + "Country": row.original.country, + "Elevation": row.original.elevation, + "Lat": row.original.lat, + "Lon": row.original.lon, + })), + + licensing: (rows) => rows.map((row) => ({ + "Category": row.original.category, + "Name": row.original.name, + "Number": row.original.number, + "Issued": row.original.issued, + "Valid From": row.original.valid_from, + "Valid Until": row.original.valid_until, + "Remarks": row.original.remarks, + })), + + logbook: (rows) => rows.map((row) => ({ "Date": row.original.date, "Departure Place": row.original.departure.place, "Departure Time": row.original.departure.time, @@ -39,19 +72,29 @@ const handleExportRows = (rows) => { "SIM Time": row.original.sim.time, "PIC Name": row.original.pic_name, "Remarks": row.original.remarks, - })); + })), +}; + +const handleExportRows = (rows, type) => { + const mapper = exportMappers[type]; + if (!mapper) return; + + const rowData = mapper(rows); + const csvConfig = mkConfig({ ...defaultConfig, filename: type }); const csv = generateCsv(csvConfig)(rowData); download(csvConfig)(csv); }; -export const CSVExportButton = ({ table }) => { - const handleCSVExport = useCallback((table) => { - handleExportRows(table.getPrePaginationRowModel().rows); +export const CSVExportButton = ({ table, type }) => { + const handleCSVExport = useCallback((table, type) => { + handleExportRows(table.getPrePaginationRowModel().rows, type); }, []); return ( - handleCSVExport(table)} size="small"> + handleCSVExport(table, type)} size="small"> + + ) } diff --git a/app/ui/src/constants/constants.jsx b/app/ui/src/constants/constants.jsx index 8eff744..9e0620b 100644 --- a/app/ui/src/constants/constants.jsx +++ b/app/ui/src/constants/constants.jsx @@ -8,6 +8,7 @@ import FileUploadOutlinedIcon from '@mui/icons-material/FileUploadOutlined'; import SettingsIcon from '@mui/icons-material/Settings'; import GitHubIcon from '@mui/icons-material/GitHub'; import FlightOutlinedIcon from '@mui/icons-material/FlightOutlined'; +import FlightTakeoffOutlinedIcon from '@mui/icons-material/FlightTakeoffOutlined'; import dayjs from 'dayjs'; export const DRAWER_WIDTH = 200; @@ -20,6 +21,7 @@ export const NAV_ITEMS = [ { segment: 'licensing', title: 'Licensing', icon: }, { segment: 'map', title: 'Map', icon: }, { segment: 'aircrafts', title: 'Aircrafts', icon: }, + { segment: 'airports', title: 'Airports', icon: }, { kind: 'divider' }, { segment: 'stats', title: 'Statistics', icon: , children: [ @@ -153,4 +155,10 @@ export const tableJSONCodec = { } }, stringify: (value) => JSON.stringify(value), -} \ No newline at end of file +} + +export const defaultColumnFilterTextFieldProps = ({ column }) => ({ + label: `Filter by ${column.columnDef.header}`, + placeholder: '', + InputLabelProps: { shrink: true }, +}); \ No newline at end of file diff --git a/app/ui/src/main.jsx b/app/ui/src/main.jsx index abf0103..d777153 100644 --- a/app/ui/src/main.jsx +++ b/app/ui/src/main.jsx @@ -18,6 +18,7 @@ import SettingsPage from './pages/SettingsPage.jsx'; import SignInPage from './pages/SignInPage.jsx'; import LogoutPage from './pages/LogoutPage.jsx'; import AircraftsPage from './pages/AircraftsPage.jsx'; +import AirportsPage from './pages/AirportsPage.jsx'; const router = createBrowserRouter([ { @@ -44,6 +45,7 @@ const router = createBrowserRouter([ }, { path: 'map', element: }, { path: 'aircrafts', element: }, + { path: 'airports', element: }, { path: 'settings', element: }, ] }, diff --git a/app/ui/src/pages/AirportsPage.jsx b/app/ui/src/pages/AirportsPage.jsx new file mode 100644 index 0000000..5a205c6 --- /dev/null +++ b/app/ui/src/pages/AirportsPage.jsx @@ -0,0 +1,10 @@ +import Airports from "../components/Airports/Airports" + +export const AirportsPage = () => { + + return ( + + ); +} + +export default AirportsPage; \ No newline at end of file diff --git a/app/ui/src/util/http/airport.jsx b/app/ui/src/util/http/airport.jsx index beda36d..170857e 100644 --- a/app/ui/src/util/http/airport.jsx +++ b/app/ui/src/util/http/airport.jsx @@ -10,4 +10,14 @@ export const fetchAirport = async ({ signal, id, navigate }) => { signal: signal, }; return await handleFetch(url, options, navigate, 'Cannot fetch airport'); -} \ No newline at end of file +} + +export const fetchAirports = async ({ signal, navigate }) => { + const url = `${API_URL}/airport/standard-list`; + const options = { + method: 'GET', + headers: { 'Authorization': `Bearer ${getAuthToken()}` }, + signal: signal, + }; + return await handleFetch(url, options, navigate, 'Cannot fetch airports'); +}