diff --git a/.vscode/settings.json b/.vscode/settings.json index 2a4c0a2..71faa4d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -28,11 +28,13 @@ "srid", "tagname", "tailwindcss", + "tanstack", "topo", "ugrc", "usgs", "vite", - "wkid" + "wkid", + "wrimaps" ], "editor.codeActionsOnSave": { "source.organizeImports": "explicit" diff --git a/package-lock.json b/package-lock.json index 578d206..6fcef55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,11 @@ "dependencies": { "@arcgis/core": "^4.30.9", "@heroicons/react": "^2.1.5", + "@tanstack/react-query": "^5.52.1", "@ugrc/layer-selector": "^6.2.7", "@ugrc/utah-design-system": "^1.4.1", "firebase": "^10.13.0", + "ky": "^1.7.1", "react": "^18.3.1", "react-aria": "^3.34.3", "react-aria-components": "^1.3.3", @@ -22,6 +24,7 @@ }, "devDependencies": { "@eslint/js": "^9.9.0", + "@tanstack/eslint-plugin-query": "^5.52.0", "@types/eslint__js": "^8.42.3", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", @@ -3744,6 +3747,151 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.52.0.tgz", + "integrity": "sha512-i02fOM3TRURI46AswPNlKb4Gwu+/mAPssI+pVu0AifA7/qzOJRgco17vdqjq/VgChKLLIltd9/KI4MCJFFfWEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "8.0.0-alpha.30" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8 || ^9" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.30.tgz", + "integrity": "sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.30.tgz", + "integrity": "sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.30.tgz", + "integrity": "sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.30.tgz", + "integrity": "sha512-rfhqfLqFyXhHNDwMnHiVGxl/Z2q/3guQ1jLlGQ0hi9Rb7inmwz42crM+NnLPR+2vEnwyw1P/g7fnQgQ3qvFx4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.30" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.30.tgz", + "integrity": "sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.52.0.tgz", + "integrity": "sha512-U1DOEgltjUwalN6uWYTewSnA14b+tE7lSylOiASKCAO61ENJeCq9VVD/TXHA6O5u9+6v5+UgGYBSccTKDoyMqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.52.1.tgz", + "integrity": "sha512-soyn4dNIUZ8US8NaPVXv06gkZFHaZnPfKWPDjRJjFRW3Y7WZ0jx72eT6zhw3VQlkMPysmXye8l35ewPHspKgbQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.52.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -7295,11 +7443,10 @@ } }, "node_modules/ky": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.6.0.tgz", - "integrity": "sha512-MG7hlH26oShC4Lysk5TYzXshHLfEY52IJ0ofOeCsifquqTymbXCSTx+g4rXO30XYxoM6Y1ed5pNnpULe9Rx19A==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.7.1.tgz", + "integrity": "sha512-KJ/IXXkFhTDqxcN8wKqMXk1/UoOpc0UnOB6H7QcqlPInh/M2B5Mlj+i9exez1w4RSwJhNFmHiUDPriAYFwb5VA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, diff --git a/package.json b/package.json index 7bfb708..9754d71 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,11 @@ "dependencies": { "@arcgis/core": "^4.30.9", "@heroicons/react": "^2.1.5", + "@tanstack/react-query": "^5.52.1", "@ugrc/layer-selector": "^6.2.7", "@ugrc/utah-design-system": "^1.4.1", "firebase": "^10.13.0", + "ky": "^1.7.1", "react": "^18.3.1", "react-aria": "^3.34.3", "react-aria-components": "^1.3.3", @@ -49,6 +51,7 @@ }, "devDependencies": { "@eslint/js": "^9.9.0", + "@tanstack/eslint-plugin-query": "^5.52.0", "@types/eslint__js": "^8.42.3", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", diff --git a/src/components/Filter.tsx b/src/components/Filter.tsx index a9f3fb2..6db190b 100644 --- a/src/components/Filter.tsx +++ b/src/components/Filter.tsx @@ -1,15 +1,47 @@ import FeatureLayer from '@arcgis/core/layers/FeatureLayer'; +import { useQuery } from '@tanstack/react-query'; import { Button, Checkbox, CheckboxGroup, TextField } from '@ugrc/utah-design-system'; +import ky from 'ky'; import { useEffect } from 'react'; +import config from '../config'; import { useMap } from './hooks'; -const secureServiceUrl = import.meta.env.VITE_PROXY_URL; +const emptyDefinition = '1=0'; -// TODO!: replace partial list with actual list -const purpose = ['Depletion estimate', 'Mark-Recapture', 'Disease certification', 'Genetics', 'Other']; +type DomainValue = { + name: string; + code: string; +}; +type Field = { + name: string; + domain: { + codedValues: DomainValue[]; + }; +}; +type FeatureLayerDefinition = { + fields: Field[]; +}; + +async function getPurposes(): Promise { + // TODO: this should probably come from env var + // if we do end up staying with the public service, then it will need to be published to the test server (wrimaps.at.utah.gov) + const url = 'https://wrimaps.utah.gov/arcgis/rest/services/Electrofishing/Public/MapServer/1?f=json'; + const responseJson = (await ky(url).json()) as FeatureLayerDefinition; + + const purposeField = responseJson.fields.find( + (field: Field) => field.name === config.fieldNames.events.SURVEY_PURPOSE, + ); + + if (!purposeField) { + throw new Error(`${config.fieldNames.events.SURVEY_PURPOSE} field not found in ${url}`); + } + + return purposeField.domain.codedValues; +} export default function Filter() { const { addLayers, mapView } = useMap(); + const purposeQuery = useQuery({ queryKey: ['purposes'], queryFn: getPurposes }); useEffect(() => { if (!mapView) { @@ -17,8 +49,8 @@ export default function Filter() { } const stations = new FeatureLayer({ - url: `${secureServiceUrl}/mapservice/0`, - id: 'stations', + url: 'https://wrimaps.utah.gov/arcgis/rest/services/Electrofishing/Public/MapServer/0', + definitionExpression: emptyDefinition, }); addLayers([stations]); }, [addLayers, mapView]); @@ -30,10 +62,10 @@ export default function Filter() {

Purpose

- {purpose.map((p) => ( -
- - + {purposeQuery.data?.map(({ name, code }) => ( +
+ +
))} diff --git a/src/config.js b/src/config.js index 5c5ec81..1cb5f8f 100644 --- a/src/config.js +++ b/src/config.js @@ -3,6 +3,11 @@ const config = { WEB_MERCATOR_WKID: 3857, MARKER_FILL_COLOR: [234, 202, 0, 0.5], MARKER_OUTLINE_COLOR: [77, 42, 84, 1], + fieldNames: { + events: { + SURVEY_PURPOSE: 'SURVEY_PURPOSE', + }, + }, }; export default config; diff --git a/src/main.tsx b/src/main.tsx index 9c9c6ac..b33ba8d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,4 +1,5 @@ import '@arcgis/core/assets/esri/themes/light/main.css'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { ErrorBoundary } from 'react-error-boundary'; @@ -34,14 +35,18 @@ const MainErrorFallback = ({ error, resetErrorBoundary }: { error: Error; resetE ); }; +const queryClient = new QueryClient(); + createRoot(document.getElementById('root')!).render( window.location.reload()}> - - - + + + + +