Compare commits
3 Commits
6e3db0294f
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fef5d771ba | ||
|
|
1284dd9dd6 | ||
|
|
538c99166f |
@@ -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
|
||||
3579
Cargo.lock
generated
3579
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "athome"
|
||||
version = "0.4.1"
|
||||
version = "0.2.0"
|
||||
authors = ["Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -8,27 +8,17 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
dioxus = { version = "0.6", features = ["fullstack", "router"] }
|
||||
dioxus = { version = "0.7.0", features = ["fullstack", "router"] }
|
||||
|
||||
# Debug
|
||||
tracing = "0.1.40"
|
||||
dioxus-logger = "0.6.0"
|
||||
dioxus-i18n = "0.4.0"
|
||||
dioxus-logger = "0.7.0"
|
||||
dioxus-i18n = { git = "https://github.com/Kannen/dioxus-i18n/", branch = "main"}
|
||||
tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["web"]
|
||||
web = ["dioxus/web"]
|
||||
desktop = ["dioxus/desktop"]
|
||||
mobile = ["dioxus/mobile"]
|
||||
server = ["dioxus/server"]
|
||||
|
||||
[profile]
|
||||
|
||||
[profile.wasm-dev]
|
||||
inherits = "dev"
|
||||
opt-level = 1
|
||||
|
||||
[profile.server-dev]
|
||||
inherits = "dev"
|
||||
|
||||
[profile.android-dev]
|
||||
inherits = "dev"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -723,18 +723,10 @@ video {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -747,14 +739,6 @@ video {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.content-center {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -771,10 +755,6 @@ video {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-stretch {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
@@ -801,10 +781,6 @@ video {
|
||||
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.self-stretch {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
@@ -1071,10 +1047,6 @@ video {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.leading-none {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -16,7 +15,7 @@ cv_introduction_title = DevOps Engineer | Homelab Enthusiast
|
||||
cv_introduction_0 =
|
||||
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.
|
||||
@@ -62,20 +61,19 @@ 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 = 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_workexperience_dd_devops_description = Als DevOps Engineer war ich
|
||||
verantwortlich für das Design, die Implementierung und die Wartung
|
||||
skalierbarer Infrastrukturlösungen in verschiedenen SaaS-Produktumgebungen.
|
||||
Dies umfasste die Standardisierung und Optimierung von CI/CD-Pipelines sowie
|
||||
den Aufbau robuster Monitoring-Frameworks zur Gewährleistung hoher
|
||||
Verfügbarkeit und Performance. Meine Arbeit konzentrierte sich auf einen
|
||||
"Shift-Left"-Ansatz, der Entwicklungsteams durch Self-Service-Funktionen und
|
||||
optimierte operative Workflows befähigte, sowie auf die Verbesserung der
|
||||
Systemzuverlässigkeit und -sicherheit.
|
||||
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
|
||||
@@ -111,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.
|
||||
@@ -119,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
|
||||
@@ -137,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
|
||||
@@ -146,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_name = Tuan-Dat Tran
|
||||
footer_rights = . All Rights Reserved.
|
||||
footer_contact = Kontakt
|
||||
link_opens_new_tab = (öffnet in neuem Tab)
|
||||
|
||||
@@ -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
|
||||
@@ -14,6 +13,7 @@ home_card_text =
|
||||
home_card_contact_button = Get in touch.
|
||||
cv_introduction_title = DevOps Engineer | Homelab Enthusiast
|
||||
cv_introduction_0 =
|
||||
A results-driven DevOps Engineer with a career in technology spanning over 8 years. My journey began with foundational part-time roles in software development (C#, Python) and academic research during my university studies. Now, I apply this deep technical understanding to my full-time DevOps role, where I specialize in building and maintaining scalable, high-availability SaaS infrastructure using Kubernetes, Azure, ArgoCD, and Ansible.
|
||||
DevOps Engineer and Software Developer with a strong academic background in
|
||||
networking technologies and software development. Specialized in Kubernetes,
|
||||
Ansible, Azure, and modern cloud technologies. Hands-on research experience
|
||||
@@ -58,19 +58,18 @@ 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 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_workexperience_dd_devops_description = As a DevOps Engineer, I was
|
||||
responsible for designing, implementing, and maintaining scalable
|
||||
infrastructure solutions across multiple SaaS product environments. This
|
||||
involved standardizing and optimizing CI/CD pipelines and establishing
|
||||
robust monitoring frameworks to ensure high availability and performance.
|
||||
My work focused on a "shift-left" approach, empowering development teams
|
||||
with self-service capabilities and streamlined operational workflows as
|
||||
well as enhancement of system reliability and security.
|
||||
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
|
||||
@@ -106,7 +105,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
|
||||
@@ -115,12 +113,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
|
||||
@@ -130,7 +126,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
|
||||
@@ -138,15 +133,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_name = Tuan-Dat Tran
|
||||
footer_rights = . All Rights Reserved.
|
||||
footer_contact = Contact
|
||||
link_opens_new_tab = (opens in a new tab)
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -4,6 +4,9 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001753"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.3"
|
||||
}
|
||||
@@ -227,6 +230,26 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001753",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz",
|
||||
"integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -782,6 +805,7 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001753"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
channel = "1.91.0"
|
||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||
|
||||
61
scripts/check_i18n.py
Normal file
61
scripts/check_i18n.py
Normal 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)
|
||||
@@ -280,3 +280,35 @@ fn random_badge_color(seed: usize) -> String {
|
||||
|
||||
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}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/cv.rs
12
src/cv.rs
@@ -13,7 +13,7 @@ pub fn CV() -> Element {
|
||||
img {
|
||||
class: "rounded-full w-24 h-24",
|
||||
alt: "headshot",
|
||||
src: asset!("./assets/pictures/headshot.webp")
|
||||
src: asset!("/assets/pictures/headshot.webp")
|
||||
}
|
||||
Introduction {},
|
||||
Socials {}
|
||||
@@ -39,11 +39,11 @@ fn Introduction() -> Element {
|
||||
P {
|
||||
{ t!("cv_introduction_tools") },
|
||||
" ",
|
||||
Link { new_tab: true, to: "https://www.lazyvim.org/", "NeoVim (LazyVim)" },
|
||||
AccessibleLink { new_tab: true, to: "https://www.lazyvim.org/", "NeoVim" },
|
||||
", ",
|
||||
Link { new_tab: true, to: "https://zellij.dev/", "Zellij" },
|
||||
AccessibleLink { new_tab: true, to: "https://zellij.dev/", "Zellij" },
|
||||
", ",
|
||||
Link { new_tab: true, to: "https://k9scli.io/", "k9s" }
|
||||
AccessibleLink { new_tab: true, to: "https://k9scli.io/", "k9s" }
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -57,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!["Kubernetes".to_string(), "ArgoCD".to_string(), "Ansible".to_string(), "Azure".to_string(), "ELK".to_string(), "Helm".to_string()],
|
||||
technologies: vec!["Kubernetes".to_string(), "ArgoCD".to_string(), "Ansible".to_string(), "Azure".to_string(), "Elastic Stack".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"),
|
||||
@@ -210,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" } }},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::components::{Card, P};
|
||||
use crate::components::{AccessibleLink, Card, P};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_i18n::t;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub fn Home() -> Element {
|
||||
Card {
|
||||
name: t!("home_card_name"),
|
||||
gender: t!("home_card_gender"),
|
||||
picture: asset!("./assets/pictures/headshot.webp"),
|
||||
picture: asset!("/assets/pictures/headshot.webp"),
|
||||
div {
|
||||
class: "py-4",
|
||||
div {
|
||||
@@ -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") }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,59 +1,30 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_i18n::t;
|
||||
|
||||
use crate::components::{H1, HR, P};
|
||||
|
||||
#[component]
|
||||
pub fn Impressum() -> Element {
|
||||
let mut impressum = use_signal(Vec::<String>::new);
|
||||
let mut contact = use_signal(Vec::<String>::new);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
div {
|
||||
class: "flex flex-col items-center",
|
||||
button {
|
||||
onclick: move |_| async move {
|
||||
if let Ok(data) = get_impressum().await {
|
||||
impressum.set(data.clone());
|
||||
}
|
||||
if let Ok(data) = get_contact().await {
|
||||
contact.set(data.clone());
|
||||
}
|
||||
},
|
||||
H1 { { t!("impressum_on") } },
|
||||
},
|
||||
H1 { "Impressum" },
|
||||
},
|
||||
HR{},
|
||||
div {
|
||||
class: "flex flex-col items-center",
|
||||
for line in impressum() {
|
||||
P { {line} }
|
||||
}
|
||||
P { {"Tuan-Dat Tran"} },
|
||||
P { {"c/o AutorenServices.de"} },
|
||||
P { {"Birkenallee 24"} },
|
||||
P { {"36037 Fulda"} },
|
||||
}
|
||||
if !impressum.read().is_empty() { HR{} },
|
||||
HR{},
|
||||
div {
|
||||
class: "flex flex-col items-center",
|
||||
for line in contact() {
|
||||
P { {line} }
|
||||
}
|
||||
P { {"tuan-dat.tran(at)tudattr(dot)dev"} },
|
||||
P { {"+49 17(six) 83(four)683(eight)8"} },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[server(GetServerData)]
|
||||
async fn get_impressum() -> Result<Vec<String>, ServerFnError> {
|
||||
Ok(vec![
|
||||
"Tuan-Dat Tran".to_string(),
|
||||
"c/o AutorenServices.de".to_string(),
|
||||
"Birkenallee 24".to_string(),
|
||||
"36037 Fulda".to_string(),
|
||||
])
|
||||
}
|
||||
|
||||
async fn get_contact() -> Result<Vec<String>, ServerFnError> {
|
||||
Ok(vec![
|
||||
"tuan-dat.tran(at)tudattr(dot)dev".to_string(),
|
||||
"+49 17(six) 83(four)683(eight)8".to_string(),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ pub fn Footer() -> Element {
|
||||
span {
|
||||
class:"text-sm sm:text-center text-gray-400",
|
||||
{ t!("footer_year") },
|
||||
" ",
|
||||
a { href: "#", class: "hover:underline", { t!("footer_name") }},
|
||||
" ",
|
||||
{ t!("footer_rights") }
|
||||
}
|
||||
ul {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_i18n::{prelude::i18n, t, unic_langid::langid};
|
||||
use dioxus_i18n::{prelude::*, t, unic_langid::langid};
|
||||
|
||||
use crate::Route;
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn Header() -> Element {
|
||||
Link {
|
||||
to: Route::Home {},
|
||||
class: "rounded-md shadow-sm",
|
||||
img { src:asset!("./assets/pictures/ClackCat_t.webp"), class:"rounded-full h-8", alt:"TuDatTr Logo" },
|
||||
img { src:asset!("/assets/pictures/ClackCat_t.webp"), class:"rounded-full h-8", alt:"TuDatTr Logo" },
|
||||
},
|
||||
},
|
||||
li { HeaderLink { url: Route::Home {}, text: t!("headers_home")} },
|
||||
|
||||
34
src/main.rs
34
src/main.rs
@@ -3,13 +3,11 @@
|
||||
use components::H1;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use dioxus_i18n::prelude::use_init_i18n;
|
||||
use dioxus_i18n::prelude::I18nConfig;
|
||||
use dioxus_i18n::prelude::Locale;
|
||||
use dioxus_i18n::unic_langid::langid;
|
||||
use dioxus_i18n::{prelude::*, 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,32 +33,42 @@ 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 {
|
||||
use_init_i18n(|| {
|
||||
I18nConfig::new(langid!("en-GB"))
|
||||
.with_locale((langid!("de-DE"), include_str!("../languages/de-DE.ftl")))
|
||||
.with_locale(Locale::new_static(
|
||||
langid!("en-GB"),
|
||||
include_str!("../languages/en-GB.ftl"),
|
||||
))
|
||||
.with_locale(Locale::new_static(
|
||||
langid!("de-DE"),
|
||||
include_str!("../languages/de-DE.ftl"),
|
||||
))
|
||||
});
|
||||
|
||||
rsx! {
|
||||
document::Link { rel: "stylesheet", href: asset!("./assets/tailwind.css") }
|
||||
document::Link { rel: "icon", href: asset!("./assets/favicon.ico") }
|
||||
document::Link { rel: "stylesheet", href: asset!("/assets/tailwind.css") }
|
||||
document::Link { rel: "icon", href: asset!("/assets/favicon.ico") }
|
||||
meta {
|
||||
name: "description",
|
||||
content: "Visit Tuan-Dat Tran's website for his CV, publications, projects, and consulting services. Connect for collaboration.",
|
||||
|
||||
@@ -81,7 +81,7 @@ fn Publications() -> Element {
|
||||
authors: t!("publications_projects_publications_iot_fuzzers_authors"),
|
||||
technologies: vec![],
|
||||
kind: t!("publications_projects_publications_iot_fuzzers_conference"),
|
||||
url: "/#",
|
||||
url: "/publications/#",
|
||||
description: t!("publications_projects_publications_iot_fuzzers_description")
|
||||
},
|
||||
}
|
||||
@@ -97,7 +97,7 @@ fn Projects() -> Element {
|
||||
authors: t!("publications_projects_projects_bpba_authors"),
|
||||
technologies: vec![],
|
||||
kind: t!("publications_projects_projects_bpba_kind"),
|
||||
url: "/#",
|
||||
url: "/publications/#",
|
||||
description: t!("publications_projects_projects_bpba_description")
|
||||
},
|
||||
Project {
|
||||
@@ -105,7 +105,7 @@ fn Projects() -> Element {
|
||||
authors: t!("publications_projects_projects_dotfiles_authors"),
|
||||
technologies: vec![],
|
||||
kind: t!("publications_projects_projects_dotfiles_kind"),
|
||||
url: "/#",
|
||||
url: "/publications/#",
|
||||
description: t!("publications_projects_projects_dotfiles_description")
|
||||
},
|
||||
Project {
|
||||
@@ -113,7 +113,7 @@ fn Projects() -> Element {
|
||||
authors: t!("publications_projects_projects_homelab_authors"),
|
||||
technologies: vec![],
|
||||
kind: t!("publications_projects_projects_homelab_kind"),
|
||||
url: "/#",
|
||||
url: "/publications/#",
|
||||
description: t!("publications_projects_projects_homelab_description")
|
||||
}
|
||||
Project {
|
||||
@@ -121,7 +121,7 @@ fn Projects() -> Element {
|
||||
authors: t!("publications_projects_projects_athome_authors"),
|
||||
technologies: vec![],
|
||||
kind: t!("publications_projects_projects_athome_kind"),
|
||||
url: "/#",
|
||||
url: "/publications/#",
|
||||
description: t!("publications_projects_projects_athome_description")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user