diff --git a/src/components/LetterGlitch.jsx b/src/components/LetterGlitch.jsx deleted file mode 100644 index a866edd..0000000 --- a/src/components/LetterGlitch.jsx +++ /dev/null @@ -1,245 +0,0 @@ -import { useRef, useEffect } from 'react'; - -const LetterGlitch = ({ - glitchColors = ['#2b4539', '#61dca3', '#61b3dc'], - className = '', - glitchSpeed = 50, - centerVignette = false, - outerVignette = true, - smooth = true, - characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$&*()-_+=/[]{};:<>.,0123456789' -}) => { - const canvasRef = useRef(null); - const animationRef = useRef(null); - const letters = useRef([]); - const grid = useRef({ columns: 0, rows: 0 }); - const context = useRef(null); - const lastGlitchTime = useRef(Date.now()); - - const lettersAndSymbols = Array.from(characters); - - const fontSize = 16; - const charWidth = 10; - const charHeight = 20; - - const getRandomChar = () => { - return lettersAndSymbols[Math.floor(Math.random() * lettersAndSymbols.length)]; - }; - - const getRandomColor = () => { - return glitchColors[Math.floor(Math.random() * glitchColors.length)]; - }; - - const hexToRgb = hex => { - const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, (m, r, g, b) => { - return r + r + g + g + b + b; - }); - - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } - : null; - }; - - const interpolateColor = (start, end, factor) => { - const result = { - r: Math.round(start.r + (end.r - start.r) * factor), - g: Math.round(start.g + (end.g - start.g) * factor), - b: Math.round(start.b + (end.b - start.b) * factor) - }; - return `rgb(${result.r}, ${result.g}, ${result.b})`; - }; - - const calculateGrid = (width, height) => { - const columns = Math.ceil(width / charWidth); - const rows = Math.ceil(height / charHeight); - return { columns, rows }; - }; - - const initializeLetters = (columns, rows) => { - grid.current = { columns, rows }; - const totalLetters = columns * rows; - letters.current = Array.from({ length: totalLetters }, () => ({ - char: getRandomChar(), - color: getRandomColor(), - targetColor: getRandomColor(), - colorProgress: 1 - })); - }; - - const resizeCanvas = () => { - const canvas = canvasRef.current; - if (!canvas) return; - const parent = canvas.parentElement; - if (!parent) return; - - const dpr = window.devicePixelRatio || 1; - const rect = parent.getBoundingClientRect(); - - canvas.width = rect.width * dpr; - canvas.height = rect.height * dpr; - - canvas.style.width = `${rect.width}px`; - canvas.style.height = `${rect.height}px`; - - if (context.current) { - context.current.setTransform(dpr, 0, 0, dpr, 0, 0); - } - - const { columns, rows } = calculateGrid(rect.width, rect.height); - initializeLetters(columns, rows); - - drawLetters(); - }; - - const drawLetters = () => { - if (!context.current || letters.current.length === 0) return; - const ctx = context.current; - const { width, height } = canvasRef.current.getBoundingClientRect(); - ctx.clearRect(0, 0, width, height); - ctx.font = `${fontSize}px monospace`; - ctx.textBaseline = 'top'; - - letters.current.forEach((letter, index) => { - const x = (index % grid.current.columns) * charWidth; - const y = Math.floor(index / grid.current.columns) * charHeight; - ctx.fillStyle = letter.color; - ctx.fillText(letter.char, x, y); - }); - }; - - const updateLetters = () => { - if (!letters.current || letters.current.length === 0) return; - - const updateCount = Math.max(1, Math.floor(letters.current.length * 0.05)); - - for (let i = 0; i < updateCount; i++) { - const index = Math.floor(Math.random() * letters.current.length); - if (!letters.current[index]) continue; - - letters.current[index].char = getRandomChar(); - letters.current[index].targetColor = getRandomColor(); - - if (!smooth) { - letters.current[index].color = letters.current[index].targetColor; - letters.current[index].colorProgress = 1; - } else { - letters.current[index].colorProgress = 0; - } - } - }; - - const handleSmoothTransitions = () => { - let needsRedraw = false; - letters.current.forEach(letter => { - if (letter.colorProgress < 1) { - letter.colorProgress += 0.05; - if (letter.colorProgress > 1) letter.colorProgress = 1; - - const startRgb = hexToRgb(letter.color); - const endRgb = hexToRgb(letter.targetColor); - if (startRgb && endRgb) { - letter.color = interpolateColor(startRgb, endRgb, letter.colorProgress); - needsRedraw = true; - } - } - }); - - if (needsRedraw) { - drawLetters(); - } - }; - - const animate = () => { - const now = Date.now(); - if (now - lastGlitchTime.current >= glitchSpeed) { - updateLetters(); - drawLetters(); - lastGlitchTime.current = now; - } - - if (smooth) { - handleSmoothTransitions(); - } - - animationRef.current = requestAnimationFrame(animate); - }; - - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; - - context.current = canvas.getContext('2d'); - resizeCanvas(); - animate(); - - let resizeTimeout; - - const handleResize = () => { - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(() => { - cancelAnimationFrame(animationRef.current); - resizeCanvas(); - animate(); - }, 100); - }; - - window.addEventListener('resize', handleResize); - - return () => { - cancelAnimationFrame(animationRef.current); - window.removeEventListener('resize', handleResize); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [glitchSpeed, smooth]); - - const containerStyle = { - position: 'relative', - width: '100%', - height: '100%', - backgroundColor: '#000000', - overflow: 'hidden' - }; - - const canvasStyle = { - display: 'block', - width: '100%', - height: '100%' - }; - - const outerVignetteStyle = { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - pointerEvents: 'none', - background: 'radial-gradient(circle, rgba(0,0,0,0) 60%, rgba(0,0,0,1) 100%)' - }; - - const centerVignetteStyle = { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - pointerEvents: 'none', - background: 'radial-gradient(circle, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 60%)' - }; - - return ( -
- - {outerVignette &&
} - {centerVignette &&
} -
- ); -}; - -export default LetterGlitch; - diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index aff47a2..503a4ee 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -1,7 +1,7 @@ import { useState } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { useAuth } from '../context/AuthContext' -import LetterGlitch from '../components/LetterGlitch' +import Logo from '../components/Logo' import { safeReturnPath } from '../lib/returnPath' import { NO_STAFF_ACCESS } from '../lib/staffAccess' @@ -83,167 +83,99 @@ export default function LoginPage() { setPassword('') } - const linkButton = { - background: 'none', - border: 0, - padding: 0, - color: '#e2e8f0', - textDecoration: 'underline', - cursor: 'pointer', - font: 'inherit', - } - const codeStep = mfaPending && ( -
- {error && ( -
- {error} -
- )} -

+ <> +

Code eingeben

+

{!mfaPending.challengeId ? 'Wir schicken dir einen Code an deine E-Mail-Adresse.' : `Wir haben dir einen Code an ${mfaPending.email || 'deine E-Mail-Adresse'} geschickt.`}

- {notice &&

{notice}

} - {mfaPending.challengeId ? ( - <> -
- - setCode(e.target.value.replace(/\D/g, ''))} - inputMode="numeric" - autoComplete="one-time-code" - maxLength={6} - required - autoFocus - style={{ letterSpacing: '0.3em' }} - /> -
- -

- - {' · '} - -

- - ) : ( - <> - + + ) : ( + -

- -

- - )} -
+ )} + +
+ {mfaPending.challengeId && ( + + )} + +
+ ) return ( -
- {/* LetterGlitch Hintergrund */} -
- -
- - {/* Login-Formular */} -
-
-
-

Webklar WOMS 2.0

-
-
- {codeStep ||
- {error && ( -
- {error} + <> +
+
+ + {codeStep || ( + <> +

Anmelden

+

Ticketsystem für das WEBklar-Team

+ + {error &&
{error}
} +
+ + setEmail(e.target.value)} + autoComplete="username" + required + placeholder="deine@email.com" + />
- )} - -
- - setEmail(e.target.value)} - required - placeholder="deine@email.com" - /> -
- -
- - setPassword(e.target.value)} - required - placeholder="••••••••" - minLength={8} - /> -
- - - } -
+
+ + setPassword(e.target.value)} + autoComplete="current-password" + required + minLength={8} + /> +
+ + + + )}
-
+
Webklar Ticket-Plattform · © {new Date().getFullYear()}
+ ) } diff --git a/src/styles/global.css b/src/styles/global.css index f0b456a..1a06d80 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1034,8 +1034,8 @@ a.list-row:hover .cell-title { color: var(--accent-ink); } .ui-btn-primary:hover { background: var(--accent-hover); } .ui-btn-danger { background: none; box-shadow: none; padding: 0 4px; color: var(--danger); } .ui-btn-danger:hover { background: none; text-decoration: underline; text-underline-offset: 3px; } -.btn-dark, .btn-teal, .btn-green { background: var(--accent); color: #fff; box-shadow: var(--shadow-btn); } -.btn-dark:hover, .btn-teal:hover, .btn-green:hover { background: var(--accent-hover); } +.btn-dark, .btn-teal { background: var(--accent); color: #fff; box-shadow: var(--shadow-btn); } +.btn-dark:hover, .btn-teal:hover { background: var(--accent-hover); } /* Formular (.form-group, .form-label, .form-control) wie .field, Beschriftung und .input / .select / .textarea */ .form-group { margin-bottom: 16px; }