Compare commits
175 commits
Pre-releas
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d25401733d | |||
| f150a56e62 | |||
| 37d515051c | |||
| 883aa55fd2 | |||
| 7b5b439ba7 | |||
| b09fee494e | |||
| 8bc297ec93 | |||
| 03c65ed275 | |||
| 30b97b33f5 | |||
| 16b63d9950 | |||
| 750ed60a26 | |||
| 1dea6f95b3 | |||
| f0eb117bc1 | |||
| 385353ac1e | |||
| 2c72c461e4 | |||
| 75393cb0bd | |||
| dc0d3cbad1 | |||
| d10f2697d9 | |||
| 19d35efb84 | |||
| d7a338f99a | |||
| f8df325f6e | |||
| cabbdccce1 | |||
| 481f376c8d | |||
| d42f97ebad | |||
| 47778ad0ed | |||
| eba8836fad | |||
| 7914916dd2 | |||
| 5eb110d70c | |||
| 63acef1c92 | |||
| 80a789bb17 | |||
| 7317e3efb2 | |||
| 5f62c7bcad | |||
| 8f3d531f06 | |||
| 0890de465a | |||
| 3e393e77c6 | |||
| fa6ba191af | |||
| f6ca40854f | |||
| 29f7c2458c | |||
| 87bff90861 | |||
| 5f104f445d | |||
| abe325e352 | |||
| 13fb3c10f9 | |||
| c988433c2c | |||
| e058883694 | |||
| b255a61fb8 | |||
| be11557c0b | |||
| 47ace40b86 | |||
| 0012323d9f | |||
| cb86fabffe | |||
| 4f548e45e3 | |||
| e60a920876 | |||
| d309db6f06 | |||
| 1d84bbb0f7 | |||
| 04db5ac019 | |||
| 5557c0ce77 | |||
| 763728b5d4 | |||
| 005f485924 | |||
| e91da70bf8 | |||
| 3be9909ac6 | |||
| 423bdf0708 | |||
| 0586e4cd72 | |||
| 91c2773f9d | |||
| bcd894db2e | |||
| 41046abd3e | |||
| 6c17730559 | |||
| 2151d497d2 | |||
| 8fab38dc80 | |||
| 4ed8a89542 | |||
| 5359ffc0d7 | |||
| f2ca9fe839 | |||
| 5a1c6ce90b | |||
| 5abba4eeab | |||
| a1c5ecadf5 | |||
| 0ed2d30950 | |||
| 285031c7d0 | |||
| 6f93ee6af9 | |||
| c1c26d2d70 | |||
| dcc7228915 | |||
| 3974990af0 | |||
| aff1616ddc | |||
| adaa6c7b99 | |||
| 3232ad6c8f | |||
| 9b7b4cec20 | |||
| db80cf2c64 | |||
| 7818fb5cf3 | |||
| bfc98c8f5d | |||
| 15ef092546 | |||
| 87583cc9a4 | |||
| 9645772071 | |||
| c714c5d039 | |||
| 2ed85d14cd | |||
| 681703ba0a | |||
| b31ece1bbc | |||
| 169fd35e59 | |||
| 02626fc835 | |||
| 6bbf298c32 | |||
| fb4d2629e7 | |||
| 66441564af | |||
| 4d288c393c | |||
| e1e932eee1 | |||
| 7884401576 | |||
| f1c5852f65 | |||
| 8744b3ce99 | |||
| 9d18d8c83c | |||
|
|
577c084223 | ||
|
|
2fa719ba7a | ||
|
|
78aa936514 | ||
|
|
ef915d260a | ||
|
|
5ee60380e4 | ||
|
|
0cdad09ea3 | ||
|
|
8910d0fb80 | ||
|
|
8d69d148cf | ||
|
|
74fd12e2ad | ||
|
|
7fbbc114da | ||
|
|
befa2ba1db | ||
|
|
47fd49e95a | ||
| 58a18329ad | |||
| 4632ee3a38 | |||
| 2445ec7126 | |||
| f43e5f1b7a | |||
|
|
ddeb9eaa39 | ||
|
|
f09b45b513 | ||
| dc7f3b420e | |||
| 157d330614 | |||
|
|
15edccf437 | ||
|
|
3acf056883 | ||
| add7049605 | |||
| 774658ff2b | |||
|
|
39eb3c93a9 | ||
| e2388408a3 | |||
| eb5c619700 | |||
| c06d7fd214 | |||
|
|
b58118dba8 | ||
|
|
7d6f77638e | ||
| 38a4d6d6e9 | |||
| 09c34647fc | |||
| 0b9f2f0ddf | |||
| 094bac23f5 | |||
| 267f482360 | |||
| 1b56577054 | |||
| 32f7bfe8d9 | |||
|
|
43e94e587e | ||
|
|
d53ed9dd54 | ||
|
|
542ea226fe | ||
|
|
972b73b1eb | ||
|
|
4f1259b80e | ||
| a03e67a235 | |||
| ff5d26c5d1 | |||
| 95f3fd77b2 | |||
| b0a9502699 | |||
| 007cdff868 | |||
|
|
2968b4b837 | ||
|
|
0dce6998b4 | ||
|
|
21b694fed2 | ||
|
|
aec32baa4a | ||
|
|
6a4ce8f2a0 | ||
|
|
987949a8cd | ||
|
|
68024823cc | ||
|
|
2f43ad188e | ||
|
|
de331070a1 | ||
|
|
29b5a50cd5 | ||
|
|
5d120580fa | ||
|
|
95218563ef | ||
|
|
745d1c29a1 | ||
|
|
2eb895110e | ||
|
|
6705b49341 | ||
|
|
74ba750ec0 | ||
|
|
defb36e370 | ||
|
|
d40d0cf845 | ||
|
|
f498dbab59 | ||
|
|
cc0f09523a | ||
|
|
000e2b4d28 | ||
| 13a89e9db0 | |||
| ced0f0c38e | |||
|
|
aa559d335d |
41 changed files with 2581 additions and 3704 deletions
38
.forgejo/workflows/master.yml
Normal file
38
.forgejo/workflows/master.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
name: Deploy to production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: docker
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
|
||||
- name: Connect to server
|
||||
run: |
|
||||
apt-get update &&
|
||||
apt-get install -y rsync &&
|
||||
rsync --version &&
|
||||
rsync -avz --no-perms --no-times --delete -e "ssh -o StrictHostKeyChecking=no -p ${{ secrets.SSH_PORT }}" dist/ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:octubre/main/
|
||||
35
.github/workflows/master.yml
vendored
35
.github/workflows/master.yml
vendored
|
|
@ -1,35 +0,0 @@
|
|||
name: Deploy React Vite App
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
|
||||
- name: Connect to server
|
||||
run: |
|
||||
rsync -avz --delete -e "ssh -o StrictHostKeyChecking=no -p ${{ secrets.SSH_PORT }}" dist/ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:octubre/
|
||||
# ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} -p ${{ secrets.SSH_PORT }} "cd octubre/ && git stash && git remote set-url origin https://github.com/PPDBTeam6-2023-2024/PPDB-Project.git && git pull origin main --rebase"
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
# Octubre
|
||||
|
||||
Copyright (C) 2024 JeCheeseSmith, Watson Smith (this is an alias)
|
||||
Copyright (C) 2024 JeCheeseSmith
|
||||
|
||||
This is the website code used for https://www.octubre.be . It is a hobby project and I will gradually extend upon it's services.
|
||||
This is the website code used for https://www.octubre.be . It is a hobby project and I will gradually extend upon its services.
|
||||
|
||||
Feel free to report any issues or bugs! You can contact me using the issues form.
|
||||
Feel free to report any issues or bugs! You can contact me using my socials =)
|
||||
|
||||
## Architecture
|
||||
|
||||
The website currently consists of a simple React frontend which links to different services I host.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script defer src="https://octubre.be/stats/script.js" data-website-id="8bb0c911-5425-4460-aca1-fd422fdf50c8"></script>
|
||||
<title>Octubre</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
3209
package-lock.json
generated
3209
package-lock.json
generated
File diff suppressed because it is too large
Load diff
30
package.json
30
package.json
|
|
@ -10,19 +10,25 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"@fortawesome/free-brands-svg-icons": "^7.0.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.3",
|
||||
"oidc-react": "^4.0.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.8.1",
|
||||
"vite-plugin-svgr": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@types/react": "^18.3.10",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.2",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-plugin-react": "^7.37.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.12",
|
||||
"globals": "^15.9.0",
|
||||
"vite": "^5.4.8"
|
||||
"@eslint/js": "9.33.0",
|
||||
"@types/react": "19.2.6",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "5.1.1",
|
||||
"eslint": "9.33.0",
|
||||
"eslint-plugin-react": "7.37.5",
|
||||
"eslint-plugin-react-hooks": "5.2.0",
|
||||
"eslint-plugin-react-refresh": "0.4.24",
|
||||
"globals": "16.5.0",
|
||||
"vite": "7.1.11"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
0
public/favicon.png
Normal file → Executable file
0
public/favicon.png
Normal file → Executable file
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
1
public/matrix.svg
Normal file
1
public/matrix.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Matrix</title><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>
|
||||
|
After Width: | Height: | Size: 939 B |
17
renovate.json
Normal file
17
renovate.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": [
|
||||
"config:best-practices"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch",
|
||||
"pin",
|
||||
"digest"
|
||||
],
|
||||
"automerge": true
|
||||
}
|
||||
],
|
||||
"osvVulnerabilityAlerts": true
|
||||
}
|
||||
|
|
@ -43,3 +43,5 @@ p {
|
|||
justify-content: space-evenly;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
29
src/app.jsx
29
src/app.jsx
|
|
@ -1,29 +0,0 @@
|
|||
import './app.css'
|
||||
import ButtonGroup from "./buttonGroup.jsx";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<h1>Octubre</h1>
|
||||
<h2>Home of projects</h2>
|
||||
|
||||
<p> This website is yet under construction :) 👷 </p>
|
||||
|
||||
<p>
|
||||
This is a linking page. Click on one of the buttons to go to the specified project, website or subdomain.
|
||||
</p>
|
||||
|
||||
<div className="buttonGroup">
|
||||
<ButtonGroup buttons={[ ["Home", "https://home.octubre.be"], ["Cloud", "https://cloud.octubre.be"], ["Me", "https://me.octubre.be"], ["Chat", "https://chat.octubre.be"], ["Archive", "https://archive.octubre.be"], ["Dev", "https://dev.octubre.be"], ["Blog", "https://blog.octubre.be"], ["Status", "https://status.octubre.be"], ["Log", "https://log.octubre.be"]]}/>
|
||||
</div>
|
||||
|
||||
<h3>Contact</h3>
|
||||
|
||||
<p>
|
||||
Please register any bugs of this website on <a href={"https://github.com/JeCheeseSmith/Octubre/issues"}> GitHub</a>.
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
49
src/app.tsx
Normal file
49
src/app.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import './app.css';
|
||||
import ButtonGroup from "./buttons/buttonGroup.tsx";
|
||||
import SocialButtons from "./buttons/Socials.tsx";
|
||||
import { useAuth } from 'oidc-react';
|
||||
|
||||
function App() {
|
||||
const auth = useAuth();
|
||||
|
||||
console.log(auth.userData);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Octubre</h1>
|
||||
<h2>Home of projects</h2>
|
||||
|
||||
<p>
|
||||
This is a linking page. Click on one of the buttons to go to the specified project, website or subdomain.
|
||||
</p>
|
||||
|
||||
{auth.userData && <div className="buttonGroup">
|
||||
<ButtonGroup
|
||||
buttons={[["Home", "https://home.octubre.be", "Home Automation Platform using Home Assistant"], ["Cloud", "https://cloud.octubre.be", "Personal Office Infrastructure using Nextcloud"], ["Media", "https://media.octubre.be", "Multimedia management solution using Immich"]]}/>
|
||||
</div>}
|
||||
|
||||
<div className="buttonGroup">
|
||||
<ButtonGroup
|
||||
buttons={[["Blog", "https://blog.octubre.be", "Blog about this hobby project and it's development roadmap"], ["Portfolio", "https://me.octubre.be", "My portfolio page including Github Projects"], ["Chat", "https://chat.octubre.be", "Federated chat instance using Matrix & Element"], ["Log 👷", "https://log.octubre.be", "Update log linked to the blog - Under construction"], ["Status", "https://status.octubre.be", "External status page of the different Octubre services"], ["Git", "https://git.octubre.be", "Forgejo based gitserver, alternative for my Github account"],["Fandom", "https://fandom.octubre.be", 'Website dedicated to fanart & creations about things I like'], ["Archive", "https://archive.octubre.be", "Separate website to host old, no longer maintained packages & websites"], ["Dev", "https://dev.octubre.be", "Development subdomain for alfa & beta releases"]]}/>
|
||||
</div>
|
||||
|
||||
{auth.userData && <div className="buttonGroup">
|
||||
<ButtonGroup
|
||||
buttons={[["Syncthing", "https://octubre.be/syncthing/", "Backup Synchronisation Service"], ["BOINC", "https://octubre.be/boinc/", "Share computing power for science!"], ["FAH", "https://octubre.be/fah", "Share computing power for science!"], ["Stats", "https://octubre.be/stats", "Anonymously keep track of visits"], ["irc", "https://irc.octubre.be", "Internet Relay Chat service using thelounge software"], ["", "https://octubre.be", ""]]}/>
|
||||
</div>}
|
||||
|
||||
<h3>Contact</h3>
|
||||
<p>
|
||||
Feel free to reach out to me! ✉️ 📬
|
||||
</p>
|
||||
<SocialButtons/>
|
||||
|
||||
<footer>
|
||||
Don't have a good day, have a great day! 😊
|
||||
</footer>
|
||||
<br/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<html>
|
||||
<head> <title>Archive</title> </head>
|
||||
|
||||
<h1>Archief pagina van oude websites</h1>
|
||||
|
||||
|
||||
<body>
|
||||
Website dedicated to host old versions of this website, serving as a small internet archive.
|
||||
|
||||
<a href="https://archive.octubre.be/portfolio">Kunst- & cultuurportfolio 6e Middelbaar | Previously hosted on webnode</a>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<html>
|
||||
<head> <title>Archive</title> </head>
|
||||
|
||||
<h1>Archief pagina van oude websites</h1>
|
||||
|
||||
|
||||
<body>
|
||||
<a href="https://archive.octubre.be/portfolio/index.html">Kunst- & cultuurportfolio></a>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,25 +0,0 @@
|
|||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
|
||||
margin: 0.5vw;
|
||||
padding: 10px 10px;
|
||||
|
||||
text-align: center;
|
||||
width: 150px;
|
||||
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: rgb(255, 102, 0);
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
89
src/buttons/Button.module.css
Normal file
89
src/buttons/Button.module.css
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
.controlButtonContainer{
|
||||
position: relative; /* Needed for absolute positioning of the tooltip */
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.controlButton {
|
||||
border-radius: 8px;
|
||||
border: 2px solid var(--border-color);
|
||||
|
||||
margin: 0.5vw;
|
||||
padding: 10px 10px;
|
||||
|
||||
text-align: center;
|
||||
width: 150px;
|
||||
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
|
||||
color: var(--foreground-color);
|
||||
background-color: var(--on-background-color-primary);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
.controlButton:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
.controlButton:focus,
|
||||
.controlButton:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.controlButtonNote {
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
top: 100%;
|
||||
background-color: var(--on-background-color-secondary);
|
||||
color: var(--foreground-color);
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
z-index: 1000; /* Ensure it appears above other elements */
|
||||
border: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.controlButtonNoteRight {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
background-color: var(--on-background-color-secondary);
|
||||
color: var(--foreground-color);
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
z-index: 1000; /* Ensure it appears above other elements */
|
||||
}
|
||||
|
||||
/* Title & Shortcut in One Row */
|
||||
.tooltipHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid var(--on-background-color-3);
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Title on the Left */
|
||||
.tooltipTitle {
|
||||
color: var(--primary-color);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Shortcut on the Right */
|
||||
.tooltipShortcut {
|
||||
background: var(--on-background-color-3);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Note Below */
|
||||
.tooltipNote {
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
color: var(--foreground-color);
|
||||
font-family: inherit;
|
||||
}
|
||||
24
src/buttons/Socials.module.css
Normal file
24
src/buttons/Socials.module.css
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.socialContainer {
|
||||
display: inline-block;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 3vh;
|
||||
}
|
||||
|
||||
.socialButton {
|
||||
color: rgb(64, 64, 64);
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.socialButton:hover {
|
||||
color: rgb(255, 102, 0); /* = #FF6600 */
|
||||
}
|
||||
|
||||
.matrixIcon {
|
||||
margin-top: 2vh;
|
||||
filter: invert(14%) sepia(0%) saturate(4178%) hue-rotate(139deg) brightness(125%) contrast(72%);
|
||||
}
|
||||
|
||||
.matrixIcon:hover {
|
||||
filter: invert(49%) sepia(84%) saturate(3557%) hue-rotate(0deg) brightness(102%) contrast(105%); /*Thanks to https://codepen.io/sosuke/pen/Pjoqqp for */
|
||||
}
|
||||
69
src/buttons/Socials.tsx
Normal file
69
src/buttons/Socials.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import styles from './Socials.module.css'; // Optional CSS
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faEnvelope,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import {
|
||||
faLinkedin,
|
||||
faGithub,
|
||||
} from '@fortawesome/free-brands-svg-icons';
|
||||
|
||||
interface SocialButton {
|
||||
icon: any;
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const socialButtons: SocialButton[] = [
|
||||
{
|
||||
icon: faEnvelope,
|
||||
label: 'Email',
|
||||
href: 'mailto:kars.van.velzen@gmail.com',
|
||||
},
|
||||
{
|
||||
icon: faLinkedin,
|
||||
label: 'LinkedIn',
|
||||
href: 'https://www.linkedin.com/in/kars-van-velzen',
|
||||
},
|
||||
{
|
||||
icon: faGithub,
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/JeCheeseSmith',
|
||||
},
|
||||
// {
|
||||
// icon: "custom-matrix",
|
||||
// label: 'Matrix',
|
||||
// href: '',
|
||||
// },
|
||||
];
|
||||
|
||||
const SocialButtons: React.FC = () => {
|
||||
return (
|
||||
<div className={styles.socialContainer}>
|
||||
{socialButtons.map((btn) => (
|
||||
<a
|
||||
key={btn.label}
|
||||
href={btn.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={btn.label}
|
||||
className={styles.socialButton}
|
||||
>
|
||||
<FontAwesomeIcon icon={btn.icon} size="2xl"/>
|
||||
</a>
|
||||
))}
|
||||
|
||||
<a key={"Matrix"}
|
||||
href={"https://matrix.to/#/@jecheesesmith:octubre.be"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={"Matrix"}
|
||||
className={styles.socialButton}>
|
||||
<img src="/matrix.svg" className={styles.matrixIcon} alt={"Matrix Logo"}/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialButtons;
|
||||
51
src/buttons/button.tsx
Normal file
51
src/buttons/button.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { useState } from 'react';
|
||||
import styles from './Button.module.css';
|
||||
import * as React from "react";
|
||||
|
||||
interface ButtonProps {
|
||||
note?: string;
|
||||
onClick: () => void;
|
||||
clicked?: boolean;
|
||||
title: string;
|
||||
right?: boolean;
|
||||
text?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
note = '',
|
||||
onClick,
|
||||
title,
|
||||
right = false,
|
||||
text = '',
|
||||
style,
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const controlButtonNote = right ? styles.controlButtonNoteRight : styles.controlButtonNote;
|
||||
|
||||
return (
|
||||
<div className={styles.controlButtonContainer} style={style}>
|
||||
<button
|
||||
aria-label={note}
|
||||
className={styles.controlButton}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onClick={onClick}
|
||||
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
{isHovered && note!='' &&
|
||||
<span className={controlButtonNote}>
|
||||
<div className={styles.tooltipHeader}>
|
||||
<span className={styles.tooltipTitle}>{title}</span>
|
||||
<span className={styles.tooltipShortcut}>{''}</span>
|
||||
</div>
|
||||
<p className={styles.tooltipNote}>{note}</p>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import "./button.css";
|
||||
import Button from "./button.jsx";
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const ButtonGroup = ({ buttons }) => {
|
||||
const ButtonGroup = ({ buttons }: { buttons: string[][] }) => {
|
||||
// @ts-ignore
|
||||
const openInNewTab = url => {
|
||||
window.open(url, '_blank', 'noopener,noreferrer');
|
||||
};
|
||||
|
|
@ -9,9 +9,8 @@ const ButtonGroup = ({ buttons }) => {
|
|||
<>
|
||||
{/* eslint-disable-next-line react/prop-types */}
|
||||
{buttons.map((buttonLabel, i) => (
|
||||
<button key={i} name={buttonLabel[0]} onClick={() => openInNewTab(buttonLabel[1])}>
|
||||
{buttonLabel[0]}
|
||||
</button>
|
||||
<Button key={i} text={buttonLabel[0]} onClick={() => openInNewTab(buttonLabel[1])} note={buttonLabel[2]}
|
||||
title={buttonLabel[1]}/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
7
src/header/Header.module.css
Normal file
7
src/header/Header.module.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.Header{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
text-align: right; /* override parent's center */
|
||||
gap: 1rem;
|
||||
}
|
||||
22
src/header/Header.tsx
Normal file
22
src/header/Header.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import styles from './themeSwitch.module.css';
|
||||
import styles2 from './Header.module.css';
|
||||
import ThemeSwitch from "./themeSwitch.tsx";
|
||||
import { useAuth } from 'oidc-react';
|
||||
|
||||
const Header = () => {
|
||||
const auth = useAuth();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles2.Header}>
|
||||
<ThemeSwitch />
|
||||
<button className={styles.button} onClick={() => auth.signIn()}>
|
||||
Log in
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
||||
14
src/header/themeSwitch.module.css
Normal file
14
src/header/themeSwitch.module.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.button {
|
||||
border: 2px solid var(--border-color);
|
||||
color: var(--foreground-color);
|
||||
background-color: var(--on-background-color-primary);
|
||||
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
transition: background 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
21
src/header/themeSwitch.tsx
Normal file
21
src/header/themeSwitch.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { useTheme } from './useTheme.ts';
|
||||
import styles from './themeSwitch.module.css';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
|
||||
const ThemeToggleButton = () => {
|
||||
const [theme, toggleTheme] = useTheme();
|
||||
|
||||
return (
|
||||
<button className={styles.button} onClick={toggleTheme} aria-label="Toggle dark mode">
|
||||
{theme === 'dark' ? (
|
||||
<FontAwesomeIcon icon={faSun}/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faMoon}/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeToggleButton;
|
||||
25
src/header/useTheme.ts
Normal file
25
src/header/useTheme.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
type Theme = 'light' | 'dark';
|
||||
|
||||
export const useTheme = (): [Theme, () => void] => {
|
||||
const getInitialTheme = (): Theme => {
|
||||
const stored = localStorage.getItem('theme');
|
||||
if (stored === 'light' || stored === 'dark') return stored;
|
||||
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
return prefersDark ? 'dark' : 'light';
|
||||
};
|
||||
|
||||
const [theme, setTheme] = useState<Theme>(getInitialTheme);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.remove('light', 'dark');
|
||||
document.body.classList.add(theme);
|
||||
localStorage.setItem('theme', theme);
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
|
||||
|
||||
return [theme, toggleTheme];
|
||||
};
|
||||
28
src/main.css
28
src/main.css
|
|
@ -2,11 +2,35 @@
|
|||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: white;
|
||||
color: #000000;
|
||||
|
||||
--foreground-color: #000000;
|
||||
--background-color: white;
|
||||
--on-background-color-primary: white;
|
||||
--on-background-color-secondary: #f9f9f9;
|
||||
--on-background-color-3: #000000;
|
||||
--primary-color: rgb(255, 102, 0);
|
||||
--border-color: rgb(64, 64, 64);
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
--foreground-color: rgba(255, 255, 255, 0.87);
|
||||
--background-color: #242424;
|
||||
--on-background-color-primary: #1a1a1a;
|
||||
--on-background-color-secondary: rgb(64, 64, 64);
|
||||
--on-background-color-3: rgba(255, 255, 255, 0.2);
|
||||
--primary-color: rgb(255, 102, 0);
|
||||
--border-color: transparent;
|
||||
|
||||
}
|
||||
10
src/main.jsx
10
src/main.jsx
|
|
@ -1,10 +0,0 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './app.jsx'
|
||||
import './main.css'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
26
src/main.tsx
Normal file
26
src/main.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './app.tsx'
|
||||
import './main.css'
|
||||
import {AuthProvider} from 'oidc-react';
|
||||
import Header from "./header/Header.tsx";
|
||||
|
||||
const oidcConfig = {
|
||||
onSignIn: () => {
|
||||
console.log('hi');
|
||||
},
|
||||
authority: 'https://auth.octubre.be',
|
||||
clientId: '0WU8Mb9NfKQPvB29U0di3GGXVxv5uvolvOgcJiusjSw8eb0O8ghfVcLHLAg8MkGv',
|
||||
redirectUri: 'https://octubre.be',
|
||||
autoSignIn: false,
|
||||
autoSignOut: false
|
||||
};
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<AuthProvider {...oidcConfig}>
|
||||
<Header/>
|
||||
<App/>
|
||||
</AuthProvider>
|
||||
</StrictMode>
|
||||
)
|
||||
7
src/vite-env.d.ts
vendored
Normal file
7
src/vite-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/// <reference types="vite/client" />
|
||||
declare module '*.svg' {
|
||||
import * as React from 'react';
|
||||
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
29
tsconfig.json
Normal file
29
tsconfig.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"files": [],
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
14
vite.config.ts
Normal file
14
vite.config.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
svgr({
|
||||
svgrOptions: {
|
||||
icon: true,
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue