3 Commits

Author SHA1 Message Date
Tuan-Dat Tran
538c99166f chore(deps): update rust dependencies
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-08-15 08:18:54 +02:00
Tuan-Dat Tran
6e3db0294f Add favorite tools
Some checks failed
Build Docker Image / Build (push) Has been cancelled
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-04-06 18:23:26 +02:00
Tuan-Dat Tran
a1fd3ea358 feat(badge): Streamlined badging
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-04-06 17:11:44 +02:00
14 changed files with 909 additions and 713 deletions

View File

@@ -1,18 +0,0 @@
name: Build Docker Image
on:
push:
branches:
- main
- dev
- cicd
jobs:
build:
name: Build
runs-on: [ubuntu-latest, aya01]
steps:
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
tags: tudattr/athome:latest

1155
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,8 @@ dioxus = { version = "0.6", features = ["fullstack", "router"] }
# Debug
tracing = "0.1.40"
dioxus-logger = "0.6.0"
dioxus-i18n = "0.3.0"
dioxus-i18n = "0.4.0"
tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
[features]
default = []

View File

@@ -24,4 +24,4 @@ title = "Tuan-Dat Tran"
reload_html = true
# which files or dirs will be watcher monitoring
watch_path = ["src", "assets"]
watch_path = ["src", "assets", "languages"]

View File

@@ -19,7 +19,10 @@ ENV PATH="/.cargo/bin:$PATH"
# Create the final bundle folder. Bundle always executes in release mode with optimizations enabled
RUN dx bundle --platform web
FROM chef AS runtime
FROM debian:bookworm-slim AS runtime
# Install ca-certificates for HTTPS requests if the server makes any outgoing calls
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/dx/athome/release/web/ /usr/local/app
# set our port and make sure to listen for all connections

View File

@@ -1,7 +1,6 @@
headers_home = Home
headers_cv = Lebenslauf
headers_publications_projects = Artikel/Projekte
headers_consulting = Consulting
headers_about = Impressum
headers_language_buttons_english = 🇬🇧 Englisch
headers_language_buttons_german = 🇩🇪 Deutsch
@@ -12,13 +11,15 @@ home_card_text =
Willkommen auf meiner kleinen Webseite im World Wide Web.
Mein Name ist Tuan und ich bin Linux-Bastler, IT-Security Enthusiast und IT-Automatisierer aus Leidenschaft.
home_card_contact_button = Get in touch.
cv_introduction_title = DevOps Engineer | Homelab Enthusiast
cv_introduction_0 =
DevOps-Engineer und Softwareentwickler mit starkem akademischen Hintergrund
DevOps Engineer und Softwareentwickler mit starkem akademischen Hintergrund
in Netzwerktechnologien und Softwareentwicklung. Spezialisiert auf Kubernetes,
Ansible, Azure und moderne Cloud-Technologien. Praxisnahe
Ansible, Azure und modernen Cloud-Technologien. Praxisnahe
Forschungserfahrung in Software-Defined Networking, 5G und Maschinellem Lernen.
Leidenschaft für effiziente IT-Infrastrukturen, Automatisierung und
innovative Softwarelösungen.
cv_introduction_tools = Lieblingstools:
cv_workexperience_title = Berufserfahrung
cv_workexperience_se1_gefeba_title = Software Entwickler @ gefeba Engineering GmbH
cv_workexperience_se1_gefeba_time = 2013 - 2015
@@ -61,20 +62,18 @@ cv_workexperience_ra_ude_description = Während meiner Tätigkeit bei der
cv_workexperience_dd_devops_title = DevOps Engineer @ DextraData
cv_workexperience_dd_devops_time = 2025 - Jetzt
cv_workexperience_dd_devops_description = Als DevOps Engineer bei DextraData
bin ich Teil eines horizontal strukturierten DevOps-Teams und verantwortlich
für die Verwaltung und Optimierung sowohl cloudbasierter als auch On-Premise-
Infrastrukturen, die unsere Single-Tenant-SaaS-Lösungen hosten. Meine Aufgabe
besteht nicht nur darin, diese Umgebungen zu warten und zu verbessern, sondern
auch Automatisierung und Effizienz durch moderne DevOps-Praktiken
voranzutreiben. Ich war maßgeblich an der Umstellung unserer Kubernetes-
basierten Deployments von manuellen Prozessen auf einen stärker
automatisierten und skalierbaren Ansatz beteiligt, indem ich Infrastructure as
Code mit Azure Resource Manager und Ansible eingesetzt habe.
bin ich für die Cloud-basierte Infrastruktur hinter den vielfältigen
SaaS-Produkten des Unternehmens verantwortlich. Ich arbeite eng mit
Software-Entwicklern, Customer Success Managern und Customer Success Engineers
zusammen, um betriebliche Exzellenz und unterbrechungsfreie Servicebereitstellung
für unsere Kunden zu gewährleisten, wobei ihre spezifischen Anforderungen im
Vordergrund stehen. Ich verwalte Kubernetes-Cluster-Deployments, überwache
kontinuierlich Deployment-Gesundheitsmetriken und implementiere
Infrastructure-as-Code-Lösungen.
cv_socials_title = Profile
cv_education_title = Bildungsweg
cv_education_bachelor_title = BSc Angewandte Informatik - Systems Engineering
cv_education_bachelor_time = 2015 - jetzt
cv_education_bachelor_description = ""
cv_skills_title = Fähigkeiten
cv_skills_devops_title = DevOps
cv_skills_devops_ansible = Ansible
@@ -110,7 +109,6 @@ publications_projects_publications_rpm_description = In diesem Artikel stellen
publications_projects_publications_iot_fuzzers_title = Overview of IoT Fuzzing Techniques
publications_projects_publications_iot_fuzzers_authors = Tuan-Dat Tran
publications_projects_publications_iot_fuzzers_conference = Seminar
publications_projects_publications_iot_fuzzers_url = https://git.tudattr.dev/AISE/seminar/src/branch/main/paper.pdf
publications_projects_publications_iot_fuzzers_description = In dieser Arbeit
vergleichen wir Methoden, die speziell von IoT Fuzzern genutzt werden um die
von IoT Geräten stammenden Herausforderungen und Einschränkungen zu umgehen.
@@ -118,13 +116,11 @@ publications_projects_projects_title = Projekte
publications_projects_projects_bpba_title = Unbenannter Ethereum Smart Contract Fuzzer
publications_projects_projects_bpba_authors = Tuan-Dat Tran
publications_projects_projects_bpba_kind = Bachelorprojekt/Bachelorarbeit
publications_projects_projects_bpba_url = https://git.ude-syssec.de/uni-due-syssec/students/2022_tuan-dat_tran_libAFLEVMFuzzer/ethfuzz/
publications_projects_projects_bpba_description = In diesem aktuell laufendem
Projekt entwickle ich einen Ethereum Smart Contract Fuzzer. Mehr Infos folgen...
publications_projects_projects_dotfiles_title = .dotfiles
publications_projects_projects_dotfiles_authors = Tuan-Dat Tran
publications_projects_projects_dotfiles_kind = Personal
publications_projects_projects_dotfiles_url = https://git.tudattr.dev/tudattr/dotfiles
publications_projects_projects_dotfiles_description = dotfiles ist ein
umgangssprachlicher Begriff, der normalerweise für Konfigurationsdateien in
Linux-basierten Systemen verwendet wird. Meine Dotfiles enthalten
@@ -136,7 +132,6 @@ publications_projects_projects_dotfiles_description = dotfiles ist ein
publications_projects_projects_homelab_title = Homelab
publications_projects_projects_homelab_authors = Tuan-Dat Tran
publications_projects_projects_homelab_kind = Personal
publications_projects_projects_homelab_url = https://git.tudattr.dev/tudattr/ansible
publications_projects_projects_homelab_description = Ansible ist ein
Automatisierungs-Werkzeug, die eine automatische Maschinenbereitstellung,
Konfigurationsverwaltung und Anwendungsbereitstellung ermöglicht. Ich
@@ -145,15 +140,14 @@ publications_projects_projects_homelab_description = Ansible ist ein
publications_projects_projects_athome_title = Diese Website
publications_projects_projects_athome_authors = Tuan-Dat Tran
publications_projects_projects_athome_kind = Personal
publications_projects_projects_athome_url = /#
publications_projects_projects_athome_description = Diese Website ist eine mit
dem auf Rust basiertem Dioxus Framwork und TailwindCSS gebaute Full Stack
WASM Website, die sowohl zum Auffrischen von Web Themen, sowie als Rust
Hobbyprojekt dient.
impressum_off = Impressum anzeigen
impressum_on = Impressum
component_under_construction = Diese Seite befindet sich gerade im Aufbau
footer_year = © 2025
footer_name = Tuan-Dat Tran
footer_rights = . All Rights Reserved.
footer_contact = Kontakt
link_opens_new_tab = (öffnet in neuem Tab)

View File

@@ -1,7 +1,6 @@
headers_home = Home
headers_cv = Résumé
headers_publications_projects = Publications/Projects
headers_consulting = Consulting
headers_about = About
headers_language_buttons_english = 🇬🇧 English
headers_language_buttons_german = 🇩🇪 German
@@ -12,6 +11,7 @@ home_card_text =
Welcome to my little place on the internet
My name is Tuan and I'm passionate about Linux, system security, automation, performance tweaking and all things tech.
home_card_contact_button = Get in touch.
cv_introduction_title = DevOps Engineer | Homelab Enthusiast
cv_introduction_0 =
DevOps Engineer and Software Developer with a strong academic background in
networking technologies and software development. Specialized in Kubernetes,
@@ -19,6 +19,7 @@ cv_introduction_0 =
in Software-Defined Networking, 5G, and Machine Learning. Passionate about
efficient IT infrastructures, automation, and innovative software
solutions.
cv_introduction_tools = Favorite Tools:
cv_workexperience_title = Work Experience
cv_workexperience_se1_gefeba_title = Software Engineer @ gefeba Engineering GmbH
cv_workexperience_se1_gefeba_time = 2013 - 2015
@@ -57,19 +58,17 @@ cv_workexperience_ra_ude_description = While working at the Network
cv_workexperience_dd_devops_title = DevOps Engineer @ DextraData
cv_workexperience_dd_devops_time = 2025 - now
cv_workexperience_dd_devops_description = As a DevOps Engineer at DextraData,
I am part of a horizontally structured DevOps team, responsible for managing
and optimizing both cloud and on-premise infrastructure that hosts our
single-tenant SaaS solutions. My role involves not only maintaining and
improving these environments but also driving automation and efficiency
through modern DevOps practices. I have been actively involved in
transitioning our Kubernetes-based deployments from manual processes to a
more automated and scalable approach by leveraging Infrastructure as Code
with Azure Resource Manager and Ansible.
I'm responsible for the cloud-based infrastructure behind the company's many
SaaS products. I work closely with software developers, Customer Success
Managers, and Customer Success Engineers to ensure operational excellence
and seamless service delivery for our customers, always keeping their
specific needs in mind. My duties include managing Kubernetes cluster
deployments, continuously monitoring deployment health metrics, and
implementing Infrastructure-as-Code solutions.
cv_socials_title = Socials
cv_education_title = Education
cv_education_bachelor_title = BSc Systems Engineering
cv_education_bachelor_time = 2015 - now
cv_education_bachelor_description = ""
cv_skills_title = Skills
cv_skills_devops_title = DevOps
cv_skills_devops_ansible = Ansible
@@ -105,7 +104,6 @@ publications_projects_publications_rpm_description = In this paper, we present
publications_projects_publications_iot_fuzzers_title = Overview of IoT Fuzzing Techniques
publications_projects_publications_iot_fuzzers_authors = Tuan-Dat Tran
publications_projects_publications_iot_fuzzers_conference = Seminar
publications_projects_publications_iot_fuzzers_url = https://git.tudattr.dev/AISE/seminar/src/branch/main/paper.pdf
publications_projects_publications_iot_fuzzers_description = In this paper, we
are comparing techniques used by IoT fuzzers to circumvent the challenges
presented by IoT devices and the constraints of the solutions proposed by the
@@ -114,12 +112,10 @@ publications_projects_projects_title = Projects
publications_projects_projects_bpba_title = Undisclosed Ethereum Smart Contract Fuzzer
publications_projects_projects_bpba_authors = Tuan-Dat Tran
publications_projects_projects_bpba_kind = Bachelor Project/Bachelor Thesis
publications_projects_projects_bpba_url = https://git.ude-syssec.de/uni-due-syssec/students/2022_tuan-dat_tran_libAFLEVMFuzzer/ethfuzz/
publications_projects_projects_bpba_description = In this ongoing project I am building an Ethereum Smart Contract Fuzzer. More info will follow.
publications_projects_projects_dotfiles_title = .dotfiles
publications_projects_projects_dotfiles_authors = Tuan-Dat Tran
publications_projects_projects_dotfiles_kind = Personal
publications_projects_projects_dotfiles_url = https://git.tudattr.dev/tudattr/dotfiles
publications_projects_projects_dotfiles_description = dotfiles is a slang term
usually used for configuration files in Linux based systems. My dotfiles
contain configurations for tools I frequently use as well as a documentation
@@ -129,7 +125,6 @@ publications_projects_projects_dotfiles_description = dotfiles is a slang term
publications_projects_projects_homelab_title = Homelab
publications_projects_projects_homelab_authors = Tuan-Dat Tran
publications_projects_projects_homelab_kind = Personal
publications_projects_projects_homelab_url = https://git.tudattr.dev/tudattr/ansible
publications_projects_projects_homelab_description = Ansible is a automation
tool which allows for automatic provisioning, configuration management and
application deployment. I use ansible to set up my homelab, which serves as a
@@ -137,15 +132,14 @@ publications_projects_projects_homelab_description = Ansible is a automation
publications_projects_projects_athome_title = This Website
publications_projects_projects_athome_authors = Tuan-Dat Tran
publications_projects_projects_athome_kind = Personal
publications_projects_projects_athome_url = /#
publications_projects_projects_athome_description = This website is a
full-stack WASM site built using the Rust-based Dioxus framework and
TailwindCSS. It serves both as a way to refresh web development topics and as
a Rust hobby project.
impressum_off = Show Impressum
impressum_on = Impressum
component_under_construction = This page is currently under construction
footer_year = © 2025
footer_name = Tuan-Dat Tran
footer_rights = . All Rights Reserved.
footer_contact = Contact
link_opens_new_tab = (opens in a new tab)

View File

@@ -1,3 +1,3 @@
[toolchain]
channel = "1.85.0"
channel = "1.86.0"
components = ["rustfmt", "clippy", "rust-analyzer"]

61
scripts/check_i18n.py Normal file
View File

@@ -0,0 +1,61 @@
import os
import re
def check_i18n_keys(project_root):
src_dir = os.path.join(project_root, "src")
languages_dir = os.path.join(project_root, "languages")
rust_key_regex = re.compile(r't!\("([a-zA-Z0-9_.]+)"\)')
ftl_key_regex = re.compile(r"^([a-zA-Z0-9_.-]+)\s*=")
used_keys = set()
defined_keys = set()
# Extract keys from Rust files
for root, _, files in os.walk(src_dir):
for file in files:
if file.endswith(".rs"):
file_path = os.path.join(root, file)
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
for match in rust_key_regex.finditer(content):
used_keys.add(match.group(1))
# Extract keys from FTL files
for root, _, files in os.walk(languages_dir):
for file in files:
if file.endswith(".ftl"):
file_path = os.path.join(root, file)
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
match = ftl_key_regex.match(line)
if match:
defined_keys.add(match.group(1))
print("--- i18n Key Check Report ---")
missing_keys = used_keys - defined_keys
if not missing_keys:
print("✅ No missing translation keys found in FTL files.")
else:
print(
"❌ Missing translation keys (used in code but not defined in FTL files):"
)
for key in sorted(list(missing_keys)):
print(f" - {key}")
unused_keys = defined_keys - used_keys
if not unused_keys:
print("✅ No unused translation keys found in FTL files.")
else:
print("⚠️ Unused translation keys (defined in FTL files but not used in code):")
for key in sorted(list(unused_keys)):
print(f" - {key}")
print("-----------------------------")
if __name__ == "__main__":
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
check_i18n_keys(project_root)

View File

@@ -242,3 +242,73 @@ pub fn Urling(prop: UrlingProp) -> Element {
}
}
}
#[component]
pub fn BadgeList(list: Vec<String>) -> Element {
rsx!(
ul {
class: "flex flex-wrap gap-2",
for (index, value) in list.iter().enumerate() {
li { key: "{index}", RandomBadge { text: "{value}"} }
}
}
)
}
#[component]
fn RandomBadge(text: String) -> Element {
let badge_color = random_badge_color(text.len());
rsx! {
span {
class:"text-xs font-medium me-2 px-2.5 py-0.5 rounded {badge_color}",
"{text}"
}
}
}
fn random_badge_color(seed: usize) -> String {
let colors = [
"bg-blue-900 text-blue-300",
"bg-gray-700 text-gray-300",
"bg-red-900 text-red-300",
"bg-green-900 text-green-300",
"bg-yellow-900 text-yellow-300",
"bg-indigo-900 text-indigo-300",
"bg-purple-900 text-purple-300",
"bg-pink-900 text-pink-300",
];
colors[seed % colors.len()].to_string()
}
#[derive(PartialEq, Props, Clone)]
pub struct AccessibleLinkProps {
to: String,
#[props(default = "".to_string())]
class: String,
#[props(default = false)]
new_tab: bool,
children: Element,
}
#[component]
pub fn AccessibleLink(props: AccessibleLinkProps) -> Element {
let mut aria_label = String::new();
let mut rel = String::new();
if props.new_tab {
aria_label = t!("link_opens_new_tab").to_string();
rel = "noopener noreferrer".to_string();
}
rsx! {
Link {
to: "{props.to}",
class: "{props.class}",
new_tab: props.new_tab,
rel: "{rel}",
aria_label: "{aria_label}",
{props.children}
}
}
}

View File

@@ -1,7 +1,7 @@
use dioxus::prelude::*;
use dioxus_i18n::t;
use crate::components::{H4, HR};
use crate::components::*;
#[component]
pub fn CV() -> Element {
@@ -33,8 +33,18 @@ pub fn CV() -> Element {
fn Introduction() -> Element {
rsx! {
div {
class: "flex",
class: "flex-col",
h6 { class: "text-lg font-semibold text-white", { t!("cv_introduction_title") } },
P { { t!("cv_introduction_0") } },
P {
{ t!("cv_introduction_tools") },
" ",
AccessibleLink { new_tab: true, to: "https://www.lazyvim.org/", "NeoVim (LazyVim)" },
", ",
AccessibleLink { new_tab: true, to: "https://zellij.dev/", "Zellij" },
", ",
AccessibleLink { new_tab: true, to: "https://k9scli.io/", "k9s" }
},
},
}
}
@@ -47,7 +57,7 @@ fn WorkExperience() -> Element {
ol {
class:"relative border-s border-gray-700",
CVEntry {time: t!("cv_workexperience_dd_devops_time"), title: t!("cv_workexperience_dd_devops_title"),
technologies: vec!["Kubenertes".to_string(), "ArgoCD".to_string(), "Ansible".to_string(), "Azure".to_string(), "Docker".to_string(), "ELK".to_string()],
technologies: vec!["Kubernetes".to_string(), "ArgoCD".to_string(), "Ansible".to_string(), "Azure".to_string(), "ELK".to_string(), "Helm".to_string()],
description: t!("cv_workexperience_dd_devops_description")
},
CVEntry {time: t!("cv_workexperience_ra_ude_time"), title: t!("cv_workexperience_ra_ude_title"),
@@ -186,44 +196,13 @@ fn CVEntry(props: CVEntryProps) -> Element {
div { class:"absolute w-3 h-3 rounded-full mt-1.5 -start-1.5 border border-gray-900 bg-gray-700"},
time { class:"mb-1 text-sm font-normal leading-none text-gray-500", "{props.time}"},
h6 { class: "text-lg font-semibold text-white", "{props.title}"}
ul {
class: "flex flex-wrap gap-2",
for (index, value) in props.technologies.iter().enumerate() {
li { key: "{index}", RandomBadge { text: "{value}"} }
}
}
BadgeList{ list: props.technologies }
p { class:"text-base font-normal text-gray-400", "{props.description}"},
{props.children}
}
}
}
#[component]
fn RandomBadge(text: String) -> Element {
let badge_color = random_badge_color(text.len());
rsx! {
span {
class:"text-xs font-medium me-2 px-2.5 py-0.5 rounded {badge_color}",
"{text}"
}
}
}
fn random_badge_color(seed: usize) -> String {
let colors = [
"bg-blue-900 text-blue-300",
"bg-gray-700 text-gray-300",
"bg-red-900 text-red-300",
"bg-green-900 text-green-300",
"bg-yellow-900 text-yellow-300",
"bg-indigo-900 text-indigo-300",
"bg-purple-900 text-purple-300",
"bg-pink-900 text-pink-300",
];
colors[seed % colors.len()].to_string()
}
fn Socials() -> Element {
rsx! {
div {
@@ -231,7 +210,7 @@ fn Socials() -> Element {
H4 { { t!("cv_socials_title") } },
div {
class: "flex justify-center items-center space-x-4",
P { Link { to:"https://www.linkedin.com/in/tudattr/", class:"hover:underline", new_tab: true, img { class: "h-8", src:asset!("./assets/pictures/LI-Bug.svg.original.svg"), alt:"LinkedIn Logo" } }},
P { AccessibleLink { to:"https://www.linkedin.com/in/tudattr/", class:"hover:underline", new_tab: true, img { class: "h-8", src:asset!("./assets/pictures/LI-Bug.svg.original.svg"), alt:"LinkedIn Logo" } }},
}
}
}

View File

@@ -1,4 +1,4 @@
use crate::components::{Card, P};
use crate::components::{AccessibleLink, Card, P};
use dioxus::prelude::*;
use dioxus_i18n::t;
@@ -20,9 +20,10 @@ pub fn Home() -> Element {
}
},
},
Link {
AccessibleLink {
to: "mailto:tuan-dat.tran@tudattr.dev",
class: "text-gray-900 bg-gradient-to-br from-green-400 to-blue-600 group-hover:from-green-400 group-hover:to-blue-600 hover:text-white rounded-full shadow-lg py-4 px-4",
new_tab: true,
{ t!("home_card_contact_button") }
}
},

View File

@@ -9,7 +9,8 @@ use dioxus_i18n::prelude::Locale;
use dioxus_i18n::unic_langid::langid;
use layout::footer::Footer;
use layout::header::Header;
use tracing::Level;
use tracing::{Level, info};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
pub mod components;
mod cv;
@@ -35,14 +36,27 @@ pub enum Route {
PublicationsProjects {},
#[route("/resume")]
CV {},
#[route("/health")]
Health {},
#[end_layout]
#[route("/:..route")]
PageNotFound { route: Vec<String> },
}
fn main() {
dioxus_logger::init(Level::DEBUG).expect("failed to init logger");
LaunchBuilder::new().launch(App)
// Configure tracing to output JSON logs
tracing_subscriber::registry()
.with(EnvFilter::from_default_env().add_directive(Level::INFO.into()))
.with(fmt::layer().json())
.init();
info!("Starting Dioxus application...");
LaunchBuilder::new().launch(App);
}
#[component]
fn Health() -> Element {
rsx! { "OK" }
}
fn App() -> Element {
@@ -50,11 +64,11 @@ fn App() -> Element {
I18nConfig::new(langid!("en-GB"))
.with_locale(Locale::new_static(
langid!("en-GB"),
include_str!("./languages/en-GB.ftl"),
include_str!("../languages/en-GB.ftl"),
))
.with_locale(Locale::new_static(
langid!("de-DE"),
include_str!("./languages/de-DE.ftl"),
include_str!("../languages/de-DE.ftl"),
))
});

View File

@@ -1,7 +1,7 @@
use dioxus::prelude::*;
use dioxus_i18n::t;
use crate::components::{Bolding, H1, HR};
use crate::components::{BadgeList, Bolding, H1, HR};
#[component]
pub fn PublicationsProjects() -> Element {
@@ -21,108 +21,13 @@ pub fn PublicationsProjects() -> Element {
}
}
#[derive(Clone, PartialEq, Props)]
struct PublicationProp {
#[props(default = "".to_string())]
doi: String,
authors: String,
title: String,
conference: String,
#[props(default = "".to_string())]
description: String,
}
fn Publications() -> Element {
rsx! {
div {
class: "flex gap-4 items-center flex-wrap",
Publication {
title: t!("publications_projects_publications_rpm_title"),
authors: t!("publications_projects_publications_rpm_authors"),
conference: t!("publications_projects_publications_rpm_conference"),
doi: t!("publications_projects_publications_rpm_url"),
description: t!("publications_projects_publications_rpm_description")
},
Publication {
title: t!("publications_projects_publications_iot_fuzzers_title"),
authors: t!("publications_projects_publications_iot_fuzzers_authors"),
conference: t!("publications_projects_publications_iot_fuzzers_conference"),
doi: "/#",
description: t!("publications_projects_publications_iot_fuzzers_description")
},
}
}
}
fn Publication(prop: PublicationProp) -> Element {
let pattern = vec!["T.-D. Tran".to_string(), "Tuan-Dat Tran".to_string()];
rsx! {
Link {
class:"block max-w-sm p-6 border rounded-lg shadow bg-gray-800 border-gray-700 hover:bg-gray-700",
to:"{prop.doi}",
new_tab: true,
h5 {
class:"mb-2 text-2xl font-bold tracking-tight text-white",
"{prop.title}",
},
span { class: "text-lg text-white", "{prop.conference}" },
p {
class:"font-normal text-gray-400 italic",
Bolding {
authors: "{prop.authors}",
patterns: pattern,
},
}
p {
class:"font-normal text-gray-400",
"{prop.description}",
}
}
}
}
fn Projects() -> Element {
rsx! {
div {
class: "flex gap-4 items-center flex-wrap",
Project {
title: t!("publications_projects_projects_bpba_title"),
authors: t!("publications_projects_projects_bpba_authors"),
kind: t!("publications_projects_projects_bpba_kind"),
url: "/#",
description: t!("publications_projects_projects_bpba_description")
},
Project {
title: t!("publications_projects_projects_dotfiles_title"),
authors: t!("publications_projects_projects_dotfiles_authors"),
kind: t!("publications_projects_projects_dotfiles_kind"),
url: "/#",
description: t!("publications_projects_projects_dotfiles_description")
},
Project {
title: t!("publications_projects_projects_homelab_title"),
authors: t!("publications_projects_projects_homelab_authors"),
kind: t!("publications_projects_projects_homelab_kind"),
url: "/#",
description: t!("publications_projects_projects_homelab_description")
}
Project {
title: t!("publications_projects_projects_athome_title"),
authors: t!("publications_projects_projects_athome_authors"),
kind: t!("publications_projects_projects_athome_kind"),
url: "/#",
description: t!("publications_projects_projects_athome_description")
}
}
}
}
#[derive(Clone, PartialEq, Props)]
struct ProjectProp {
#[props(default = "".to_string())]
url: String,
authors: String,
title: String,
technologies: Vec<String>,
kind: String,
#[props(default = "".to_string())]
description: String,
@@ -140,6 +45,7 @@ fn Project(prop: ProjectProp) -> Element {
class:"mb-2 text-2xl font-bold tracking-tight text-white",
"{prop.title}",
},
p {}
p { class: "text-lg text-white", "{prop.kind}" },
p {
class:"font-normal text-gray-400",
@@ -147,7 +53,8 @@ fn Project(prop: ProjectProp) -> Element {
authors: "{prop.authors}",
patterns: pattern,
},
}
},
BadgeList{list: prop.technologies},
p {
class:"font-normal text-gray-400",
"{prop.description}",
@@ -155,3 +62,68 @@ fn Project(prop: ProjectProp) -> Element {
}
}
}
fn Publications() -> Element {
rsx! {
div {
class: "flex gap-4 items-center flex-wrap",
Project {
title: t!("publications_projects_publications_rpm_title"),
authors: t!("publications_projects_publications_rpm_authors"),
technologies: vec![],
kind: t!("publications_projects_publications_rpm_conference"),
url: t!("publications_projects_publications_rpm_url"),
description: t!("publications_projects_publications_rpm_description")
},
Project {
title: t!("publications_projects_publications_iot_fuzzers_title"),
authors: t!("publications_projects_publications_iot_fuzzers_authors"),
technologies: vec![],
kind: t!("publications_projects_publications_iot_fuzzers_conference"),
url: "/#",
description: t!("publications_projects_publications_iot_fuzzers_description")
},
}
}
}
fn Projects() -> Element {
rsx! {
div {
class: "flex gap-4 items-center flex-wrap",
Project {
title: t!("publications_projects_projects_bpba_title"),
authors: t!("publications_projects_projects_bpba_authors"),
technologies: vec![],
kind: t!("publications_projects_projects_bpba_kind"),
url: "/#",
description: t!("publications_projects_projects_bpba_description")
},
Project {
title: t!("publications_projects_projects_dotfiles_title"),
authors: t!("publications_projects_projects_dotfiles_authors"),
technologies: vec![],
kind: t!("publications_projects_projects_dotfiles_kind"),
url: "/#",
description: t!("publications_projects_projects_dotfiles_description")
},
Project {
title: t!("publications_projects_projects_homelab_title"),
authors: t!("publications_projects_projects_homelab_authors"),
technologies: vec![],
kind: t!("publications_projects_projects_homelab_kind"),
url: "/#",
description: t!("publications_projects_projects_homelab_description")
}
Project {
title: t!("publications_projects_projects_athome_title"),
authors: t!("publications_projects_projects_athome_authors"),
technologies: vec![],
kind: t!("publications_projects_projects_athome_kind"),
url: "/#",
description: t!("publications_projects_projects_athome_description")
}
}
}
}