feat(badge): Streamlined badging
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -1190,13 +1190,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dioxus-i18n"
|
name = "dioxus-i18n"
|
||||||
version = "0.3.0"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eaa2df724b94e2a93229609951a0570a4b8807215c588e78f88a8532bdde319f"
|
checksum = "a97da8c5cbb956baaa8faffeb7ffba342dc8f2dc02f16aeaf9d94708bcf2b221"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dioxus-lib",
|
"dioxus-lib",
|
||||||
"fluent",
|
"fluent",
|
||||||
|
"thiserror 2.0.12",
|
||||||
"unic-langid",
|
"unic-langid",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ dioxus = { version = "0.6", features = ["fullstack", "router"] }
|
|||||||
# Debug
|
# Debug
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
dioxus-logger = "0.6.0"
|
dioxus-logger = "0.6.0"
|
||||||
dioxus-i18n = "0.3.0"
|
dioxus-i18n = "0.4.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -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_title = DevOps Engineer @ DextraData
|
||||||
cv_workexperience_dd_devops_time = 2025 - Jetzt
|
cv_workexperience_dd_devops_time = 2025 - Jetzt
|
||||||
cv_workexperience_dd_devops_description = Als DevOps Engineer bei DextraData
|
cv_workexperience_dd_devops_description = Als DevOps Engineer bei DextraData
|
||||||
bin ich Teil eines horizontal strukturierten DevOps-Teams und verantwortlich
|
bin ich für die Cloud-basierte Infrastruktur hinter den vielfältigen
|
||||||
für die Verwaltung und Optimierung sowohl cloudbasierter als auch On-Premise-
|
SaaS-Produkten des Unternehmens verantwortlich. Ich arbeite eng mit
|
||||||
Infrastrukturen, die unsere Single-Tenant-SaaS-Lösungen hosten. Meine Aufgabe
|
Software-Entwicklern, Customer Success Managern und Customer Success Engineers
|
||||||
besteht nicht nur darin, diese Umgebungen zu warten und zu verbessern, sondern
|
zusammen, um betriebliche Exzellenz und unterbrechungsfreie Servicebereitstellung
|
||||||
auch Automatisierung und Effizienz durch moderne DevOps-Praktiken
|
für unsere Kunden zu gewährleisten, wobei ihre spezifischen Anforderungen im
|
||||||
voranzutreiben. Ich war maßgeblich an der Umstellung unserer Kubernetes-
|
Vordergrund stehen. Ich verwalte Kubernetes-Cluster-Deployments, überwache
|
||||||
basierten Deployments von manuellen Prozessen auf einen stärker
|
kontinuierlich Deployment-Gesundheitsmetriken und implementiere
|
||||||
automatisierten und skalierbaren Ansatz beteiligt, indem ich Infrastructure as
|
Infrastructure-as-Code-Lösungen.
|
||||||
Code mit Azure Resource Manager und Ansible eingesetzt habe.
|
|
||||||
cv_socials_title = Profile
|
cv_socials_title = Profile
|
||||||
cv_education_title = Bildungsweg
|
cv_education_title = Bildungsweg
|
||||||
cv_education_bachelor_title = BSc Angewandte Informatik - Systems Engineering
|
cv_education_bachelor_title = BSc Angewandte Informatik - Systems Engineering
|
||||||
@@ -56,15 +56,14 @@ cv_workexperience_ra_ude_description = While working at the Network
|
|||||||
infractructure, inventory system and online presence.
|
infractructure, inventory system and online presence.
|
||||||
cv_workexperience_dd_devops_title = DevOps Engineer @ DextraData
|
cv_workexperience_dd_devops_title = DevOps Engineer @ DextraData
|
||||||
cv_workexperience_dd_devops_time = 2025 - now
|
cv_workexperience_dd_devops_time = 2025 - now
|
||||||
cv_workexperience_dd_devops_description = As a DevOps Engineer at DextraData,
|
cv_workexperience_dd_devops_description = At DextraData as a DevOps Engineer,
|
||||||
I am part of a horizontally structured DevOps team, responsible for managing
|
I am responsible for the cloud-based infrastructure hosting our companies
|
||||||
and optimizing both cloud and on-premise infrastructure that hosts our
|
diverse portfolio of SaaS products, collaborating with Software Developers,
|
||||||
single-tenant SaaS solutions. My role involves not only maintaining and
|
Customer Success Managers, and Customer Success Engineers to ensure
|
||||||
improving these environments but also driving automation and efficiency
|
operational excellence and uninterrupted service delivery for our customers
|
||||||
through modern DevOps practices. I have been actively involved in
|
with their specific requirements at the forefront. I manage
|
||||||
transitioning our Kubernetes-based deployments from manual processes to a
|
Kubernetes cluster deployments, continuously monitor deployment health metrics,
|
||||||
more automated and scalable approach by leveraging Infrastructure as Code
|
and implement Infrastructure as Code solutions.
|
||||||
with Azure Resource Manager and Ansible.
|
|
||||||
cv_socials_title = Socials
|
cv_socials_title = Socials
|
||||||
cv_education_title = Education
|
cv_education_title = Education
|
||||||
cv_education_bachelor_title = BSc Systems Engineering
|
cv_education_bachelor_title = BSc Systems Engineering
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.85.0"
|
channel = "1.86.0"
|
||||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
|||||||
37
src/cv.rs
37
src/cv.rs
@@ -1,7 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_i18n::t;
|
use dioxus_i18n::t;
|
||||||
|
|
||||||
use crate::components::{H4, HR};
|
use crate::components::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn CV() -> Element {
|
pub fn CV() -> Element {
|
||||||
@@ -47,7 +47,7 @@ fn WorkExperience() -> Element {
|
|||||||
ol {
|
ol {
|
||||||
class:"relative border-s border-gray-700",
|
class:"relative border-s border-gray-700",
|
||||||
CVEntry {time: t!("cv_workexperience_dd_devops_time"), title: t!("cv_workexperience_dd_devops_title"),
|
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")
|
description: t!("cv_workexperience_dd_devops_description")
|
||||||
},
|
},
|
||||||
CVEntry {time: t!("cv_workexperience_ra_ude_time"), title: t!("cv_workexperience_ra_ude_title"),
|
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"},
|
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}"},
|
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}"}
|
h6 { class: "text-lg font-semibold text-white", "{props.title}"}
|
||||||
ul {
|
BadgeList{ list: props.technologies }
|
||||||
class: "flex flex-wrap gap-2",
|
|
||||||
for (index, value) in props.technologies.iter().enumerate() {
|
|
||||||
li { key: "{index}", RandomBadge { text: "{value}"} }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p { class:"text-base font-normal text-gray-400", "{props.description}"},
|
p { class:"text-base font-normal text-gray-400", "{props.description}"},
|
||||||
{props.children}
|
{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 {
|
fn Socials() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ fn App() -> Element {
|
|||||||
I18nConfig::new(langid!("en-GB"))
|
I18nConfig::new(langid!("en-GB"))
|
||||||
.with_locale(Locale::new_static(
|
.with_locale(Locale::new_static(
|
||||||
langid!("en-GB"),
|
langid!("en-GB"),
|
||||||
include_str!("./languages/en-GB.ftl"),
|
include_str!("../languages/en-GB.ftl"),
|
||||||
))
|
))
|
||||||
.with_locale(Locale::new_static(
|
.with_locale(Locale::new_static(
|
||||||
langid!("de-DE"),
|
langid!("de-DE"),
|
||||||
include_str!("./languages/de-DE.ftl"),
|
include_str!("../languages/de-DE.ftl"),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_i18n::t;
|
use dioxus_i18n::t;
|
||||||
|
|
||||||
use crate::components::{Bolding, H1, HR};
|
use crate::components::{BadgeList, Bolding, H1, HR};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PublicationsProjects() -> Element {
|
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)]
|
#[derive(Clone, PartialEq, Props)]
|
||||||
struct ProjectProp {
|
struct ProjectProp {
|
||||||
#[props(default = "".to_string())]
|
#[props(default = "".to_string())]
|
||||||
url: String,
|
url: String,
|
||||||
authors: String,
|
authors: String,
|
||||||
title: String,
|
title: String,
|
||||||
|
technologies: Vec<String>,
|
||||||
kind: String,
|
kind: String,
|
||||||
#[props(default = "".to_string())]
|
#[props(default = "".to_string())]
|
||||||
description: String,
|
description: String,
|
||||||
@@ -140,6 +45,7 @@ fn Project(prop: ProjectProp) -> Element {
|
|||||||
class:"mb-2 text-2xl font-bold tracking-tight text-white",
|
class:"mb-2 text-2xl font-bold tracking-tight text-white",
|
||||||
"{prop.title}",
|
"{prop.title}",
|
||||||
},
|
},
|
||||||
|
p {}
|
||||||
p { class: "text-lg text-white", "{prop.kind}" },
|
p { class: "text-lg text-white", "{prop.kind}" },
|
||||||
p {
|
p {
|
||||||
class:"font-normal text-gray-400",
|
class:"font-normal text-gray-400",
|
||||||
@@ -147,7 +53,8 @@ fn Project(prop: ProjectProp) -> Element {
|
|||||||
authors: "{prop.authors}",
|
authors: "{prop.authors}",
|
||||||
patterns: pattern,
|
patterns: pattern,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
BadgeList{list: prop.technologies},
|
||||||
p {
|
p {
|
||||||
class:"font-normal text-gray-400",
|
class:"font-normal text-gray-400",
|
||||||
"{prop.description}",
|
"{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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user