commit
75c64a7227
|
@ -0,0 +1,10 @@
|
||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
/dist/
|
||||||
|
/static/
|
||||||
|
/.dioxus/
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "athome"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
|
||||||
|
dioxus = { version = "0.5", features = ["fullstack", "router"] }
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
tracing = "0.1.40"
|
||||||
|
dioxus-logger = "0.5.0"
|
||||||
|
manganis = "0.2.2"
|
||||||
|
dioxus-free-icons = { version = "0.8", features = ["font-awesome-brands"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
server = ["dioxus/axum"]
|
||||||
|
web = ["dioxus/web"]
|
|
@ -0,0 +1,43 @@
|
||||||
|
[application]
|
||||||
|
|
||||||
|
# App (Project) Name
|
||||||
|
name = "athome"
|
||||||
|
|
||||||
|
# Dioxus App Default Platform
|
||||||
|
# desktop, web
|
||||||
|
default_platform = "web"
|
||||||
|
|
||||||
|
# `build` & `serve` dist path
|
||||||
|
out_dir = "dist"
|
||||||
|
|
||||||
|
# resource (assets) file folder
|
||||||
|
asset_dir = "assets"
|
||||||
|
|
||||||
|
[web.app]
|
||||||
|
|
||||||
|
# HTML title tag content
|
||||||
|
title = "athome"
|
||||||
|
|
||||||
|
[web.watcher]
|
||||||
|
|
||||||
|
# when watcher trigger, regenerate the `index.html`
|
||||||
|
reload_html = true
|
||||||
|
|
||||||
|
# which files or dirs will be watcher monitoring
|
||||||
|
watch_path = ["src", "assets"]
|
||||||
|
|
||||||
|
# include `assets` in web platform
|
||||||
|
[web.resource]
|
||||||
|
|
||||||
|
# CSS style file
|
||||||
|
|
||||||
|
style = ["tailwind.css"]
|
||||||
|
|
||||||
|
# Javascript code file
|
||||||
|
script = []
|
||||||
|
|
||||||
|
[web.resource.dev]
|
||||||
|
|
||||||
|
# Javascript code file
|
||||||
|
# serve: [dev-server] only
|
||||||
|
script = []
|
|
@ -0,0 +1,17 @@
|
||||||
|
FROM rust:1.77.2 as builder
|
||||||
|
WORKDIR /athome/
|
||||||
|
RUN cargo install dioxus-cli@^0.5
|
||||||
|
RUN apt update && apt install nodejs npm -y && rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN npm install -D tailwindcss
|
||||||
|
|
||||||
|
FROM builder
|
||||||
|
WORKDIR /athome/
|
||||||
|
COPY ./src/ ./src/
|
||||||
|
COPY ./assets/ ./assets/
|
||||||
|
COPY ./Cargo.toml ./Cargo.toml
|
||||||
|
COPY ./input.css ./input.css
|
||||||
|
COPY ./Dioxus.toml ./Dioxus.toml
|
||||||
|
COPY ./tailwind.config.js ./tailwind.config.js
|
||||||
|
RUN npx tailwindcss -i ./input.css -o ./assets/tailwind.css
|
||||||
|
RUN dx build --platform fullstack --release
|
||||||
|
ENTRYPOINT [ "./dist/athome" ]
|
|
@ -0,0 +1,7 @@
|
||||||
|
# @Home
|
||||||
|
|
||||||
|
My personal website.
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|
[[./resources/screenshot.png]]
|
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,40 @@
|
||||||
|
body {
|
||||||
|
background-color: #111216;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
|
||||||
|
}
|
||||||
|
|
||||||
|
#links {
|
||||||
|
width: 400px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: x-large;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#links a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin: 10px;
|
||||||
|
border: white 1px solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#links a:hover {
|
||||||
|
background-color: #1f1f1f;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"tailwindcss": "^3.4.3"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 172 KiB |
|
@ -0,0 +1,99 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
pub struct PProps {
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
pub class: String,
|
||||||
|
pub children: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn P(props: PProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "{props.class}",
|
||||||
|
p {
|
||||||
|
class: "mb-2 font-normal text-gray-900 dark:text-white",
|
||||||
|
{props.children}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn H1(props: PProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "{props.class}",
|
||||||
|
h1 {
|
||||||
|
class: "mb-4 text-3xl font-extrabold text-gray-900 dark:text-white md:text-5xl lg:text-6xl",
|
||||||
|
span {
|
||||||
|
class: "text-transparent bg-clip-text bg-gradient-to-r to-emerald-600 from-sky-400",
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn H4(props: PProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "{props.class}",
|
||||||
|
class: "mb-4",
|
||||||
|
h3 {
|
||||||
|
class: "text-lg uppercase text-cyan-600 dark:text-cyan-400 tracking-widest mb-4 font-bold",
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn H5(props: PProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "{props.class}",
|
||||||
|
h5 {
|
||||||
|
class: "mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white",
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
pub struct CardProp {
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
class: String,
|
||||||
|
children: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Card(prop: CardProp) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "flex flex-col py-10 items-center min-w-fit",
|
||||||
|
div {
|
||||||
|
class: "text-white bg-gradient-to-r from-purple-500 to-blue-500 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-cyan-300 dark:focus:ring-cyan-800 rounded-lg px-5 py-2.5 min-w-fit max-w-8 center",
|
||||||
|
{ prop.children }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn UnderConstruction() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class:"rounded flex justify-between w-full p-4 border-b border-gray-200 bg-gray-50 dark:bg-gray-700 dark:border-gray-600 m-16",
|
||||||
|
div {
|
||||||
|
class:"flex items-center mx-auto",
|
||||||
|
p {
|
||||||
|
class:"flex items-center text-sm font-normal text-gray-500 dark:text-gray-400",
|
||||||
|
span {
|
||||||
|
"This site is currenlty under construction",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::H4;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CV() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "flex flex-col ",
|
||||||
|
Introduction {},
|
||||||
|
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"},
|
||||||
|
div {
|
||||||
|
class: "flex justify-between",
|
||||||
|
WorkExperience {},
|
||||||
|
Miscellaneous {},
|
||||||
|
},
|
||||||
|
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"},
|
||||||
|
Socials {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Introduction() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "flex",
|
||||||
|
img {
|
||||||
|
class: "rounded-full w-16 h-16 mx-16",
|
||||||
|
src: "/pictures/portrait.webp"
|
||||||
|
}
|
||||||
|
P {
|
||||||
|
"While studying for my bachelors degree I collected a lot of
|
||||||
|
industry and academic experience. My professional and personal
|
||||||
|
intererests are DevOps/IaC, Systems/Software Security and Computer
|
||||||
|
Networking. All of which I deepen in personal projects such as my
|
||||||
|
homelab and CTF challenges."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn WorkExperience() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "ms-8 max-w-3/4",
|
||||||
|
H4 { "Work Experience" },
|
||||||
|
ol {
|
||||||
|
class:"relative border-s border-gray-200 dark:border-gray-700",
|
||||||
|
CVEntry {time: "2021 - now", title: "Research Assistant @ UDE",
|
||||||
|
technologies: vec!["Rust".to_string(), "Python".to_string(), "P4".to_string(), "Linux".to_string(), "Docker".to_string(), "Kubernetes".to_string()],
|
||||||
|
description:
|
||||||
|
"While working for the Network Communication System Research
|
||||||
|
Group at the University Duisburg-Essen as a research assistant
|
||||||
|
I've assisted in research around software defined networking,
|
||||||
|
5G, congestion control algorithms and federated machine learning.
|
||||||
|
I've established and managed the research groups on-premise and
|
||||||
|
cloud infractructure, inventory system and online presence."
|
||||||
|
},
|
||||||
|
CVEntry {time: "2021 - 2022", title: "Mentoring @ UDE",
|
||||||
|
technologies: vec!["Powerpoint".to_string()],
|
||||||
|
description:
|
||||||
|
"As a mentor for students enrolling into the computer
|
||||||
|
science program of the University Duisburg-Essen I
|
||||||
|
introduced groups of ~20 freshmen to their new academic
|
||||||
|
environment at the beginning of each semester. Offering
|
||||||
|
additional organizational and technical guidance for an
|
||||||
|
two-semesters."
|
||||||
|
},
|
||||||
|
CVEntry {time: "2018 - 2020", title: "Software Engineer @ gefeba Engineering GmbH",
|
||||||
|
technologies: vec!["C#".to_string(), "Angular".to_string(), "bootstrap".to_string(), "Entity Framework".to_string()],
|
||||||
|
description:
|
||||||
|
"As a software engineer at gefeba Engineering I worked
|
||||||
|
worked the companies main software product, which was a
|
||||||
|
frame-based data exchange system to monitor industry
|
||||||
|
machinary using C# and Entity Framework. Another project
|
||||||
|
I worked on was a real time log visualization application
|
||||||
|
for the same machinary."
|
||||||
|
},
|
||||||
|
CVEntry {time: "2016 - 2019", title: "Student Council Member @ UDE",
|
||||||
|
technologies: vec!["Linux".to_string(), "Networking".to_string(), "LaTeX".to_string()],
|
||||||
|
description:
|
||||||
|
"As a student council member I participated in faculty
|
||||||
|
committees and organized social events. My
|
||||||
|
main responsabilities as a member were the management
|
||||||
|
of the IT infrastructure and supporting students, be it
|
||||||
|
organizationally or subject-specific."
|
||||||
|
},
|
||||||
|
CVEntry {time: "2013 - 2015", title: "Software Engineer @ gefeba Engineering GmbH",
|
||||||
|
technologies: vec!["C#".to_string(), "HTML".to_string(), "Javascript".to_string(), "CSS".to_string()],
|
||||||
|
description:
|
||||||
|
"After a school internship I got offered a job as a
|
||||||
|
Software Engineer. I mostly worked on internal ERP
|
||||||
|
projects, designed a tool which aided in managing
|
||||||
|
project related mail traffic and worked on the
|
||||||
|
internal master data management tool."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Miscellaneous() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "",
|
||||||
|
Education {},
|
||||||
|
Skills {},
|
||||||
|
Languages {},
|
||||||
|
Interests {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Education() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
H4 {"Education"},
|
||||||
|
Entry {
|
||||||
|
title: "BSc Systems Engineering",
|
||||||
|
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", "2015 - now"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Skills() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
H4 {"Skills"},
|
||||||
|
Entry {
|
||||||
|
title: "DevOps",
|
||||||
|
P { "Ansible" },
|
||||||
|
P { "Kubernetes" },
|
||||||
|
P { "GitOps" },
|
||||||
|
}Entry {
|
||||||
|
title: "Software Development",
|
||||||
|
P { "Rust" },
|
||||||
|
P { "Python" },
|
||||||
|
P { "Java" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Languages() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
H4 {"Languages"},
|
||||||
|
Entry {
|
||||||
|
P { "German (Native)" },
|
||||||
|
P { "English (C2)" },
|
||||||
|
P { "Vietnamese (B1)" },
|
||||||
|
P { "Japanese (A1)" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Interests() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
H4 {"Interests"}
|
||||||
|
Entry {
|
||||||
|
P { "Coffee" },
|
||||||
|
P { "Mechanical Keyboards" },
|
||||||
|
P { "Tech/IT" },
|
||||||
|
P { "Music" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
struct EntryProps {
|
||||||
|
#[props(default = "mb-4".to_string())]
|
||||||
|
class: String,
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
title: String,
|
||||||
|
children: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Entry(props: EntryProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "{props.class}",
|
||||||
|
h6 { class: "font-semibold text-gray-900 dark:text-white", "{props.title}"}
|
||||||
|
{props.children},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
struct CVEntryProps {
|
||||||
|
#[props(default = "mb-10 ms-4".to_string())]
|
||||||
|
class: String,
|
||||||
|
time: String,
|
||||||
|
title: String,
|
||||||
|
technologies: Vec<String>,
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
description: String,
|
||||||
|
children: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CVEntry(props: CVEntryProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
li {
|
||||||
|
class: "max-w-xl",
|
||||||
|
class: "{props.class}",
|
||||||
|
div { class:"absolute w-3 h-3 bg-gray-200 rounded-full mt-1.5 -start-1.5 border border-white dark:border-gray-900 dark:bg-gray-700"},
|
||||||
|
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", "{props.time}"},
|
||||||
|
h6 { class: "text-lg font-semibold text-gray-900 dark:text-white", "{props.title}"}
|
||||||
|
for tech in props.technologies {
|
||||||
|
RandomBadge {text: "{tech}"}
|
||||||
|
}
|
||||||
|
p { class:"text-base font-normal text-gray-500 dark: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 ",
|
||||||
|
class: "{badge_color}",
|
||||||
|
"{text}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_badge_color(seed: usize) -> String {
|
||||||
|
let colors = [
|
||||||
|
"bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300",
|
||||||
|
"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",
|
||||||
|
"bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300",
|
||||||
|
"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300",
|
||||||
|
"bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300",
|
||||||
|
"bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300",
|
||||||
|
"bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300",
|
||||||
|
"bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-300",
|
||||||
|
];
|
||||||
|
|
||||||
|
colors[seed % colors.len()].to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Socials() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
"todo!()"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn P(children: Element) -> Element {
|
||||||
|
rsx! {
|
||||||
|
p {
|
||||||
|
class: "text-base font-normal text-gray-500 dark:text-gray-400",
|
||||||
|
{children},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
use crate::components::{Card, H5, P};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Home() -> Element {
|
||||||
|
rsx! {
|
||||||
|
Card {
|
||||||
|
div {
|
||||||
|
class: "p-5",
|
||||||
|
div {
|
||||||
|
class: "pb-4",
|
||||||
|
div {
|
||||||
|
class: "flex justify-between",
|
||||||
|
Picture {src: "/pictures/comfy.webp"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
H5 { "Tuan-Dat Tran", span { class: "text-grey-600 dark:text-grey-500 text-lg", " (He/Him)" } },
|
||||||
|
div {
|
||||||
|
class: "py-4",
|
||||||
|
P { "Hey there! 👋🏻👋🏼👋🏽👋🏾👋🏿" },
|
||||||
|
P { "Welcome to my little place on the internet." },
|
||||||
|
P { "While you're here, why don't you check out my other projects over on my ",
|
||||||
|
Link {
|
||||||
|
to: "https://git.tudattr.dev/explore/repos",
|
||||||
|
new_tab: true,
|
||||||
|
class: "inline-flex items-center font-medium hover:underline",
|
||||||
|
"gitea"},
|
||||||
|
"?"
|
||||||
|
},
|
||||||
|
P { "I also offer IT-related consulting." },
|
||||||
|
P { "Have a look at my CV and contact me if you're interested." },
|
||||||
|
},
|
||||||
|
Link {
|
||||||
|
to: "mailto:tuan-dat.tran@tudattr.dev",
|
||||||
|
class: "w-fit relative inline-flex items-center justify-center p-1 mb-2 me-2 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-green-400 to-blue-600 group-hover:from-green-400 group-hover:to-blue-600 hover:text-white dark:text-white w-fill",
|
||||||
|
p {
|
||||||
|
class: "p-1",
|
||||||
|
"Get in touch."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
struct PictureProp {
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
class: String,
|
||||||
|
src: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Picture(prop: PictureProp) -> Element {
|
||||||
|
rsx! {
|
||||||
|
img {
|
||||||
|
class: "w-24 h-24 rounded-full shadow-lg",
|
||||||
|
src: "{prop.src}",
|
||||||
|
alt: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
use dioxus::prelude::{server_fn::error::ServerFnError, *};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::components::{H1, P};
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Impressum() -> Element {
|
||||||
|
let mut show_impressum = use_signal(|| false);
|
||||||
|
let mut check_impressum = move || {
|
||||||
|
if !show_impressum() {
|
||||||
|
show_impressum.set(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
if show_impressum() {
|
||||||
|
div {
|
||||||
|
class: "flex flex-col items-center",
|
||||||
|
H1 { "Impressum" },
|
||||||
|
P { "Tuan-Dat Tran" },
|
||||||
|
P { "c/o AutorenServices.de" },
|
||||||
|
P { "Birkenallee 24" },
|
||||||
|
P { "36037 Fulda" },
|
||||||
|
}
|
||||||
|
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"}
|
||||||
|
div {
|
||||||
|
class: "flex flex-col items-center",
|
||||||
|
P { "tuan-dat.tran@tudattr.dev" },
|
||||||
|
P { "+49 176 83468388" },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
div {
|
||||||
|
class: "flex flex-col items-center p-3",
|
||||||
|
button {
|
||||||
|
onclick: move |_| {
|
||||||
|
info!("Showing impressum.");
|
||||||
|
check_impressum();
|
||||||
|
},
|
||||||
|
|
||||||
|
H1 { "Show Impressum" },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(GetImpressum)]
|
||||||
|
pub async fn get_impressum() -> Result<(), ServerFnError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::H1;
|
||||||
|
|
||||||
|
pub fn Footer() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
ToolsUsed {},
|
||||||
|
footer {
|
||||||
|
class:"bg-white rounded-lg shadow dark:bg-gray-800 my-4:wa
|
||||||
|
",
|
||||||
|
div {
|
||||||
|
class:"w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-between",
|
||||||
|
span {
|
||||||
|
class:"text-sm text-gray-500 sm:text-center dark:text-gray-400",
|
||||||
|
"© 2024 ",
|
||||||
|
a { href: "#", class: "hover:underline", "Tuan-Dat Tran"},
|
||||||
|
". All Rights Reserved."
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
class:"flex flex-wrap items-center mt-3 text-sm font-medium text-gray-500 dark:text-gray-400 sm:mt-0",
|
||||||
|
li {
|
||||||
|
Link { to:"mailto:tuan-dat.tran@tudattr.dev", class:"hover:underline", "Contact" }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ToolsUsed() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class:"items-center bg-white rounded-lg shadow dark:bg-gray-800 p-4 my-4",
|
||||||
|
div {
|
||||||
|
H1 {class: "justify-center", "Tools used" },
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
class: "flex h-fill overflow-x-auto",
|
||||||
|
Logo {
|
||||||
|
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Rust/Rust.png",
|
||||||
|
alt: "Rust"
|
||||||
|
},
|
||||||
|
Logo {
|
||||||
|
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Tailwindcss/Tailwindcss6.png",
|
||||||
|
alt: "Tailwindcss"
|
||||||
|
},
|
||||||
|
Logo {
|
||||||
|
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Html/HTML.png",
|
||||||
|
alt: "HTML"
|
||||||
|
},
|
||||||
|
Logo {
|
||||||
|
src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Docker/DockerLogo.png",
|
||||||
|
alt: "Docker"
|
||||||
|
},
|
||||||
|
Logo {
|
||||||
|
src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Neovim/NeovimLogo.png",
|
||||||
|
alt: "NeoVim"
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Logo(src: String, alt: String) -> Element {
|
||||||
|
rsx! {
|
||||||
|
img {
|
||||||
|
class: "h-auto max-w-40 transition-all duration-300 rounded-lg cursor-pointer filter grayscale hover:grayscale-0",
|
||||||
|
src: "{src}",
|
||||||
|
alt: "{alt}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use crate::Route;
|
||||||
|
|
||||||
|
pub fn Header() -> Element {
|
||||||
|
rsx! {
|
||||||
|
nav {
|
||||||
|
div { class:" flex flex-wrap items-center justify-between mx-auto p-4",
|
||||||
|
Link {
|
||||||
|
to: Route::Home {},
|
||||||
|
class:"flex items-center space-x-3 rtl:space-x-reverse",
|
||||||
|
img { src:"/pictures/ClackCat_t.webp", class:"rounded-full h-8", alt:"Flowbite Logo" },
|
||||||
|
span { class:"self-center text-2xl font-semibold whitespace-nowrap dark:text-white", "" }
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
r#type:"button",
|
||||||
|
class:"inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600",
|
||||||
|
aria_controls:"navbar-default",
|
||||||
|
aria_expanded:"false",
|
||||||
|
span { class:"sr-only", "Open main menu" },
|
||||||
|
}
|
||||||
|
div { class:"hidden w-full md:block md:w-auto", id:"navbar-default",
|
||||||
|
ul { class:"font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700",
|
||||||
|
li {
|
||||||
|
Link { to: Route::Home {}, class:"block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500", "Home" }
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
Link { to: Route::CV {}, class:"block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500", "CV" }
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
Link { to: Route::Publications {}, class:"block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500", "Publications/Projects" }
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
Link { to: Route::Impressum {}, class:"block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500", "About" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
mod footer;
|
||||||
|
mod header;
|
||||||
|
|
||||||
|
use crate::{Body, Route};
|
||||||
|
use footer::Footer;
|
||||||
|
use header::Header;
|
||||||
|
|
||||||
|
pub fn Layout() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "flex justify-center",
|
||||||
|
div {
|
||||||
|
class: "max-w-screen-xl flex-col" ,
|
||||||
|
Header {},
|
||||||
|
Body {
|
||||||
|
Outlet::<Route> {},
|
||||||
|
}
|
||||||
|
Footer {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use tracing::Level;
|
||||||
|
|
||||||
|
pub mod components;
|
||||||
|
mod cv;
|
||||||
|
mod home;
|
||||||
|
mod impressum;
|
||||||
|
mod layout;
|
||||||
|
mod publications;
|
||||||
|
|
||||||
|
use crate::cv::CV;
|
||||||
|
use crate::home::Home;
|
||||||
|
use crate::impressum::Impressum;
|
||||||
|
use crate::layout::Layout;
|
||||||
|
use crate::publications::Publications;
|
||||||
|
|
||||||
|
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Route {
|
||||||
|
#[layout(Layout)]
|
||||||
|
#[route("/")]
|
||||||
|
Home {},
|
||||||
|
#[route("/impressum")]
|
||||||
|
Impressum {},
|
||||||
|
#[route("/publications")]
|
||||||
|
Publications {},
|
||||||
|
#[route("/resume")]
|
||||||
|
CV {},
|
||||||
|
#[end_layout]
|
||||||
|
#[route("/:..route")]
|
||||||
|
PageNotFound { route: Vec<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus_logger::init(Level::INFO).expect("failed to init logger");
|
||||||
|
launch(App);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn App() -> Element {
|
||||||
|
rsx! {
|
||||||
|
meta {
|
||||||
|
name: "robots",
|
||||||
|
content: "noindex",
|
||||||
|
},
|
||||||
|
div {
|
||||||
|
class: "bg-white dark:bg-gray-900 min-h-screen",
|
||||||
|
Router::<Route> {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn PageNotFound(route: Vec<String>) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "h-screen flex items-center justify-center",
|
||||||
|
img {
|
||||||
|
class: "size-auto",
|
||||||
|
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/404Notfound/NotFound.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props, Clone)]
|
||||||
|
pub struct BodyProp {
|
||||||
|
children: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Body(prop: BodyProp) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "flex justify-center my-4",
|
||||||
|
div {
|
||||||
|
class: "max-w-screen-md min-w-full",
|
||||||
|
{prop.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::UnderConstruction;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Publications() -> Element {
|
||||||
|
rsx! {
|
||||||
|
UnderConstruction { },
|
||||||
|
div {
|
||||||
|
class: "flex gap-4 items-center",
|
||||||
|
Publication {
|
||||||
|
doi: "https://doi.org/10.48550/arXiv.2307.09639",
|
||||||
|
conference: "IEEE LCN 2023",
|
||||||
|
title: "RPM: Reverse Path Congestion Marking on P4 Programmable Switches",
|
||||||
|
authors: "N. Baganal-Krishna, T.-D. Tran, R. Kundel and A. Rizk",
|
||||||
|
description: "
|
||||||
|
In this paper, we present Reverse Path Congestion Marking
|
||||||
|
(RPM) to accelerate the reaction to network congestion
|
||||||
|
events without changing the end-host stack. RPM decouples
|
||||||
|
the conges- tion signal from the downstream path after the
|
||||||
|
bottleneck while maintaining the stability of the
|
||||||
|
congestion control loop. We show that RPM improves
|
||||||
|
throughput fairness for RTT- heterogeneous TCP flows as
|
||||||
|
well as the flow completion time, especially for small
|
||||||
|
Data Center TCP (DCTCP) flows. around P4 programmable ASIC
|
||||||
|
switches."
|
||||||
|
}
|
||||||
|
Publication {
|
||||||
|
doi: "https://git.tudattr.dev/AISE/seminar/src/branch/main/paper.pdf",
|
||||||
|
conference: "Seminar",
|
||||||
|
title: "Overview of IoT Fuzzing Techniques",
|
||||||
|
authors: "Tuan-Dat Tran",
|
||||||
|
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 IoT fuzzers."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 Publication(prop: PublicationProp) -> Element {
|
||||||
|
rsx! {
|
||||||
|
Link {
|
||||||
|
class:"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700",
|
||||||
|
to:"{prop.doi}",
|
||||||
|
new_tab: true,
|
||||||
|
h5 {
|
||||||
|
class:"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white",
|
||||||
|
"{prop.title}",
|
||||||
|
},
|
||||||
|
p {
|
||||||
|
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||||
|
"{prop.authors}",
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||||
|
"{prop.description}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "all",
|
||||||
|
content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
Loading…
Reference in New Issue