From cc4b14f8629cbec23eaea46e3d72ef4028bd1126 Mon Sep 17 00:00:00 2001 From: BOHA Date: Sun, 7 Jun 2026 01:24:11 +0200 Subject: [PATCH] feat(mui): migrate Login (auth shell + 2FA) onto MUI kit; drop login.css Co-Authored-By: Claude Opus 4.8 (1M context) --- src/admin/AdminApp.tsx | 1 - src/admin/login.css | 143 -------------- src/admin/pages/Login.tsx | 405 ++++++++++++++++++++++++-------------- 3 files changed, 254 insertions(+), 295 deletions(-) delete mode 100644 src/admin/login.css diff --git a/src/admin/AdminApp.tsx b/src/admin/AdminApp.tsx index 51c9d81..3080560 100644 --- a/src/admin/AdminApp.tsx +++ b/src/admin/AdminApp.tsx @@ -20,7 +20,6 @@ import "./datepicker.css"; import "./filemanager.css"; import "./pagination.css"; import "./responsive.css"; -import "./login.css"; import "./dashboard.css"; import "./attendance.css"; import "./plan.css"; diff --git a/src/admin/login.css b/src/admin/login.css deleted file mode 100644 index faf4b5c..0000000 --- a/src/admin/login.css +++ /dev/null @@ -1,143 +0,0 @@ -/* ============================================================================ - Login Page - ============================================================================ */ - -.admin-login { - min-height: 100vh; - min-height: 100dvh; - max-height: 100vh; - max-height: 100dvh; - display: flex; - align-items: center; - justify-content: center; - padding: 1rem; - background: var(--bg-primary); - position: relative; - overflow: hidden; - box-sizing: border-box; -} - -.admin-login .bg-orb { - position: absolute; - border-radius: 50%; - filter: blur(80px); - pointer-events: none; -} - -.admin-login .bg-orb-1 { - width: 400px; - height: 400px; - background: var(--orb-color-1); - top: 10%; - left: 20%; - animation: float 20s ease-in-out infinite; -} - -.admin-login .bg-orb-2 { - width: 320px; - height: 320px; - background: var(--orb-color-2); - bottom: 20%; - right: 15%; - animation: float 25s ease-in-out infinite reverse; -} - -.admin-login-theme-btn { - position: absolute; - top: 1rem; - right: 1rem; - display: flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - padding: 0; - background: var(--glass-bg); - backdrop-filter: blur(10px); - border: 1px solid var(--glass-border); - color: var(--text-secondary); - cursor: pointer; - border-radius: 50%; - overflow: hidden; - transition: var(--transition); - z-index: 10; -} - -.admin-login-theme-btn:hover { - background: var(--glass-bg-solid); - color: var(--text-primary); - border-color: var(--border-color-hover); - transform: scale(1.05); -} - -.admin-login-card { - width: 100%; - max-width: 420px; - max-height: calc(100vh - 2rem); - max-height: calc(100dvh - 2rem); - padding: 2rem; - background: var(--glass-bg); - backdrop-filter: blur(20px); - border: 1px solid var(--glass-border); - border-radius: var(--border-radius-lg); - box-shadow: var(--glass-shadow); - position: relative; - z-index: 1; - overflow-y: auto; -} - -@media (min-width: 640px) { - .admin-login-card { - padding: 2.5rem; - } -} - -.admin-login-header { - text-align: center; - margin-bottom: 2rem; -} - -.admin-login-logo { - height: 48px; - width: auto; - margin-bottom: 1rem; -} - -.admin-login-2fa-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 56px; - height: 56px; - border-radius: 50%; - background: var(--accent-light); - color: var(--accent-color); - margin-bottom: 1rem; -} - -.admin-login-title { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); - font-family: var(--font-heading); - margin-bottom: 0.5rem; -} - -.admin-login-subtitle { - color: var(--text-secondary); - font-size: 0.95rem; -} - -.admin-back-link { - color: var(--text-secondary); - text-decoration: none; - font-size: 0.875rem; - transition: var(--transition); - margin-top: 1.5rem; - display: inline-block; -} - -.admin-back-link:hover { - color: var(--text-primary); - text-decoration: underline; -} diff --git a/src/admin/pages/Login.tsx b/src/admin/pages/Login.tsx index b2d6865..6eeebe9 100644 --- a/src/admin/pages/Login.tsx +++ b/src/admin/pages/Login.tsx @@ -1,6 +1,10 @@ import { useState, useEffect, useRef, useCallback } from "react"; import { Navigate } from "react-router-dom"; import { motion, AnimatePresence } from "framer-motion"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import IconButton from "@mui/material/IconButton"; +import CircularProgress from "@mui/material/CircularProgress"; import { useAuth } from "../context/AuthContext"; import { useAlert } from "../context/AlertContext"; import { useTheme } from "../../context/ThemeContext"; @@ -8,7 +12,7 @@ import { shouldShowSessionExpiredAlert, shouldShowLogoutAlert, } from "../utils/api"; -import FormField from "../components/FormField"; +import { TextField, Button, CheckboxField, Field } from "../ui"; export default function Login() { const { login, verify2FA, isAuthenticated, loading: authLoading } = useAuth(); @@ -132,11 +136,17 @@ export default function Login() { if (authLoading) { return ( -
-
-
-
-
+ + + ); } @@ -144,32 +154,115 @@ export default function Login() { return ; } + // Shared glass card sx (replaces .admin-login-card) + const cardSx = { + width: "100%", + maxWidth: 420, + maxHeight: "calc(100dvh - 2rem)", + p: { xs: 4, sm: 5 }, + background: "var(--glass-bg)", + backdropFilter: "blur(20px)", + border: "1px solid var(--glass-border)", + borderRadius: "var(--border-radius-lg)", + boxShadow: "var(--glass-shadow)", + position: "relative" as const, + zIndex: 1, + overflowY: "auto" as const, + }; + + // Framer entrance + shake choreography for the card panels (unchanged) + const cardMotion = { + initial: { opacity: 0, y: 30 }, + animate: shake + ? { opacity: 1, y: 0, x: [0, -12, 12, -8, 8, -4, 4, 0] } + : { opacity: 1, y: 0 }, + exit: { opacity: 0, y: -20 }, + transition: shake + ? { x: { duration: 0.5, ease: "easeOut" as const } } + : { duration: 0.3 }, + }; + return ( - -
-
+ {/* Animated background orbs (replaces .bg-orb) */} + + - + )} + {!show2FA ? ( - -
- + + { (e.target as HTMLImageElement).src = theme === "dark" ? "/images/logo-dark.png" : "/images/logo-light.png"; }} + sx={{ height: 48, width: "auto", mb: 2 }} /> -

Interní systém

-

Přihlaste se ke svému účtu

-
+ + Interní systém + + + Přihlaste se ke svému účtu + +
-
- - + + setUsername(e.target.value)} required autoComplete="username" - className="admin-form-input" placeholder="Zadejte uživatelské jméno" /> - + - - + setPassword(e.target.value)} required autoComplete="current-password" - className="admin-form-input" placeholder="Zadejte heslo" /> - + - + - -
- + +
+ ) : ( - -
-
+ + + -
-

Dvoufaktorové ověření

-

+ + + Dvoufaktorové ověření + + {useBackupCode ? "Zadejte jeden ze záložních kódů" : "Zadejte 6místný kód z autentizační aplikace"} -

-
+ + -
- - + + { const val = useBackupCode @@ -351,80 +441,93 @@ export default function Login() { }} required autoComplete="one-time-code" - className="admin-form-input" placeholder={useBackupCode ? "XXXXXXXX" : "000000"} - style={ - useBackupCode - ? {} - : { - textAlign: "center", - fontSize: "1.5rem", - letterSpacing: "0.5rem", - fontFamily: "monospace", - } - } + slotProps={{ + htmlInput: { + inputMode: useBackupCode ? "text" : "numeric", + pattern: useBackupCode ? undefined : "[0-9]*", + maxLength: useBackupCode ? 8 : 6, + style: useBackupCode + ? {} + : { + textAlign: "center", + fontSize: "1.5rem", + letterSpacing: "0.5rem", + fontFamily: "monospace", + }, + }, + }} /> - + - -
+ + -
- - -
-
+ + + )} - + ); }