bring over code challenge

This commit is contained in:
Friedhelm Filler 2021-05-11 20:35:45 +01:00
commit 76b596da09
No known key found for this signature in database
GPG key ID: BA6A6C5B1AC9E30A
20 changed files with 51221 additions and 0 deletions

34
.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# Code Challenge: NextJS
This is a simple app to lookup airport information.
## Getting Started
The app is designed to work out of the box with no external dependencies, other than node modules.
```shell
yarn install
```
## Running locally
```shell
yarn dev
```
Once started, the app should be available via http://localhost:3000
## Help
For detailed explanation on how things work, check out [Next.js docs](https://nextjs.org).

7
components/layout.tsx Normal file
View file

@ -0,0 +1,7 @@
const Layout: React.FC = ({ children }) => {
return <div className='container mx-auto py-5 px-5 md:px-0 md:py-10'>
{children}
</div>
}
export default Layout

48586
data/airports.json Normal file

File diff suppressed because it is too large Load diff

16
hooks/use-api-data.ts Normal file
View file

@ -0,0 +1,16 @@
import axios from "axios"
import { useEffect, useState } from "react"
export const useApiData = <T>(path: string, defaultValue: any): T => {
const [ data, setData ] = useState<T>(defaultValue)
useEffect(() => {
axios.get<T>(path).catch(err => err.response).then(response => {
setData(response.data)
})
}, [])
return data
}
export default useApiData

10
models/airport.ts Normal file
View file

@ -0,0 +1,10 @@
import airports from '../data/airports.json'
import Airport from '../types/airport'
export const findAirportByIata = async (iata: string): Promise<Airport | undefined> => {
return airports.find(airport => airport.iata === iata.toUpperCase())
}
export const allAirports = async (): Promise<Airport[]> => {
return airports
}

2
next-env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "code-challenge-nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"axios": "^0.21.1",
"next": "10.0.7",
"react": "17.0.1",
"react-dom": "17.0.1"
},
"devDependencies": {
"@types/node": "^14.14.31",
"@types/react": "^17.0.2",
"autoprefixer": "^10.2.4",
"postcss": "^8.2.6",
"tailwindcss": "^2.0.3",
"typescript": "^4.2.2"
}
}

7
pages/_app.tsx Normal file
View file

@ -0,0 +1,7 @@
import 'tailwindcss/tailwind.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp

29
pages/airports/[iata].tsx Normal file
View file

@ -0,0 +1,29 @@
import { GetServerSideProps, NextPage } from 'next'
import Layout from '../../components/layout'
import { findAirportByIata } from '../../models/airport'
import Airport from '../../types/airport'
interface Props {
airport: Airport | undefined
}
const Page: NextPage<Props> = ({ airport }) => {
return <Layout>
<h1 className='text-2xl'>Airport: {airport.name}</h1>
<pre className='mt-10 text-gray-500 text-sm'>{JSON.stringify(airport, undefined, 2)}</pre>
</Layout>
}
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const { iata } = params
const airport = await findAirportByIata(iata.toString())
return {
props: {
airport,
}
}
}
export default Page

9
pages/api/airports.ts Normal file
View file

@ -0,0 +1,9 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { allAirports } from '../../models/airport'
export default async (req: NextApiRequest, res: NextApiResponse) => {
const airports = await allAirports()
res.status(200).json(airports)
}

30
pages/index.tsx Normal file
View file

@ -0,0 +1,30 @@
import { NextPage } from 'next'
import Layout from '../components/layout'
import useApiData from '../hooks/use-api-data'
import Airport from '../types/airport'
const Page: NextPage = () => {
const airports = useApiData<Airport[]>('/api/airports', [])
return <Layout>
<h1 className='text-2xl'>Code Challenge: Airports</h1>
<h2 className="mt-10 text-xl">All Airports</h2>
<div>
{airports.map(airport => (
<a href={`/airports/${airport.iata.toLowerCase()}`} key={airport.iata} className='mt-5 flex items-center shadow p-5 border'>
<div>
{airport.name}, {airport.city}
</div>
<div className='ml-auto text-mono'>
{airport.country}
</div>
</a>
))}
</div>
</Layout>
}
export default Page

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

4
public/vercel.svg Normal file
View file

@ -0,0 +1,4 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

14
tailwind.config.js Normal file
View file

@ -0,0 +1,14 @@
module.exports = {
purge: [
'./pages/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}

29
tsconfig.json Normal file
View file

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

10
types/airport.ts Normal file
View file

@ -0,0 +1,10 @@
interface Airport {
name: string
iata: string
city: string
country: string
longitude: number
latitude: number
}
export default Airport

11
utils/axios.ts Normal file
View file

@ -0,0 +1,11 @@
import axios from 'axios'
const instance = axios.create({
validateStatus: (status: number) => false,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
export default instance

2365
yarn.lock Normal file

File diff suppressed because it is too large Load diff