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