use actix_web::HttpRequest;
use actix_web::Result as AwResult;
use actix_web::get;
use crate::constants::CONTACT_EMAIL;
use crate::view::contact::contact_info;
use crate::view::layout::{is_hx_request, render_layout};
fn interest_symbol(kind: &str) -> maud::Markup {
maud::html! {
svg class="interest-symbol" viewBox="0 0 48 48" aria-hidden="true" {
@match kind {
"software" => {
path d="M17 13L7 24L17 35" {}
path d="M31 13L41 24L31 35" {}
path d="M28 8L20 40" {}
}
"handball" => {
circle cx="24" cy="24" r="17" {}
path d="M24 7C20 14 20 34 24 41" {}
path d="M24 7C28 14 28 34 24 41" {}
path d="M8 19C17 22 31 22 40 19" {}
path d="M8 29C17 26 31 26 40 29" {}
}
"books" => {
path d="M24 14C20 10 14 9 8 10V36C14 35 20 36 24 40Z" {}
path d="M24 14C28 10 34 9 40 10V36C34 35 28 36 24 40Z" {}
path d="M24 14V40" {}
}
"cooking" => {
path d="M9 25H39C39 34 33 39 24 39C15 39 9 34 9 25Z" {}
path d="M7 42H41" {}
path d="M17 20C13 16 20 13 16 9" {}
path d="M27 20C23 16 30 13 26 9" {}
}
_ => {}
}
}
}
}
fn hero_section() -> maud::Markup {
maud::html! {
section id="home" class="content-section hero-section w-100 pa4 pa5-ns" data-scroll-section {
div class="hero-content center mw8" {
div class="section-heading flex items-end justify-between mb4" data-motion-group {
p class="eyebrow ma0" data-motion-item { "00 / Home" }
p class="section-note white-50 dn db-ns ma0" data-motion-item { "Software engineering / Switzerland" }
}
div class="editorial-list hero-profile-list w-100" data-motion-group {
div class="editorial-row hero-profile-row hero-name-row" data-motion-item {
span class="hero-profile-label" { "Name" }
h1 class="hero-title fw6 white ma0" {
span { "Silen " }
span class="text-gradient-pink" { "Locatelli" }
}
span class="editorial-rule" aria-hidden="true" {}
}
div class="editorial-row hero-profile-row" data-motion-item {
span class="hero-profile-label" { "Role" }
p class="hero-statement f4 f3-ns white-70 fw3 ma0" { "Software engineer working across languages and systems." }
span class="editorial-rule" aria-hidden="true" {}
}
div class="editorial-row hero-profile-row" data-motion-item {
span class="hero-profile-label" { "Profile" }
p class="hero-copy f5 white-60 measure lh-copy ma0" {
"Nearly a decade of experience building and maintaining software across a range of languages and technologies."
}
span class="editorial-rule" aria-hidden="true" {}
}
div class="editorial-row hero-profile-row" data-motion-item {
span class="hero-profile-label" { "Links" }
div class="hero-actions flex justify-start flex-wrap" {
a
href="https://fig.silenlocatelli.ch"
class="button-primary pa3 bg-white black bn pointer f5 no-underline mr3"
{
"View projects"
span aria-hidden="true" { "↗" }
}
a
href=(format!("mailto:{CONTACT_EMAIL}"))
class="button-secondary pa3 bg-transparent white ba b--white-30 pointer f5 no-underline"
{
"Write an email"
}
}
span class="editorial-rule" aria-hidden="true" {}
}
}
}
}
}
}
fn what_i_do_section() -> maud::Markup {
let interests = [
(
"software",
"Software",
"Nearly a decade of experience building and maintaining software across a range of languages and technologies.",
),
(
"handball",
"Handball",
"I played handball for over a decade, first as a goalkeeper and now on the field.",
),
("books", "Books", "I read non-fiction and fantasy."),
(
"cooking",
"Cooking",
"I cook Italian dishes and traditional Indian curries.",
),
];
maud::html! {
section id="about" class="content-section w-100 pa4 pa5-ns" data-scroll-section {
div class="center mw8" {
div class="section-heading flex items-end justify-between mb4" data-motion-group {
div data-motion-item {
p class="eyebrow ma0 mb2" { "01 / About" }
h2 class="section-title f2 fw6 white ma0" { "Experience and interests" }
}
p class="section-note white-50 dn db-ns ma0" data-motion-item { "Work and interests" }
}
div class="editorial-list experience-list" data-motion-group {
@for (item_index, (kind, title, desc)) in interests.iter().enumerate() {
article class="editorial-row experience-row" data-motion-item {
span class="experience-number" aria-hidden="true" { (format!("{:02}", item_index + 1)) }
div class="experience-icon" { (interest_symbol(kind)) }
h3 class="experience-title f4 fw6 white ma0" { (title) }
p class="experience-copy f6 white-70 lh-copy ma0" { (desc) }
span class="editorial-rule" aria-hidden="true" {}
}
}
}
}
}
}
}
fn projects_preview() -> maud::Markup {
maud::html! {
section id="projects" class="content-section projects-section w-100 pa4 pa5-ns" data-scroll-section {
div class="center mw8" {
div class="section-heading flex justify-between items-end mb4" data-motion-group {
div data-motion-item {
p class="eyebrow ma0 mb2" { "02 / Selected work" }
h2 class="section-title f2 fw6 white ma0" { "Selected project" }
}
p class="section-note white-50 dn db-ns ma0" data-motion-item { "Rust / Self-hosted systems" }
}
div class="editorial-list project-list" data-motion-group {
article class="editorial-row project-row" data-motion-item {
span class="experience-number" aria-hidden="true" { "01" }
p class="project-kicker pink-accent f6 fw6 ma0" { "Fig / Git server" }
div class="project-summary" {
h3 class="project-title white ma0 mb3" { "Git, on my own terms." }
p class="f5 white-70 measure lh-copy ma0" {
"I built Fig in Rust to host and browse my projects."
}
}
a href="https://fig.silenlocatelli.ch" class="row-link link white f6" {
"Open Fig"
span aria-hidden="true" { "↗" }
}
span class="editorial-rule" aria-hidden="true" {}
}
}
}
}
}
}
fn contact_section() -> maud::Markup {
maud::html! {
section id="contact" class="content-section home-contact-section w-100 pa4 pa5-ns" data-scroll-section {
div class="center mw8" {
div class="section-heading flex justify-between items-end mb4" data-motion-group {
div data-motion-item {
p class="eyebrow ma0 mb2" { "03 / Contact" }
h2 class="section-title f2 fw6 white ma0" { "Contact" }
}
p class="section-note white-50 dn db-ns ma0" data-motion-item { "Work inquiries and collaboration" }
}
(contact_info())
}
}
}
}
#[get("/")]
pub async fn index(req: HttpRequest) -> AwResult<maud::Markup> {
let content = maud::html! {
(hero_section())
(what_i_do_section())
(projects_preview())
(contact_section())
};
if is_hx_request(&req) {
Ok(content)
} else {
Ok(render_layout(&content, "/"))
}
}