Base frame for login version

This commit is contained in:
Kars van Velzen 2025-08-21 17:30:23 +02:00
parent 8744b3ce99
commit f1c5852f65
10 changed files with 154 additions and 16 deletions

89
package-lock.json generated
View file

@ -11,8 +11,10 @@
"@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": {
@ -2055,6 +2057,15 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/cosmiconfig": {
"version": "8.3.6",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
@ -3670,6 +3681,15 @@
"node": ">=4.0"
}
},
"node_modules/jwt-decode": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -3930,6 +3950,31 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/oidc-client-ts": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.3.0.tgz",
"integrity": "sha512-t13S540ZwFOEZKLYHJwSfITugupW4uYLwuQSSXyKH/wHwZ+7FvgHE7gnNJh1YQIZ1Yd1hKSRjqeXGSUtS0r9JA==",
"license": "Apache-2.0",
"dependencies": {
"jwt-decode": "^4.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/oidc-react": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/oidc-react/-/oidc-react-4.0.1.tgz",
"integrity": "sha512-UjsSRlg/mux5dYb7hR1NvL0bcCVTShIRh/hzFU6h64RJ4jqFlZL6CD5cu1YuUS2MNurMGboiG9SHuYI59ZSBEg==",
"license": "MIT",
"dependencies": {
"oidc-client-ts": "^3.2.0"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^16.8.0 || || ^17.0.0 || ^18.0.0"
}
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@ -4188,6 +4233,44 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.1.tgz",
"integrity": "sha512-5cy/M8DHcG51/KUIka1nfZ2QeylS4PJRs6TT8I4PF5axVsI5JUxp0hC0NZ/AEEj8Vw7xsEoD7L/6FY+zoYaOGA==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.1.tgz",
"integrity": "sha512-NkgBCF3sVgCiAWIlSt89GR2PLaksMpoo3HDCorpRfnCEfdtRPLiuTf+CNXvqZMI5SJLZCLpVCvcZrTdtGW64xQ==",
"license": "MIT",
"dependencies": {
"react-router": "7.8.1"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@ -4368,6 +4451,12 @@
"semver": "bin/semver.js"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",

View file

@ -13,8 +13,10 @@
"@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": {

View file

@ -1,9 +1,14 @@
import './app.css';
import ButtonGroup from "./buttons/buttonGroup.tsx";
import SocialButtons from "./buttons/Socials.tsx";
import { useAuth } from 'oidc-react';
function App() {
return (
const auth = useAuth();
console.log(auth.userData);
return (
<>
<h1>Octubre</h1>
<h2>Home of projects</h2>
@ -14,6 +19,8 @@ function App() {
This is a linking page. Click on one of the buttons to go to the specified project, website or subdomain.
</p>
{auth.userData && <p>You are logged in!</p>}
<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"], ["Blog", "https://blog.octubre.be", "Blog about this hobby project and it's development roadmap"], ["Me", "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"]]}/>

View file

@ -3,37 +3,39 @@ import styles from './Button.module.css';
import * as React from "react";
interface ButtonProps {
note: string;
note?: string;
onClick: () => void;
clicked?: boolean;
title: string;
right?: boolean;
text?: string;
style?: React.CSSProperties;
}
const Button: React.FC<ButtonProps> = ({
note,
note = '',
onClick,
title,
right = false,
text = '',
style,
}) => {
const [isHovered, setIsHovered] = useState(false);
const controlButtonNote = right ? styles.controlButtonNoteRight : styles.controlButtonNote;
return (
<div className={styles.controlButtonContainer}>
<div className={styles.controlButtonContainer} style={style}>
<button
aria-label={note}
className={styles.controlButton}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={onClick}
>
{/*<FontAwesomeIcon icon={faIcon} style={{color: clicked ? '#c084fc' : '#ffffff'}}/>*/}
{text}
</button>
{isHovered &&
{isHovered && note!='' &&
<span className={controlButtonNote}>
<div className={styles.tooltipHeader}>
<span className={styles.tooltipTitle}>{title}</span>

View 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
View 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(); // ✅ use the hook at the top level
return (
<div>
<div className={styles2.Header}>
<ThemeSwitch />
<button className={styles.button} onClick={() => auth.signIn()}>
Log in
</button>
</div>
</div>
);
};
export default Header;

View file

@ -3,15 +3,10 @@
color: var(--foreground-color);
background-color: var(--on-background-color-primary);
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
border-radius: 6px;
transition: background 0.3s, color 0.3s;
top: 1vh;
right: 1vw;
position: absolute;
}
.button:hover {

View file

@ -8,7 +8,7 @@ const ThemeToggleButton = () => {
const [theme, toggleTheme] = useTheme();
return (
<button className={styles.button} onClick={toggleTheme} aria-label="Toggle dark mode">
<button className={styles.button} onClick={toggleTheme} aria-label="Toggle dark mode">
{theme === 'dark' ? (
<FontAwesomeIcon icon={faSun}/>
) : (

View file

@ -2,11 +2,25 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './app.tsx'
import './main.css'
import ThemeToggleButton from "./theme/themeSwitch.tsx";
import {AuthProvider} from 'oidc-react';
import Header from "./header/Header.tsx";
const oidcConfig = {
onSignIn: () => {
// Redirect?
},
authority: 'https://auth.octubre.be',
clientId: '', // TODO Paste Client ID & set to Public
redirect_uri: 'https://octubre.be',
autoSignIn: false,
autoSignOut: false
};
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ThemeToggleButton/>
<App/>
</StrictMode>,
<AuthProvider {...oidcConfig}>
<Header/>
<App/>
</AuthProvider>
</StrictMode>
)