feat(badge): Streamlined badging

Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
This commit is contained in:
Tuan-Dat Tran
2025-04-06 17:11:44 +02:00
parent a387293f94
commit a1fd3ea358
9 changed files with 135 additions and 156 deletions

6
Cargo.lock generated
View File

@@ -1190,13 +1190,15 @@ dependencies = [
[[package]]
name = "dioxus-i18n"
version = "0.3.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaa2df724b94e2a93229609951a0570a4b8807215c588e78f88a8532bdde319f"
checksum = "a97da8c5cbb956baaa8faffeb7ffba342dc8f2dc02f16aeaf9d94708bcf2b221"
dependencies = [
"dioxus-lib",
"fluent",
"thiserror 2.0.12",
"unic-langid",
"walkdir",
]
[[package]]

View File

@@ -13,7 +13,7 @@ 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"
[features]
default = []

View File

@@ -61,15 +61,14 @@ 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

View File

@@ -56,15 +56,14 @@ cv_workexperience_ra_ude_description = While working at the Network
infractructure, inventory system and online presence.
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.
cv_workexperience_dd_devops_description = At DextraData as a DevOps Engineer,
I am responsible for the cloud-based infrastructure hosting our companies
diverse portfolio of SaaS products, collaborating with Software Developers,
Customer Success Managers, and Customer Success Engineers to ensure
operational excellence and uninterrupted service delivery for our customers
with their specific requirements at the forefront. I manage
Kubernetes cluster deployments, continuously monitor deployment health metrics,
and implement Infrastructure as Code solutions.
cv_socials_title = Socials
cv_education_title = Education
cv_education_bachelor_title = BSc Systems Engineering

View File

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

View File

@@ -242,3 +242,41 @@ 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()
}

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 {
@@ -47,7 +47,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 +186,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 {

View File

@@ -50,11 +50,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")
}
}
}
}