feat: single country page, borders
This commit is contained in:
parent
433ff827b7
commit
ae2b3d1327
|
@ -540,6 +540,10 @@ video {
|
|||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.-mb-4 {
|
||||
margin-bottom: -1rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
@ -576,6 +580,42 @@ video {
|
|||
height: 350px;
|
||||
}
|
||||
|
||||
.h-32 {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.h-28 {
|
||||
height: 7rem;
|
||||
}
|
||||
|
||||
.h-36 {
|
||||
height: 9rem;
|
||||
}
|
||||
|
||||
.h-52 {
|
||||
height: 13rem;
|
||||
}
|
||||
|
||||
.h-56 {
|
||||
height: 14rem;
|
||||
}
|
||||
|
||||
.h-60 {
|
||||
height: 15rem;
|
||||
}
|
||||
|
||||
.h-10 {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.h-9 {
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
@ -592,6 +632,10 @@ video {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.w-20 {
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
@ -602,6 +646,10 @@ video {
|
|||
appearance: none;
|
||||
}
|
||||
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -634,6 +682,22 @@ video {
|
|||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.gap-y-8 {
|
||||
row-gap: 2rem;
|
||||
}
|
||||
|
||||
.gap-y-9 {
|
||||
row-gap: 2.25rem;
|
||||
}
|
||||
|
||||
.gap-y-10 {
|
||||
row-gap: 2.5rem;
|
||||
}
|
||||
|
||||
.space-y-1 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
|
@ -652,6 +716,12 @@ video {
|
|||
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.space-y-2 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
@ -660,6 +730,10 @@ video {
|
|||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.rounded-t-lg {
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-top-right-radius: 0.5rem;
|
||||
|
@ -679,6 +753,16 @@ video {
|
|||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-400 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(96 165 250 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(252 165 165 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-8 {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
@ -698,15 +782,57 @@ video {
|
|||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.py-10 {
|
||||
padding-top: 2.5rem;
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
|
||||
.pb-8 {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
@ -735,6 +861,52 @@ video {
|
|||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-xl {
|
||||
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-xl {
|
||||
--tw-drop-shadow: drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_15px_rgba\(0\2c 0\2c 0\2c 0\.25\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 15px rgba(0,0,0,0.25));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_25px_rgba\(0\2c 0\2c 0\2c 0\.25\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 25px rgba(0,0,0,0.25));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow {
|
||||
--tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_15px_rgba\(0\2c 0\2c 0\2c 0\.75\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 15px rgba(0,0,0,0.75));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_5px_rgba\(0\2c 0\2c 0\2c 0\.75\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 5px rgba(0,0,0,0.75));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_3px_rgba\(0\2c 0\2c 0\2c 0\.75\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 3px rgba(0,0,0,0.75));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.drop-shadow-\[0_0_3px_rgba\(0\2c 0\2c 0\2c 0\.5\)\] {
|
||||
--tw-drop-shadow: drop-shadow(0 0 3px rgba(0,0,0,0.5));
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Exploring countries</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="../public/output.css" th:href=@{~/public/output.css}
|
||||
rel="stylesheet" />
|
||||
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
|
||||
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<!-- Place favicon.ico in the root directory -->
|
||||
</head>
|
||||
<body class="bg-very-light-gray">
|
||||
<!--[if lt IE 8]>
|
||||
<p class="browserupgrade">
|
||||
You are using an <strong>outdated</strong> browser. Please
|
||||
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
|
||||
your experience.
|
||||
</p>
|
||||
<![endif]-->
|
||||
<header
|
||||
class="h-20 flex flex-row justify-between items-center px-4 shadow-md"
|
||||
>
|
||||
<h1 class="font-bold">Where in the world?</h1>
|
||||
<p>Dark Mode</p>
|
||||
</header>
|
||||
<nav class="h-32 px-8 py-10">
|
||||
<a
|
||||
href="/"
|
||||
class="w-20 h-12 bg-white drop-shadow-[0_0_3px_rgba(0,0,0,0.5)] text-md rounded px-5 py-2"
|
||||
>arr Back</a
|
||||
>
|
||||
</nav>
|
||||
|
||||
<main class="px-8 pb-10 flex flex-col gap-y-10">
|
||||
<img
|
||||
src="https://flagcdn.com/de.svg"
|
||||
th:src="${country.flag}"
|
||||
th:alt="|flag of ${country.name}|"
|
||||
class="h-60"
|
||||
/>
|
||||
<h1 class="text-2xl font-bold pt-2 -mb-4" th:text="${country.name}">
|
||||
Germany
|
||||
</h1>
|
||||
|
||||
<dl class="space-y-2" id="main-stats">
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Native name:</dt>
|
||||
<dd th:text="${country.nativeName}">Belgie</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Population:</dt>
|
||||
<dd
|
||||
th:text="${#numbers.formatInteger(country.population, 3, 'COMMA')}"
|
||||
>
|
||||
81,771,900
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Region:</dt>
|
||||
<dd th:text="${country.region}">Europe</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Sub Region:</dt>
|
||||
<dd th:text="${country.subregion}">Western Europe</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Capital:</dt>
|
||||
<dd th:text="${country.capital}">Berlin</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<dl class="space-y-2" id="additional-stats">
|
||||
<div th:if="${country.topLevelDomain.nonEmpty()}" class="flex">
|
||||
<dt class="font-bold mr-2">Top Level Domain:</dt>
|
||||
<dd th:text="${country.topLevelDomain.get()}">.be</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Currencies:</dt>
|
||||
<!-- <dd th:text="${#strings.listJoin(country.currencies.map(_.name),',')}">Euro</dd> -->
|
||||
<dd th:text="${#strings.listJoin(country.currenciesView, ',')}">
|
||||
Euro
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<dt class="font-bold mr-2">Languages:</dt>
|
||||
<dd th:text="${#strings.listJoin(country.languagesView, ', ')}">
|
||||
Dutch, French, German
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<section id="neighboring-countries">
|
||||
<h2 class="font-bold text-lg">Border Countries:</h2>
|
||||
<div
|
||||
class="grid grid-cols-3 gap-2"
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
class="text-center min-h-9 bg-white drop-shadow text-md rounded px-5 py-2"
|
||||
th:each="border : ${borderCountries}"
|
||||
th:text="${border}"
|
||||
th:href="@{~/country(countryName=${border})}"
|
||||
>France</a
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package example
|
||||
|
||||
import upickle.default._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
final case class Country(
|
||||
name: String,
|
||||
|
@ -15,7 +16,10 @@ final case class Country(
|
|||
currencies: List[Currency] = List.empty,
|
||||
languages: List[Language],
|
||||
borders: List[String] = List.empty,
|
||||
) derives ReadWriter
|
||||
) derives ReadWriter {
|
||||
def currenciesView = currencies.map(_.name).asJava
|
||||
def languagesView = languages.map(_.name).asJava
|
||||
}
|
||||
|
||||
final case class Currency(
|
||||
code: String,
|
||||
|
|
|
@ -13,10 +13,9 @@ case class Routes(countries: List[Country])(implicit
|
|||
log: cask.Logger
|
||||
) extends cask.Routes {
|
||||
|
||||
/**
|
||||
* initializing thymeleaf template engine
|
||||
* which finds and renders html templates by name
|
||||
*/
|
||||
/** initializing thymeleaf template engine which finds and renders html
|
||||
* templates by name
|
||||
*/
|
||||
def buildTemplateEngine(): TemplateEngine = {
|
||||
val templateResolver = new ClassLoaderTemplateResolver()
|
||||
templateResolver.setTemplateMode(TemplateMode.HTML)
|
||||
|
@ -37,7 +36,7 @@ case class Routes(countries: List[Country])(implicit
|
|||
|
||||
val regions = countries.map(_.region).distinct.sorted.asJava
|
||||
val selectedCountries = region match {
|
||||
case None => countries
|
||||
case None => countries
|
||||
case Some(selectedRegion) => countries.filter(_.region == selectedRegion)
|
||||
}
|
||||
|
||||
|
@ -55,7 +54,23 @@ case class Routes(countries: List[Country])(implicit
|
|||
|
||||
@cask.get("/country")
|
||||
def getCountryPage(countryName: String) = {
|
||||
s"counrty $countryName was requested"
|
||||
val context = new Context()
|
||||
countries.find(_.name == countryName) match {
|
||||
case Some(selectedCountry) =>
|
||||
context.setVariable("country", selectedCountry)
|
||||
val borderCountries = countries
|
||||
.filter(c => selectedCountry.borders.contains(c.alpha3Code))
|
||||
.map(_.name).asJava
|
||||
|
||||
context.setVariable("borderCountries", borderCountries)
|
||||
|
||||
val countryPage = engine.process("country", context)
|
||||
Response(
|
||||
countryPage,
|
||||
headers = Seq("Content-Type" -> "text/html; charset=utf-8")
|
||||
)
|
||||
case None => Response("", 400)
|
||||
}
|
||||
}
|
||||
|
||||
@cask.post("/do-thing")
|
||||
|
|
Loading…
Reference in New Issue