Compare commits
4 Commits
0.1.10
...
6e3db0294f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3db0294f | ||
|
|
a1fd3ea358 | ||
|
|
a387293f94 | ||
|
|
64aa115ef4 |
743
Cargo.lock
generated
743
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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 = []
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -723,10 +723,18 @@ video {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -739,6 +747,14 @@ video {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.content-center {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -755,6 +771,10 @@ video {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-stretch {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
@@ -781,6 +801,10 @@ video {
|
||||
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.self-stretch {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
@@ -797,6 +821,11 @@ video {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.rounded-e-lg {
|
||||
border-start-end-radius: 0.5rem;
|
||||
border-end-end-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.rounded-s-lg {
|
||||
border-start-start-radius: 0.5rem;
|
||||
border-end-start-radius: 0.5rem;
|
||||
@@ -818,11 +847,6 @@ video {
|
||||
border-inline-start-width: 1px;
|
||||
}
|
||||
|
||||
.border-gray-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-gray-600 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(75 85 99 / var(--tw-border-opacity));
|
||||
@@ -833,29 +857,14 @@ video {
|
||||
border-color: rgb(55 65 81 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-white {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(255 255 255 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-gray-900 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(17 24 39 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-100 {
|
||||
.bg-blue-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-200 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-700 {
|
||||
@@ -868,51 +877,11 @@ video {
|
||||
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-green-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-indigo-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(224 231 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-pink-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(252 231 243 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-purple-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(243 232 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-yellow-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-green-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 83 45 / var(--tw-bg-opacity));
|
||||
@@ -1118,9 +1087,14 @@ video {
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.text-blue-800 {
|
||||
.text-blue-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 64 175 / var(--tw-text-opacity));
|
||||
color: rgb(147 197 253 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(59 130 246 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-cyan-400 {
|
||||
@@ -1128,6 +1102,11 @@ video {
|
||||
color: rgb(34 211 238 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(209 213 219 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
@@ -1138,70 +1117,11 @@ video {
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(55 65 81 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(31 41 55 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-900 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(22 101 52 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-indigo-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(55 48 163 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-pink-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(157 23 77 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-purple-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 33 168 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(153 27 27 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-transparent {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-yellow-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(133 77 14 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(147 197 253 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(209 213 219 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(134 239 172 / var(--tw-text-opacity));
|
||||
@@ -1227,6 +1147,15 @@ video {
|
||||
color: rgb(252 165 165 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-transparent {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-yellow-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(253 224 71 / var(--tw-text-opacity));
|
||||
@@ -1379,11 +1308,6 @@ video {
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.md\:text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(59 130 246 / var(--tw-text-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
@@ -1393,115 +1317,3 @@ video {
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark\:border-gray-700 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(55 65 81 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark\:border-gray-900 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(17 24 39 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-blue-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-gray-700 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-gray-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-green-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 83 45 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-indigo-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(49 46 129 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-pink-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(131 24 67 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-purple-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(88 28 135 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-red-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(127 29 29 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-yellow-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(113 63 18 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-blue-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(147 197 253 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-gray-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(209 213 219 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-gray-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-green-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(134 239 172 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-indigo-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(165 180 252 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-pink-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(249 168 212 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-purple-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(216 180 254 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-red-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(252 165 165 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-yellow-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(253 224 71 / var(--tw-text-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,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
|
||||
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
|
||||
@@ -60,14 +62,15 @@ cv_workexperience_ra_ude_description = Während meiner Tätigkeit bei der
|
||||
aufgebaut und verwaltet.
|
||||
cv_workexperience_dd_devops_title = DevOps Engineer @ DextraData
|
||||
cv_workexperience_dd_devops_time = 2025 - Jetzt
|
||||
cv_workexperience_dd_devops_description = Bei DextraData, einem führenden
|
||||
SaaS-Anbieter im Bereich Governance, Risk und Compliance (GRC), stelle ich
|
||||
die Verfügbarkeit und Stabilität von Software-Deployments für unsere Kunden
|
||||
sowie für interne Entwicklungsteams über mehrere Produkte hinweg sicher. In
|
||||
Zusammenarbeit mit Customer-Success-Teams und Softwareentwicklern
|
||||
gewährleisten wir einen reibungslosen Betrieb unserer Multi-Tenant- und Single
|
||||
-Tenant-Instanzen – von der Vorentwicklung bis hin zu den
|
||||
Produktionsumgebungen.
|
||||
cv_workexperience_dd_devops_description = Als DevOps Engineer bei DextraData
|
||||
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
|
||||
@@ -12,6 +12,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 +20,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
|
||||
@@ -56,13 +58,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 = At DextraData, a leading SaaS
|
||||
provider in the Governance, Risk, and Compliance (GRC) sector, I ensure the
|
||||
availability and health of software deployments for our customers and
|
||||
internal development teams across multiple products. Collaborating with
|
||||
Customer Success and Software Engineers alike we ensure smooth operations on
|
||||
our multi-tenant as well as single tenant instances from pre-dev to
|
||||
production environments.
|
||||
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
|
||||
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
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()
|
||||
}
|
||||
|
||||
49
src/cv.rs
49
src/cv.rs
@@ -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") },
|
||||
" ",
|
||||
Link { new_tab: true, to: "https://www.lazyvim.org/", "NeoVim (LazyVim)" },
|
||||
", ",
|
||||
Link { new_tab: true, to: "https://zellij.dev/", "Zellij" },
|
||||
", ",
|
||||
Link { 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 {
|
||||
|
||||
@@ -44,7 +44,7 @@ fn LanguageButtonGroup() -> Element {
|
||||
onclick: change_to_english,
|
||||
label { { t!("headers_language_buttons_english") } } },
|
||||
button {
|
||||
class: "px-4 py-2 text-sm font-medium border rounded-s-lg focus:z-10 focus:ring-2 bg-gray-800 border-gray-700 text-white hover:text-white hover:bg-gray-700 focus:ring-blue-500 focus:text-white",
|
||||
class: "px-4 py-2 text-sm font-medium border rounded-e-lg focus:z-10 focus:ring-2 bg-gray-800 border-gray-700 text-white hover:text-white hover:bg-gray-700 focus:ring-blue-500 focus:text-white",
|
||||
onclick: change_to_german,
|
||||
label { { t!("headers_language_buttons_german") } } }
|
||||
}
|
||||
@@ -54,6 +54,6 @@ fn LanguageButtonGroup() -> Element {
|
||||
#[component]
|
||||
fn HeaderLink(url: Route, text: String) -> Element {
|
||||
rsx! {
|
||||
Link { to: url, class:"md:bg-transparent md:p-0 text-white md:text-blue-500", {text} }
|
||||
Link { to: url, class:"md:bg-transparent md:p-0 text-blue-500", {text} }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
))
|
||||
});
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user