Skip to content

Commit

Permalink
Update challenge (#18)
Browse files Browse the repository at this point in the history
* Add Valeriy to reviewers

* Ignore IDE

* Upgrade next to v14

* Upgrade packages

* Eslint config

* Prettier, TS type

* Sort package json

* Take over some more stuff from most recent next app template

* Imports

* Layout

* Imports

* Remove tsbuildinfo

* Gitignore

* Fix eslint

* Add format/lint commands to README.md

* Use <Link> for fluent navigation
  • Loading branch information
arechsteiner authored Jul 23, 2024
1 parent f1d71aa commit 57bb523
Show file tree
Hide file tree
Showing 20 changed files with 2,659 additions and 415 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage
Expand Down Expand Up @@ -32,3 +33,10 @@ yarn-error.log*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# IDE
.idea/
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ We expect you to work on this challenge for a maximum of 1h.
All data comes from a JSON file; for this challenge we don't need to set up a database to keep it simple.

We mainly care about your thought process, which can include
* edge-cases you would think of before shipping this to production
* what makes for a great UI for this?
* how well would it scale and what we might need to change for high traffic?

- edge-cases you would think of before shipping this to production
- what makes for a great UI for this?
- how well would it scale and what we might need to change for high traffic?

## Objective

Expand All @@ -21,14 +21,10 @@ We want a simple UI that allows users to find and display an airport.
You may assume, that a colleague will do any backend changes and thus pretend it works as expected.
For missing functionality, explain to what and why you would need. Bonus point if you can do small adjustments yourself.



## Deliverables

* A video of your screen recording where you guide us through you doing the challenge (unlisted YouTube, Loom, .. any platform from where we can stream the video works). This means, you record yourself, while you solve the challenge.
* Do not push a public fork with your solution, instead keep it unlinked to the original repo or make it private (if private, please invite @swiknaba, @arechsteiner, and @michaelmikh)


- A video of your screen recording where you guide us through you doing the challenge (unlisted YouTube, Loom, .. any platform from where we can stream the video works). This means, you record yourself, while you solve the challenge.
- Do not push a public fork with your solution, instead keep it unlinked to the original repo or make it private (if private, please invite @swiknaba, @arechsteiner, @michaelmikh, and @vbykanov)

## Getting Started

Expand All @@ -38,8 +34,6 @@ The app is designed to work out of the box with no external dependencies, other
yarn install
```



## Running locally

```shell
Expand All @@ -48,7 +42,25 @@ yarn dev

Once started, the app should be available via http://localhost:3000

## Helpful commands

Run ESLint:

```shell
yarn lint
```

Format code:

```shell
yarn format
```

Check TS:

```shell
yarn tsc
```

## Help

Expand Down
16 changes: 12 additions & 4 deletions components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
const Layout: React.FC = ({ children }) => {
return <div className='container mx-auto py-5 px-5 md:px-0 md:py-10'>
{children}
</div>
import { FC, ReactNode } from 'react'

interface Props {
children: ReactNode
}
const Layout: FC<Props> = ({ children }) => {
return (
<div className="container mx-auto py-5 px-5 md:px-0 md:py-10">
<header className="py-5">DBL Code Challenge</header>
{children}
</div>
)
}

export default Layout
15 changes: 9 additions & 6 deletions hooks/use-api-data.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import axios from "axios"
import { useEffect, useState } from "react"
import axios from 'axios'
import { useEffect, useState } from 'react'

export const useApiData = <T>(path: string, defaultValue: any): T => {
const [ data, setData ] = useState<T>(defaultValue)
const [data, setData] = useState<T>(defaultValue)

useEffect(() => {
axios.get<T>(path).catch(err => err.response).then(response => {
setData(response.data)
})
axios
.get<T>(path)
.catch((err) => err.response)
.then((response) => {
setData(response.data)
})
}, [])

return data
Expand Down
6 changes: 3 additions & 3 deletions models/airport.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import airports from '../data/airports.json'
import Airport from '../types/airport'
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())
return airports.find((airport) => airport.iata === iata.toUpperCase())
}

export const allAirports = async (): Promise<Airport[]> => {
Expand Down
4 changes: 4 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}

export default nextConfig
40 changes: 26 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
{
"name": "code-challenge-nextjs",
"version": "0.1.0",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"dev": "next dev",
"format": "yarn prettier --write './**/*.{js,json,ts,tsx,mjs}'",
"lint": "next lint",
"start": "next start"
},
"engines": {
"node": ">= 16.15 <19"
"prettier": {
"printWidth": 100,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
},
"dependencies": {
"axios": "^0.27.2",
"next": "12.2.0",
"react": "18.2.0",
"react-dom": "18.2.0"
"axios": "^1.7.2",
"next": "^14.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/node": "^18.0.3",
"@types/react": "^18.0.15",
"autoprefixer": "^10.4.7",
"postcss": "^8.4.14",
"tailwindcss": "^3.1.4",
"typescript": "^4.7.4"
"@types/node": "^20.14.11",
"@types/react": "^18.3.3",
"autoprefixer": "^10.4.19",
"eslint": "^8",
"eslint-config-next": "^14.2.5",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"tailwindcss": "^3.4.6",
"typescript": "^5.5.4"
},
"engines": {
"node": ">=18"
}
}
3 changes: 2 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'tailwindcss/tailwind.css'
import { AppProps } from 'next/app'

function MyApp({ Component, pageProps }) {
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}

Expand Down
29 changes: 18 additions & 11 deletions pages/airports/[iata].tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
import { GetServerSideProps, NextPage } from 'next'

import Layout from '../../components/layout'
import { findAirportByIata } from '../../models/airport'
import Airport from '../../types/airport'
import Layout from '@/components/layout'
import { findAirportByIata } from '@/models/airport'
import Airport from '@/types/airport'
import { ParsedUrlQuery } from 'node:querystring'

interface Params extends ParsedUrlQuery {
iata: string
}

interface Props {
airport: Airport | undefined
airport?: Airport
}

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>
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
export const getServerSideProps: GetServerSideProps<Props, Params> = async ({ params }) => {
const { iata } = params!
const airport = await findAirportByIata(iata.toString())

return {
props: {
airport,
}
},
}
}

Expand Down
6 changes: 4 additions & 2 deletions pages/api/airports.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { NextApiRequest, NextApiResponse } from 'next'

import { allAirports } from '../../models/airport'
import { allAirports } from '@/models/airport'

export default async (req: NextApiRequest, res: NextApiResponse) => {
const airports = async (req: NextApiRequest, res: NextApiResponse) => {
const airports = await allAirports()

res.status(200).json(airports)
}

export default airports
43 changes: 23 additions & 20 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import { NextPage } from 'next'

import Layout from '../components/layout'
import useApiData from '../hooks/use-api-data'
import Airport from '../types/airport'
import Layout from '@/components/layout'
import useApiData from '@/hooks/use-api-data'
import Airport from '@/types/airport'
import Link from 'next/link'

const Page: NextPage = () => {
const airports = useApiData<Airport[]>('/api/airports', [])

return <Layout>
<h1 className='text-2xl'>DBL Code Challenge: Airports</h1>
return (
<Layout>
<h1 className="text-2xl">All 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>
<div>
{airports.map((airport) => (
<Link
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>
</Link>
))}
</div>
</Layout>
)
}

export default Page
6 changes: 0 additions & 6 deletions postcss.config.js

This file was deleted.

8 changes: 8 additions & 0 deletions postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
}

export default config
4 changes: 0 additions & 4 deletions public/vercel.svg

This file was deleted.

14 changes: 0 additions & 14 deletions tailwind.config.js

This file was deleted.

15 changes: 15 additions & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Config } from 'tailwindcss'

const config: Config = {
content: ['./components/**/*.{ts,tsx}', './pages/**/*.{ts,tsx}', './src/**/*.{ts,tsx}'],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [],
}
export default config
Loading

0 comments on commit 57bb523

Please sign in to comment.