9

Opakování: LeviExpress

Všechno, co jsme se v Reactu naučili si procvičíme na projektu takzvaně „ze života“.

Pokud jste všemi předchozíme lekcemi doputovali až sem, naučili jste se toho opravdu hodně. Možná vám nově nabyté znalost v hlavách pořád šplouchají a hledají ty správné šuplíky. Proto na závěr kapitoly o Reactu zkusíme vytvořit jednu ucelenou webovou aplikaci, ve které spojíme dohromady všechny věci, které jste se v Reactu naučili.

Zadání

Vaším úkolem bude vytvořit webovou stránku fiktivní dopravní společnosti s názvem LeviExpress. Ta každý den vypravuje dvě autobusové linky spojující východní a západní Evropu. Jedna linka vyjíždí o půlnoci z Budapešti a končí opět o půlnoci v Amsterdamu. Protí ní vždy vyráží linka v opačném směru, tedy z Amsterdamu do Budapešti.

Výsledná webová stránka bude mít dvě podstránky. Jedna bude umožňovat rezeraci jízdenky, druhá bude zobrazovat již rezervovanou jízdenku.

Aplikaci budeme vytvářet postupně v několika hlavní milnících.

  1. Založení aplikace a rozběhání routování
  2. Výběr startovní a cílové lokace
  3. Zobrazení vyhledané cesty a výběru sedadla
  4. Zprovoznění rezervace
  5. Zobrazení rezervovaní jízdenky

Založení aplikace a rozběhání routování

1

Seznámení s projektem

to dáš

V praxi často začnete pracovat na projektu, na kterém už pracoval někdo před vámi. V tomto cvičení tomu nebude jinak a také začneme s projektem, který již obsahuje nějaký kód.

  1. Nejprve si prohlédněte dopředu připravený design webu, ze kterého budeme vycházet. V připraveném designu najdete kromě úvodní stránky taky stránku s detailem jízdenky. Jde o statické stránky, které nemají žádnou funkčnost. Obsahují pouze HTML a CSS. Zdrojové kódy nejdete v tomto repozitáři. Odsud můžete vzít všechny potřebné styly, HTML a obrázky pro váš projekt.
  2. Na svén GitHub účtu si vytvořte fork repozitáře leviexpress-starter. V tomto repozitáři nejdete již rozpracovaný základ aplikace.
  3. Svůj repozitář si naklonujte do počítače a otevřete ve VS Code.
  4. Nainstalujte všechny závislosti pomocí npm install a spusťte projekt pomocí npm run start.
  5. Prohlédněte si strukturu projektu a projděte si kód připravených komponent.
2

Routování

to dáš

Hned na začátku rozběháme routování, abychom ho pak nemuseli složitě roubovat do již rozpracovaného projektu.

Naše aplikace bude mít dvě hlavní stránky: Home a Reservation. Obě obsahují stejnou hlavičku i patičku. Stránka Home bude pod routou /, stránka s detaily rezervace bude mít adresu /reservation.

  1. Uvnitř komponenty App již máte připravenou strukturu s hlavičkou a patičkou stránky. Dále máte již hotovou kostru komponenty Home. Setavte router tak, aby komponenty Header a Footer byly na stránce zobrazeny vždy. Mezi ně vložte Switch, který bude mít dvě routy: / a /reservation. V první routě zobrazte komponentu Home, ve druhé zatím necháme jen nadpis h2 s textem “Detail jízdenky”. Vyzkoušejte, že vaše stránka správně zobrazuje obě stránky. Odkazy (Link) na stránku nedávejte – uživatel přijde vždy na stránku /, na stránku /reservation se dostane až po kliknutí na tlačítko pro objednání, které zprovozníte v desáté části.
  2. Vytvořte komponentu Reservation. Tuto komponentu zobrazte na adrese /reservation. Zatím může vracet pouze nadpis h2 abychom viděli, že se na stránce něco děje. Obsah komponenty vytvoříme později.
  3. Vyzkoušejte, že vaše stránka správně funguje (adresu /reservation vyzkoušejte tak, že ji napíšete do adresního řádku prohlížeče).
  4. Proveďte commit změn.

Výběr startovní a cílové lokace

3

Údaje o cestě

to dáš

V tomto cvičení vytvoříme hlavní komponentu pro výběr startu a cíle. Komponenta získá seznam měst a termíny cest z API, nechá uživateli vybrat výchozí a cílové město a datum. Po kliknutí na tlačítko pošle vybrané údaje své rodičovské komponentě.

Ve svém projektu již máte vytvořen základy komponenty s názvem JourneyPicker. Ta je součástí komponenty Home a zatím vrací pouze statické JSX

  1. V komponentě JourenyPicker si připravte funkci handleSubmit(event), která se bude volat při odeslání formuláře. Ošetřete, aby prohlížeč sám neodesílal formulář a zatím si ve funkci jen vypište do konzole text 'Odesílám formulář s cestou'.
  2. Napojte funkci handleSubmit na událost submit ve formuláři. Ověřte v prohlížeči, že po kliknutí na Vyhledat spoj se v konzoli objeví výše uvedený text.
  3. Pomocí useState si v komponentě připravte tři stavy: fromCity, toCity a date. Výchozí hodnotou bude ve všech třech případech prázdný řetězec '';
  4. Napojte každý ze stavů na správý <select> tak, aby select zobrazoval vybraný stav a změna v selectu se promítla do stavu. Vytvoříte tedy dvoucestný databinding, kdy se např. stav fromCity bude promítat do value příslušného selectu, a při události onChange na selectu se nová hodnota zapíše do stavu fromCity. Obdobně i pro další dva selecty a stavy toCity a date.
  5. Upravte funkci handleSubmit tak, aby do konzole vypsala všechny tři stavy. Vyzkoušejte, že výběrem stavu v selectu se změní stav – po kliknutí na tlačítko se do konzole vypíše změněný stav. Tím, že si dočasně změnít výchozí hodnotu v useState('') na některou z hodnot (atribut value) v <option> můžete ověřit, že funguje správně nastavení výchozího stavu selectu.
  6. Commitněte změny.
4

Výběr města

to dáš

Na konci tohoto cvičení si uživatel bude schopen vybrat startovní a cílové město ze saznamu měst stažených z API. Vytvoříte komponentu CityOptions, která dostane na vstupu pole se seznamem destinací a vytvoří z něj elementy <option> do selectů pro výběr výchozího a cílového města. Seznam měst se bude stahovat v komponentě JourneyPicker z API endpointu /api/cities. Prohlédněte si strukturu dat, která endpoint vrací.

  1. Přímo v souboru index.jsx komponenty JourneyPicker si vytvořte novou komponentu CityOptions. Přesuňte do ní všechny <option> ze selectu pro výběr výchozího města. Komponentu CityOptions použijte v obou selectech pro výběr města. Zkontrolujte, že v prhohlížeči vše funguje stejně.
  2. Vytvořte si pomocí useState další stav cities. V tomto stavu bude seznam měst, mezi kterými lze cestovat. Pro otestování nastavte do seznamu dvě města:
    [{ name: 'Praha', code: 'CZ-PRG' }, { name: 'Brno', code: 'CZ-BRQ' }]
    
  3. Komponenta CityOptions bude v props přijímat cities. V něm bude pole – seznam měst, které může uživatel vybrat. Upravte tedy příslušným způsobem hlavičku komponenty CityOptions a získanou property cities si vypište do konzole.
  4. Na obou místech, kde máte komonentu CityOptions použitou, nastavte property/atribut cities na hodnotu stavu cities. V prohlížeči se teď při zobrazení stránky vypíše dvakrát do konzole seznam měst – Praha a Brno.
  5. Upravte komponentu CityOptions tak, aby se na základě seznamu měst v property cities vypsaly jednotlivé <option>. Pro text option se použije název destinace, jako hodnota (atribut value) a také jako klíč pro React (key) se použije kód destinace.
  6. Nechceme, aby při zobrazení stránky byla rovnou vybraná první města ze seznamu. Proto před elementy option vytvořené z pole ještě ručně vložíme jeden option s textem „Vyberte“. Atribut value tohoto option bude prázdný.
  7. Odstraňte z komponenty CityOptions výpis do konzole a zkontrolujte v prohlížeči, že se v seznamu výchozích i cílových měst zobrazuje Praha a Brno a že stále správně funguje výběr měst.
  8. Do hlavní komponenty JourneyPicker přidejte useEffect, který se bude volat při prvním zobrazení komponenty. Přesuňte do něj nastavení stavu cities – naše dvě testovací města. Výchozí stav pro cities tedy bude prázdné pole, teprve useEffect nastaví seznam měst na Prahu a Brno. Ověřte v prohlížeči, že se v selectech stále zobrazují obě města. Dejte pozor na to, aby se efekt volal opravdu jen při prvním zobrazení komponenty – můžete si to ověřit pomocným výpisem do konzole prohlížeče, který se musí objevit jen jednou – když budete překlikávat na jiná města, výpis už se nebude opakovat.
  9. Upravte useEffect tak, že bude seznam měst získávat z API. Endpoint je na adrese
    https://leviexpress-backend.herokuapp.com/api/cities
    
    a vrací seznam měst jako JSON ve formátu, který jsme použili výše. Získaná data použijte místo Prahy a Brna ve stavu cities. Ověřte v prohlížeči, že se v seznamu měst objeví i další města.
  10. Commitněte změny.
5

Výběr data cesty

to dáš

Na konci tohoto cvičení bude uživatel schopen vybrat datum cesty podle dat stažených z API. Budeme postupovat obdobně, jako s komponentou CityOptions. Tentokrát však vytvoříme komponentu DatesOptions, která vygeneruje elementy option do výběru termínů cesty. Termíny cest se budou získávat z API endpointu /api/dates. Prohlédněte si strukturu dat, která tento endpoint vrací.

  1. Komponentu DatesOptions vytvořte opět přímo v souboru s komponentami JourneyPicker a CityOptions.
  2. HTML kód s elementy <option> pro výběr termínu přesuňte ze selectu pro výběr data do komponenty DatesOptions. V selectu použijte vytvořenou komponentu DatesOptions. Zkontrolujte v prohlížeči, že se výběr termínů zobrazuje stále stejně.
  3. Podobně jako CityOptions získává seznam měst v property cities, bude i DatesOptions získávat seznam termínů v property dates. V elementech <option> (s výjimkou prvního ručně vloženého s textem „Vyberte“) požijte jako value a key hodnotu dateBasic a jako hodnotu dateExtended použíjte jako obsah.
  4. Připravte si pomocí useState další stav dates. Pro otestování si do něj vložte tato data:
    [
      {
        "dateBasic": "28.05.2021",
        "dateExtended": "pá 28. květen 2021"
      },
      {
        "dateBasic": "29.05.2021",
        "dateExtended": "so 29. květen 2021"
      },
    ]
    
  5. Použijte stav dates pro naplnění hodnot property dates tam, kde je použita komponenta DatesOptions. Ověřte v prohlížeči, že se ve výběru termínů zobrazují dvě uvedená data.
  6. Upravte useEffect volaný při prvním zobrazení komponenty. Vedle seznamu měst bude z API získávat také seznam termínů. Endpoint je na adrese
    https://leviexpress-backend.herokuapp.com/api/dates
    
    a vrací seznam termínů ve formátu, který máme připraven. Změňte výchozí stav dates na prázdné pole a po té do něj nastavte výsledek volání uvedeného endpointu.
  7. Ověřte v prohlížeči, že se do selectů načítají data (města a termíny) a že po kliknutí na tlačítko „Vyhledat spoj“ se uživatelem zvolené údaje vypíší do konzole prohlížeče.
  8. Commitněte změny.
6

Vyhledání spoje

zapni hlavu

V tomto cvičení dokončíme komponentu pro vyhledání spojení. V komponentě už funguje výběr výchozího a cílového města a také data cesty. Nyní napojíte komponentu na API pro vyhledávání spojení.

  1. Pokračujte v komponentě JourneyPicker. Do tlačítka „Vyhledat spoj“ přidejte atribut disabled tak, aby tlačítko bylo povolené pouze v případě, že jsou vybrána obě města i datum.
  2. Při kliknutí na tlačítko „Vyhledat spoj“ se volá funkce handleSubmit, která vypíše údaje zadané uživatelem. Nyní výpis do konzole nahradíte voláním API. Bude se volat následující API endpoint
    https://leviexpress-backend.herokuapp.com/api/journey?fromCity=…&toCity=…&date=…
    
    Vytečkovaná místa se nahradí hodnotami vybranými uživatelem.
  3. Volání tohoto API vrací JSON s nalezenými spoji. Vypište si výstup do konzole prohlížeče.
  4. Nalezená spojení budeme potřebovat zobrazit v další komponentě na stránce. Potřebujeme je tedy poslat z komponenty JourneyPicker jejímu rodiči. V komponentě JourneyPicker bude prop onJourneyChange, do které rodič vloží funkci, která se zavolá s údaji nalezeném spoji. Upravte hlavičku funkce JourneyPicker tak, aby bylo vidět, že akceptuje prop onJourneyChange.
  5. Ve funkci handleSubmit máte nyní výpis nalezených spojení do konzole prohlížeče. Tento výpis nahraďte voláním funkce uložené v property onJourneyChange, které jako paramter předáte data získaná z volání API. Pozor na to, že z vráceného výsledku se použije jenom jeho část – to, co je uložené pod klíčem data.
  6. Vraťte se do komponenty Home, ze které se volá komponenta JourneyPicker. V komponentě Home vytvořte pomocí useState nový stav journey, výchozí hodnota bude null.
  7. Propojte komponentu JourneyPicker se stavem journey – když komponenta JourneyPicker zavolá onJourneyChange s údaji o nalezeném spoji, nastaví se toto spojení do stavu journey.
  8. Upravte komponentu Home tak, aby v případě, kdy ve stavu journey je nějaké spojení, vypsala pod vyhledávací formulář text „Nalezeno spojení s id …“. Místo tří teček bude journeyId z dat o nalezeném spojení. Hodnota journeyId jednoznačně identifikuje vybrané spojení. Později ji využijeme k provedení rezervace jízdenky.
  9. Ověřte, že funguje výběr měst a data, že po zadání všech třech údajů můžete kliknout na „Vyhledat spoj“ a že se po kliknutí vyhledá nějaké spojení a vypíše se do stránky jeho id.
  10. Commitněte změny.

Detail cesty a výběr sedadla

7

Podrobnosti cesty

pohodička

V tomto cvičení vytvoříte komponentu pro zobrazení detailu cesty a komponentu pro zobrazení zastávky. Využijí se informace, které vrátilo API pro vyhledání spoje.

  1. Ve svém projektu vyvořte komponentu s názvem JourneyDetail. Do komponenty JourneyDetail zatím zkopírujte HTML kód ze zadání – celý element div s třídami journey-detail a container i s jeho obsahem. Vytvoře v komponentě také soubor style.css, do kterého zkopírujte ze zadání styly pro třídy stops a journey-detail. Naimportujte soubor se styly do komponenty.
  2. Použijete komponentu JourneyDetail v komponentě Home na místě, kde se nyní vypisuje id nalezeného spoje. Komponenta se bude zobrazovat jenom tehdy, když ve stavu journey v komponentě Home je něco jiného, než null. Ověřte, že po vyhledání spojení se na stránce zobrazí podrobnosti cesty s městy 1 až 4.
  3. V samostatné složce vytvořte komponentu s názvem BusStop. V komponentě vytvořte také soubor se styly, do kterého ze zadání zkopírujete všechny styly pro třídy začínající bus-stop.
  4. Do komponenty BusStop vložte ze zadání celý element div, který má třídu bus-stop. Je to jeden řádek ze seznamu zastávek.
  5. V komponentě JourneyDetail smažte HTML kód se seznamem zastávek. Zbyde tam jen kontejner, v něm h3 s textem „Podrobnosti cesty“ a pod ním div s třídou stops. Do tohoto divu vložte komponentu BusStop. Zkontrolujte v prohlížeči, že se zobrazí jedna zastávka.
  6. Komponenta BusStop bude očekávat tři propsname, station a time. Tam, kde používáte komponentu BusStop, přidejte komponentě odpovídající atributy a nastavte jim hodnoty „Praha“, „ÚAN Florenc“ a „15:55“. Komponentu BusStop upravte tak, aby se tyto hodnoty propsaly na správná místa v HTML. Zkontrolujte, že se v prohlížeči zobrazují správné údaje pro jednu zastávku.
  7. Komponenta JourneyDetail bude v props očekávat property journey s údaji o cestě. V property journey bude objekt, který má v sobě v property stops seznam zastávek. Property journey si můžete vypsat do konzole prohlížeče.
  8. Místo jedné komponenty BusStop použité v komponentě napište kód, který projde všechny zastávky v journey.stops a pro každou zastávku vloží jednu komponentu BusStop, které předá správné údaje. Můžete si pomoci tak, že nejprve upravíte vloženou komponentu BusStop, ve které máte „Praha“, „ÚAN Florenc“ a „15:55“ tak, aby se místo těchto údajů vložily údaje z první zastávky v journey.stops[0] a následně kód upravíte tak, aby pomocí funkce map prošel všechny zastávky v journey.stops. Jako klíč (key) pro React můžete použít property code, která je uvedená v journey.stops u každé zastávky.
  9. Nyní už zbývá jen poslat z komponenty Home do komponenty JourneyDetail údaje o cestě. Vraťte se do komponenty Home. Ve stavu journey tam jsou uloženy údaje o cestě. Nyní už jen stačí tento stav předat do prop journey komponenty JourneyDetail.
  10. Ověřte v prohlížeči, že funguje vyhledání spojení a že se pod vyhledávacím formulářem zobrazí „jízdní řád“ spoje – seznam zastávek s časy. Zastávek je u spoje víc a když vyhledáte jiné spojení, změní se i seznam zastávek.
  11. Commitněte změny.
8

Zobrazení sedadla

to dáš

V tomto cvičení vytvoříte komponentu pro výběr sedadla. Komponenta zobrazí plánek autobusu se sedadly. Obsazená sedadla budou zobrazena šedě a nepůjde na ně kliknout. API vrací seznam sedadel po řadách – nemusíte tedy řešit rozmístění sedadel, zobrazíte jednoduše každou řadu zvlášť a v ní sedadla zleva doprava.

  1. Ve vašem projektu vytvořte komponentu SeatPicker a přidejte styly pro třídy seat-picker a seats ze zadání projektu. Do komponenty SeatPicker zkopírujte ze zadání celý div s třídami seat-picker a container. Obsah divu seats nechte však prázdný.
  2. Přidejte komponentu SeatPicker do komponenty Home. Bude umístěna pod JourneyDetail. Finálně se bude zobrazovat stejně jako JourneyDetail jen v případě, kdy stav journey není prázdný. Pro vývoj komponenty ale bude rychlejší, když se prozatím bude zobrazovat stále.
  3. Dále v projektu vytvořte komponentu Seat představující jedno sedadlo v autobusu. Sedadlo je vytvořeno pomocí SVG, které najdete v zadání projektu. Do komponenty Seat přidejte všechny styly týkající se jednoho sedadla.
  4. Komponenta Seat bude mít zatím jednu prop s názvem number, ve které bude číslo sedadla.
  5. Zkusmo vložte do komponenty SeatPicker pár sedadel s různými čísly, například 1, 17 a 33. Zatím jen tak pod sebe, abychom si vyzkoušeli, že je vůbec dokážeme správně zobrazit.
  6. Zkontrolujte v prohlížeči, že se zobrazuje nadpis „Vyberte sedadlo“ a pod ním tři modrá sedadla 1, 17 a 33.
9

Řady sedadel

zapni hlavu

Nyní zařídíme zobrazování sedadel v řadách. Plánek sedadel bude vypadat tak, že v HTML bude pro každou řadu sedadel jedna komponenta SeatRow a teprve uvnitř této komponenty budou jednotlivá sedadla – komponenty Seat.

  1. V projektu vytvořte komponentu SeatRow, která představuje jednu řadu sedadel. Bude vracet div s třídou seat-row, který v dalších krocích naplníme sedadly tak, jak nám přijdou z API. Zatím do komponenty natvrdo vložte pár sedadel jen pro testovací účely. Komponentu SeatRow pak vložte do divu seats v komponentě JourneyDetail.
  2. Komponenta SeatRow bode očekávat prop s názvem row, ve které budou data pro jednu čadu sedadel. Pro testovací účely si vytvořte proměnnou testRow, která bude obsahovat takovýto objekt.
    const testRow = [
      {
        "number": 33,
        "isOccupied": false
      },
      {
        "number": 29,
        "isOccupied": true
      },
      {
        "number": 25,
        "isOccupied": false
      },
    ];
    
  3. Předejte tuto proměnou komponentě SeatRow a uvnitř ní pomocí funkce map zobrazte jednotlivá sedadla. Jako key prop u jednotlivých sedadel můžete použít samotné číslo sedadla.
  4. Nyní máme vše připraveno pro zobrazení správného plánku sedadel podle dat z API. Pracovat budeme v komponentě SeatPicker – tam, kde máme testovací řadu sedadel. Když se podíváte do konzole na výpis objektu uloženého ve stavu journey komponenty Home, uvidíte, že máte velké štěstí. Ve vlastnosti seats je pole, které představuje přímo mojednotlivé řady v autobusu.
  5. Nyní je tedy potřeba údaje o sedadlech předat z komponenty Home do komponenty SeatPicker. Zároveň se bude později hodit i identifikátor spoje. Do komponenty SeatPicker tedy přidejte dvě props – seats (vloží se do ní journey.seats) a journeyId (sem přijde joureny.journeyId).
  6. Ještě je potřeba upravit komponentu Home tak, aby komponenta SeatPicker byla vidět jedině v případě, že je stav journey jiný, než null – tedy stejně, jako se zobrazuje komponenta JourneyDetail. Ověřte v prohlížeči, že po vyhledání spoje se zobrazí podrobnosti cesty a také komponenta pro výběr sedadel – zatím s vašimi testovacími sedadly.
  7. Uvnitř komponenty SeatPicker projděte pole seats pomocí funkce map, a pro každý řádek pole vytvořte jednu komponentu SeatRow. I komponenty SeatRow potřebují prop key. Zde bohužel nemáme žádnou rozumnou datovou položku, kterou bychom jako klíč mohli použít. Vzpomeňme si však, že funkce vložená do funkce map může mít dva parametry, druhý parametr je pořadové číslo (takzvaný index) aktuálního prvku. V tomto případě jej výjimečně můžeme použít jako key pro SeatRow.
  8. Pokud jste všechno zařídili správně, měli byste po vyhledání cesty vidět sedadla rozmístěná stejně jako ve vzorovém designu stránky. V tuto chvíli už nám stačí pouze správně zobrazit zabraná sedadla. Zda je sedadlo zabrané udává vlastnost isOccupied v datech z API. Stačí tedy komponentě Seat přidat prop isOccupied a poslat do ní hodnotu obdrženou z API.
  9. Uvnitř komponenty Seat zařiďte aby se na element svg přidala CSS třída seat--occupied ve chvíli, kdy je sedadlo zabrené.
  10. Pokochejte se krásným plánkem sedadel a commitněte změny.
10

Výběř sedadla

zapni hlavu

Když už dokážeme zobrazit plánek autobusu, je na čase umožnit uživateli vybrat sedadlo.

  1. Nejprve musíme upravit komponenta Seat tak, aby správně zobrazovala vybrané sedadlo. Přidáme jí tedy dvě nové prop: isSelected a onSelect. Pokud je sedadlo vybrané (prop isSelected je nastavena na true), bude mít sedadlo nastavenu vedle seat také třídu seat--selected.
  2. Nastal čas pro zprovoznění komunikace mezi komponentou SeatPicker a Seat. Všimněte si, že je mezi nimi vztah vnuk - prarodič. Komponenta Seat očekává prop onSelect. V této prop bude funkce, kterou Seat zavolá, když uživatel vybere sedadlo. Jako parametr této funkci předejte číslo sedadla.
  3. Vzhledem k tomu, že Seat není přímým dítětem komponenty SeatPicker, musí komunikace mezi nimi probíhat skrze koponentu SeatRow. Té proto přidáme prop onSeatSelected. Tuto funkci každá SeatRow předá všem svým Seat jako prop onSelect. Takto můžeme z prarodiče SeatPicker propašovat funkci do vnuka Seat.
  4. Vraťte se do komponenty SeatPicker a vytvořte zde funkci handleSeatSelect. Tuto funkci předáme všem SeatRow, skrze které propadne až k jednolitvým sedadlům. Tato funkce dostane na vstupu číslo sedadla, které bylo vybrané. Zatím toto číslo vypište do konzole. Vyzkoušejte v prohlížeči, že při klikání na sedadlo se v konzoli zobrazí jeho číslo. Styl sedadla se zatím měnit nebude. Pokud jste došli až sem, nejtěžší máte za sebou.
  5. V komponentě SeatPicker vytvořte pomocí useState stav selectedSeatNumber. V tomto stavu si budeme pamatovat číslo vybraného sedadla. Výchozí hodnota bude null. Ve funkci handleSeatSelect nastavujte tento stav na číslo sedadla, které uživatel vybral.
  6. Nyní musíme hodnotu ze stavu selectedSeatNumber opět propašovat skrze SeatRow k jednotlivým sedadlům, abychom mohli vybrané sedadlo vysvítit. V SeatRow tedy budeme potřebovat prop selectedSeatNumber, do které vložíme hodnotu stavu. Na komponentách Seat pak změníme atribut isSelected. Místo statické hodnoty true nebo false nastavujte jeho hodnotu v závislosti na tom, zda číslo sedadla je shodné s číslem v prop selectedSeatNumber. Nyní si v prohlížeči vyzkoušejte, že při klikání na jednotlivá neobsazená sedadla se vypisuje číslo sedadla do konzole ale také se mění barva a velikost vybraného sedadla. Výpis do konzole pak můžete smazat.
  7. Smažte různé pomocné výpisy do konzole, případně testovací sedadla, pokud v kódu zůstala, a změny commitněte.

Zprovoznění rezervace

11

Potvrzení rezervace

zapni hlavu

V tomto kroku potvrdíme rezervaci kliknutím na tlačíko, zpracujeme odpověď ze serveru a přesměrujeme uživatele na detail rezervované jízdenky.

  1. Nyní je potřeba zprovoznit tlačítko „Rezervovat". Stále v komponentě SeatPicker vytvořte funkci handleBuy a zařiďte, aby byla tato funkce volána při kliknutí na tlačítko „Rezervovat". Ve funkci si zatím můžete vypsat nějakou zprávu do konzole ('Funguju!').

  2. Pomocí atributu disabled na tlačítku „Rezervovat" zabezpečte, aby bylo tlačítko přístupné jenom tehdy, když je vybrané nějaké sedadlo, tj. když stav selectedSeatNumber nemá hodnotu null. Ověřte v prohlížeči, že se tlačítko zpřístupní až po výběru sedadla a že se po kliknutí na tlačítko vypíše do konzole text, který máte ve funkci handleBuy.

  3. Podle dokumentace React Routeru se podívejte, jak lze pomocí hooku useHistory přímo v kódu změnit stránka, na které se uživatel nachází. Tedy jak uživatele přesměrovat.

  4. Nad funkcí handleBuy vytvořte proměnnou history s použitím hooku useHistory(), nezapomeňte hook naimportovat z react-router-dom. Za moment tuto proměnnou použijeme.

  5. Nákup jízdenky se ve funkci handleBuy provede tak, že zavoláte API endpoint

    https://leviexpress-backend.herokuapp.com/api/reserve
    

    Pozor, tento endpoint je nutné volat HTTP metodou POST. Tělo požadavku bude obsahovat vlastnost seat – číslo sedadla vybrané uživatelem, a vlastnost journeyId – hodnbota journeyId z props. Příklad:

    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        seat: selectedSeatNumber,
        journeyId: journeyId,
      })
    })
    
  6. Volání tohoto API vrací JSON s daty, ze kterých nás bude zajímat hodnota reservationId. Vypiště si ji do konzole.

  7. Pomocí naší history proměnné, její metody push a hodnoty reservationId přesměrujte uživatele na stránku detailu rezervace. Takové volání bude může vypadat následovně:

    history.push(`/reservation/${reservationId}`);
    
  8. Zkontrolujte, že po vybrání sedadla a kliknutí na tlačíko „Rezervovat" se stránka přesměruje například na adresu

    http://localhost:8080/reservation/HAQBAQASf7M
    

    kde záhy vytvoříme detail jízdenky!

  9. Commitněte změny.

Zobrazení rezervovaní jízdenky

12

Detail jízdenky

zapni hlavu

Stačí nám vytvořit detail pro jednu jízdenku a zobrazit její data.

  1. V komponentě App připravte již existující <Route path="/reservation"> komponentu na správné zpracování detailu jízdenky. Upravte path prop tak, aby dynamicky zpracovávala :id parametr. Výsledná path bude vypadat takto: path="/reservation/:id". Uvnitř <Route> komponenty ponechte <Reservation />.
  2. Do komponenty Reservation zkopírujte HTML kód ze zadání. Ze souboru reservation.html převezměte element div s třídou reservation i s jeho obsahem.
  3. Vytvoře v komponentě soubor style.css, do kterého zkopírujte ze zadání styly pro třídy začínající reservation. Naimportujte soubor se styly do komponenty.
  4. Komponenta musí zjistit, na detailu které jízdenky se nachází. Naimportujte si useParams hook z react-router-dom. Uvnitř komponenty tento hook zavolejte a získejte z něj parametr id. Příklad použití.
  5. Pomocí useState vytvořte nový stav reservation, výchozí hodnota bude null.
  6. Přidejte useEffect volaný při prvním zobrazení komponenty. Bude z API získávat podrobné informace o jedné jízdence. Endpoint je na adrese
    https://leviexpress-backend.herokuapp.com/api/reservation?id=…
    
    kde tečky nahradí id konkrétní jízdenky. Toto id vezměte z vytvořené proměnné id z useParams hooku.
  7. Endpoint vrací údaje o jízdence, pomocí funkce setReservation z useState hooku nastavte do proměnné reservation výsledek volání uvedeného endpointu.
  8. Data z endpointu si také můžete vypsat do konzole. Podívejte se na jejich formát a porovnejte strukturu dat s daty, která jsou natvrdo v JSX (vykopírované z reservation.html).
  9. Nahraďte data jízdenky vepsaná natvrdo za ta, která jsou nyní uložená v proměnné reservation. Nezapomeňte zobrazení dat podmínit tím, že stav reservation nemá hodnotu null, jinak bude prohlížeč hlásit chybu, že nelze číst z undefined.
  10. Zkontrolujte, že stránka vzhledově odpovídá zadání.
  11. Commitněte a pushněte změny a kochejte se hotovou aplikací!