summaryrefslogtreecommitdiff
path: root/src/handlers/frontend_builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/handlers/frontend_builder.rs')
-rw-r--r--src/handlers/frontend_builder.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/handlers/frontend_builder.rs b/src/handlers/frontend_builder.rs
new file mode 100644
index 0000000..8e3a9f2
--- /dev/null
+++ b/src/handlers/frontend_builder.rs
@@ -0,0 +1,189 @@
+use axum::response::IntoResponse;
+use maud::{html, Markup, DOCTYPE};
+
+use crate::db::product::{Product, ProductMarker};
+use crate::error::AppError;
+
+pub async fn home() -> Result<impl IntoResponse, AppError> {
+ Ok(html!((page("Home".to_string(), Markup::default()))))
+}
+
+fn page(page_title: String, content: Markup) -> Markup {
+ html!(
+ (head(page_title))
+ body class="bg-[#eae3d2] text-[#333] dark:bg-[#333] dark:text-[#eae3d2] font-mono text-base" {
+ (header())
+ (navbar())
+ (content)
+ }
+ )
+}
+
+fn header() -> Markup {
+ html!(
+ header {
+ h1 class="underline decoration-[#601B69] dark:decoration-[#e1b1e7] text-center tracking-widest font-serif p-3 text-xl" {"Vithuran Vishnuthas"}
+ }
+ )
+}
+
+fn navbar() -> Markup {
+ html!({
+ navbar class="p-3 text-sm flex flex-row flex-wrap content-center justify-around decoration-[#eae3d2] tracking-wide" {
+ a class="hover:underline" href="/" { "Home" }
+ a class="hover:underline" href="/stories" { "Stories" }
+ a class="hover:underline" href="/" { "Tools" }
+ a class="hover:underline" href="/" { "Games" }
+ }
+ })
+}
+
+fn cards<T: PageBuilder + ProductMarker>(
+ products: Vec<SpecificProduct<T>>,
+ category: String,
+) -> Markup {
+ html!(
+ div class="m-12" {
+ h1 class="p-6 font-serif text-xl tracking-wider underline decoration-[#601B69] dark:decoration-[#E1B1E7]" { (category) }
+ div class="grid grid-cols-2 gap-8" {
+ @for product in products {
+ (card(product))
+ }
+ }
+ }
+ )
+}
+
+fn card<T: PageBuilder + ProductMarker>(specific_product: SpecificProduct<T>) -> Markup {
+ html!(
+ div class="p-9 shadow" {
+ h2 class="text-[#601B69] dark:text-[#E1B1E7]" {
+ (specific_product.product.name)
+ }
+ div {
+ (specific_product.product.description)
+ }
+ div {
+ (specific_product.specific.product_specific_card_content())
+ }
+ div class="flex flex-row space-x-5 mt-9 text-xs" {
+ span {"Update: " (specific_product.product.updatedate)}
+ span {"Upload: " (specific_product.product.uploaddate)}
+ }
+ }
+ )
+}
+
+fn head(page_title: String) -> Markup {
+ html! {
+ head {
+ (DOCTYPE)
+ meta charset="utf-8";
+ link rel="stylesheet" type="text/css" href="/static/styles.css";
+ title { (page_title) }
+ }
+ }
+}
+
+pub async fn build_page<T: Sized + ProductMarker + PageBuilder>(
+) -> Result<impl IntoResponse, AppError> {
+ Ok(html!(
+ (page(T::page_title(), cards(T::products(), T::page_title())))
+ ))
+}
+
+pub trait PageBuilder {
+ fn page_title() -> String;
+ fn products() -> Vec<SpecificProduct<Self>>
+ where
+ Self: Sized + ProductMarker;
+ fn product_specific_card_content(&self) -> Markup;
+}
+
+// TODO: Remove after first implementation
+#[derive(Hash, PartialEq, Eq)]
+pub struct DummyProduct {
+ dummy: String,
+}
+
+impl PageBuilder for DummyProduct {
+ fn page_title() -> String {
+ "Dummy Products".to_string()
+ }
+
+ fn products() -> Vec<SpecificProduct<Self>>
+ where
+ Self: Sized,
+ {
+ let product1 = Product {
+ id: 0,
+ name: "Willensfreiheit in Ketten".to_string(),
+ description:
+ "Nach der Flucht seines Bruders, verzweifelt Fahim an der Existenz der Willensfreiheit."
+ .to_string(),
+ uploaddate: 1,
+ updatedate: 1,
+ };
+
+ let product2 = Product {
+ id: 1,
+ name: "Aufbruch und Abbruch".to_string(),
+ description: "Ein frischer Absolvent muss sich vor der ersten Quest nur noch bei der Gilde für Heilkünstler anmelden.".to_string(),
+ uploaddate: 1,
+ updatedate: 1,
+ };
+
+ let product3 = Product {
+ id: 2,
+ name: "Mit gutem Gewissen".to_string(),
+ description: "Ein frischer Absolvent muss sich vor der ersten Quest nur noch bei der Gilde für Heilkünstler anmelden.".to_string(),
+ uploaddate: 1,
+ updatedate: 1,
+ };
+
+ let test1 = DummyProduct {
+ dummy: "/".to_string(),
+ };
+
+ let test2 = DummyProduct {
+ dummy: "/".to_string(),
+ };
+
+ let test3 = DummyProduct {
+ dummy: "/".to_string(),
+ };
+
+ let specific1 = SpecificProduct {
+ product: product1,
+ specific: test1,
+ };
+
+ let specific2 = SpecificProduct {
+ product: product2,
+ specific: test2,
+ };
+
+ let specific3 = SpecificProduct {
+ product: product3,
+ specific: test3,
+ };
+
+ vec![specific1, specific2, specific3]
+ }
+
+ fn product_specific_card_content(&self) -> Markup {
+ html!(
+ a class="mt-9 text-[#601B69] dark:text-[#E1B1E7]" href=(self.dummy) {
+ "Link"
+ }
+ )
+ }
+}
+
+impl ProductMarker for DummyProduct {}
+
+// TODO: Refactoring
+pub struct SpecificProduct<T: ProductMarker> {
+ product: Product,
+ specific: T,
+}