diff --git a/app.css b/app.css new file mode 100644 index 0000000..2ddde39 --- /dev/null +++ b/app.css @@ -0,0 +1,20 @@ +body { + font-family: sans-serif; + background-color: #1c1c1e; + color: #d0cd9a; + padding: 5px; +} + +.columns { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 10px; +} + +.assignment section { + position: relative; +} + +.assignment img { + max-width: 100%; +} diff --git a/pages/_app.tsx b/pages/_app.tsx index 3f5c9d5..a293b3a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,8 +1,11 @@ -import '../styles/globals.css' -import type { AppProps } from 'next/app' +import "../styles/globals.css"; +import type { AppProps } from "next/app"; + +import "../app.css"; +import "../src/components/Vehicles.css"; function MyApp({ Component, pageProps }: AppProps) { - return + return ; } -export default MyApp +export default MyApp; diff --git a/pages/api/vehicles.ts b/pages/api/vehicles.ts new file mode 100644 index 0000000..fccfae9 --- /dev/null +++ b/pages/api/vehicles.ts @@ -0,0 +1,11 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import vehicles from "../../vehicles.json"; + +export default function handler( + req: NextApiRequest, + res: NextApiResponse +) { + setTimeout(() => { + res.status(200).json(vehicles); + }, 1000); +} diff --git a/pages/index.tsx b/pages/index.tsx index 86b5b3b..e053e77 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,72 +1,23 @@ -import type { NextPage } from 'next' -import Head from 'next/head' -import Image from 'next/image' -import styles from '../styles/Home.module.css' +import type { NextPage } from "next"; +import Head from "next/head"; +import Assignments from "../src/Assignments"; +import Vehicles from "../src/components/Vehicles"; const Home: NextPage = () => { return ( -
+
- Create Next App + WG Job Interview -
-

- Welcome to Next.js! -

- -

- Get started by editing{' '} - pages/index.tsx -

- - -
- - +
+ +
+
- ) -} + ); +}; -export default Home +export default Home; diff --git a/public/czech.png b/public/czech.png new file mode 100644 index 0000000..e62c34c Binary files /dev/null and b/public/czech.png differ diff --git a/public/heavytank.svg b/public/heavytank.svg new file mode 100644 index 0000000..b26cd0d --- /dev/null +++ b/public/heavytank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/lighttank.svg b/public/lighttank.svg new file mode 100644 index 0000000..970b74c --- /dev/null +++ b/public/lighttank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/list.png b/public/list.png new file mode 100644 index 0000000..9ea62e4 Binary files /dev/null and b/public/list.png differ diff --git a/public/mediumtank.svg b/public/mediumtank.svg new file mode 100644 index 0000000..be8d491 --- /dev/null +++ b/public/mediumtank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Assignments.tsx b/src/Assignments.tsx new file mode 100644 index 0000000..9bd04ca --- /dev/null +++ b/src/Assignments.tsx @@ -0,0 +1,110 @@ +import React, { useState } from "react"; +const assignments = [ +
+

Assignment #1: Style the list

+

+ Code the folowing static content in /src/components/Vehicles.tsx according + to design. Images are in /public +

+ +

Bonus questions

+
    +
  1. How to make vehicle class SVGs respect text color?
  2. +
  3. + How prevent styles in Vehicles.css influencing the rest of the app? +
  4. +
+
, +
+

Assignment #2: Generate the list from JSON

+

+ Update /src/components/Vehicles.tsx to use:  + import vehicles from "../../vehicles.json"; +  instead of hardcoded data +

+

Bonus questions

+
    +
  1. + Have a look at src/components/getRomanNumerals.ts and try to finish the + implementation. +
  2. +
  3. + How would you recommend to update the Vehicles component if there were + thousands of vehicles in the list? +
  4. +
+
, +
+

Assignment #3: Filter the list

+

+ Update /src/components/Vehicles.tsx to filter the list by a text input. +

+

Bonus questions

+
    +
  1. + How would you lower server load, if the filtering was done on the + server? +
  2. +
  3. + What would you have to watch out for, if the filtering was done on the + server and the server response time was very irregular? How would you + deal with it? +
  4. +
+
, +
+

Assignment #4: Get list data asynchroneously

+

+ Update /src/components/Vehicles.tsx to use:  + {`const { data, error } = useVehicles();`} +  from /src/useVehicles.ts +

+

Bonus questions

+
    +
  1. + How would improve UX experience, if the API was slow and unreliable? +
  2. +
  3. + How would you reduce the loading interval, if the list had a lot of + entries? +
  4. +
+
+]; + +const Assignments = ({ initialAssignment }: { initialAssignment: number }) => { + const [currentIndex, setCurrentIndex] = useState(initialAssignment - 1); + + return ( +
+ + Show assignment:  + {assignments.map((assignment, assignmentIndex) => { + return ( + + ); + })} + {assignments[currentIndex]} +
+ ); +}; + +export default Assignments; diff --git a/src/components/Vehicles.css b/src/components/Vehicles.css new file mode 100644 index 0000000..549878f --- /dev/null +++ b/src/components/Vehicles.css @@ -0,0 +1,60 @@ +.Vehicles { + display: flex; + justify-content: center; + flex-direction: column; +} + +.Vehicle { + display: flex; + justify-content: space-around; + align-items: center; +} + +.Vehicle__premium { +} + +.Vehicle_nation { +} + +.Vehicle_flag { +} + +.Vehicle_flag__czech { + /* 29x18 /czech.png */ +} + +.Vehicle_type { +} + +.Vehicle_type_icon { +} +/* 11x13 /lighttank.svg */ +.Vehicle_type_icon__lighttank { +} + +/* 12x15 /mediumtank.svg */ +.Vehicle_type_icon__mediumtank { +} + +/* 12x15 /mediumtank.svg */ +.Vehicle_type_icon__mediumtank-prem { +} + +/* 15x18 /heavytank.svg */ +.Vehicle_type_icon__heavytank-prem { +} + +.Vehicle_tier { +} + +.Vehicle_tier_text { +} + +.Vehicle_title { +} + +.Vehicle_shape { +} + +.Vehicle_name { +} diff --git a/src/components/Vehicles.tsx b/src/components/Vehicles.tsx new file mode 100644 index 0000000..ca54404 --- /dev/null +++ b/src/components/Vehicles.tsx @@ -0,0 +1,284 @@ +import React from "react"; + +/* + For Assignment #2 + import vehicles from "../../vehicles.json"; + import getRomanNumerals from "../getRomanNumerals"; + { + "nation": "czech", + "type": "lighttank", + "tier": 1, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz06_kolohousenka/cz06_kolohousenka_icon.svg", + "name": "Kolohousenka" + }, +*/ + +const Vehicles = () => { + return ( +
+
+
+ +
+
+ +
+
+ I +
+
+ + + + Kolohousenka +
+
+
+
+ +
+
+ +
+
+ II +
+
+ + + + LT vz. 35 +
+
+
+
+ +
+
+ +
+
+ III +
+
+ + + + LT vz. 38 +
+
+
+
+ +
+
+ +
+
+ IV +
+
+ + + + ST vz. 39 +
+
+
+
+ +
+
+ +
+
+ V +
+
+ + + + Škoda T 24 +
+
+
+
+ +
+
+ +
+
+ VI +
+
+ + + + Škoda T 25 +
+
+
+
+ +
+
+ +
+
+ VI +
+
+ + + + Škoda T 40 +
+
+
+
+ +
+
+ +
+
+ VII +
+
+ + + + Konštrukta T-34/100 +
+
+
+
+ +
+
+ +
+
+ VIII +
+
+ + + + TVP VTU Koncept +
+
+
+
+ +
+
+ +
+
+ VIII +
+
+ + + + Škoda T 27 +
+
+
+
+ +
+
+ +
+
+ IX +
+
+ + + + Škoda T 50 +
+
+
+
+ +
+
+ +
+
+ X +
+
+ + + + TVP T 50/51 +
+
+
+
+ +
+
+ +
+
+ VII +
+
+ + + + Škoda T 45 +
+
+
+ ); +}; + +export default Vehicles; diff --git a/src/getRomanNumerals.test.ts b/src/getRomanNumerals.test.ts new file mode 100644 index 0000000..15aaea4 --- /dev/null +++ b/src/getRomanNumerals.test.ts @@ -0,0 +1,19 @@ +import getRomanNumerals from "./getRomanNumerals"; + +describe("getRomanNumerals", () => { + it.each([ + [1, "I"], + [2, "II"], + [3, "III"], + [4, "IV"], + [5, "V"], + [6, "VI"], + [7, "VII"], + [8, "VIII"], + [9, "IX"], + [10, "X"] + ])("with parameter %s should return %s", (number, expected) => { + const actual = getRomanNumerals(number); + expect(actual).toBe(expected); + }); +}); diff --git a/src/getRomanNumerals.ts b/src/getRomanNumerals.ts new file mode 100644 index 0000000..1cf8a23 --- /dev/null +++ b/src/getRomanNumerals.ts @@ -0,0 +1,31 @@ +/* export enum RomanNumerals { + I = 1, + V = 5, + X = 10, + L = 50, + C = 100, + D = 500, + M = 1000 +} */ +/** + * Adapt an integer to roman numerals + * E.g: + * 1 => 'I' + * 2 => 'II' + * 3 => 'III' + * 4 => 'IV' + * 5 => 'V' + * 6 => 'VI' + * 7 => 'VII' + * 8 => 'VIII' + * 9 => 'IX' + * 10 => 'X' + * + * @param number integer to adapt + * @returns romanNumeral string + */ +const getRomanNumerals = (number: number): string => { + return number.toString(); +}; + +export default getRomanNumerals; diff --git a/src/useVehicles.ts b/src/useVehicles.ts new file mode 100644 index 0000000..32f4d92 --- /dev/null +++ b/src/useVehicles.ts @@ -0,0 +1,10 @@ +import useSWR from "swr"; + +const fetcher: typeof fetch = (input, init) => + fetch(input, init).then((res) => res.json()); + +const useVehicles = () => { + return useSWR("/api/vehicles", fetcher); +}; + +export default useVehicles; diff --git a/vehicles.json b/vehicles.json new file mode 100644 index 0000000..d6c970e --- /dev/null +++ b/vehicles.json @@ -0,0 +1,106 @@ +[ + { + "nation": "czech", + "type": "lighttank", + "tier": 1, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz06_kolohousenka/cz06_kolohousenka_icon.svg", + "name": "Kolohousenka" + }, + { + "nation": "czech", + "type": "lighttank", + "tier": 2, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz03_lt_vz35/cz03_lt_vz35_icon.svg", + "name": "LT vz. 35" + }, + { + "nation": "czech", + "type": "lighttank", + "tier": 3, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz10_lt_vz38/cz10_lt_vz38_icon.svg", + "name": "LT vz. 38" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 4, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz11_v_8_h/cz11_v_8_h_icon.svg", + "name": "ST vz. 39" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 5, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz09_t_24/cz09_t_24_icon.svg", + "name": "Škoda T 24" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 6, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz08_t_25/cz08_t_25_icon.svg", + "name": "Škoda T 25" + }, + { + "nation": "czech", + "type": "mediumtank-prem", + "tier": 6, + "isPremium": true, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz01_skoda_t40/cz01_skoda_t40_icon.svg", + "name": "Škoda T 40" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 7, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz05_t34_100/cz05_t34_100_icon.svg", + "name": "Konštrukta T-34/100" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 8, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz07_tvp_46/cz07_tvp_46_icon.svg", + "name": "TVP VTU Koncept" + }, + { + "nation": "czech", + "type": "mediumtank-prem", + "tier": 8, + "isPremium": true, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz13_t_27/cz13_t_27_icon.svg", + "name": "Škoda T 27" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 9, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz02_tvp_t50/cz02_tvp_t50_icon.svg", + "name": "Škoda T 50" + }, + { + "nation": "czech", + "type": "mediumtank", + "tier": 10, + "isPremium": false, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz04_t50_51/cz04_t50_51_icon.svg", + "name": "TVP T 50/51" + }, + { + "nation": "czech", + "type": "heavytank-prem", + "tier": 7, + "isPremium": true, + "shapeUri": "//eu-wotp.wgcdn.co/dcont/tankopedia_images/cz15_skoda_t-45_prem/cz15_skoda_t-45_prem_icon.svg", + "name": "Škoda T 45" + } +]