diff --git a/frontend/.env.sample b/frontend/.env.sample index 1ce8cad1..1c535640 100644 --- a/frontend/.env.sample +++ b/frontend/.env.sample @@ -1,3 +1,46 @@ VITE_BASE_API_URL = 'http://localhost:8000/api/v1/' # backend api url -VITE_MATOMO_ID= 0 -VITE_MATOMO_APP_DOMAIN = "subdomain.hotosm.org" \ No newline at end of file +VITE_MATOMO_ID = 0 +VITE_MATOMO_APP_DOMAIN = "subdomain.hotosm.org" + +# The maximum allowed area size (in square meters) for training areas. +# Data type: Positive Integer e.g 500000 +MAX_TRAINING_AREA_SIZE = + +# The minimum allowed area size (in square meters) for training areas. +# Data type: Positive Integer e.g 500000 +MIN_TRAINING_AREA_SIZE = + +# The maximum file size (in bytes) allowed for training area upload. +# Data type: Positive Integer e.g 500000 +MAX_TRAINING_AREA_UPLOAD_FILE_SIZE = + +# The current version of the application. +# This is used in the OSM redirect callback when a training area is opened in OSM. +# Data type: String e.g 'v1.1' +FAIR_VERSION = + +# Comma separated hashtags to add to the OSM ID Editor redirection +# Data type: String e.g '#HOT-fAIr, #AI-Assited-Mapping' +OSM_HASHTAGS = + + +# The maximum zoom level for the map. +# Data type: Positive Integer e.g 22. Must be between 0 - 24 +MAX_ZOOM_LEVEL = + +# The minimum zoom level to show the training area labels. +# Data type: Positive Integer e.g 18. Must be between 0 - 24 +TRAINING_LABELS_MIN_ZOOM_LEVEL = + +# Training area and labels styles. +# Opacities are between 0 and 1 . E.g 0.5 +# Widths must be Positive Integers e.g 1, 2 etc. +# Colors must be hex codes or valid colors. E.g 'red', 'green', '#fff' +TRAINING_AREAS_AOI_FILL_COLOR = +TRAINING_AREAS_AOI_OUTLINE_COLOR = +TRAINING_AREAS_AOI_OUTLINE_WIDTH = +TRAINING_AREAS_AOI_FILL_OPACITY = +TRAINING_AREAS_AOI_LABELS_FILL_OPACITY = +TRAINING_AREAS_AOI_LABELS_OUTLINE_WIDTH = +TRAINING_AREAS_AOI_LABELS_FILL_COLOR = +TRAINING_AREAS_AOI_LABELS_OUTLINE_COLOR = \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index a547bf36..e440e68b 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,6 @@ dist-ssr *.njsproj *.sln *.sw? + +tsconfig.app.tsbuildinfo +tsconfig.node.tsbuildinfo \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index b826bb38..72da6a36 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,22 +14,34 @@ "@tanstack/react-query": "^5.59.0", "@tanstack/react-query-devtools": "^5.59.0", "@tanstack/react-table": "^8.20.5", + "@terraformer/wkt": "^2.2.1", + "@turf/area": "^7.1.0", + "@turf/bbox": "^7.1.0", "axios": "^1.7.7", "clsx": "^2.1.1", "framer-motion": "^11.5.4", + "geojson": "^0.5.0", "maplibre-gl": "^4.7.1", "react": "^18.3.1", + "react-confetti-explosion": "^2.1.2", "react-dom": "^18.3.1", + "react-dropzone": "^14.2.10", "react-error-boundary": "^4.0.13", "react-helmet-async": "^2.0.5", + "react-markdown": "^9.0.1", "react-router-dom": "^6.26.2", - "tailwind-merge": "^2.5.2" + "remark-gfm": "^4.0.0", + "tailwind-merge": "^2.5.2", + "terra-draw": "1.0.0-beta.8" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@tailwindcss/typography": "^0.5.15", "@tanstack/eslint-plugin-query": "^5.58.1", + "@types/geojson": "^7946.0.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/terraformer__wkt": "^2.0.3", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.20", "eslint": "^9.9.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 86ccc886..1485ac06 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -19,6 +19,15 @@ importers: "@tanstack/react-table": specifier: ^8.20.5 version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@terraformer/wkt": + specifier: ^2.2.1 + version: 2.2.1 + "@turf/area": + specifier: ^7.1.0 + version: 7.1.0 + "@turf/bbox": + specifier: ^7.1.0 + version: 7.1.0 axios: specifier: ^1.7.7 version: 1.7.7 @@ -27,41 +36,68 @@ importers: version: 2.1.1 framer-motion: specifier: ^11.5.4 - version: 11.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 11.9.0(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + geojson: + specifier: ^0.5.0 + version: 0.5.0 maplibre-gl: specifier: ^4.7.1 version: 4.7.1 react: specifier: ^18.3.1 version: 18.3.1 + react-confetti-explosion: + specifier: ^2.1.2 + version: 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-dropzone: + specifier: ^14.2.10 + version: 14.2.10(react@18.3.1) react-error-boundary: specifier: ^4.0.13 version: 4.0.13(react@18.3.1) react-helmet-async: specifier: ^2.0.5 version: 2.0.5(react@18.3.1) + react-markdown: + specifier: ^9.0.1 + version: 9.0.1(@types/react@18.3.10)(react@18.3.1) react-router-dom: specifier: ^6.26.2 version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + remark-gfm: + specifier: ^4.0.0 + version: 4.0.0 tailwind-merge: specifier: ^2.5.2 version: 2.5.2 + terra-draw: + specifier: 1.0.0-beta.8 + version: 1.0.0-beta.8 devDependencies: "@eslint/js": specifier: ^9.9.0 version: 9.11.1 + "@tailwindcss/typography": + specifier: ^0.5.15 + version: 0.5.15(tailwindcss@3.4.13) "@tanstack/eslint-plugin-query": specifier: ^5.58.1 version: 5.58.1(eslint@9.11.1(jiti@1.21.6))(typescript@5.6.2) + "@types/geojson": + specifier: ^7946.0.14 + version: 7946.0.14 "@types/react": specifier: ^18.3.3 version: 18.3.10 "@types/react-dom": specifier: ^18.3.0 version: 18.3.0 + "@types/terraformer__wkt": + specifier: ^2.0.3 + version: 2.0.3 "@vitejs/plugin-react": specifier: ^4.3.1 version: 4.3.2(vite@5.4.8) @@ -284,6 +320,18 @@ packages: } engines: { node: ">=14" } + "@emotion/is-prop-valid@0.7.3": + resolution: + { + integrity: sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==, + } + + "@emotion/memoize@0.7.1": + resolution: + { + integrity: sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==, + } + "@esbuild/aix-ppc64@0.21.5": resolution: { @@ -882,6 +930,14 @@ packages: } engines: { node: ">=14.17.0" } + "@tailwindcss/typography@0.5.15": + resolution: + { + integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==, + } + peerDependencies: + tailwindcss: ">=3.0.0 || insiders || >=4.0.0-alpha.20" + "@tanstack/eslint-plugin-query@5.58.1": resolution: { @@ -936,6 +992,36 @@ packages: } engines: { node: ">=12" } + "@terraformer/wkt@2.2.1": + resolution: + { + integrity: sha512-XDUsW/lvbMzFi7GIuRD9+UqR4QyP+5C+TugeJLMDczKIRbaHoE9J3N8zLSdyOGmnJL9B6xTS3YMMlBnMU0Ar5A==, + } + + "@turf/area@7.1.0": + resolution: + { + integrity: sha512-w91FEe02/mQfMPRX2pXua48scFuKJ2dSVMF2XmJ6+BJfFiCPxp95I3+Org8+ZsYv93CDNKbf0oLNEPnuQdgs2g==, + } + + "@turf/bbox@7.1.0": + resolution: + { + integrity: sha512-PdWPz9tW86PD78vSZj2fiRaB8JhUHy6piSa/QXb83lucxPK+HTAdzlDQMTKj5okRCU8Ox/25IR2ep9T8NdopRA==, + } + + "@turf/helpers@7.1.0": + resolution: + { + integrity: sha512-dTeILEUVeNbaEeoZUOhxH5auv7WWlOShbx7QSd4s0T4Z0/iz90z9yaVCtZOLbU89umKotwKaJQltBNO9CzVgaQ==, + } + + "@turf/meta@7.1.0": + resolution: + { + integrity: sha512-ZgGpWWiKz797Fe8lfRj7HKCkGR+nSJ/5aKXMyofCvLSc2PuYJs/qyyifDPWjASQQCzseJ7AlF2Pc/XQ/3XkkuA==, + } + "@types/babel__core@7.20.5": resolution: { @@ -960,6 +1046,18 @@ packages: integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==, } + "@types/debug@4.1.12": + resolution: + { + integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==, + } + + "@types/estree-jsx@1.0.5": + resolution: + { + integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==, + } + "@types/estree@1.0.6": resolution: { @@ -978,6 +1076,12 @@ packages: integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==, } + "@types/hast@3.0.4": + resolution: + { + integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==, + } + "@types/json-schema@7.0.15": resolution: { @@ -996,6 +1100,18 @@ packages: integrity: sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==, } + "@types/mdast@4.0.4": + resolution: + { + integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, + } + + "@types/ms@0.7.34": + resolution: + { + integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==, + } + "@types/pbf@3.0.5": resolution: { @@ -1026,12 +1142,30 @@ packages: integrity: sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==, } + "@types/terraformer__wkt@2.0.3": + resolution: + { + integrity: sha512-60CGvi30kMIKl2QERrE6LD5iPm4lutZ1M/mqBY4wrn6H/QlZQa/5CN1e6trZ6ZtSRHLbHLwG+egt/nAIDbPG0A==, + } + "@types/trusted-types@2.0.7": resolution: { integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, } + "@types/unist@2.0.11": + resolution: + { + integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==, + } + + "@types/unist@3.0.3": + resolution: + { + integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, + } + "@typescript-eslint/eslint-plugin@8.8.0": resolution: { @@ -1113,6 +1247,12 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + "@ungap/structured-clone@1.2.0": + resolution: + { + integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==, + } + "@vitejs/plugin-react@4.3.2": resolution: { @@ -1224,6 +1364,13 @@ packages: integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, } + attr-accept@2.2.4: + resolution: + { + integrity: sha512-2pA6xFIbdTUDCAwjN8nQwI+842VwzbDUXO2IYlpPXQIORgKnavorcr4Ce3rwh+zsNg9zK7QPsdvDj3Lum4WX4w==, + } + engines: { node: ">=4" } + autoprefixer@10.4.20: resolution: { @@ -1240,6 +1387,12 @@ packages: integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==, } + bail@2.0.2: + resolution: + { + integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==, + } + balanced-match@1.0.2: resolution: { @@ -1312,6 +1465,12 @@ packages: integrity: sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==, } + ccount@2.0.1: + resolution: + { + integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==, + } + chalk@2.4.2: resolution: { @@ -1326,6 +1485,30 @@ packages: } engines: { node: ">=10" } + character-entities-html4@2.1.0: + resolution: + { + integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==, + } + + character-entities-legacy@3.0.0: + resolution: + { + integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==, + } + + character-entities@2.0.2: + resolution: + { + integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==, + } + + character-reference-invalid@2.0.1: + resolution: + { + integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==, + } + chokidar@3.6.0: resolution: { @@ -1372,6 +1555,12 @@ packages: } engines: { node: ">= 0.8" } + comma-separated-tokens@2.0.3: + resolution: + { + integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==, + } + commander@4.1.1: resolution: { @@ -1404,6 +1593,18 @@ packages: } engines: { node: ">= 8" } + css-jss@10.10.0: + resolution: + { + integrity: sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==, + } + + css-vendor@2.0.8: + resolution: + { + integrity: sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==, + } + cssesc@3.0.0: resolution: { @@ -1430,6 +1631,12 @@ packages: supports-color: optional: true + decode-named-character-reference@1.0.2: + resolution: + { + integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==, + } + deep-is@0.1.4: resolution: { @@ -1443,6 +1650,19 @@ packages: } engines: { node: ">=0.4.0" } + dequal@2.0.3: + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: ">=6" } + + devlop@1.1.0: + resolution: + { + integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, + } + didyoumean@1.2.2: resolution: { @@ -1514,6 +1734,13 @@ packages: } engines: { node: ">=10" } + escape-string-regexp@5.0.0: + resolution: + { + integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, + } + engines: { node: ">=12" } + eslint-config-prettier@9.1.0: resolution: { @@ -1619,6 +1846,12 @@ packages: } engines: { node: ">=4.0" } + estree-util-is-identifier-name@3.0.0: + resolution: + { + integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==, + } + esutils@2.0.3: resolution: { @@ -1640,6 +1873,12 @@ packages: } engines: { node: ">=0.10.0" } + extend@3.0.2: + resolution: + { + integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, + } + fast-deep-equal@3.1.3: resolution: { @@ -1684,6 +1923,13 @@ packages: } engines: { node: ">=16.0.0" } + file-selector@0.6.0: + resolution: + { + integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==, + } + engines: { node: ">= 12" } + fill-range@7.1.1: resolution: { @@ -1787,6 +2033,13 @@ packages: integrity: sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==, } + geojson@0.5.0: + resolution: + { + integrity: sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==, + } + engines: { node: ">= 0.10" } + get-stream@6.0.1: resolution: { @@ -1889,6 +2142,36 @@ packages: } engines: { node: ">= 0.4" } + hast-util-to-jsx-runtime@2.3.2: + resolution: + { + integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==, + } + + hast-util-whitespace@3.0.0: + resolution: + { + integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==, + } + + hoist-non-react-statics@3.3.2: + resolution: + { + integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==, + } + + html-url-attributes@3.0.1: + resolution: + { + integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==, + } + + hyphenate-style-name@1.1.0: + resolution: + { + integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==, + } + ieee754@1.2.1: resolution: { @@ -1923,12 +2206,30 @@ packages: } engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + inline-style-parser@0.2.4: + resolution: + { + integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==, + } + invariant@2.2.4: resolution: { integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==, } + is-alphabetical@2.0.1: + resolution: + { + integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==, + } + + is-alphanumerical@2.0.1: + resolution: + { + integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==, + } + is-binary-path@2.1.0: resolution: { @@ -1943,6 +2244,12 @@ packages: } engines: { node: ">= 0.4" } + is-decimal@2.0.1: + resolution: + { + integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==, + } + is-extendable@0.1.1: resolution: { @@ -1978,6 +2285,18 @@ packages: } engines: { node: ">=0.10.0" } + is-hexadecimal@2.0.1: + resolution: + { + integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==, + } + + is-in-browser@1.1.3: + resolution: + { + integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==, + } + is-number@7.0.0: resolution: { @@ -1992,6 +2311,13 @@ packages: } engines: { node: ">=8" } + is-plain-obj@4.1.0: + resolution: + { + integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, + } + engines: { node: ">=12" } + is-plain-object@2.0.4: resolution: { @@ -2085,6 +2411,90 @@ packages: engines: { node: ">=6" } hasBin: true + jss-plugin-camel-case@10.10.0: + resolution: + { + integrity: sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==, + } + + jss-plugin-compose@10.10.0: + resolution: + { + integrity: sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==, + } + + jss-plugin-default-unit@10.10.0: + resolution: + { + integrity: sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==, + } + + jss-plugin-expand@10.10.0: + resolution: + { + integrity: sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==, + } + + jss-plugin-extend@10.10.0: + resolution: + { + integrity: sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==, + } + + jss-plugin-global@10.10.0: + resolution: + { + integrity: sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==, + } + + jss-plugin-nested@10.10.0: + resolution: + { + integrity: sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==, + } + + jss-plugin-props-sort@10.10.0: + resolution: + { + integrity: sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==, + } + + jss-plugin-rule-value-function@10.10.0: + resolution: + { + integrity: sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==, + } + + jss-plugin-rule-value-observable@10.10.0: + resolution: + { + integrity: sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==, + } + + jss-plugin-template@10.10.0: + resolution: + { + integrity: sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==, + } + + jss-plugin-vendor-prefixer@10.10.0: + resolution: + { + integrity: sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==, + } + + jss-preset-default@10.10.0: + resolution: + { + integrity: sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==, + } + + jss@10.10.0: + resolution: + { + integrity: sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==, + } + kdbush@4.0.2: resolution: { @@ -2156,20 +2566,44 @@ packages: } engines: { node: ">=10" } - lodash.merge@4.6.2: + lodash.castarray@4.4.0: resolution: { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==, } - loose-envify@1.4.0: + lodash.isplainobject@4.0.6: resolution: { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, + integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, } - hasBin: true - lru-cache@10.4.3: + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } + + lodash@4.17.21: + resolution: + { + integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, + } + + longest-streak@3.1.0: + resolution: + { + integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, + } + + loose-envify@1.4.0: + resolution: + { + integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, + } + hasBin: true + + lru-cache@10.4.3: resolution: { integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, @@ -2188,6 +2622,102 @@ packages: } engines: { node: ">=16.14.0", npm: ">=8.1.0" } + markdown-table@3.0.4: + resolution: + { + integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, + } + + mdast-util-find-and-replace@3.0.1: + resolution: + { + integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==, + } + + mdast-util-from-markdown@2.0.2: + resolution: + { + integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==, + } + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: + { + integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, + } + + mdast-util-gfm-footnote@2.0.0: + resolution: + { + integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==, + } + + mdast-util-gfm-strikethrough@2.0.0: + resolution: + { + integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==, + } + + mdast-util-gfm-table@2.0.0: + resolution: + { + integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==, + } + + mdast-util-gfm-task-list-item@2.0.0: + resolution: + { + integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, + } + + mdast-util-gfm@3.0.0: + resolution: + { + integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==, + } + + mdast-util-mdx-expression@2.0.1: + resolution: + { + integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==, + } + + mdast-util-mdx-jsx@3.1.3: + resolution: + { + integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==, + } + + mdast-util-mdxjs-esm@2.0.1: + resolution: + { + integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==, + } + + mdast-util-phrasing@4.1.0: + resolution: + { + integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, + } + + mdast-util-to-hast@13.2.0: + resolution: + { + integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==, + } + + mdast-util-to-markdown@2.1.2: + resolution: + { + integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==, + } + + mdast-util-to-string@4.0.0: + resolution: + { + integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, + } + merge2@1.4.1: resolution: { @@ -2195,6 +2725,174 @@ packages: } engines: { node: ">= 8" } + micromark-core-commonmark@2.0.2: + resolution: + { + integrity: sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==, + } + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: + { + integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, + } + + micromark-extension-gfm-footnote@2.1.0: + resolution: + { + integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, + } + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: + { + integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, + } + + micromark-extension-gfm-table@2.1.0: + resolution: + { + integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==, + } + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: + { + integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, + } + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: + { + integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, + } + + micromark-extension-gfm@3.0.0: + resolution: + { + integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, + } + + micromark-factory-destination@2.0.1: + resolution: + { + integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, + } + + micromark-factory-label@2.0.1: + resolution: + { + integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, + } + + micromark-factory-space@2.0.1: + resolution: + { + integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==, + } + + micromark-factory-title@2.0.1: + resolution: + { + integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, + } + + micromark-factory-whitespace@2.0.1: + resolution: + { + integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, + } + + micromark-util-character@2.1.1: + resolution: + { + integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, + } + + micromark-util-chunked@2.0.1: + resolution: + { + integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, + } + + micromark-util-classify-character@2.0.1: + resolution: + { + integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, + } + + micromark-util-combine-extensions@2.0.1: + resolution: + { + integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, + } + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: + { + integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, + } + + micromark-util-decode-string@2.0.1: + resolution: + { + integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, + } + + micromark-util-encode@2.0.1: + resolution: + { + integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, + } + + micromark-util-html-tag-name@2.0.1: + resolution: + { + integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, + } + + micromark-util-normalize-identifier@2.0.1: + resolution: + { + integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, + } + + micromark-util-resolve-all@2.0.1: + resolution: + { + integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, + } + + micromark-util-sanitize-uri@2.0.1: + resolution: + { + integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, + } + + micromark-util-subtokenize@2.0.2: + resolution: + { + integrity: sha512-xKxhkB62vwHUuuxHe9Xqty3UaAsizV2YKq5OV344u3hFBbf8zIYrhYOWhAQb94MtMPkjTOzzjJ/hid9/dR5vFA==, + } + + micromark-util-symbol@2.0.1: + resolution: + { + integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, + } + + micromark-util-types@2.0.1: + resolution: + { + integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==, + } + + micromark@4.0.1: + resolution: + { + integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==, + } + micromatch@4.0.8: resolution: { @@ -2342,6 +3040,12 @@ packages: } engines: { node: ">=6" } + parse-entities@4.0.1: + resolution: + { + integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==, + } + path-exists@4.0.0: resolution: { @@ -2445,6 +3149,13 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-selector-parser@6.0.10: + resolution: + { + integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, + } + engines: { node: ">=4" } + postcss-selector-parser@6.1.2: resolution: { @@ -2493,6 +3204,18 @@ packages: engines: { node: ">=14" } hasBin: true + prop-types@15.8.1: + resolution: + { + integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, + } + + property-information@6.5.0: + resolution: + { + integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==, + } + protocol-buffers-schema@3.6.0: resolution: { @@ -2536,6 +3259,21 @@ packages: integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==, } + react-confetti-explosion@2.1.2: + resolution: + { + integrity: sha512-4UzDFBajAGXmF9TSJoRMO2QOBCIXc66idTxH8l7Mkul48HLGtk+tMzK9HYDYsy7Zmw5sEGchi2fbn4AJUuLrZw==, + } + peerDependencies: + react: ^18.x + react-dom: ^18.x + + react-display-name@0.2.5: + resolution: + { + integrity: sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==, + } + react-dom@18.3.1: resolution: { @@ -2544,6 +3282,15 @@ packages: peerDependencies: react: ^18.3.1 + react-dropzone@14.2.10: + resolution: + { + integrity: sha512-Y98LOCYxGO2jOFWREeKJlL7gbrHcOlTBp+9DCM1dh9XQ8+P/8ThhZT7kFb05C+bPcTXq/rixpU+5+LzwYrFLUw==, + } + engines: { node: ">= 10.13" } + peerDependencies: + react: ">= 16.8 || 18.0.0" + react-error-boundary@4.0.13: resolution: { @@ -2566,6 +3313,29 @@ packages: peerDependencies: react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-is@16.13.1: + resolution: + { + integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, + } + + react-jss@10.10.0: + resolution: + { + integrity: sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==, + } + peerDependencies: + react: ">=16.8.6" + + react-markdown@9.0.1: + resolution: + { + integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==, + } + peerDependencies: + "@types/react": ">=18" + react: ">=18" + react-refresh@0.14.2: resolution: { @@ -2618,6 +3388,30 @@ packages: integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==, } + remark-gfm@4.0.0: + resolution: + { + integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==, + } + + remark-parse@11.0.0: + resolution: + { + integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, + } + + remark-rehype@11.1.1: + resolution: + { + integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==, + } + + remark-stringify@11.0.0: + resolution: + { + integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, + } + resolve-from@4.0.0: resolution: { @@ -2693,6 +3487,12 @@ packages: } engines: { node: ">=0.10.0" } + shallow-equal@1.2.1: + resolution: + { + integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==, + } + shallowequal@1.1.0: resolution: { @@ -2748,6 +3548,12 @@ packages: } engines: { node: ">=0.10.0" } + space-separated-tokens@2.0.2: + resolution: + { + integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, + } + split-string@3.1.0: resolution: { @@ -2769,6 +3575,12 @@ packages: } engines: { node: ">=12" } + stringify-entities@4.0.4: + resolution: + { + integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==, + } + strip-ansi@6.0.1: resolution: { @@ -2790,6 +3602,12 @@ packages: } engines: { node: ">=8" } + style-to-object@1.0.8: + resolution: + { + integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==, + } + sucrase@3.35.0: resolution: { @@ -2825,6 +3643,13 @@ packages: } engines: { node: ">= 0.4" } + symbol-observable@1.2.0: + resolution: + { + integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==, + } + engines: { node: ">=0.10.0" } + synckit@0.9.1: resolution: { @@ -2846,12 +3671,27 @@ packages: engines: { node: ">=14.0.0" } hasBin: true + terra-draw@1.0.0-beta.8: + resolution: + { + integrity: sha512-40kOjgOQkDDmRIkz7QZ4urjwb9v/+Zm7tPf3RqeDY4UtKm3JodZ5iz3fFm93u3nzd+QVQlOZF0VF15ew0esQ7A==, + } + text-table@0.2.0: resolution: { integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, } + theming@3.3.0: + resolution: + { + integrity: sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==, + } + engines: { node: ">=8" } + peerDependencies: + react: ">=16.3" + thenify-all@1.6.0: resolution: { @@ -2865,6 +3705,12 @@ packages: integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, } + tiny-warning@1.0.3: + resolution: + { + integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==, + } + tinyqueue@3.0.0: resolution: { @@ -2885,6 +3731,18 @@ packages: } engines: { node: ">=8.0" } + trim-lines@3.0.1: + resolution: + { + integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, + } + + trough@2.2.0: + resolution: + { + integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, + } + ts-api-utils@1.3.0: resolution: { @@ -2958,6 +3816,12 @@ packages: integrity: sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==, } + unified@11.0.5: + resolution: + { + integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, + } + union-value@1.0.1: resolution: { @@ -2965,6 +3829,36 @@ packages: } engines: { node: ">=0.10.0" } + unist-util-is@6.0.0: + resolution: + { + integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==, + } + + unist-util-position@5.0.0: + resolution: + { + integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==, + } + + unist-util-stringify-position@4.0.0: + resolution: + { + integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==, + } + + unist-util-visit-parents@6.0.1: + resolution: + { + integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==, + } + + unist-util-visit@5.0.0: + resolution: + { + integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==, + } + update-browserslist-db@1.1.1: resolution: { @@ -2986,6 +3880,18 @@ packages: integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, } + vfile-message@4.0.2: + resolution: + { + integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==, + } + + vfile@6.0.3: + resolution: + { + integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==, + } + vite-tsconfig-paths@5.0.1: resolution: { @@ -3095,6 +4001,12 @@ packages: } engines: { node: ">=10" } + zwitch@2.0.4: + resolution: + { + integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==, + } + snapshots: "@alloc/quick-lru@5.2.0": {} @@ -3233,6 +4145,12 @@ snapshots: "@ctrl/tinycolor@4.1.0": {} + "@emotion/is-prop-valid@0.7.3": + dependencies: + "@emotion/memoize": 0.7.1 + + "@emotion/memoize@0.7.1": {} + "@esbuild/aix-ppc64@0.21.5": optional: true @@ -3506,6 +4424,14 @@ snapshots: transitivePeerDependencies: - "@types/react" + "@tailwindcss/typography@0.5.15(tailwindcss@3.4.13)": + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.13 + "@tanstack/eslint-plugin-query@5.58.1(eslint@9.11.1(jiti@1.21.6))(typescript@5.6.2)": dependencies: "@typescript-eslint/utils": 8.8.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.6.2) @@ -3537,6 +4463,32 @@ snapshots: "@tanstack/table-core@8.20.5": {} + "@terraformer/wkt@2.2.1": {} + + "@turf/area@7.1.0": + dependencies: + "@turf/helpers": 7.1.0 + "@turf/meta": 7.1.0 + "@types/geojson": 7946.0.14 + tslib: 2.7.0 + + "@turf/bbox@7.1.0": + dependencies: + "@turf/helpers": 7.1.0 + "@turf/meta": 7.1.0 + "@types/geojson": 7946.0.14 + tslib: 2.7.0 + + "@turf/helpers@7.1.0": + dependencies: + "@types/geojson": 7946.0.14 + tslib: 2.7.0 + + "@turf/meta@7.1.0": + dependencies: + "@turf/helpers": 7.1.0 + "@types/geojson": 7946.0.14 + "@types/babel__core@7.20.5": dependencies: "@babel/parser": 7.25.6 @@ -3558,6 +4510,14 @@ snapshots: dependencies: "@babel/types": 7.25.6 + "@types/debug@4.1.12": + dependencies: + "@types/ms": 0.7.34 + + "@types/estree-jsx@1.0.5": + dependencies: + "@types/estree": 1.0.6 + "@types/estree@1.0.6": {} "@types/geojson-vt@3.2.5": @@ -3566,6 +4526,10 @@ snapshots: "@types/geojson@7946.0.14": {} + "@types/hast@3.0.4": + dependencies: + "@types/unist": 3.0.3 + "@types/json-schema@7.0.15": {} "@types/mapbox__point-geometry@0.1.4": {} @@ -3576,6 +4540,12 @@ snapshots: "@types/mapbox__point-geometry": 0.1.4 "@types/pbf": 3.0.5 + "@types/mdast@4.0.4": + dependencies: + "@types/unist": 3.0.3 + + "@types/ms@0.7.34": {} + "@types/pbf@3.0.5": {} "@types/prop-types@15.7.13": {} @@ -3593,8 +4563,16 @@ snapshots: dependencies: "@types/geojson": 7946.0.14 + "@types/terraformer__wkt@2.0.3": + dependencies: + "@types/geojson": 7946.0.14 + "@types/trusted-types@2.0.7": {} + "@types/unist@2.0.11": {} + + "@types/unist@3.0.3": {} + "@typescript-eslint/eslint-plugin@8.8.0(@typescript-eslint/parser@8.8.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.1(jiti@1.21.6))(typescript@5.6.2)": dependencies: "@eslint-community/regexpp": 4.11.1 @@ -3676,6 +4654,8 @@ snapshots: "@typescript-eslint/types": 8.8.0 eslint-visitor-keys: 3.4.3 + "@ungap/structured-clone@1.2.0": {} + "@vitejs/plugin-react@4.3.2(vite@5.4.8)": dependencies: "@babel/core": 7.25.2 @@ -3731,6 +4711,8 @@ snapshots: asynckit@0.4.0: {} + attr-accept@2.2.4: {} + autoprefixer@10.4.20(postcss@8.4.47): dependencies: browserslist: 4.24.0 @@ -3749,6 +4731,8 @@ snapshots: transitivePeerDependencies: - debug + bail@2.0.2: {} + balanced-match@1.0.2: {} binary-extensions@2.3.0: {} @@ -3788,6 +4772,8 @@ snapshots: caniuse-lite@1.0.30001664: {} + ccount@2.0.1: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -3799,6 +4785,14 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -3829,6 +4823,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@4.1.1: {} composed-offset-position@0.0.4: {} @@ -3843,6 +4839,17 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-jss@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + jss-preset-default: 10.10.0 + + css-vendor@2.0.8: + dependencies: + "@babel/runtime": 7.25.6 + is-in-browser: 1.1.3 + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -3851,10 +4858,20 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + deep-is@0.1.4: {} delayed-stream@1.0.0: {} + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + didyoumean@1.2.2: {} dlv@1.1.3: {} @@ -3901,6 +4918,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-prettier@9.1.0(eslint@9.11.1(jiti@1.21.6)): dependencies: eslint: 9.11.1(jiti@1.21.6) @@ -3991,6 +5010,8 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + esutils@2.0.3: {} extend-shallow@2.0.1: @@ -4002,6 +5023,8 @@ snapshots: assign-symbols: 1.0.0 is-extendable: 1.0.1 + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -4026,6 +5049,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-selector@0.6.0: + dependencies: + tslib: 2.7.0 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4057,10 +5084,11 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@11.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@11.9.0(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: tslib: 2.7.0 optionalDependencies: + "@emotion/is-prop-valid": 0.7.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -4073,6 +5101,8 @@ snapshots: geojson-vt@4.0.2: {} + geojson@0.5.0: {} + get-stream@6.0.1: {} get-value@2.0.6: {} @@ -4120,6 +5150,38 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.2: + dependencies: + "@types/estree": 1.0.6 + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.8 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + "@types/hast": 3.0.4 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + html-url-attributes@3.0.1: {} + + hyphenate-style-name@1.1.0: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -4133,10 +5195,19 @@ snapshots: ini@4.1.3: {} + inline-style-parser@0.2.4: {} + invariant@2.2.4: dependencies: loose-envify: 1.4.0 + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -4145,6 +5216,8 @@ snapshots: dependencies: hasown: 2.0.2 + is-decimal@2.0.1: {} + is-extendable@0.1.1: {} is-extendable@1.0.1: @@ -4159,10 +5232,16 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + + is-in-browser@1.1.3: {} + is-number@7.0.0: {} is-path-inside@3.0.3: {} + is-plain-obj@4.1.0: {} + is-plain-object@2.0.4: dependencies: isobject: 3.0.1 @@ -4199,6 +5278,98 @@ snapshots: json5@2.2.3: {} + jss-plugin-camel-case@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + hyphenate-style-name: 1.1.0 + jss: 10.10.0 + + jss-plugin-compose@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-default-unit@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + + jss-plugin-expand@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + + jss-plugin-extend@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-global@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + + jss-plugin-nested@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-props-sort@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + + jss-plugin-rule-value-function@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-rule-value-observable@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + symbol-observable: 1.2.0 + + jss-plugin-template@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-vendor-prefixer@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + css-vendor: 2.0.8 + jss: 10.10.0 + + jss-preset-default@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + jss: 10.10.0 + jss-plugin-camel-case: 10.10.0 + jss-plugin-compose: 10.10.0 + jss-plugin-default-unit: 10.10.0 + jss-plugin-expand: 10.10.0 + jss-plugin-extend: 10.10.0 + jss-plugin-global: 10.10.0 + jss-plugin-nested: 10.10.0 + jss-plugin-props-sort: 10.10.0 + jss-plugin-rule-value-function: 10.10.0 + jss-plugin-rule-value-observable: 10.10.0 + jss-plugin-template: 10.10.0 + jss-plugin-vendor-prefixer: 10.10.0 + + jss@10.10.0: + dependencies: + "@babel/runtime": 7.25.6 + csstype: 3.1.3 + is-in-browser: 1.1.3 + tiny-warning: 1.0.3 + kdbush@4.0.2: {} keyv@4.5.4: @@ -4238,8 +5409,16 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.castarray@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + lodash.merge@4.6.2: {} + lodash@4.17.21: {} + + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -4279,8 +5458,354 @@ snapshots: tinyqueue: 3.0.0 vt-pbf: 3.1.3 + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.1: + dependencies: + "@types/mdast": 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + "@types/mdast": 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.1.3: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.1 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + "@types/mdast": 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@ungap/structured-clone": 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + "@types/mdast": 4.0.4 + merge2@1.4.1: {} + micromark-core-commonmark@2.0.2: + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-table@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.1 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.1 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.1 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.0.2: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.1: {} + + micromark@4.0.1: + dependencies: + "@types/debug": 4.1.12 + debug: 4.3.7 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -4351,6 +5876,17 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@4.0.1: + dependencies: + "@types/unist": 2.0.11 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4399,6 +5935,11 @@ snapshots: postcss: 8.4.47 postcss-selector-parser: 6.1.2 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 @@ -4422,6 +5963,14 @@ snapshots: prettier@3.3.3: {} + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@6.5.0: {} + protocol-buffers-schema@3.6.0: {} proxy-from-env@1.1.0: {} @@ -4436,12 +5985,28 @@ snapshots: quickselect@3.0.0: {} + react-confetti-explosion@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-jss: 10.10.0(react@18.3.1) + + react-display-name@0.2.5: {} + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 + react-dropzone@14.2.10(react@18.3.1): + dependencies: + attr-accept: 2.2.4 + file-selector: 0.6.0 + prop-types: 15.8.1 + react: 18.3.1 + react-error-boundary@4.0.13(react@18.3.1): dependencies: "@babel/runtime": 7.25.6 @@ -4456,6 +6021,40 @@ snapshots: react-fast-compare: 3.2.2 shallowequal: 1.1.0 + react-is@16.13.1: {} + + react-jss@10.10.0(react@18.3.1): + dependencies: + "@babel/runtime": 7.25.6 + "@emotion/is-prop-valid": 0.7.3 + css-jss: 10.10.0 + hoist-non-react-statics: 3.3.2 + is-in-browser: 1.1.3 + jss: 10.10.0 + jss-preset-default: 10.10.0 + prop-types: 15.8.1 + react: 18.3.1 + shallow-equal: 1.2.1 + theming: 3.3.0(react@18.3.1) + tiny-warning: 1.0.3 + + react-markdown@9.0.1(@types/react@18.3.10)(react@18.3.1): + dependencies: + "@types/hast": 3.0.4 + "@types/react": 18.3.10 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.2 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-refresh@0.14.2: {} react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -4484,6 +6083,40 @@ snapshots: regenerator-runtime@0.14.1: {} + remark-gfm@4.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.1 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.1: + dependencies: + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + resolve-from@4.0.0: {} resolve-protobuf-schema@2.1.0: @@ -4541,6 +6174,8 @@ snapshots: is-plain-object: 2.0.4 split-string: 3.1.0 + shallow-equal@1.2.1: {} + shallowequal@1.1.0: {} shebang-command@2.0.0: @@ -4566,6 +6201,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + split-string@3.1.0: dependencies: extend-shallow: 3.0.2 @@ -4582,6 +6219,11 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -4592,6 +6234,10 @@ snapshots: strip-json-comments@3.1.1: {} + style-to-object@1.0.8: + dependencies: + inline-style-parser: 0.2.4 + sucrase@3.35.0: dependencies: "@jridgewell/gen-mapping": 0.3.5 @@ -4616,6 +6262,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-observable@1.2.0: {} + synckit@0.9.1: dependencies: "@pkgr/core": 0.1.1 @@ -4650,8 +6298,18 @@ snapshots: transitivePeerDependencies: - ts-node + terra-draw@1.0.0-beta.8: {} + text-table@0.2.0: {} + theming@3.3.0(react@18.3.1): + dependencies: + hoist-non-react-statics: 3.3.2 + prop-types: 15.8.1 + react: 18.3.1 + react-display-name: 0.2.5 + tiny-warning: 1.0.3 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -4660,6 +6318,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tiny-warning@1.0.3: {} + tinyqueue@3.0.0: {} to-fast-properties@2.0.0: {} @@ -4668,6 +6328,10 @@ snapshots: dependencies: is-number: 7.0.0 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@1.3.0(typescript@5.6.2): dependencies: typescript: 5.6.2 @@ -4703,6 +6367,16 @@ snapshots: dependencies: typewise-core: 1.2.0 + unified@11.0.5: + dependencies: + "@types/unist": 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + union-value@1.0.1: dependencies: arr-union: 3.1.0 @@ -4710,6 +6384,29 @@ snapshots: is-extendable: 0.1.1 set-value: 2.0.1 + unist-util-is@6.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-position@5.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + update-browserslist-db@1.1.1(browserslist@4.24.0): dependencies: browserslist: 4.24.0 @@ -4722,6 +6419,16 @@ snapshots: util-deprecate@1.0.2: {} + vfile-message@4.0.2: + dependencies: + "@types/unist": 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + "@types/unist": 3.0.3 + vfile-message: 4.0.2 + vite-tsconfig-paths@5.0.1(typescript@5.6.2)(vite@5.4.8): dependencies: debug: 4.3.7 @@ -4774,3 +6481,5 @@ snapshots: yaml@2.5.1: {} yocto-queue@0.1.0: {} + + zwitch@2.0.4: {} diff --git a/frontend/src/app/index.tsx b/frontend/src/app/index.tsx index 6413ef79..9f48e1d1 100644 --- a/frontend/src/app/index.tsx +++ b/frontend/src/app/index.tsx @@ -1,5 +1,4 @@ import { AppRouter } from "@/app/router"; -import { useToast } from "@/app/providers/toast-provider"; import { useEffect } from "react"; import { ENVS } from "@/config/env"; import { @@ -8,6 +7,7 @@ import { QueryClientProvider, } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { showErrorToast } from "@/utils"; export const App = () => { const hotTrackingTagName = "hot-tracking"; @@ -28,15 +28,13 @@ export const App = () => { return; }, []); - const { notify } = useToast(); - const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: (error, query) => { // only show error toasts if we already have data in the cache // which indicates a failed background update if (query.state.data !== undefined) { - notify(`Something went wrong: ${error.message}`, "danger"); + showErrorToast(error); } }, }), diff --git a/frontend/src/app/providers/auth-provider.tsx b/frontend/src/app/providers/auth-provider.tsx index e7055e93..1fa881da 100644 --- a/frontend/src/app/providers/auth-provider.tsx +++ b/frontend/src/app/providers/auth-provider.tsx @@ -3,19 +3,14 @@ import { authService } from "@/services"; import { apiClient } from "@/services/api-client"; import { TUser } from "@/types/api"; import { - APP_CONTENT, HOT_FAIR_LOCAL_STORAGE_ACCESS_TOKEN_KEY, HOT_FAIR_LOGIN_SUCCESSFUL_SESSION_KEY, HOT_FAIR_SESSION_REDIRECT_KEY, + showErrorToast, + showSuccessToast, + TOAST_NOTIFICATIONS, } from "@/utils"; -import React, { - createContext, - useContext, - useState, - useEffect, - useMemo, -} from "react"; -import { useToast } from "./toast-provider"; +import React, { createContext, useContext, useState, useEffect } from "react"; type TAuthContext = { token: string; @@ -47,14 +42,10 @@ export const AuthProvider: React.FC = ({ children }) => { ); const [user, setUser] = useState(null); - const { notify } = useToast(); // For use across the application. + const isAuthenticated = user !== null && token !== undefined; - const isAuthenticated = useMemo(() => { - return user !== null && token !== undefined; - }, [token, user]); - - //set token globally to eliminate the need to rewrite it + // Set token globally to eliminate the need to rewrite it apiClient.defaults.headers.common["access-token"] = token ? `${token}` : null; useEffect(() => { @@ -68,21 +59,21 @@ export const AuthProvider: React.FC = ({ children }) => { if (redirectTo) { // remove it before redirecting. removeSessionValue(HOT_FAIR_SESSION_REDIRECT_KEY); - // this this is the last stage of the auth, we can assume that the login is successful, then store a reference + // This is the last stage of the auth, we can assume that the login is successful, then store a reference // in the session storage. setSessionValue(HOT_FAIR_LOGIN_SUCCESSFUL_SESSION_KEY, "success"); window.location.replace(redirectTo); } }; - //To show the login success after completing redirection if any. + // To show the login success after completing redirection if any. useEffect(() => { const loginSuccessful = getSessionValue( HOT_FAIR_LOGIN_SUCCESSFUL_SESSION_KEY, ); if (loginSuccessful == "success") { - notify(APP_CONTENT.toasts.loginSuccess, "success"); + showSuccessToast(TOAST_NOTIFICATIONS.loginSuccess); removeSessionValue(HOT_FAIR_LOGIN_SUCCESSFUL_SESSION_KEY); } }, []); @@ -106,7 +97,7 @@ export const AuthProvider: React.FC = ({ children }) => { const user = await authService.getUser(); setUser(user); } catch (error) { - console.error("Failed to fetch user profile:", error); + showErrorToast(error); } }; @@ -117,7 +108,7 @@ export const AuthProvider: React.FC = ({ children }) => { setToken(undefined); setUser(null); removeValue(HOT_FAIR_LOCAL_STORAGE_ACCESS_TOKEN_KEY); - notify(APP_CONTENT.toasts.logoutSuccess, "success"); + showSuccessToast(TOAST_NOTIFICATIONS.logoutSuccess); }; /** @@ -133,8 +124,7 @@ export const AuthProvider: React.FC = ({ children }) => { fetchUserProfile(); handleRedirection(); } catch (error) { - notify(APP_CONTENT.toasts.authenticationFailed, "danger"); - console.error("Authentication failed:", error); + showErrorToast(error, TOAST_NOTIFICATIONS.authenticationFailed); } }; diff --git a/frontend/src/app/providers/index.tsx b/frontend/src/app/providers/index.tsx index 3fd9bd47..d0bbe6ac 100644 --- a/frontend/src/app/providers/index.tsx +++ b/frontend/src/app/providers/index.tsx @@ -1,13 +1,10 @@ import { HelmetProvider } from "react-helmet-async"; -import { ToastProvider } from "./toast-provider"; import { AuthProvider } from "./auth-provider"; const ContextProviders = ({ children }: { children: React.ReactNode }) => { return ( - - {children} - + {children} ); }; diff --git a/frontend/src/app/providers/map-provider.tsx b/frontend/src/app/providers/map-provider.tsx new file mode 100644 index 00000000..16f934fe --- /dev/null +++ b/frontend/src/app/providers/map-provider.tsx @@ -0,0 +1,83 @@ +import { + createContext, + useContext, + ReactNode, + useState, + useMemo, + useEffect, + useCallback, +} from "react"; +import { Map } from "maplibre-gl"; +import { TerraDraw } from "terra-draw"; +import { setupTerraDraw } from "@/components/map/setup-terra-draw"; +import { DrawingModes } from "@/enums"; + +const MapContext = createContext<{ + map: Map | null; + setMap: React.Dispatch>; + terraDraw: TerraDraw | undefined; + drawingMode: DrawingModes; + setDrawingMode: React.Dispatch>; + currentZoom: number; +}>({ + map: null, + setMap: () => {}, + terraDraw: undefined, + drawingMode: DrawingModes.STATIC, + setDrawingMode: () => DrawingModes, + currentZoom: 0, +}); + +export const MapProvider = ({ children }: { children: ReactNode }) => { + const [map, setMap] = useState(null); + const [currentZoom, setCurrentZoom] = useState(0); + const terraDraw = useMemo(() => { + if (map) { + const terraDraw = setupTerraDraw(map); + terraDraw.start(); + return terraDraw; + } + }, [map]); + + const [drawingMode, setDrawingMode] = useState( + DrawingModes.STATIC, + ); + + // sync the modes + useEffect(() => { + terraDraw?.setMode(drawingMode); + }, [terraDraw, drawingMode]); + + const updateZoom = useCallback(() => { + if (!map) return; + setCurrentZoom(map.getZoom()); + }, [map]); + + useEffect(() => { + if (!map) return; + const handleMapMove = () => { + updateZoom(); + }; + map.on("moveend", handleMapMove); + return () => { + map.off("moveend", handleMapMove); + }; + }, [map]); + + return ( + + {children} + + ); +}; + +export const useMap = () => useContext(MapContext); diff --git a/frontend/src/app/providers/models-provider.tsx b/frontend/src/app/providers/models-provider.tsx new file mode 100644 index 00000000..af814ae6 --- /dev/null +++ b/frontend/src/app/providers/models-provider.tsx @@ -0,0 +1,510 @@ +import { BASE_MODELS, TrainingType, TrainingDatasetOption } from "@/enums"; +import { useCreateTrainingDataset } from "@/features/model-creation/hooks/use-training-datasets"; +import { + APPLICATION_ROUTES, + MODELS_BASE, + MODELS_ROUTES, + showErrorToast, + showSuccessToast, + TMS_URL_REGEX_PATTERN, + TOAST_NOTIFICATIONS, +} from "@/utils"; +import { UseMutationResult } from "@tanstack/react-query"; +import React, { + createContext, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; +import { Feature, TTrainingDataset, TTrainingDetails } from "@/types"; +import { + TCreateTrainingDatasetArgs, + TCreateTrainingRequestArgs, +} from "@/features/model-creation/api/create-trainings"; +import { + useCreateModel, + useCreateModelTrainingRequest, + useUpdateModel, +} from "@/features/model-creation/hooks/use-models"; +import { LngLatBoundsLike } from "maplibre-gl"; +import { useModelDetails } from "@/features/models/hooks/use-models"; +import { useGetTrainingDataset } from "@/features/models/hooks/use-dataset"; + +/** + * The names here are the same with the `initialFormState` object keys. + * They are also the same with the form validation configuration object keys. + */ +export enum MODEL_CREATION_FORM_NAME { + MODEL_NAME = "modelName", + DATASET_NAME = "datasetName", + MODEL_DESCRIPTION = "modelDescription", + BASE_MODELS = "baseModel", + TRAINING_DATASET_OPTION = "trainingDatasetOption", + ZOOM_LEVELS = "zoomLevels", + TRAINING_TYPE = "trainingType", + EPOCH = "epoch", + CONTACT_SPACING = "contactSpacing", + BATCH_SIZE = "batchSize", + BOUNDARY_WIDTH = "boundaryWidth", + TMS_URL = "tmsURL", + TMS_URL_VALIDITY = "tmsURLValidation", + SELECTED_TRAINING_DATASET_ID = "selectedTrainingDatasetId", + OAM_TILE_NAME = "oamTileName", + OAM_BOUNDS = "oamBounds", + TRAINING_AREAS = "trainingAreas", + TRAINING_SETTINGS_IS_VALID = "trainingSettingsIsValid", +} + +export const FORM_VALIDATION_CONFIG = { + [MODEL_CREATION_FORM_NAME.MODEL_NAME]: { + maxLength: 40, + minLength: 10, + }, + [MODEL_CREATION_FORM_NAME.TMS_URL]: { + pattern: TMS_URL_REGEX_PATTERN.source, + }, + [MODEL_CREATION_FORM_NAME.MODEL_DESCRIPTION]: { + maxLength: 500, + minLength: 10, + }, + [MODEL_CREATION_FORM_NAME.DATASET_NAME]: { + maxLength: 40, + minLength: 10, + }, + [BASE_MODELS.RAMP]: { + epoch: { + max: 30, + min: 1, + }, + contactSpacing: { + max: 8, + min: 1, + }, + batchSize: { + max: 12, + min: 1, + }, + boundaryWidth: { + max: 8, + min: 1, + }, + }, + [BASE_MODELS.YOLOV8_V1]: { + epoch: { + max: 150, + min: 20, + }, + batchSize: { + max: 16, + min: 8, + }, + // These are not used + contactSpacing: { + max: 8, + min: 1, + }, + boundaryWidth: { + max: 8, + min: 1, + }, + }, + [BASE_MODELS.YOLOV8_V2]: { + epoch: { + max: 150, + min: 20, + }, + + batchSize: { + max: 16, + min: 8, + }, + // These are not used + contactSpacing: { + max: 8, + min: 1, + }, + boundaryWidth: { + max: 8, + min: 1, + }, + }, +}; + +type FormData = { + modelName: string; + modelDescription: string; + baseModel: BASE_MODELS; + trainingDatasetOption: TrainingDatasetOption; + datasetName: string; + tmsURL: string; + tmsURLValidation: { + valid: boolean; + message: string; + }; + selectedTrainingDatasetId: string; + trainingAreas: Feature[]; + oamTileName: string; + oamBounds: number[]; + trainingType: TrainingType; + contactSpacing: number; + epoch: number; + batchSize: number; + boundaryWidth: number; + zoomLevels: number[]; + trainingSettingsIsValid: boolean; +}; + +const initialFormState: FormData = { + [MODEL_CREATION_FORM_NAME.MODEL_NAME]: "", + [MODEL_CREATION_FORM_NAME.MODEL_DESCRIPTION]: "", + [MODEL_CREATION_FORM_NAME.BASE_MODELS]: BASE_MODELS.RAMP, + [MODEL_CREATION_FORM_NAME.TRAINING_DATASET_OPTION]: + TrainingDatasetOption.NONE, + // create new dataset form + [MODEL_CREATION_FORM_NAME.DATASET_NAME]: "", + [MODEL_CREATION_FORM_NAME.TMS_URL]: "", + [MODEL_CREATION_FORM_NAME.TMS_URL_VALIDITY]: { + valid: false, + message: "", + }, + [MODEL_CREATION_FORM_NAME.SELECTED_TRAINING_DATASET_ID]: "", + [MODEL_CREATION_FORM_NAME.TRAINING_AREAS]: [], + // oam tms info + [MODEL_CREATION_FORM_NAME.OAM_TILE_NAME]: "", + [MODEL_CREATION_FORM_NAME.OAM_BOUNDS]: [], + // Training settings - defaults to basic configurations + [MODEL_CREATION_FORM_NAME.TRAINING_TYPE]: TrainingType.BASIC, + [MODEL_CREATION_FORM_NAME.EPOCH]: 2, + [MODEL_CREATION_FORM_NAME.CONTACT_SPACING]: 4, + [MODEL_CREATION_FORM_NAME.BATCH_SIZE]: 8, + [MODEL_CREATION_FORM_NAME.BOUNDARY_WIDTH]: 3, + [MODEL_CREATION_FORM_NAME.ZOOM_LEVELS]: [19, 20, 21], + [MODEL_CREATION_FORM_NAME.TRAINING_SETTINGS_IS_VALID]: true, +}; + +const ModelsContext = createContext<{ + formData: typeof initialFormState; + setFormData: React.Dispatch>; + handleChange: ( + field: string, + value: + | string + | boolean + | number + | number[] + | Record + | LngLatBoundsLike, + ) => void; + createNewTrainingDatasetMutation: UseMutationResult< + TTrainingDataset, + Error, + TCreateTrainingDatasetArgs, + unknown + >; + hasLabeledTrainingAreas: boolean; + hasAOIsWithGeometry: boolean; + resetState: () => void; + createNewTrainingRequestMutation: UseMutationResult< + TTrainingDetails, + Error, + TCreateTrainingRequestArgs, + unknown + >; + isEditMode: boolean; + modelId?: string; + getFullPath: (path: string) => string; + trainingDatasetCreationInProgress: boolean; + handleModelCreationAndUpdate: () => void; + handleTrainingDatasetCreation: () => void; + validateEditMode: boolean; +}>({ + formData: initialFormState, + setFormData: () => { }, + handleChange: () => { }, + createNewTrainingDatasetMutation: {} as UseMutationResult< + TTrainingDataset, + Error, + TCreateTrainingDatasetArgs, + unknown + >, + createNewTrainingRequestMutation: {} as UseMutationResult< + TTrainingDetails, + Error, + TCreateTrainingRequestArgs, + unknown + >, + hasLabeledTrainingAreas: false, + hasAOIsWithGeometry: false, + resetState: () => { }, + isEditMode: false, + modelId: "", + getFullPath: () => "", + handleModelCreationAndUpdate: () => { }, + trainingDatasetCreationInProgress: false, + handleTrainingDatasetCreation: () => { }, + validateEditMode: false, +}); + +export const ModelsProvider: React.FC<{ + children: React.ReactNode; +}> = ({ children }) => { + const navigate = useNavigate(); + const { pathname } = useLocation(); + const { modelId } = useParams(); + const [formData, setFormData] = + useState(initialFormState); + + const handleChange = ( + field: string, + value: + | string + | boolean + | number + | number[] + | Record + | LngLatBoundsLike, + ) => { + setFormData((prev) => ({ ...prev, [field]: value })); + }; + + const getFullPath = (path: string) => + `${isEditMode ? MODELS_BASE + "/" + modelId : MODELS_ROUTES.CREATE_MODEL_BASE}/${path}/`; + + const timeOutRef = useRef(null); + + const isEditMode = Boolean(modelId && !pathname.includes("new")); + + const { data, isPending, isError } = useModelDetails( + modelId as string, + isEditMode, + ); + + const { + data: trainingDataset, + isPending: trainingDatasetIsPending, + isError: trainingDatasetIsError, + } = useGetTrainingDataset( + Number(data?.dataset), + Boolean(isEditMode && data?.dataset), + ); + + // Will be used in the route validator component to delay the redirection for a while until the data are retrieved + const validateEditMode = useMemo( + () => formData.selectedTrainingDatasetId !== "" && formData.tmsURL !== "", + [formData], + ); + + // Fetch and prefill model details + useEffect(() => { + if (!isEditMode || isPending || !data) return; + + if (isError) { + navigate(APPLICATION_ROUTES.NOTFOUND); + } + + handleChange(MODEL_CREATION_FORM_NAME.BASE_MODELS, data.base_model); + handleChange(MODEL_CREATION_FORM_NAME.MODEL_DESCRIPTION, data.description); + handleChange(MODEL_CREATION_FORM_NAME.MODEL_NAME, data.name); + handleChange( + MODEL_CREATION_FORM_NAME.SELECTED_TRAINING_DATASET_ID, + data.dataset, + ); + }, [isEditMode, isError, isPending, data]); + + // Fetch and prefill training dataset + useEffect(() => { + if (!isEditMode || trainingDatasetIsPending || trainingDatasetIsError) + return; + handleChange(MODEL_CREATION_FORM_NAME.DATASET_NAME, trainingDataset.name); + handleChange( + MODEL_CREATION_FORM_NAME.TMS_URL, + trainingDataset.source_imagery, + ); + }, [ + isEditMode, + trainingDatasetIsPending, + trainingDataset, + trainingDatasetIsError, + ]); + + useEffect(() => { + // Cleanup the timeout on component unmount + return () => { + if (timeOutRef.current) { + clearTimeout(timeOutRef.current); + } + }; + }, []); + + const createNewTrainingRequestMutation = useCreateModelTrainingRequest({ + mutationConfig: { + onSuccess: () => { + showSuccessToast(TOAST_NOTIFICATIONS.trainingRequestSubmittedSuccess); + timeOutRef.current = setTimeout(() => { + setFormData(initialFormState); + }, 2000); + }, + onError: (error) => { + showErrorToast(error); + }, + }, + }); + + const createNewTrainingDatasetMutation = useCreateTrainingDataset({ + mutationConfig: { + onSuccess: (data) => { + showSuccessToast(TOAST_NOTIFICATIONS.trainingDatasetCreationSuccess); + handleChange(MODEL_CREATION_FORM_NAME.DATASET_NAME, data.name); + handleChange(MODEL_CREATION_FORM_NAME.TMS_URL, data.source_imagery); + handleChange( + MODEL_CREATION_FORM_NAME.SELECTED_TRAINING_DATASET_ID, + data.id, + ); + // Navigate to the next step + navigate(APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_AREA); + }, + onError: (error) => { + showErrorToast(error); + }, + }, + }); + + const handleModelCreationOrUpdateSuccess = (modelId: string) => { + showSuccessToast( + isEditMode + ? TOAST_NOTIFICATIONS.modelUpdateSuccess + : TOAST_NOTIFICATIONS.modelCreationSuccess, + ); + navigate(`${getFullPath(MODELS_ROUTES.CONFIRMATION)}?id=${modelId}`); + // Submit the model for training request + createNewTrainingRequestMutation.mutate({ + model: modelId, + input_boundary_width: formData.boundaryWidth, + input_contact_spacing: formData.contactSpacing, + epochs: formData.epoch, + batch_size: formData.batchSize, + zoom_level: formData.zoomLevels, + }); + }; + + const modelCreateMutation = useCreateModel({ + mutationConfig: { + onSuccess: (data) => { + handleModelCreationOrUpdateSuccess(data.id); + }, + onError: (error) => { + showErrorToast(error); + }, + }, + }); + + const modelUpdateMutation = useUpdateModel({ + mutationConfig: { + onSuccess: (data) => { + handleModelCreationOrUpdateSuccess(data.id); + }, + onError: (error) => { + showErrorToast(error); + }, + }, + modelId: modelId as string, + }); + + // Confirm that all the training areas labels has been retrieved + const hasLabeledTrainingAreas = useMemo(() => { + return ( + formData.trainingAreas.length > 0 && + formData.trainingAreas.filter( + (aoi: Feature) => aoi.properties.label_fetched === null, + ).length === 0 + ); + }, [formData]); + // Confirm that all of the training areas has a geometry + const hasAOIsWithGeometry = useMemo(() => { + return ( + formData.trainingAreas.length > 0 && + formData.trainingAreas.filter((aoi: Feature) => aoi.geometry === null) + .length === 0 + ); + }, [formData]); + + const resetState = () => { + setFormData(initialFormState); + }; + + const handleTrainingDatasetCreation = () => { + createNewTrainingDatasetMutation.mutate({ + source_imagery: formData.tmsURL, + name: formData.datasetName, + }); + }; + const trainingDatasetCreationInProgress = + createNewTrainingDatasetMutation.isPending; + + const handleModelCreationAndUpdate = () => { + if (isEditMode) { + modelUpdateMutation.mutate({ + dataset: formData.selectedTrainingDatasetId, + name: formData.modelName, + description: formData.modelDescription, + base_model: formData.baseModel as BASE_MODELS, + modelId: modelId as string, + }); + } else { + modelCreateMutation.mutate({ + dataset: formData.selectedTrainingDatasetId, + name: formData.modelName, + description: formData.modelDescription, + base_model: formData.baseModel, + }); + } + }; + + const memoizedValues = useMemo( + () => ({ + setFormData, + handleChange, + createNewTrainingDatasetMutation, + hasLabeledTrainingAreas, + hasAOIsWithGeometry, + formData, + resetState, + createNewTrainingRequestMutation, + isEditMode, + modelId, + getFullPath, + handleModelCreationAndUpdate, + handleTrainingDatasetCreation, + trainingDatasetCreationInProgress, + validateEditMode, + }), + [ + setFormData, + formData, + handleChange, + createNewTrainingDatasetMutation, + hasLabeledTrainingAreas, + hasAOIsWithGeometry, + resetState, + createNewTrainingRequestMutation, + isEditMode, + modelId, + getFullPath, + handleModelCreationAndUpdate, + handleTrainingDatasetCreation, + trainingDatasetCreationInProgress, + validateEditMode, + ], + ); + + return ( + + {children} + + ); +}; + +export const useModelsContext = () => useContext(ModelsContext); diff --git a/frontend/src/app/providers/toast-provider.tsx b/frontend/src/app/providers/toast-provider.tsx deleted file mode 100644 index 8a54b610..00000000 --- a/frontend/src/app/providers/toast-provider.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { createContext, useContext, ReactNode } from "react"; -import "@shoelace-style/shoelace/dist/components/alert/alert.js"; - -type TToastContext = { - notify: ( - message: string, - variant?: "primary" | "success" | "neutral" | "warning" | "danger", - duration?: number, - ) => void; -}; - -//@ts-expect-error bad type definition -const ToastContext = createContext(null); - -export const useToast = () => useContext(ToastContext); - -export const ToastProvider = ({ children }: { children: ReactNode }) => { - const notify = (message: string, variant = "primary", duration = 3000) => { - const alert = Object.assign(document.createElement("sl-alert"), { - variant, - closable: true, - duration, - innerHTML: ` - ${message} - `, - }); - - // make the variant the classname - alert.classList.add(variant); - - document.body.append(alert); - alert.toast(); - }; - - return ( - {children} - ); -}; diff --git a/frontend/src/app/router.tsx b/frontend/src/app/router.tsx index e9b1d463..38ce807c 100644 --- a/frontend/src/app/router.tsx +++ b/frontend/src/app/router.tsx @@ -7,18 +7,21 @@ import { } from "react-router-dom"; import { ProtectedPage } from "@/app/routes/protected-route"; import { MainErrorFallback } from "@/components/errors"; +import ModelFormsLayout from "@/components/layouts/model-forms-layout"; +import ModelsLayout from "@/components/layouts/models-layout"; +import { MapProvider } from "./providers/map-provider"; const router = createBrowserRouter([ - { - path: APPLICATION_ROUTES.HOMEPAGE, - lazy: async () => { - const { LandingPage } = await import("@/app/routes/landing"); - return { Component: LandingPage }; - }, - }, { element: , children: [ + { + path: APPLICATION_ROUTES.HOMEPAGE, + lazy: async () => { + const { LandingPage } = await import("@/app/routes/landing"); + return { Component: LandingPage }; + }, + }, { path: APPLICATION_ROUTES.LEARN, lazy: async () => { @@ -50,50 +53,202 @@ const router = createBrowserRouter([ }, }, { - path: APPLICATION_ROUTES.MODELS, - lazy: async () => { - const { ModelsPage } = await import("@/app/routes/models"); - return { - Component: () => , - }; - }, + element: , + children: [ + { + path: APPLICATION_ROUTES.MODEL_DETAILS, + lazy: async () => { + const { ModelDetailsPage } = await import( + "@/app/routes/models/model-details" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.MODELS, + lazy: async () => { + const { ModelsPage } = await import( + "@/app/routes/models/models-list" + ); + return { + Component: () => , + }; + }, + }, + ], }, { - path: APPLICATION_ROUTES.MODEL_DETAILS, - lazy: async () => { - const { ModelDetailsPage } = await import( - "@/app/routes/models/model-details" - ); - return { - Component: () => , - }; - }, + element: ( + + + + ), + children: [ + // Creation + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL, + lazy: async () => { + const { ModelDetailsFormPage } = await import( + "@/app/routes/models/model-details-form" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_DATASET, + lazy: async () => { + const { ModelTrainingDatasetPage } = await import( + "@/app/routes/models/training-dataset" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_AREA, + lazy: async () => { + const { ModelTrainingAreaPage } = await import( + "@/app/routes/models/training-area" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_SETTINGS, + lazy: async () => { + const { ModelTrainingSettingsPage } = await import( + "@/app/routes/models/training-settings" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL_SUMMARY, + lazy: async () => { + const { ModelSummaryPage } = await import( + "@/app/routes/models/summary" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.CREATE_NEW_MODEL_CONFIRMATION, + lazy: async () => { + const { ModelConfirmationPage } = await import( + "@/app/routes/models/confirmation" + ); + return { + Component: () => , + }; + }, + }, + //Edit + { + path: APPLICATION_ROUTES.EDIT_MODEL_DETAILS, + lazy: async () => { + const { ModelDetailsFormPage } = await import( + "@/app/routes/models/model-details-form" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.EDIT_MODEL_TRAINING_DATASET, + lazy: async () => { + const { ModelTrainingDatasetPage } = await import( + "@/app/routes/models/training-dataset" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.EDIT_MODEL_TRAINING_AREA, + lazy: async () => { + const { ModelTrainingAreaPage } = await import( + "@/app/routes/models/training-area" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.EDIT_MODEL_TRAINING_SETTINGS, + lazy: async () => { + const { ModelTrainingSettingsPage } = await import( + "@/app/routes/models/training-settings" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.EDIT_MODEL_SUMMARY, + lazy: async () => { + const { ModelSummaryPage } = await import( + "@/app/routes/models/summary" + ); + return { + Component: () => , + }; + }, + }, + { + path: APPLICATION_ROUTES.EDIT_MODEL_CONFIRMATION, + lazy: async () => { + const { ModelConfirmationPage } = await import( + "@/app/routes/models/confirmation" + ); + return { + Component: () => , + }; + }, + }, + ], }, { - path: APPLICATION_ROUTES.CREATE_NEW_MODEL, + path: APPLICATION_ROUTES.TRAINING_DATASETS, lazy: async () => { - const { ModelCreationPage } = await import( - "@/app/routes/models/create-new" + const { TrainingDatasetsPage } = await import( + "@/app/routes/training-datasets" ); return { Component: () => ( - + ), }; }, }, { - path: APPLICATION_ROUTES.TRAINING_DATASETS, + path: APPLICATION_ROUTES.START_MAPPING, lazy: async () => { - const { TrainingDatasetsPage } = await import( - "@/app/routes/training-datasets" + const { StartMappingPage } = await import( + "@/app/routes/start-mapping/start-mapping" ); return { Component: () => ( - + + + ), }; @@ -106,26 +261,18 @@ const router = createBrowserRouter([ "@/app/routes/account/settings" ); return { - Component: () => ( - - - - ), + Component: () => , }; }, }, { - path: APPLICATION_ROUTES.ACCOUNT_PROJECTS, + path: APPLICATION_ROUTES.ACCOUNT_MODELS, lazy: async () => { - const { UserAccountProjectsPage } = await import( - "@/app/routes/account/projects" + const { UserAccountModelsPage } = await import( + "@/app/routes/account/models" ); return { - Component: () => ( - - - - ), + Component: () => , }; }, }, diff --git a/frontend/src/app/routes/account/projects.tsx b/frontend/src/app/routes/account/models.tsx similarity index 68% rename from frontend/src/app/routes/account/projects.tsx rename to frontend/src/app/routes/account/models.tsx index 35bd6495..8b81868c 100644 --- a/frontend/src/app/routes/account/projects.tsx +++ b/frontend/src/app/routes/account/models.tsx @@ -1,5 +1,5 @@ import { PageUnderConstruction } from "@/components/errors"; -export const UserAccountProjectsPage = () => { +export const UserAccountModelsPage = () => { return ; }; diff --git a/frontend/src/app/routes/landing.tsx b/frontend/src/app/routes/landing.tsx index 8c4d9f60..d35cd7f8 100644 --- a/frontend/src/app/routes/landing.tsx +++ b/frontend/src/app/routes/landing.tsx @@ -1,5 +1,4 @@ -import { Footer } from "@/components/ui/footer"; -import { Header, NavBar } from "@/components/ui/header"; +import { Header } from "@/components/ui/header"; import WhatIsFAIR from "@/components/landing/about-fair/about-fair"; import CoreFeatures from "@/components/landing/core-features/core-features"; import Corevalues from "@/components/landing/core-values/core-values"; @@ -14,7 +13,6 @@ export const LandingPage = () => { return ( <> -
@@ -24,7 +22,6 @@ export const LandingPage = () => { -