Compare commits
24 Commits
75c64a7227
...
0.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f76a7a8c4c | ||
|
|
0ff5419c23 | ||
|
|
b7a47ca903 | ||
|
|
b3eb962024 | ||
| d9f904231a | |||
|
|
c2ab088799 | ||
|
|
f336daf54b | ||
|
|
991cb57c88 | ||
|
|
1fbc2b7521 | ||
|
|
20e1df177c | ||
|
|
4a18e432b2 | ||
|
|
3fb233ebcc | ||
|
|
8a6ac977b3 | ||
|
|
68bf62ee21 | ||
|
|
c73335c170 | ||
|
|
71798be854 | ||
|
|
0dd9cec4bd | ||
|
|
6ca89b3bd5 | ||
|
|
4b56557d15 | ||
|
|
a6c382fc8b | ||
|
|
21f38e7e16 | ||
|
|
116a36c4a9 | ||
|
|
212df970b8 | ||
|
|
6000f3318d |
18
.gitea/workflows/docker.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
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
|
||||
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/dist/
|
||||
/docs/
|
||||
/static/
|
||||
/.dioxus/
|
||||
/node_modules/
|
||||
|
||||
813
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "athome"
|
||||
version = "0.1.0"
|
||||
version = "0.2.2"
|
||||
authors = ["Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -8,7 +8,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
||||
dioxus = { version = "0.5", features = ["fullstack", "router"] }
|
||||
|
||||
# Debug
|
||||
@@ -16,6 +15,8 @@ tracing = "0.1.40"
|
||||
dioxus-logger = "0.5.0"
|
||||
manganis = "0.2.2"
|
||||
dioxus-free-icons = { version = "0.8", features = ["font-awesome-brands"] }
|
||||
dioxus-sdk = { version = "0.5.0", features = ["i18n"] }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -8,7 +8,7 @@ name = "athome"
|
||||
default_platform = "web"
|
||||
|
||||
# `build` & `serve` dist path
|
||||
out_dir = "dist"
|
||||
out_dir = "docs"
|
||||
|
||||
# resource (assets) file folder
|
||||
asset_dir = "assets"
|
||||
@@ -16,7 +16,7 @@ asset_dir = "assets"
|
||||
[web.app]
|
||||
|
||||
# HTML title tag content
|
||||
title = "athome"
|
||||
title = "Tuan-Dat Tran"
|
||||
|
||||
[web.watcher]
|
||||
|
||||
|
||||
15
Dockerfile
@@ -1,11 +1,10 @@
|
||||
FROM rust:1.77.2 as builder
|
||||
WORKDIR /athome/
|
||||
FROM rust:1.79.0 AS dioxus
|
||||
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
|
||||
FROM dioxus AS builder
|
||||
WORKDIR /athome/
|
||||
RUN apt-get update && apt-get install nodejs npm libssl-dev musl-tools -y && rm -rf /var/lib/apt/lists/*
|
||||
RUN npm install -D tailwindcss
|
||||
COPY ./src/ ./src/
|
||||
COPY ./assets/ ./assets/
|
||||
COPY ./Cargo.toml ./Cargo.toml
|
||||
@@ -14,4 +13,8 @@ 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" ]
|
||||
|
||||
FROM dioxus AS runner
|
||||
WORKDIR /app/
|
||||
COPY --from=builder /athome/docs/ ./docs/
|
||||
CMD [ "./docs/athome" ]
|
||||
|
||||
@@ -4,4 +4,4 @@ My personal website.
|
||||
|
||||
## Screenshot
|
||||
|
||||
[[./resources/screenshot.png]]
|
||||
[[./resources/screenshot.webp]]
|
||||
|
||||
BIN
assets/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 742 B |
BIN
assets/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 15 KiB |
1
assets/pictures/Gitea_Logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" style="enable-background:new 0 0 640 640;" xml:space="preserve" viewBox="5.67 143.05 628.65 387.55"> <g> <path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"></path> <g> <g> <path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"></path> <path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"></path> </g> </g> </g> </svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
5
assets/pictures/LI-Bug.svg.original.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg id="Group_1282" data-name="Group 1282" xmlns="http://www.w3.org/2000/svg" width="76.624" height="65.326" viewBox="0 0 76.624 65.326">
|
||||
<path id="Path_2525" data-name="Path 2525" d="M1165,274.515a1.2,1.2,0,0,0,1.21-1.269c0-.9-.543-1.33-1.657-1.33h-1.8v4.712h.677v-2.054h.832l.019.025,1.291,2.029h.724l-1.389-2.1Zm-.783-.472h-.785V272.45h.995c.514,0,1.1.084,1.1.757,0,.774-.593.836-1.314.836" transform="translate(-1092.136 -213.406)" fill="#0a66c2"/>
|
||||
<path id="Path_2520" data-name="Path 2520" d="M958.98,112.559h-9.6V97.525c0-3.585-.064-8.2-4.993-8.2-5,0-5.765,3.906-5.765,7.939v15.294h-9.6V81.642h9.216v4.225h.129a10.1,10.1,0,0,1,9.093-4.994c9.73,0,11.524,6.4,11.524,14.726ZM918.19,77.416a5.571,5.571,0,1,1,5.57-5.572,5.571,5.571,0,0,1-5.57,5.572m4.8,35.143h-9.61V81.642h9.61Zm40.776-55.2h-55.21a4.728,4.728,0,0,0-4.781,4.67v55.439a4.731,4.731,0,0,0,4.781,4.675h55.21a4.741,4.741,0,0,0,4.8-4.675V62.025a4.738,4.738,0,0,0-4.8-4.67" transform="translate(-903.776 -57.355)" fill="#0a66c2"/>
|
||||
<path id="Path_2526" data-name="Path 2526" d="M1156.525,264.22a4.418,4.418,0,1,0,.085,0h-.085m0,8.33a3.874,3.874,0,1,1,3.809-3.938c0,.022,0,.043,0,.065a3.791,3.791,0,0,1-3.708,3.871h-.1" transform="translate(-1084.362 -207.809)" fill="#0a66c2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 34 KiB |
BIN
assets/pictures/headshot.webp
Normal file
|
After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 75 KiB |
41
assets/robots.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
User-agent: CCBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: img2dataset
|
||||
Disallow: /
|
||||
|
||||
User-agent: Google-Extended
|
||||
Disallow: /
|
||||
|
||||
User-agent: anthropic-ai
|
||||
Disallow: /
|
||||
|
||||
User-agent: Claude-Web
|
||||
Disallow: /
|
||||
|
||||
User-agent: Omgilibot
|
||||
Disallow: /
|
||||
|
||||
User-agent: Omgili
|
||||
Disallow: /
|
||||
|
||||
User-agent: FacebookBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: Bytespider
|
||||
Disallow: /
|
||||
|
||||
User-agent: magpie-crawler
|
||||
Disallow: /
|
||||
|
||||
User-Agent: GPTBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: *
|
||||
Disallow: /impressum
|
||||
Allow: /
|
||||
Allow: /resume
|
||||
Allow: /publications
|
||||
Allow: /consulting
|
||||
Sitemap: https://www.tudattr.dev/sitemap.xml
|
||||
|
||||
1
assets/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
27
assets/sitemap.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://www.tudattr.dev/</loc>
|
||||
<lastmod>2024-07-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.tudattr.dev/resume</loc>
|
||||
<lastmod>2024-07-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.tudattr.dev/publications</loc>
|
||||
<lastmod>2024-07-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.tudattr.dev/consulting</loc>
|
||||
<lastmod>2024-07-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -554,20 +554,42 @@ video {
|
||||
--tw-contain-style: ;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
@media (min-width: 640px) {
|
||||
.container {
|
||||
max-width: 640px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 768px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 1024px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1536px) {
|
||||
.container {
|
||||
max-width: 1536px;
|
||||
}
|
||||
}
|
||||
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
@@ -586,38 +608,6 @@ video {
|
||||
inset-inline-start: -0.375rem;
|
||||
}
|
||||
|
||||
.start-0 {
|
||||
inset-inline-start: 0px;
|
||||
}
|
||||
|
||||
.top-0 {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.bottom-0 {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.top-3 {
|
||||
top: 0.75rem;
|
||||
}
|
||||
|
||||
.top-4 {
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.top-auto {
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.z-50 {
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.m-16 {
|
||||
margin: 4rem;
|
||||
}
|
||||
|
||||
.mx-16 {
|
||||
margin-left: 4rem;
|
||||
margin-right: 4rem;
|
||||
@@ -658,10 +648,6 @@ video {
|
||||
margin-inline-end: 0.5rem;
|
||||
}
|
||||
|
||||
.me-3 {
|
||||
margin-inline-end: 0.75rem;
|
||||
}
|
||||
|
||||
.ms-4 {
|
||||
margin-inline-start: 1rem;
|
||||
}
|
||||
@@ -682,39 +668,14 @@ video {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.size-auto {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.h-10 {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.h-16 {
|
||||
height: 4rem;
|
||||
}
|
||||
@@ -727,14 +688,6 @@ video {
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.h-6 {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-7 {
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
@@ -747,18 +700,14 @@ video {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
.h-32 {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
@@ -771,36 +720,15 @@ video {
|
||||
width: 0.75rem;
|
||||
}
|
||||
|
||||
.w-6 {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-7 {
|
||||
width: 1.75rem;
|
||||
}
|
||||
|
||||
.w-fit {
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-8 {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.min-w-fit {
|
||||
min-width: -moz-fit-content;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.min-w-full {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.max-w-40 {
|
||||
max-width: 10rem;
|
||||
}
|
||||
@@ -809,12 +737,8 @@ video {
|
||||
max-width: 2rem;
|
||||
}
|
||||
|
||||
.max-w-screen-md {
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
.max-w-screen-xl {
|
||||
max-width: 1280px;
|
||||
.max-w-md {
|
||||
max-width: 28rem;
|
||||
}
|
||||
|
||||
.max-w-sm {
|
||||
@@ -825,12 +749,8 @@ video {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.max-w-xs {
|
||||
max-width: 20rem;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
@@ -849,6 +769,10 @@ video {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -861,28 +785,28 @@ video {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
|
||||
.space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(0.75rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.self-center {
|
||||
align-self: center;
|
||||
.space-y-2 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(1rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.overflow-x-auto {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
@@ -895,6 +819,20 @@ video {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.rounded-e-lg {
|
||||
border-start-end-radius: 0.5rem;
|
||||
border-end-end-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.rounded-s-lg {
|
||||
border-start-start-radius: 0.5rem;
|
||||
border-end-start-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
@@ -911,15 +849,6 @@ video {
|
||||
border-inline-start-width: 1px;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.border-gray-100 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(243 244 246 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-gray-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
@@ -935,11 +864,6 @@ video {
|
||||
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-700 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-gray-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||
@@ -1033,18 +957,6 @@ video {
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.p-1\.5 {
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.p-3 {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
@@ -1053,10 +965,6 @@ video {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-5 {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
@@ -1071,9 +979,9 @@ video {
|
||||
padding-right: 0.625rem;
|
||||
}
|
||||
|
||||
.px-3 {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
@@ -1169,6 +1077,10 @@ video {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.leading-none {
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -1181,11 +1093,6 @@ video {
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.text-blue-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(37 99 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 64 175 / var(--tw-text-opacity));
|
||||
@@ -1260,23 +1167,6 @@ video {
|
||||
color: rgb(133 77 14 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(59 130 246 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.decoration-solid {
|
||||
text-decoration-style: solid;
|
||||
}
|
||||
|
||||
.underline-offset-2 {
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
@@ -1289,6 +1179,12 @@ video {
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-sm {
|
||||
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.grayscale {
|
||||
--tw-grayscale: grayscale(100%);
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
@@ -1313,18 +1209,13 @@ video {
|
||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-200:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gradient-to-br:hover {
|
||||
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
|
||||
}
|
||||
|
||||
.hover\:text-gray-900:hover {
|
||||
.hover\:text-blue-700:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
||||
color: rgb(29 78 216 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-white:hover {
|
||||
@@ -1336,15 +1227,20 @@ video {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.hover\:no-underline:hover {
|
||||
text-decoration-line: none;
|
||||
}
|
||||
|
||||
.hover\:grayscale-0:hover {
|
||||
--tw-grayscale: grayscale(0);
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.focus\:z-10:focus {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.focus\:text-blue-700:focus {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(29 78 216 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.focus\:outline-none:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
@@ -1362,16 +1258,16 @@ video {
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.focus\:ring-blue-700:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(29 78 216 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.focus\:ring-cyan-300:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(103 232 249 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.focus\:ring-gray-200:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.group:hover .group-hover\:from-green-400 {
|
||||
--tw-gradient-from: #4ade80 var(--tw-gradient-from-position);
|
||||
--tw-gradient-to: rgb(74 222 128 / 0) var(--tw-gradient-to-position);
|
||||
@@ -1387,63 +1283,26 @@ video {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.sm\:flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(0px * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.sm\:text-center {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:mt-0 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.md\:block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.md\:flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.md\:hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md\:w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.md\:flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.md\:items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md\:justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.md\:space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.md\:border-0 {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.md\:bg-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.md\:bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.md\:p-0 {
|
||||
padding: 0px;
|
||||
}
|
||||
@@ -1466,10 +1325,6 @@ video {
|
||||
}
|
||||
}
|
||||
|
||||
.rtl\:space-x-reverse:where([dir="rtl"], [dir="rtl"] *) > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 1;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark\:border-gray-600 {
|
||||
--tw-border-opacity: 1;
|
||||
@@ -1491,11 +1346,6 @@ video {
|
||||
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-gray-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-gray-700 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
||||
@@ -1541,21 +1391,11 @@ video {
|
||||
background-color: rgb(113 63 18 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-blue-800 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(30 64 175 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-blue-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(147 197 253 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(59 130 246 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-cyan-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(34 211 238 / var(--tw-text-opacity));
|
||||
@@ -1611,16 +1451,6 @@ video {
|
||||
color: rgb(253 224 71 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-blue-200 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(191 219 254 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-gray-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-gray-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
||||
@@ -1631,24 +1461,24 @@ video {
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:focus\:text-white:focus {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:focus\:ring-blue-500:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.dark\:focus\:ring-cyan-800:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(21 94 117 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.dark\:focus\:ring-gray-600:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.md\:dark\:bg-gray-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.md\:dark\:text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(59 130 246 / var(--tw-text-opacity));
|
||||
|
||||
|
Before Width: | Height: | Size: 172 KiB |
BIN
resources/screenshot.webp
Normal file
|
After Width: | Height: | Size: 46 KiB |
20
scripts/docker-push.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
TAG=$(git branch | grep "*" | awk '{ print $2 }')
|
||||
LOCAL_IMAGE="athome"
|
||||
REGISTRY="mos4"
|
||||
REMOTE_IMAGE="athome"
|
||||
DOCKERFILE="./Dockerfile"
|
||||
|
||||
echo "DOCKERFILE: $DOCKERFILE"
|
||||
echo "TAG: $TAG"
|
||||
echo "LOCAL_IMAGE: $LOCAL_IMAGE"
|
||||
echo "REGISTRY: $REGISTRY"
|
||||
echo "REMOTE_IMAGE: $REMOTE_IMAGE"
|
||||
|
||||
#docker buildx build --platform linux/amd64,linux/arm64 -f ${DOCKERFILE} -t \
|
||||
# ${REGISTRY}/${REMOTE_IMAGE}:${TAG} . --push
|
||||
|
||||
docker buildx build --platform linux/amd64 -f ${DOCKERFILE} \
|
||||
-t ${REGISTRY}/${REMOTE_IMAGE}:${TAG} \
|
||||
-t ${REGISTRY}/${REMOTE_IMAGE}:latest . --push
|
||||
@@ -1,4 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
|
||||
#[derive(PartialEq, Props, Clone)]
|
||||
pub struct PProps {
|
||||
@@ -20,6 +21,19 @@ pub fn P(props: PProps) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Title(props: PProps) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "{props.class}",
|
||||
h1 {
|
||||
class: "mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white",
|
||||
{props.children}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn H1(props: PProps) -> Element {
|
||||
rsx! {
|
||||
@@ -66,34 +80,65 @@ pub fn H5(props: PProps) -> Element {
|
||||
pub struct CardProp {
|
||||
#[props(default = "".to_string())]
|
||||
class: String,
|
||||
name: String,
|
||||
gender: String,
|
||||
picture: String,
|
||||
children: Element,
|
||||
}
|
||||
|
||||
pub fn Card(prop: CardProp) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex flex-col py-10 items-center min-w-fit",
|
||||
class: "flex flex-col py-10 items-center min-w-fit 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",
|
||||
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 }
|
||||
class: "pb-4",
|
||||
div {
|
||||
class: "justify-between",
|
||||
Picture {src: "{prop.picture}"},
|
||||
}
|
||||
}
|
||||
Title { "{prop.name}", span { class: "text-grey-600 dark:text-grey-500 text-lg", " {prop.gender}" } },
|
||||
{ prop.children }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn UnderConstruction() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
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",
|
||||
class:"rounded justify-between w-full p-4 border-b border-gray-200 bg-gray-50 dark:bg-gray-700 dark:border-gray-600 my-8",
|
||||
div {
|
||||
class:"flex items-center mx-auto",
|
||||
class:"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",
|
||||
}
|
||||
class:"items-center text-sm font-normal text-gray-500 dark:text-gray-400",
|
||||
span { { translate!(i18, "components.under_construction") } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn HR() -> Element {
|
||||
rsx! {
|
||||
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/consulting.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::components::UnderConstruction;
|
||||
|
||||
#[component]
|
||||
pub fn Consulting() -> Element {
|
||||
rsx! {
|
||||
UnderConstruction { },
|
||||
}
|
||||
}
|
||||
152
src/cv.rs
@@ -1,98 +1,75 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
|
||||
use crate::components::H4;
|
||||
use crate::components::{H4, HR};
|
||||
|
||||
#[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",
|
||||
Introduction {},
|
||||
Socials {}
|
||||
},
|
||||
|
||||
HR {}
|
||||
div {
|
||||
class: "flex justify-between",
|
||||
WorkExperience {},
|
||||
Miscellaneous {},
|
||||
},
|
||||
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"},
|
||||
Socials {}
|
||||
HR {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Introduction() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex",
|
||||
|
||||
img {
|
||||
class: "rounded-full w-16 h-16 mx-16",
|
||||
src: "/pictures/portrait.webp"
|
||||
src: "/pictures/headshot.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."
|
||||
}
|
||||
}
|
||||
P { { translate!(i18, "cv.introduction_0") } },
|
||||
P { { translate!(i18, "cv.introduction_1") } }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn WorkExperience() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "ms-8 max-w-3/4",
|
||||
H4 { "Work Experience" },
|
||||
H4 { { translate!(i18, "cv.workexperience.title") } },
|
||||
ol {
|
||||
class:"relative border-s border-gray-200 dark:border-gray-700",
|
||||
CVEntry {time: "2021 - now", title: "Research Assistant @ UDE",
|
||||
CVEntry {time: translate!(i18, "cv.workexperience.ra_ude.time"), title: translate!(i18, "cv.workexperience.ra_ude.title"),
|
||||
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."
|
||||
description: translate!(i18, "cv.workexperience.ra_ude.description")
|
||||
},
|
||||
CVEntry {time: "2021 - 2022", title: "Mentoring @ UDE",
|
||||
CVEntry {time: translate!(i18, "cv.workexperience.mentoring_ude.time"), title: translate!(i18, "cv.workexperience.mentoring_ude.title"),
|
||||
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."
|
||||
description: translate!(i18, "cv.workexperience.mentoring_ude.description")
|
||||
},
|
||||
CVEntry {time: "2018 - 2020", title: "Software Engineer @ gefeba Engineering GmbH",
|
||||
CVEntry {time: translate!(i18, "cv.workexperience.se2_gefeba.time"), title: translate!(i18, "cv.workexperience.se2_gefeba.title"),
|
||||
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."
|
||||
description: translate!(i18, "cv.workexperience.se2_gefeba.description")
|
||||
},
|
||||
CVEntry {time: "2016 - 2019", title: "Student Council Member @ UDE",
|
||||
CVEntry {time: translate!(i18, "cv.workexperience.student_fse.time"), title: translate!(i18, "cv.workexperience.student_fse.title"),
|
||||
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."
|
||||
description: translate!(i18, "cv.workexperience.student_fse.description")
|
||||
},
|
||||
CVEntry {time: "2013 - 2015", title: "Software Engineer @ gefeba Engineering GmbH",
|
||||
CVEntry {time: translate!(i18, "cv.workexperience.se1_gefeba.time"), title: translate!(i18, "cv.workexperience.se1_gefeba.title"),
|
||||
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."
|
||||
description: translate!(i18, "cv.workexperience.se1_gefeba.description")
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -112,59 +89,67 @@ fn Miscellaneous() -> Element {
|
||||
}
|
||||
|
||||
fn Education() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
H4 {"Education"},
|
||||
H4 { { translate!(i18, "cv.education.title") } },
|
||||
Entry {
|
||||
title: "BSc Systems Engineering",
|
||||
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", "2015 - now"},
|
||||
title: translate!(i18, "cv.education.bachelor.title"),
|
||||
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", { translate!(i18, "cv.education.bachelor.time") } },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Skills() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
H4 {"Skills"},
|
||||
H4 { { translate!(i18, "cv.skills.title") }},
|
||||
Entry {
|
||||
title: "DevOps",
|
||||
P { "Ansible" },
|
||||
P { "Kubernetes" },
|
||||
P { "GitOps" },
|
||||
title: translate!(i18, "cv.skills.devops.title"),
|
||||
P { { translate!(i18, "cv.skills.devops.ansible") } },
|
||||
P { { translate!(i18, "cv.skills.devops.kubernetes") } },
|
||||
P { { translate!(i18, "cv.skills.devops.gitops") } },
|
||||
}Entry {
|
||||
title: "Software Development",
|
||||
P { "Rust" },
|
||||
P { "Python" },
|
||||
P { "Java" },
|
||||
title: translate!(i18, "cv.skills.software_engineering.title"),
|
||||
P { { translate!(i18, "cv.skills.software_engineering.rust") } },
|
||||
P { { translate!(i18, "cv.skills.software_engineering.python") } },
|
||||
P { { translate!(i18, "cv.skills.software_engineering.csharp") } },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Languages() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
H4 {"Languages"},
|
||||
H4 { { translate!(i18, "cv.languages.title") } },
|
||||
Entry {
|
||||
P { "German (Native)" },
|
||||
P { "English (C2)" },
|
||||
P { "Vietnamese (B1)" },
|
||||
P { "Japanese (A1)" },
|
||||
P { {translate!(i18, "cv.languages.german") } },
|
||||
P { {translate!(i18, "cv.languages.english") } },
|
||||
P { {translate!(i18, "cv.languages.vietnamese") } },
|
||||
P { {translate!(i18, "cv.languages.japanese") } },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Interests() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
H4 {"Interests"}
|
||||
H4 { { translate!(i18, "cv.interests.title") } },
|
||||
Entry {
|
||||
P { "Coffee" },
|
||||
P { "Mechanical Keyboards" },
|
||||
P { "Tech/IT" },
|
||||
P { "Music" },
|
||||
P { { translate!(i18, "cv.interests.coffee") } },
|
||||
P { { translate!(i18, "cv.interests.tech_it") } },
|
||||
P { { translate!(i18, "cv.interests.guitar") } },
|
||||
P { { translate!(i18, "cv.interests.mechanical_keyboards") } },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,8 +194,11 @@ fn CVEntry(props: CVEntryProps) -> Element {
|
||||
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}"}
|
||||
ul {
|
||||
class: "flex",
|
||||
for (index, value) in props.technologies.iter().enumerate() {
|
||||
li { key: "{index}", RandomBadge { text: "{value}"} }
|
||||
}
|
||||
}
|
||||
p { class:"text-base font-normal text-gray-500 dark:text-gray-400", "{props.description}"},
|
||||
{props.children}
|
||||
@@ -246,9 +234,17 @@ fn random_badge_color(seed: usize) -> String {
|
||||
}
|
||||
|
||||
fn Socials() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
"todo!()"
|
||||
class: "ms-8 max-w-3/4",
|
||||
H4 { { translate!(i18, "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:"/pictures/LI-Bug.svg.original.svg", alt:"LinkedIn Logo" } }},
|
||||
P { Link { to:"https://git.tudattr.dev/tudattr", class:"hover:underline", new_tab: true, img { class: "h-8", src:"/pictures/Gitea_Logo.svg", alt:"Gitea Logo" } }},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
src/home.rs
@@ -1,61 +1,40 @@
|
||||
use crate::components::{Card, H5, P};
|
||||
use crate::components::{Card, P};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
|
||||
#[component]
|
||||
pub fn Home() -> Element {
|
||||
let i18 = use_i18();
|
||||
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: "container mx-auto p-4 flex items-center justify-center max-w-md w-full",
|
||||
Card {
|
||||
name: translate!(i18, "home.card.name"),
|
||||
gender: translate!(i18, "home.card.gender"),
|
||||
picture: "/pictures/headshot.webp",
|
||||
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"},
|
||||
"?"
|
||||
div {
|
||||
class: "mb-2",
|
||||
P { { translate!(i18, "home.card.l1") } },
|
||||
P { { translate!(i18, "home.card.l2") } },
|
||||
P { { translate!(i18, "home.card.l3") } },
|
||||
P { { translate!(i18, "home.card.l4") }
|
||||
Link {
|
||||
to: "https://git.tudattr.dev/explore/repos",
|
||||
new_tab: true,
|
||||
class: "items-center font-medium hover:underline",
|
||||
"gitea"},
|
||||
{ translate!(i18, "home.card.l4_1") }
|
||||
},
|
||||
},
|
||||
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: "",
|
||||
},
|
||||
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",
|
||||
{ translate!(i18, "home.card.contact_button") }
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
use dioxus::prelude::{server_fn::error::ServerFnError, *};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
use tracing::info;
|
||||
|
||||
use crate::components::{H1, P};
|
||||
use crate::components::{H1, HR, 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);
|
||||
}
|
||||
};
|
||||
let i18 = use_i18();
|
||||
|
||||
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" },
|
||||
div {
|
||||
class: "flex flex-col items-center",
|
||||
button {
|
||||
onclick: move |_| {
|
||||
info!("Hide impressum.");
|
||||
},
|
||||
H1 { { translate!(i18, "impressum.on") } },
|
||||
},
|
||||
P { "Tuan-Dat Tran" },
|
||||
P { "c/o AutorenServices.de" },
|
||||
P { "Birkenallee 24" },
|
||||
P { "36037 Fulda" },
|
||||
},
|
||||
HR {}
|
||||
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();
|
||||
onclick: move |_| async move {
|
||||
show_impressum.set(true);
|
||||
},
|
||||
|
||||
H1 { "Show Impressum" },
|
||||
H1 { { translate!(i18, "impressum.off") } },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[server(GetImpressum)]
|
||||
pub async fn get_impressum() -> Result<(), ServerFnError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
2
src/languages.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub static EN_GB: &str = include_str!("./languages/en-GB.json");
|
||||
pub static DE_DE: &str = include_str!("./languages/de-DE.json");
|
||||
155
src/languages/de-DE.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"id": "de-DE",
|
||||
"texts": {
|
||||
"headers": {
|
||||
"home": "Home",
|
||||
"cv": "Lebenslauf",
|
||||
"publications_projects": "Artikel/Projekte",
|
||||
"consulting": "Consulting",
|
||||
"about": "Impressum",
|
||||
"language_buttons": {
|
||||
"english": "🇬🇧 Englisch",
|
||||
"german": "🇩🇪 Deutsch"
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"card": {
|
||||
"name": "Tuan-Dat Tran",
|
||||
"gender": "",
|
||||
"l1": "Hallihallo! 👋🏻👋🏼👋🏽👋🏾👋🏿",
|
||||
"l2": "Willkommen auf meiner kleinen Webseite im World Wide Web.",
|
||||
"l3": "Mein Name ist Tuan und ich bin Linux-Bastler, IT-Security Nerd und Automatisierer aus Leidenschaft.",
|
||||
"l4": "Während du hier bist, schau dir doch meine Projekte auf ",
|
||||
"l4_1": " an.",
|
||||
"contact_button": "Get in touch."
|
||||
}
|
||||
},
|
||||
"cv": {
|
||||
"introduction_0": "Während meines Bachelorstudiums habe ich viele Erfahrungen in der Industrie und im Studium gesammelt. Meine beruflichen und persönlichen Interessen sind DevOps/IaC, Systems/Software Security und Computer Networking. All diese Interessen vertiefe ich in persönlichen Projekten wie meinem Homelab und CTF-Challenges.",
|
||||
"workexperience": {
|
||||
"title": "Berufserfahrung",
|
||||
"se1_gefeba": {
|
||||
"title": "Software Entwickler @ gefeba Engineering GmbH",
|
||||
"time": "2013 - 2015",
|
||||
"description": "Nach einem Schulpraktikum wurde mir eine Stelle als Software Entwickler angeboten. Ich arbeitete hauptsächlich an internen ERP-Projekten, entwickelte ein Tool, das bei der Verwaltung des projektbezogenen E-Mail-Verkehrs half, und arbeitete an dem internen Stammdatenmanagement-Tool."
|
||||
},
|
||||
"student_fse": {
|
||||
"title": "Fachschaftsratsmitglied @ UDE",
|
||||
"time": "2016 - 2019",
|
||||
"description": "Als Fachschaftsratsmitglied wirkte ich in Fakultätsausschüssen mit und organisierte soziale Veranstaltungen. Meine Hauptaufgaben als Mitglied waren die Verwaltung der IT-Infrastruktur und die Unterstützung der Studierenden, sei es organisatorisch oder fachspezifisch."
|
||||
},
|
||||
"se2_gefeba": {
|
||||
"title": "Software Entwickler @ gefeba Engineering GmbH",
|
||||
"time": "2018 - 2020",
|
||||
"description": "Als Software Entwickler bei gefeba Engineering arbeitete ich an dem Hauptprodukt des Unternehmens, einem Frame-basierten Datenaustauschsystem zur Überwachung von Industriemaschinen mit C# und Entity Framework. Ein weiteres Projekt, an dem ich gearbeitet habe, war eine Echtzeit-Anwendung zur Visualisierung von Protokollen für dieselben Maschinen."
|
||||
},
|
||||
"mentoring_ude": {
|
||||
"title": "Mentoring @ UDE",
|
||||
"time": "2021 - 2022",
|
||||
"description": "Als Mentor für Studienanfänger im Studiengang Informatik der Universität Duisburg-Essen habe ich zu Beginn jedes Semesters Gruppen von ~20 Studienanfängern in ihr neues akademisches Umfeld eingeführt. Ich bot zusätzliche organisatorische und technische Unterstützung für das erste Jahr an der Universität an."
|
||||
},
|
||||
"ra_ude": {
|
||||
"title": "Studentische Hilfskraft @ UDE",
|
||||
"time": "2021 - jetzt",
|
||||
"description": "Während meiner Tätigkeit als wissenschaftlicher Mitarbeiter in der Network Communication System Research Group an der Universität Duisburg-Essen habe ich an der Forschung rund um Software Defined Networking, 5G, Staukontrollalgorithmen und föderiertes maschinelles Lernen mitgearbeitet. Ich habe die On-Premise- und Cloud-Infrastruktur, das Inventarsystem und die Online-Präsenz der Forschungsgruppe aufgebaut und verwaltet."
|
||||
}
|
||||
},
|
||||
"socials": {
|
||||
"title": "Profile"
|
||||
},
|
||||
"education": {
|
||||
"title": "Bildungsweg",
|
||||
"bachelor": {
|
||||
"title": "BSc Angewandte Informatik - Systems Engineering",
|
||||
"time": "2015 - jetzt",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"skills": {
|
||||
"title": "Fähigkeiten",
|
||||
"devops": {
|
||||
"title": "DevOps",
|
||||
"ansible": "Ansible",
|
||||
"kubernetes": "Kubernetes",
|
||||
"gitops": "GitOps"
|
||||
},
|
||||
"software_engineering": {
|
||||
"title": "Programmiersprachen",
|
||||
"rust": "Rust",
|
||||
"python": "Python",
|
||||
"csharp": "C#"
|
||||
}
|
||||
},
|
||||
"languages": {
|
||||
"title": "Sprachkenntnisse",
|
||||
"german": "Deutsch (Muttersprache)",
|
||||
"english": "Englisch (C2)",
|
||||
"vietnamese": "Vietnamesisch (B1)",
|
||||
"japanese": "Japanisch (A1)"
|
||||
},
|
||||
"interests": {
|
||||
"title": "Interessen",
|
||||
"coffee": "Kaffee",
|
||||
"tech_it": "Tech/IT",
|
||||
"guitar": "Gitarre",
|
||||
"mechanical_keyboards": "Mechanische Tastaturen"
|
||||
}
|
||||
},
|
||||
"publications_projects": {
|
||||
"publications": {
|
||||
"title": "Veröffentlichungen",
|
||||
"rpm": {
|
||||
"title": "RPM: Reverse Path Congestion Marking on P4 Programmable Switches",
|
||||
"authors": "N. Baganal-Krishna, T.-D. Tran, R. Kundel and A. Rizk",
|
||||
"conference": "IEEE LCN 2023",
|
||||
"url": "https://doi.org/10.48550/arXiv.2307.09639",
|
||||
"description": "In diesem Artikel stellen wir Reverse Path Congestion Marking (RPM) vor, um die Reaktion auf Netzwerküberlastungen zu beschleunigen, ohne den End-Host-Stack zu verändern. RPM entkoppelt das Stausignal vom nachgelagerten Pfad nach dem Engpass, während die Stabilität der Staukontrollschleife erhalten bleibt. Wir zeigen, dass RPM die Durchsatzfairness für RTT bei heterogenen TCP-Flüssen sowie die Flussabwicklungszeit verbessert, insbesondere für kleine Data Center TCP (DCTCP)-Flows um P4 programmierbare ASIC-Switches."
|
||||
},
|
||||
"iot_fuzzers": {
|
||||
"title": "Overview of IoT Fuzzing Techniques",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"conference": "Seminar",
|
||||
"url": "https://git.tudattr.dev/AISE/seminar/src/branch/main/paper.pdf",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"title": "Projekte",
|
||||
"bachelorproject": {
|
||||
"title": "Unbenannter Ethereum Smart Contract Fuzzer",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Bachelorprojekt",
|
||||
"url": "https://git.ude-syssec.de/uni-due-syssec/students/2022_tuan-dat_tran_libAFLEVMFuzzer/ethfuzz/",
|
||||
"description": "In diesem aktuell laufendem Projekt entwickle ich einen Ethereum Smart Contract Fuzzer. Mehr Infos folgen..."
|
||||
},
|
||||
"dotfiles": {
|
||||
"title": ".dotfiles",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Personal",
|
||||
"url": "https://git.tudattr.dev/tudattr/dotfiles",
|
||||
"description": "dotfiles ist ein umgangssprachlicher Begriff, der normalerweise für Konfigurationsdateien in Linux-basierten Systemen verwendet wird. Meine Dotfiles enthalten Konfigurationen für Tools, die ich häufig verwende, sowie eine Dokumentation zur Einrichtung meines täglich genutzten Notebooks. Sie bieten eine Grundlage für jedes persönliche Linux-System, das ich einrichte, und ermöglichen Wiederholbarkeit, was den Prozess der Einrichtung eines ArchLinux-basierten Systems vereinfacht."
|
||||
},
|
||||
"homelab": {
|
||||
"title": "Homelab",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Personal",
|
||||
"url": "https://git.tudattr.dev/tudattr/ansible",
|
||||
"description": "Ansible ist eine Automatisierungs-Engine, die eine automatische Bereitstellung, Konfigurationsverwaltung und Anwendungsbereitstellung ermöglicht. Ich verwende Ansible, um mein Homelab einzurichten, das mir als Plattform zum Ausprobieren und Lernen neuer Technologien dient."
|
||||
}
|
||||
}
|
||||
},
|
||||
"impressum": {
|
||||
"off": "Impressum anzeigen",
|
||||
"on": "Impressum"
|
||||
},
|
||||
"components": {
|
||||
"under_construction": "Diese Seite befindet sich gerade im Aufbau"
|
||||
},
|
||||
"footer": {
|
||||
"year": "© 2024 ",
|
||||
"name": "Tuan-Dat Tran",
|
||||
"rights": ". All Rights Reserved.",
|
||||
"contact": "Kontakt"
|
||||
}
|
||||
}
|
||||
}
|
||||
155
src/languages/en-GB.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"id": "en-GB",
|
||||
"texts": {
|
||||
"headers": {
|
||||
"home": "Home",
|
||||
"cv": "CV",
|
||||
"publications_projects": "Publications/Projects",
|
||||
"consulting": "Consulting",
|
||||
"about": "About",
|
||||
"language_buttons": {
|
||||
"english": "🇬🇧 English",
|
||||
"german": "🇩🇪 German"
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"card": {
|
||||
"name": "Tuan-Dat Tran",
|
||||
"gender": "(He/Him)",
|
||||
"l1": "Hey there! 👋🏻👋🏼👋🏽👋🏾👋🏿",
|
||||
"l2": "Welcome to my little place on the internet.",
|
||||
"l3": "My name is Tuan and I'm passionate about Linux, system security, automation, network performance tweaking and all things tech.",
|
||||
"l4": "While you're here, why don't you check out my projects over on ",
|
||||
"l4_1": "?",
|
||||
"contact_button": "Get in touch."
|
||||
}
|
||||
},
|
||||
"cv": {
|
||||
"introduction_0": "While studying for my bachelors degree I accumulated 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.",
|
||||
"workexperience": {
|
||||
"title": "Work Experience",
|
||||
"se1_gefeba": {
|
||||
"title": "Software Engineer @ gefeba Engineering GmbH",
|
||||
"time": "2013 - 2015",
|
||||
"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."
|
||||
},
|
||||
"student_fse": {
|
||||
"title": "Student Council Member @ UDE",
|
||||
"time": "2016 - 2019",
|
||||
"description": "As a student council member I participated in faculty committees and organized social events. My main responsibilities as a member were the management of the IT infrastructure and supporting students, be it organizationally or subject-specific."
|
||||
},
|
||||
"se2_gefeba": {
|
||||
"title": "Software Engineer @ gefeba Engineering GmbH",
|
||||
"time": "2018 - 2020",
|
||||
"description": "As a software engineer at gefeba Engineering I worked on the companies main software product, which was a frame-based data exchange system to monitor industry machinery using C# and Entity Framework. Another project I worked on was a real time log visualization application for the same machinery."
|
||||
},
|
||||
"mentoring_ude": {
|
||||
"title": "Mentoring @ UDE",
|
||||
"time": "2021 - 2022",
|
||||
"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 their first year in university."
|
||||
},
|
||||
"ra_ude": {
|
||||
"title": "Research Assistant @ UDE",
|
||||
"time": "2021 - now",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"socials": {
|
||||
"title": "Socials"
|
||||
},
|
||||
"education": {
|
||||
"title": "Education",
|
||||
"bachelor": {
|
||||
"title": "BSc Systems Engineering",
|
||||
"time": "2015 - now",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"skills": {
|
||||
"title": "Skills",
|
||||
"devops": {
|
||||
"title": "DevOps",
|
||||
"ansible": "Ansible",
|
||||
"kubernetes": "Kubernetes",
|
||||
"gitops": "GitOps"
|
||||
},
|
||||
"software_engineering": {
|
||||
"title": "Software Engineering",
|
||||
"rust": "Rust",
|
||||
"python": "Python",
|
||||
"csharp": "C#"
|
||||
}
|
||||
},
|
||||
"languages": {
|
||||
"title": "Languages",
|
||||
"german": "German (Native)",
|
||||
"english": "English (C2)",
|
||||
"vietnamese": "Vietnamese (B1)",
|
||||
"japanese": "Japanese (A1)"
|
||||
},
|
||||
"interests": {
|
||||
"title": "Interests",
|
||||
"coffee": "Coffee",
|
||||
"tech_it": "Tech/IT",
|
||||
"guitar": "Guitar",
|
||||
"mechanical_keyboards": "Mechanical Keyboards"
|
||||
}
|
||||
},
|
||||
"publications_projects": {
|
||||
"publications": {
|
||||
"title": "Publications",
|
||||
"rpm": {
|
||||
"title": "RPM: Reverse Path Congestion Marking on P4 Programmable Switches",
|
||||
"authors": "N. Baganal-Krishna, T.-D. Tran, R. Kundel and A. Rizk",
|
||||
"conference": "IEEE LCN 2023",
|
||||
"url": "https://doi.org/10.48550/arXiv.2307.09639",
|
||||
"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 congestion 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 on heterogeneous TCP flows as well as the flow completion time, especially for small Data Center TCP (DCTCP) flows around P4 programmable ASIC switches."
|
||||
},
|
||||
"iot_fuzzers": {
|
||||
"title": "Overview of IoT Fuzzing Techniques",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"conference": "Seminar",
|
||||
"url": "https://git.tudattr.dev/AISE/seminar/src/branch/main/paper.pdf",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"title": "Projects",
|
||||
"bachelorproject": {
|
||||
"title": "Undisclosed Ethereum Smart Contract Fuzzer",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Bachelorproject",
|
||||
"url": "https://git.ude-syssec.de/uni-due-syssec/students/2022_tuan-dat_tran_libAFLEVMFuzzer/ethfuzz/",
|
||||
"description": "In this ongoing project I am building an Ethereum Smart Contract Fuzzer. More info will follow."
|
||||
},
|
||||
"dotfiles": {
|
||||
"title": ".dotfiles",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Personal",
|
||||
"url": "https://git.tudattr.dev/tudattr/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 on how to set up my daily-use notebook. It provides a baseline for any personal Linux system I set up and allows for repeatability simplifies the process of setting up an ArchLinux based system. "
|
||||
},
|
||||
"homelab": {
|
||||
"title": "Homelab",
|
||||
"authors": "Tuan-Dat Tran",
|
||||
"kind": "Personal",
|
||||
"url": "https://git.tudattr.dev/tudattr/ansible",
|
||||
"description": "Ansible is a automation engine which allows for automatic provisioning, configuration management and application deployment. I use ansible to set up my homelab, which serves as a platform for me to try out and learn new technologies. "
|
||||
}
|
||||
}
|
||||
},
|
||||
"impressum": {
|
||||
"off": "Show Impressum",
|
||||
"on": "Impressum"
|
||||
},
|
||||
"components": {
|
||||
"under_construction": "This page is currently under construction"
|
||||
},
|
||||
"footer": {
|
||||
"year": "© 2024 ",
|
||||
"name": "Tuan-Dat Tran",
|
||||
"rights": ". All Rights Reserved.",
|
||||
"contact": "Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,29 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
|
||||
use crate::components::H1;
|
||||
|
||||
pub fn Footer() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
ToolsUsed {},
|
||||
class: "container mx-auto",
|
||||
// ToolsUsed {},
|
||||
footer {
|
||||
class:"bg-white rounded-lg shadow dark:bg-gray-800 my-4:wa
|
||||
",
|
||||
class:"bg-white rounded-lg shadow dark:bg-gray-800",
|
||||
div {
|
||||
class:"w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-between",
|
||||
class:"w-full mx-auto p-4 flex items-center 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."
|
||||
{ translate!(i18, "footer.year") },
|
||||
a { href: "#", class: "hover:underline", { translate!(i18, "footer.name") }},
|
||||
{ translate!(i18, "footer.rights") }
|
||||
}
|
||||
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" }
|
||||
Link { to:"mailto:tuan-dat.tran@tudattr.dev", class:"hover:underline", { translate!(i18, "footer.contact") } }
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -29,6 +32,7 @@ pub fn Footer() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn ToolsUsed() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
@@ -39,15 +43,15 @@ fn ToolsUsed() -> Element {
|
||||
div {
|
||||
class: "flex h-fill overflow-x-auto",
|
||||
Logo {
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Rust/Rust.png",
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Rust/Rust.png",
|
||||
alt: "Rust"
|
||||
},
|
||||
Logo {
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Tailwindcss/Tailwindcss6.png",
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Tailwindcss/Tailwindcss6.png",
|
||||
alt: "Tailwindcss"
|
||||
},
|
||||
Logo {
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/ServiceLogos/main/Html/HTML.png",
|
||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Html/HTML.png",
|
||||
alt: "HTML"
|
||||
},
|
||||
Logo {
|
||||
|
||||
@@ -1,41 +1,57 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::*, translate};
|
||||
|
||||
use crate::Route;
|
||||
|
||||
pub fn Header() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
// class: "justify-between p-4 space-x-8",
|
||||
class: "container mx-auto p-4",
|
||||
|
||||
ul {
|
||||
class:"flex flex-col justify-between sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-s-4",
|
||||
li {
|
||||
Link {
|
||||
to: Route::Home {},
|
||||
class: "rounded-md shadow-sm",
|
||||
img { src:"/pictures/ClackCat_t.webp", class:"rounded-full h-8", alt:"TuDatTr Logo" },
|
||||
},
|
||||
},
|
||||
li { HeaderLink { url: Route::Home {}, text: translate!(i18, "headers.home")} },
|
||||
li { HeaderLink { url: Route::CV {}, text: translate!(i18, "headers.cv") } },
|
||||
li { HeaderLink { url: Route::PublicationsProjects {}, text: translate!(i18, "headers.publications_projects") } },
|
||||
li { HeaderLink { url: Route::Consulting {}, text: translate!(i18, "headers.consulting") } },
|
||||
li { HeaderLink { url: Route::Impressum {}, text: translate!(i18, "headers.about") } },
|
||||
li { LanguageButtonGroup {} },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn LanguageButtonGroup() -> Element {
|
||||
let mut i18 = use_i18();
|
||||
|
||||
let change_to_english = move |_| i18.set_language("en-GB".parse().unwrap());
|
||||
let change_to_german = move |_| i18.set_language("de-DE".parse().unwrap());
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "rounded-md shadow-sm justify-end",
|
||||
button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-s-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_english, label { { translate!(i18, "headers.language_buttons.english") } } },
|
||||
button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-e-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_german, label { { translate!(i18, "headers.language_buttons.german") } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn HeaderLink(url: Route, text: String) -> Element {
|
||||
rsx! {
|
||||
Link { to: url, class:"md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500", {text} }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
mod footer;
|
||||
mod header;
|
||||
pub mod footer;
|
||||
pub mod header;
|
||||
|
||||
use crate::{Body, Route};
|
||||
use footer::Footer;
|
||||
@@ -10,14 +10,11 @@ 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 {},
|
||||
class: "flex flex-col min-h-screen",
|
||||
Header {},
|
||||
Body {
|
||||
Outlet::<Route> {},
|
||||
Footer {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
src/main.rs
@@ -1,20 +1,32 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use components::H1;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::i18n::*;
|
||||
|
||||
use layout::footer::Footer;
|
||||
use layout::header::Header;
|
||||
use tracing::Level;
|
||||
|
||||
pub mod components;
|
||||
mod consulting;
|
||||
mod cv;
|
||||
mod home;
|
||||
mod impressum;
|
||||
mod languages;
|
||||
mod layout;
|
||||
mod publications;
|
||||
|
||||
use crate::consulting::Consulting;
|
||||
use crate::cv::CV;
|
||||
use crate::home::Home;
|
||||
use crate::impressum::Impressum;
|
||||
use crate::languages::DE_DE;
|
||||
use crate::languages::EN_GB;
|
||||
use crate::layout::Layout;
|
||||
use crate::publications::Publications;
|
||||
use crate::publications::PublicationsProjects;
|
||||
|
||||
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Route {
|
||||
@@ -24,25 +36,68 @@ pub enum Route {
|
||||
#[route("/impressum")]
|
||||
Impressum {},
|
||||
#[route("/publications")]
|
||||
Publications {},
|
||||
PublicationsProjects {},
|
||||
#[route("/resume")]
|
||||
CV {},
|
||||
#[route("/consulting")]
|
||||
Consulting {},
|
||||
#[end_layout]
|
||||
#[route("/:..route")]
|
||||
PageNotFound { route: Vec<String> },
|
||||
}
|
||||
|
||||
fn main() {
|
||||
dioxus_logger::init(Level::INFO).expect("failed to init logger");
|
||||
launch(App);
|
||||
dioxus_logger::init(Level::DEBUG).expect("failed to init logger");
|
||||
LaunchBuilder::fullstack()
|
||||
.with_cfg(server_only!(dioxus::fullstack::Config::new().addr(
|
||||
std::net::SocketAddrV4::new(std::net::Ipv4Addr::new(0, 0, 0, 0), 8080,)
|
||||
)))
|
||||
.launch(App)
|
||||
}
|
||||
|
||||
fn App() -> Element {
|
||||
use_init_i18n("en-GB".parse().unwrap(), "en-GB".parse().unwrap(), || {
|
||||
let en_gb = Language::from_str(EN_GB).unwrap();
|
||||
let de_de = Language::from_str(DE_DE).unwrap();
|
||||
vec![en_gb, de_de]
|
||||
});
|
||||
|
||||
rsx! {
|
||||
meta {
|
||||
name: "robots",
|
||||
content: "noindex",
|
||||
name: "description",
|
||||
content: "Visit Tuan-Dat Tran's website for his CV, publications, projects, and consulting services. Connect for collaboration.",
|
||||
},
|
||||
script {
|
||||
r#type: "application/ld+json",
|
||||
"
|
||||
{{
|
||||
\"@context\": \"https://schema.org\",
|
||||
\"@type\": \"ProfilePage\",
|
||||
\"mainEntity\": {{
|
||||
\"@type\": \"Person\",
|
||||
\"name\": \"Tuan-Dat Tran\",
|
||||
\"alternateName\": \"TuDatTr\",
|
||||
\"image\": \"https://www.tudattr.dev/pictures/headshot.webp\",
|
||||
}}
|
||||
}}
|
||||
"
|
||||
}
|
||||
// meta {
|
||||
// property: "og:title",
|
||||
// content: "Tuan-Dat Trans Personal Website",
|
||||
// }
|
||||
// meta {
|
||||
// property: "og:description",
|
||||
// content: "Explore Tuan-Dat Tran's personal website featuring his CV, publications, projects, and consulting services. Get insights into his professional journey and connect for collaboration opportunities.",
|
||||
// }
|
||||
// meta {
|
||||
// property: "og:image",
|
||||
// content: "https://www.tudattr.dev/pictures/headshot.webp",
|
||||
// }
|
||||
// meta {
|
||||
// property: "og:url",
|
||||
// content: "https://tudattr.dev",
|
||||
// }
|
||||
div {
|
||||
class: "bg-white dark:bg-gray-900 min-h-screen",
|
||||
Router::<Route> {},
|
||||
@@ -54,12 +109,16 @@ fn App() -> Element {
|
||||
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"
|
||||
class: "flex flex-col min-h-screen items",
|
||||
Header {},
|
||||
div {
|
||||
class: "container mx-auto p-4 flex items-center justify-center max-w-md w-full",
|
||||
H1 {
|
||||
"Site not found (404)"
|
||||
},
|
||||
}
|
||||
}
|
||||
Footer {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +130,8 @@ pub struct BodyProp {
|
||||
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}
|
||||
}
|
||||
class: "flex-grow container mx-auto p-4",
|
||||
{prop.children}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,24 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_sdk::{i18n::use_i18, translate};
|
||||
|
||||
use crate::components::UnderConstruction;
|
||||
use crate::components::{UnderConstruction, H1, HR};
|
||||
|
||||
#[component]
|
||||
pub fn Publications() -> Element {
|
||||
pub fn PublicationsProjects() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
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."
|
||||
class: "flex flex-col ",
|
||||
UnderConstruction { },
|
||||
div {
|
||||
H1 { { translate!(i18, "publications_projects.publications.title") } }
|
||||
Publications { },
|
||||
},
|
||||
HR {},
|
||||
div {
|
||||
H1 { { translate!(i18, "publications_projects.projects.title") } }
|
||||
Projects { },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +35,32 @@ struct PublicationProp {
|
||||
description: String,
|
||||
}
|
||||
|
||||
fn Publications() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex gap-4 items-center flex-wrap",
|
||||
Publication {
|
||||
title: translate!(i18, "publications_projects.publications.rpm.title"),
|
||||
authors: translate!(i18, "publications_projects.publications.rpm.authors"),
|
||||
conference: translate!(i18, "publications_projects.publications.rpm.conference"),
|
||||
doi: translate!(i18, "publications_projects.publications.rpm.url"),
|
||||
description: translate!(i18, "publications_projects.publications.rpm.description")
|
||||
},
|
||||
|
||||
Publication {
|
||||
title: translate!(i18, "publications_projects.publications.iot_fuzzers.title"),
|
||||
authors: translate!(i18, "publications_projects.publications.iot_fuzzers.authors"),
|
||||
conference: translate!(i18, "publications_projects.publications.iot_fuzzers.conference"),
|
||||
doi: translate!(i18, "publications_projects.publications.iot_fuzzers.url"),
|
||||
description: translate!(i18, "publications_projects.publications.iot_fuzzers.description")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
fn Publication(prop: PublicationProp) -> Element {
|
||||
let pattern = "T.-D. Tran";
|
||||
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",
|
||||
@@ -61,9 +70,13 @@ fn Publication(prop: PublicationProp) -> Element {
|
||||
class:"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white",
|
||||
"{prop.title}",
|
||||
},
|
||||
span { class: "text-lg text-gray-900 dark:text-white", "{prop.conference}" },
|
||||
p {
|
||||
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||
"{prop.authors}",
|
||||
class:"font-normal text-gray-700 dark:text-gray-400 italic",
|
||||
Authors {
|
||||
authors: "{prop.authors}",
|
||||
pattern: "{pattern}",
|
||||
},
|
||||
}
|
||||
p {
|
||||
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||
@@ -72,3 +85,93 @@ fn Publication(prop: PublicationProp) -> Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Projects() -> Element {
|
||||
let i18 = use_i18();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex gap-4 items-center flex-wrap",
|
||||
Project {
|
||||
title: translate!(i18, "publications_projects.projects.bachelorproject.title"),
|
||||
authors: translate!(i18, "publications_projects.projects.bachelorproject.authors"),
|
||||
kind: translate!(i18, "publications_projects.projects.bachelorproject.kind"),
|
||||
url: translate!(i18, "publications_projects.projects.bachelorproject.url"),
|
||||
description: translate!(i18, "publications_projects.projects.bachelorproject.description")
|
||||
},
|
||||
Project {
|
||||
title: translate!(i18, "publications_projects.projects.dotfiles.title"),
|
||||
authors: translate!(i18, "publications_projects.projects.dotfiles.authors"),
|
||||
kind: translate!(i18, "publications_projects.projects.dotfiles.kind"),
|
||||
url: translate!(i18, "publications_projects.projects.dotfiles.url"),
|
||||
description: translate!(i18, "publications_projects.projects.dotfiles.description")
|
||||
},
|
||||
Project {
|
||||
title: translate!(i18, "publications_projects.projects.homelab.title"),
|
||||
authors: translate!(i18, "publications_projects.projects.homelab.authors"),
|
||||
kind: translate!(i18, "publications_projects.projects.homelab.kind"),
|
||||
url: translate!(i18, "publications_projects.projects.homelab.url"),
|
||||
description: translate!(i18, "publications_projects.projects.homelab.description")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Props)]
|
||||
struct ProjectProp {
|
||||
#[props(default = "".to_string())]
|
||||
url: String,
|
||||
authors: String,
|
||||
title: String,
|
||||
kind: String,
|
||||
#[props(default = "".to_string())]
|
||||
description: String,
|
||||
}
|
||||
|
||||
fn Project(prop: ProjectProp) -> Element {
|
||||
let pattern = "T.-D. Tran";
|
||||
|
||||
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.url}",
|
||||
new_tab: true,
|
||||
h5 {
|
||||
class:"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white",
|
||||
"{prop.title}",
|
||||
},
|
||||
p { class: "text-lg text-gray-900 dark:text-white", "{prop.kind}" },
|
||||
p {
|
||||
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||
Authors {
|
||||
authors: "{prop.authors}",
|
||||
pattern: "{pattern}",
|
||||
},
|
||||
}
|
||||
p {
|
||||
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||
"{prop.description}",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Props)]
|
||||
struct AuthorProp {
|
||||
authors: String,
|
||||
pattern: String,
|
||||
}
|
||||
|
||||
fn Authors(prop: AuthorProp) -> Element {
|
||||
if let Some(start) = prop.authors.find(&prop.pattern) {
|
||||
let end = start + prop.pattern.len();
|
||||
let left = &prop.authors[..start];
|
||||
let middle = &prop.authors[start..end];
|
||||
let right = &prop.authors[end..];
|
||||
rsx! {
|
||||
"{left}" , b { "{middle}" }, "{right}",
|
||||
}
|
||||
} else {
|
||||
rsx! { "{prop.authors}" }
|
||||
}
|
||||
}
|
||||
|
||||