//! `/` — two-door entry into the design-dna library. //! //! Shows all cycles (each with its works + themes columns) plus the //! artifacts strip. v0.2: cycle 1 (philosophy) + cycle 2 (design //! theory) + manifest + applied-principles. use leptos::prelude::*; use leptos_router::hooks::use_query_map; use crate::api::{fetch_landing, ArtifactRef, CycleSnapshot, LandingData}; use crate::corpus::{Lang, Source, Theme}; use crate::pages::shared::LangToggle; #[component] pub fn Landing() -> impl IntoView { let query = use_query_map(); let lang = Memo::new(move |_| { Lang::from_query(query.read().get("lang").as_deref()) }); let data = Resource::new( move || lang.get().as_str().to_string(), |l| fetch_landing(l), ); let hero_title = move || match lang.get() { Lang::Ru => "дизайн днк", Lang::En => "design dna", }; let hero_subtitle = move || match lang.get() { Lang::Ru => "философия · теория дизайна · дистилляция", Lang::En => "philosophy · design theory · distillation", }; let hero_question = move || match lang.get() { Lang::Ru => "почему форма трогает человеческое сердце?", Lang::En => "why does form move the human heart?", }; let artifacts_label = move || match lang.get() { Lang::Ru => "артефакты", Lang::En => "artifacts", }; let works_label = move || match lang.get() { Lang::Ru => "работы", Lang::En => "works", }; let themes_label = move || match lang.get() { Lang::Ru => "темы", Lang::En => "themes", }; view! {

{hero_title}

{hero_subtitle}

{hero_question}

"…"

}> {move || { data.get().map(|res| { let lang_val = lang.get(); match res { Ok(LandingData { cycles, artifacts }) => { view! { {if !artifacts.is_empty() { let art_label = artifacts_label(); view! {
}.into_any() } else { ().into_any() }} }.into_any() } Err(e) => view! {

{format!("{e}")}

}.into_any(), } }) }}
} } #[component] fn CycleSection( cycle: CycleSnapshot, lang: Lang, works_label: &'static str, themes_label: &'static str, ) -> impl IntoView { let cycle_title = cycle.title.clone(); let cycle_subtitle = cycle.subtitle.clone(); let sources = cycle.sources.clone(); let themes = cycle.themes.clone(); let order = cycle.order.clone(); let mut by_slug: std::collections::HashMap = std::collections::HashMap::with_capacity(sources.len()); for s in sources { by_slug.insert(s.slug.clone(), s); } let sources_suffix = move |n: usize| match lang { Lang::Ru => format!(" · {n} источников"), Lang::En => format!(" · {n} sources"), }; view! {

{cycle_title}

{cycle_subtitle}

{works_label}

{themes_label}

} } #[component] fn WorkLine(source: Option, slug: String, lang: Lang) -> impl IntoView { let href = format!("/source/{slug}?lang={}", lang.as_str()); match source { None => view! {
  • }.into_any(), Some(s) => view! {
  • {s.author} " · " {s.title}
    {s.core_claim}
  • }.into_any(), } } #[component] fn ThemeLine(theme: Theme, lang: Lang, suffix: F) -> impl IntoView where F: Fn(usize) -> String + 'static + Send + Sync + Clone, { let href = format!("/theme/{}?lang={}", theme.slug, lang.as_str()); let count = theme.contributing.len(); view! {
  • {theme.title} {suffix(count)}
  • } } #[component] fn ArtifactLine(artifact: ArtifactRef, lang: Lang) -> impl IntoView { let href = format!("/artifact/{}?lang={}", artifact.slug, lang.as_str()); view! {
  • {artifact.title} " · " {artifact.subtitle}
  • } }