From 69745d8d6ea5ff4c2f2b5f9b6038591d9e5339be Mon Sep 17 00:00:00 2001 From: stdavis Date: Fri, 1 Nov 2024 14:50:00 -0600 Subject: [PATCH] feat: add download functionality Ref #186 --- src/components/Download.tsx | 98 ++++++++++++++++++++++++ src/components/ResultsGrid.tsx | 136 ++++++++++++++++++--------------- src/components/Table.tsx | 2 +- src/config.ts | 1 + 4 files changed, 176 insertions(+), 61 deletions(-) create mode 100644 src/components/Download.tsx diff --git a/src/components/Download.tsx b/src/components/Download.tsx new file mode 100644 index 0000000..9f24c9a --- /dev/null +++ b/src/components/Download.tsx @@ -0,0 +1,98 @@ +import { submitJob } from '@arcgis/core/rest/geoprocessor'; +import { useQueryClient } from '@tanstack/react-query'; +import { Button, Select, SelectItem, Spinner } from '@ugrc/utah-design-system'; +import { useState } from 'react'; +import config from '../config'; + +async function download(eventIds: string[], format: string): Promise { + const jobInfo = await submitJob(config.urls.download, { + Event_Ids: eventIds.join(';'), + Format: format, + }); + + await jobInfo.waitForJobCompletion(); + + const parameter = await jobInfo.fetchResultData('Zip_File'); + const value = parameter.value as __esri.DataFile; + + return value.url.replace('http', 'https'); +} + +type State = { + format: string; + error: string | null; + isBusy: boolean; +}; + +export default function Download({ eventIds }: { eventIds: string[] }): JSX.Element { + const queryClient = useQueryClient(); + const [state, setState] = useState({ + format: '', + error: null, + isBusy: false, + }); + + const updateState = (newState: Partial) => setState((prev) => ({ ...prev, ...newState })); + + const onDownloadClick = async () => { + if (eventIds.length === 0) { + console.warn('No data to download'); + + return; + } + + updateState({ isBusy: true, error: null }); + + let url; + try { + url = await queryClient.fetchQuery({ + queryKey: ['download', eventIds], + queryFn: () => download(eventIds as string[], state.format), + }); + } catch (error) { + console.error('Error downloading data', error); + updateState({ error: 'There was an error with the download service', isBusy: false }); + + return; + } + + const link = document.createElement('a'); + link.href = url; + link.download = 'data.zip'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + updateState({ isBusy: false }); + }; + + return ( +
+

This can take a few minutes to process.

+ +

+ +

+ {state.error &&
{state.error}
} +
+ ); +} diff --git a/src/components/ResultsGrid.tsx b/src/components/ResultsGrid.tsx index d759760..311f7a7 100644 --- a/src/components/ResultsGrid.tsx +++ b/src/components/ResultsGrid.tsx @@ -2,9 +2,10 @@ import { useQuery } from '@tanstack/react-query'; import { Spinner, useFirebaseAuth } from '@ugrc/utah-design-system'; import { User } from 'firebase/auth'; import ky from 'ky'; -import { TableBody } from 'react-aria-components'; +import { Tab, TableBody, TabList, TabPanel, Tabs } from 'react-aria-components'; import config from '../config'; import { useFilter } from './contexts/FilterProvider'; +import Download from './Download'; import { getGridQuery, removeIrrelevantWhiteSpace } from './queryHelpers'; import { Cell, Column, Row, Table, TableHeader } from './Table'; @@ -125,67 +126,82 @@ export default function ResultsGrid() { return {error.message}; } + const eventIds = data?.length ? (data.map((row) => row[config.fieldNames.EVENT_ID]) as string[]) : ([] as string[]); + return ( <> -
Results: {data?.length}
- - - - Event Date - - - Observers - - - Stream - - - Stream ID - - - Stream Reach Code - - - Lake - - - Lake ID - - - Lake Reach Code - - - Station Name - - - Species Codes - - - Equipment - - - Event ID - - - - {(row) => ( - - {new Date(row[config.fieldNames.EVENT_DATE] as number).toLocaleDateString()} - {row[config.fieldNames.OBSERVERS]} - {row[`${config.fieldNames.WaterName}_Stream`]} - {row[`${config.fieldNames.DWR_WaterID}_Stream`]} - {row[`${config.fieldNames.ReachCode}_Stream`]} - {row[`${config.fieldNames.WaterName}_Lake`]} - {row[`${config.fieldNames.DWR_WaterID}_Lake`]} - {row[`${config.fieldNames.ReachCode}_Lake`]} - {row[STATION_NAME]} - {row[config.fieldNames.SPECIES]} - {row[config.fieldNames.TYPES]} - {row[config.fieldNames.EVENT_ID]} - - )} - -
+ + Records: {data?.length} + + + + Results + Download + + + + + + Event Date + + + Observers + + + Stream + + + Stream ID + + + Stream Reach Code + + + Lake + + + Lake ID + + + Lake Reach Code + + + Station Name + + + Species Codes + + + Equipment + + + Event ID + + + + {(row) => ( + + {new Date(row[config.fieldNames.EVENT_DATE] as number).toLocaleDateString()} + {row[config.fieldNames.OBSERVERS]} + {row[`${config.fieldNames.WaterName}_Stream`]} + {row[`${config.fieldNames.DWR_WaterID}_Stream`]} + {row[`${config.fieldNames.ReachCode}_Stream`]} + {row[`${config.fieldNames.WaterName}_Lake`]} + {row[`${config.fieldNames.DWR_WaterID}_Lake`]} + {row[`${config.fieldNames.ReachCode}_Lake`]} + {row[STATION_NAME]} + {row[config.fieldNames.SPECIES]} + {row[config.fieldNames.TYPES]} + {row[config.fieldNames.EVENT_ID]} + + )} + +
+
+ + + +
); } diff --git a/src/components/Table.tsx b/src/components/Table.tsx index b7ba74d..0d2afb7 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -24,7 +24,7 @@ import { composeTailwindRenderProps, focusRing } from './utils.ts'; export function Table(props: TableProps) { return ( - + ); diff --git a/src/config.ts b/src/config.ts index 825d6f8..37029c8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -65,6 +65,7 @@ const config = { 'https://gis.trustlands.utah.gov/hosting/rest/services/Hosted/Land_Ownership_WM_VectorTile/VectorTileServer', streams: `${referenceMapService}/0`, lakes: `${referenceMapService}/1`, + download: `${functionsUrl}/toolbox/Download`, }, };