Skip to content

Commit

Permalink
Merge pull request #44 from jpedroh/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jpedroh authored May 6, 2021
2 parents c91495a + 445ff87 commit b368b44
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 190 deletions.
4 changes: 2 additions & 2 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"license": "MIT",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.0.0",
"@typescript-eslint/parser": "^3.7.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^7.5.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.2",
"eslint-import-resolver-typescript": "^2.0.0",
"eslint-plugin-import": "^2.22.0",
Expand Down
83 changes: 83 additions & 0 deletions packages/front/contexts/FlightsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Flight from '@mach/common'
import { createContext, FC, useCallback, useReducer } from 'react'
import getFlights, { GetFlightsQuery } from '../actions/get-flights'

type FlightsState = {
data: { items: Flight[]; count: number; query?: GetFlightsQuery }
loading: boolean
error: Error | null
}

type FlightsContextData = {
state: FlightsState
loadFlights: (params: GetFlightsQuery) => Promise<void>
}

const FlightsContext = createContext({} as FlightsContextData)

const initialState = {
data: { items: [], count: 0, query: null },
loading: false,
error: null
}

const reducer = (
state: FlightsState,
action: {
type: FlightsActions
payload?: any
}
): typeof state => {
switch (action.type) {
case 'LOAD_FLIGHTS_INIT':
return {
...state,
data: { ...state.data, query: action.payload },
loading: true,
error: null
}
case 'LOAD_FLIGHTS_SUCCESS':
return {
...state,
data: { ...state.data, ...action.payload },
loading: false,
error: null
}
case 'LOAD_FLIGHTS_ERROR':
return {
...state,
data: { items: [], count: 0 },
loading: false,
error: action.payload
}
default:
throw new Error('Invalid operation provided')
}
}

type FlightsActions =
| 'LOAD_FLIGHTS_INIT'
| 'LOAD_FLIGHTS_SUCCESS'
| 'LOAD_FLIGHTS_ERROR'

const FlightsProvider: FC = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState)

const loadFlights = useCallback(async (params: GetFlightsQuery) => {
try {
dispatch({ type: 'LOAD_FLIGHTS_INIT', payload: params })
const payload = await getFlights(params)
dispatch({ type: 'LOAD_FLIGHTS_SUCCESS', payload })
} catch (error) {
dispatch({ type: 'LOAD_FLIGHTS_ERROR', payload: error })
}
}, [])

return (
<FlightsContext.Provider value={{ state, loadFlights }}>
{children}
</FlightsContext.Provider>
)
}

export { FlightsContext, FlightsProvider }
4 changes: 2 additions & 2 deletions packages/front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"@mach/common": "^4.1.5",
"bootstrap": "^4.5.2",
"next": "^10.2.0",
"react": "^16.13.1",
"react": "^17.0.2",
"react-bootstrap": "^1.3.0",
"react-data-table-component": "^6.11.5",
"react-dom": "^16.13.1",
"react-dom": "^17.0.2",
"styled-components": "^5.2.0"
},
"devDependencies": {
Expand Down
11 changes: 8 additions & 3 deletions packages/front/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import React, { useEffect } from 'react'
import 'bootstrap/dist/css/bootstrap.min.css'
import { AppProps } from 'next/app'
import React, { useEffect } from 'react'
import { FlightsProvider } from '../contexts/FlightsContext'

const App: React.FC<AppProps> = ({ Component, pageProps }) => {
useEffect(() => {
document.title = 'Mach - Flight Planning'
})
return <Component {...pageProps} />
}, [])
return (
<FlightsProvider>
<Component {...pageProps} />
</FlightsProvider>
)
}

export default App
47 changes: 26 additions & 21 deletions packages/front/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import { useRouter } from 'next/router'
import React from 'react'
import React, { useContext } from 'react'
import Alert from 'react-bootstrap/Alert'
import Button from 'react-bootstrap/Button'
import Form from 'react-bootstrap/Form'
import { GetFlightsQuery } from '../actions/get-flights'
import { FlightsContext } from '../contexts/FlightsContext'
import BaseLayout from '../layouts/base-layout'
import sanitizeParameters from '../utils/sanitize-parameters'

const Home: React.FC = () => {
const router = useRouter()
const [parameters, setParameters] = React.useState<Partial<GetFlightsQuery>>({
const { state, loadFlights } = useContext(FlightsContext)
const [parameters, setParameters] = React.useState<GetFlightsQuery>({
departureIcao: '',
arrivalIcao: ''
arrivalIcao: '',
limit: 10,
offset: 0
})
const [error, setError] = React.useState('')

const handleSubmit = evt => {
const handleSubmit = async evt => {
evt.preventDefault()
if (!parameters.departureIcao && !parameters.arrivalIcao) {
setError('Fill at least one of the fields')
return
}

router.replace({
pathname: 'search',
query: parameters
})
await loadFlights(parameters)
router.replace({ pathname: 'search' })
}

const handleChange = event => {
const updated = {}
updated[event.target.name] = event.target.value.toUpperCase()
setParameters({ ...parameters, ...updated })
setParameters(
sanitizeParameters({
...parameters,
[event.target.name]: event.target.value.toUpperCase()
})
)
}

return (
Expand Down Expand Up @@ -59,14 +59,19 @@ const Home: React.FC = () => {
placeholder="SBRF"
/>
</Form.Group>
<Button variant="primary" block type="submit">
Start search
<Button
variant="primary"
block
type="submit"
disabled={state.loading}
>
{state.loading ? 'Loading' : 'Start search'}
</Button>
</Form>

{error && (
{state.error && (
<Alert className="mt-2" variant={'danger'}>
{error}
{state.error.message}
</Alert>
)}
</div>
Expand Down
54 changes: 9 additions & 45 deletions packages/front/pages/search.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
import { NextPage } from 'next'
import { useRouter } from 'next/router'
import React from 'react'
import getFlights, { GetFlightsQuery } from '../actions/get-flights'
import React, { useContext } from 'react'
import { GetFlightsQuery } from '../actions/get-flights'
import FlightModal from '../components/flight-modal'
import ResultsLead from '../components/results-lead'
import SearchTable from '../components/search-table'
import { FlightsContext } from '../contexts/FlightsContext'
import BaseLayout from '../layouts/base-layout'
import makeBlankFlight from '../utils/make-blank-flight'

const buildParameters = query => {
const parameters: GetFlightsQuery = {
offset: 0,
limit: 10
}

if (query.departureIcao) {
parameters.departureIcao = query.departureIcao as string
}
if (query.arrivalIcao) {
parameters.arrivalIcao = query.arrivalIcao as string
}

return parameters
}

const Search: NextPage = () => {
const { query } = useRouter()

const [parameters, setParameters] = React.useState(buildParameters(query))
const [loading, setLoading] = React.useState(true)
const { state, loadFlights } = useContext(FlightsContext)
const [show, setShow] = React.useState(false)
const [apiResponse, setApiResponse] = React.useState({ count: 0, items: [] })
const [flight, setFlight] = React.useState(makeBlankFlight())

const handleClose = () => setShow(false)
Expand All @@ -39,30 +19,14 @@ const Search: NextPage = () => {
setShow(true)
}

const loadFlights = async () => {
try {
setLoading(true)
setApiResponse(await getFlights(parameters))
} catch (error) {
setApiResponse({ count: 0, items: [] })
console.error(error)
} finally {
setLoading(false)
}
}

React.useEffect(() => {
loadFlights()
}, [parameters])

return (
<BaseLayout>
<ResultsLead loading={loading} count={apiResponse.count} />
<ResultsLead loading={state.loading} count={state.data.count} />
<SearchTable
loading={loading}
data={apiResponse.items}
count={apiResponse.count}
onOffsetChange={offset => setParameters({ ...parameters, offset })}
loading={state.loading}
data={state.data.items}
count={state.data.count}
onOffsetChange={offset => loadFlights({ ...state.data.query, offset })}
onDetailsShow={handleShow}
/>
<FlightModal show={show} handleClose={handleClose} flight={flight} />
Expand Down
7 changes: 7 additions & 0 deletions packages/front/utils/sanitize-parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { GetFlightsQuery } from '../actions/get-flights'

export default function sanitizeParameters(params: Partial<GetFlightsQuery>) {
return Object.fromEntries(
Object.entries(params).filter(([_, v]) => v != '')
) as GetFlightsQuery
}
Loading

0 comments on commit b368b44

Please sign in to comment.