From c92b436cfc1964140c5cb1de951c5f42add20323 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:51:21 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Improved=20login=20ac?= =?UTF-8?q?cessibility=20and=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced browser alert() with inline error message in LoginView. - Added screen-reader-only label for email input. - Implemented aria-invalid, aria-describedby, and role="alert" for better accessibility. - Added aria-hidden="true" to decorative logo SVG. - Ensured error messages are cleared when the user starts typing. - Updated .gitignore to exclude pnpm-lock.yaml and preview.log. Co-authored-by: BenjaminWie <54136562+BenjaminWie@users.noreply.github.com> --- .Jules/palette.md | 3 +++ .gitignore | 2 ++ App.tsx | 6 ++++-- components/LoginView.tsx | 17 ++++++++++++++--- 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 .Jules/palette.md diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..801a692 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2024-05-22 - [Accessible Login Error Handling] +**Learning:** Replacing browser `alert()` with inline UI error messages significantly improves UX by not blocking the UI thread. To ensure accessibility, use `aria-invalid`, `aria-describedby`, and `role="alert"`. +**Action:** When syncing external error props to local state, use a tracker variable (e.g., `prevExternalError`) to allow the error to be cleared locally and then re-triggered even if the parent passes the same error string again. diff --git a/.gitignore b/.gitignore index a547bf3..98bfa12 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ node_modules dist dist-ssr *.local +pnpm-lock.yaml +preview.log # Editor directories and files .vscode/* diff --git a/App.tsx b/App.tsx index 28169f5..55e89c3 100644 --- a/App.tsx +++ b/App.tsx @@ -23,6 +23,7 @@ const App: React.FC = () => { const [currentUser, setCurrentUser] = useState(null); const [currentView, setCurrentView] = useState('chat'); const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [loginError, setLoginError] = useState(''); const [documents, setDocuments] = useState(() => { const saved = localStorage.getItem(STORAGE_KEY); @@ -96,12 +97,13 @@ const App: React.FC = () => { }, [personas]); const handleLogin = (email: string) => { + setLoginError(''); // If we have users loaded (from NC or Init), check them const user = users.find(u => u.email.toLowerCase() === email.toLowerCase() && u.status === 'aktiv'); if (user) { setCurrentUser(user); } else { - alert("Zugang verweigert. Nur verifizierte Wohnpro-Bewohner können sich im Wohnpro Guide anmelden."); + setLoginError("Zugang verweigert. Nur verifizierte Wohnpro-Bewohner können sich im Wohnpro Guide anmelden."); } }; @@ -256,7 +258,7 @@ const App: React.FC = () => { Synchronisiere mit Nextcloud... )} - + ); } diff --git a/components/LoginView.tsx b/components/LoginView.tsx index a7c3c0b..1a2a998 100644 --- a/components/LoginView.tsx +++ b/components/LoginView.tsx @@ -10,6 +10,12 @@ interface LoginViewProps { const LoginView: React.FC = ({ onLogin, error: externalError }) => { const [email, setEmail] = useState(''); const [error, setError] = useState(externalError || ''); + const [prevExternalError, setPrevExternalError] = useState(externalError); + + if (externalError !== prevExternalError) { + setError(externalError || ''); + setPrevExternalError(externalError); + } const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -17,6 +23,7 @@ const LoginView: React.FC = ({ onLogin, error: externalError }) setError('Bitte gib deine Wohnpro E-Mail Adresse ein.'); return; } + setPrevExternalError(undefined); // Reset to ensure the next error sync triggers onLogin(email); }; @@ -25,7 +32,7 @@ const LoginView: React.FC = ({ onLogin, error: externalError })
- + @@ -35,15 +42,19 @@ const LoginView: React.FC = ({ onLogin, error: externalError })
-
+
+ { setEmail(e.target.value); setError(''); }} placeholder="Wohnpro E-Mail Adresse" + aria-invalid={!!error} + aria-describedby={error ? "login-error" : undefined} className={`w-full bg-gray-50 border ${error ? 'border-red-200' : 'border-gray-100'} rounded-2xl px-6 py-4 focus:outline-none focus:ring-2 focus:ring-black/5 transition-all text-lg placeholder:text-gray-300`} /> - {error &&

{error}

} + {error && }