Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Exercise 2: fetching data with a form #14

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
Links,
LiveReload,
Meta,
NavLink,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react'
import { GovBanner, GridContainer } from '@trussworks/react-uswds'
import { GovBanner, GridContainer, PrimaryNav } from '@trussworks/react-uswds'

import theme from './theme.css'
import favicon_16 from '~/../node_modules/@uswds/uswds/dist/img/favicons/favicon-16.png'
Expand Down Expand Up @@ -51,6 +52,17 @@ export default function App() {
<Links />
</head>
<body>
<PrimaryNav
className="margin-top-1"
items={[
<NavLink className="" to="/data" key="/data">
Data
</NavLink>,
<NavLink className="" to="/" key="/">
Home
</NavLink>,
]}
></PrimaryNav>
<a className="usa-skipnav" href="#main-content">
Skip to main content
</a>
Expand Down
14 changes: 14 additions & 0 deletions app/routes/data._index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Link } from '@remix-run/react'

export default function () {
return (
<div>
<h1>Instrument Data</h1>
<div>
<Link to="/data/new" className="usa-button">
New Data Gathering Session
</Link>
</div>
</div>
)
}
186 changes: 186 additions & 0 deletions app/routes/data.new.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import type { DataFunctionArgs } from '@remix-run/node'
import { useFetcher } from '@remix-run/react'
import {
Button,
ButtonGroup,
FormGroup,
Grid,
GridContainer,
Icon,
Label,
Select,
TextInput,
} from '@trussworks/react-uswds'
import { useState } from 'react'

import { getInstrumentData } from './data/data.server'
import { tableOptions } from './data/data.table-options'

function validateResultCountInput(input: string) {
const numericalInput = parseFloat(input)
return numericalInput.toString() === input && numericalInput <= 100
}

export async function action({ request }: DataFunctionArgs) {
const data = await request.formData()
const format = data.get('format')?.toString()
const resultCount = data.get('resultCount')?.toString()
const instrument = data.get('instrument')?.toString()
const ra = data.get('ra')?.toString()
const dec = data.get('dec')?.toString()
const radius = data.get('radius')?.toString()

if (!instrument || !format || !resultCount || !ra || !dec || !radius)
return null
const res = await getInstrumentData(
instrument,
format,
parseInt(resultCount),
parseFloat(ra),
parseFloat(dec),
parseInt(radius)
)
return res
}

export default function () {
const [instrument, setInstrument] = useState('')
const [format, setFormat] = useState('')
const [ra, setRa] = useState('')
const [dec, setDec] = useState('')
const [radius, setRadius] = useState('')
const [resultCount, setResultCount] = useState('')
const isValid = validateResultCountInput(resultCount)

const fetcher = useFetcher()
const results = fetcher.data
const resultArray = results?.request || ['no results']

const shouldDisableForm =
!instrument || !format || !resultCount || !isValid || !ra || !dec || !radius

return (
<div>
<h1 className="text-center">Instrument Data Form</h1>
<GridContainer>
<Grid row>
<Grid tablet={{ col: true }} className="bg-base-lightest">
<h2 className="text-center">Get data by instrument</h2>
<fetcher.Form method="POST" className="margin-2">
<input hidden name="intent" defaultValue="get-data" />
<Label htmlFor="instruments">Choose Instrument</Label>
<Select
id="instrument"
name="instrument"
value={instrument}
onChange={(e) => {
setInstrument(e.target.value)
}}
>
<option value="">Select instrument</option>
{tableOptions.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</Select>
<ButtonGroup className="margin-top-2">
<Label htmlFor="ra">RA:</Label>
<TextInput
type="text"
id="ra"
name="ra"
placeholder="0"
className="width-10 margin-right-5"
onChange={(e) => {
setRa(e.target.value)
}}
></TextInput>
<Label htmlFor="dec">DEC:</Label>
<TextInput
type="text"
id="dec"
name="dec"
placeholder="0"
className="width-10"
onChange={(e) => {
setDec(e.target.value)
}}
></TextInput>
</ButtonGroup>
<Label htmlFor="radius">Radius (arcmin)</Label>
<TextInput
type="text"
id="radius"
name="radius"
placeholder="0"
className="width-10"
onChange={(e) => {
setRadius(e.target.value)
}}
></TextInput>
<Label htmlFor="format">Choose result format</Label>
<Select
id="format"
name="format"
value={format}
onChange={(e) => {
setFormat(e.target.value)
}}
>
<option value="">Select format</option>
<option value="json">json</option>
<option value="txt">txt</option>
</Select>
<Label htmlFor="resultCount">Result Count</Label>
<TextInput
type="text"
id="resultCount"
name="resultCount"
placeholder="0-100"
className="width-10"
onChange={(e) => {
setResultCount(e.target.value)
}}
></TextInput>
<FormGroup>
<Button type="submit" disabled={shouldDisableForm}>
Submit
</Button>

{fetcher.state !== 'idle' && (
<>
<Icon.Autorenew className="text-middle" /> Loading...
</>
)}
{fetcher.state === 'idle' &&
fetcher.data === null &&
!shouldDisableForm && (
<>
<Icon.Check className="text-middle" color="green" /> Saved
</>
)}
</FormGroup>
</fetcher.Form>
</Grid>
</Grid>
<hr></hr>
<Grid row>
<Grid
tablet={{ col: true }}
className="bg-primary-lighter margin-bottom-5"
>
<h2 className="margin-left-2">Results</h2>
{resultArray.map((x: object, index: number) => {
return (
<GridContainer key={index} className="margin-bottom-2">
{JSON.stringify(x, null, 2)}
</GridContainer>
)
})}
</Grid>
</Grid>
</GridContainer>
</div>
)
}
53 changes: 53 additions & 0 deletions app/routes/data/data.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
async function fetchData(
instrument: string,
format: string,
resultCount: number,
ra: number,
dec: number,
radius: number
) {
const heasarc_api_url = 'https://heasarc.gsfc.nasa.gov/xamin/QueryServlet'
const formData = new FormData()

formData.append('table', instrument)
formData.append('format', format)
formData.append('resultmax', resultCount.toString())
formData.append('radius', radius.toString())
formData.append('position[]', ra.toString())
formData.append('position[]', dec.toString())

try {
const response = await fetch(heasarc_api_url, {
method: 'POST',
body: formData,
})
const data =
format === 'json' ? await response.json() : await response.text()
return data
} catch (error) {
console.log(error)
}
}

export async function getInstrumentData(
instrument: string,
format: string,
resultCount: number,
ra: number,
dec: number,
radius: number
) {
try {
const response = await fetchData(
instrument,
format,
resultCount,
ra,
dec,
radius
)
return response
} catch (error) {
console.log(error)
}
}
Loading
Loading