Jak powstał Destinations (cz. 5): generatory testów i CSS do druku
15.10.2026 · 3 min czytania
Przez cztery części budowaliśmy wokół danych: pomysł i stack, typy, baza danych i logowanie, chronione trasy. Teraz powód, dla którego Destinations w ogóle powstało. Szkoleniowiec klika jeden przycisk, dostaje gotowy test i tabelę odpowiedzi, i drukuje. Na papierze, nie na ekranie.
Losowanie testu#
Test to zestaw losowych wartości z jednego pola destynacji — na przykład kilkanaście kodów IATA albo rozwinięć do rozpoznania. Napisałem do tego jedną funkcję:
import type { Destination } from "./../types";
export function losoweWartosci(
tablica: Destination[],
ilosc: number,
klucz: keyof Destination,
): string[] {
if (!tablica || !tablica.length || ilosc <= 0) return [];
const wylosowaneWartosci: string[] = [];
const pomocniczaTablica = [...tablica];
for (let i = 0; i < ilosc; i++) {
if (pomocniczaTablica.length === 0) break;
const losowyIndeks = Math.floor(Math.random() * pomocniczaTablica.length);
wylosowaneWartosci.push(pomocniczaTablica[losowyIndeks][klucz]);
pomocniczaTablica.splice(losowyIndeks, 1);
}
return wylosowaneWartosci;
}
Na starcie krótki strażnik: jeśli tablica jest pusta albo ilosc jest zerowa czy
ujemna, zwracam od razu []. Potem kopiuję tablicę ([...tablica], żeby nie
ruszać oryginału z bazy) i ilosc razy losuję indeks, dorzucam wartość do wyniku,
a wylosowany element usuwam przez splice.
Widok do druku#
W Destinations są właściwie dwa wydruki: test (Generator) i tabele do
nauki (PrintTables). Oba odpala ten sam, natywny mechanizm — zwykły przycisk
z window.print(), zero bibliotek:
<button onClick={() => window.print()}>DRUKUJ</button>
window.print() otwiera systemowe okno drukowania przeglądarki. Cała robota jest
w tym, żeby papier wyglądał inaczej niż ekran: bez przycisków, bez selecta, bez
paska sterowania. I tym zajmuje się CSS.
CSS, o którym mało kto myśli: @media print#
Najpierw ustawiam samą kartkę, potem chowam na wydruku wszystko, co służy tylko do klikania:
@page {
margin: 25mm;
size: a4 portrait;
}
@media print {
button,
select,
.controls {
display: none !important;
}
}
Druga sprawa to podział na strony, żeby treść nie urywała się w losowych miejscach:
/* test: zadanie 2 zaczyna się od nowej kartki */
.page:first-of-type {
page-break-after: always;
}
/* tabele: każda strefa na osobnej kartce */
.schengen-container,
.non-schengen-container {
break-after: page;
}
@media print {
tr {
break-inside: avoid;
}
}
Koniec serii#
I to tyle. Przez pięć wpisów Destinations przeszło drogę od pomysłu (cz. 1), przez typy (cz. 2), bazę i logowanie (cz. 3), chronione trasy (cz. 4), aż po to, co użytkownik trzyma na końcu w ręku: wydrukowany test (cz. 5).
W kolejnych seriach chcę trochę zwolnić i wrócić do fundamentów: HTML, CSS i JavaScript od podstaw. W końcu dobrze zbudowany dom stoi na mocnych fundamentach. Do zobaczenia w nowej serii!