feat: single country page, borders

This commit is contained in:
efim 2023-09-24 13:37:39 +00:00
parent 433ff827b7
commit ae2b3d1327
5 changed files with 311 additions and 8 deletions

View File

@ -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;
}

View File

@ -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>

View File

@ -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" />

View File

@ -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,

View File

@ -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")