LibreChat/client/src/components/Auth/AuthLayout.tsx
Dustin Healy 39cecc97bd
🛂 fix: Address Accessibility Issues - Axe Rating: Serious (#10607)
* feat: wrap main content of page in <main> tag for screen reader landmarks (439)

* feat: add italic on active convo when selected so that selection state does not rely on bg contrast ratio (562)

* feat: add border ring around SearchBar so that it passes focus contrast minimums (577)

* fix: hide decorative SVGs from screen readers (578)

* fix: stop clipping of focus outlines in My Files modal (593)

* feat: programmatically declare state of Temporary Chat toggle for screen readers (606)

* feat: add sr-only components to warn screen readers that footer links open in new tab (611)

* feat: add aria-labels to archived chat table buttons

* feat: add screen reader heading for prompt edit page (776)

* feat: increase contrast to threshold minimum for production tag in prompts advanced view (773)

* feat: increase contrast to thehold minimums for production tag and version card border highlights (770)

* fix: h2 now reads as 'control bar' to screen readers in edit prompt page (768)

* feat: add selected state tracking for simple / advanced toggle for screen readers (765)

* feat: add left padding to theme selector in prompts side nav panel so that focus outline doesnt clip

* feat: darken orange bg for warning toasts to hit 3:1 contrast minimum with white text (725)

* fix: return focus to triggering element on modal close for image preview in attach files panel (717)

* fix: hide SVG for AddMultiConvo button from screen readers (708)

* feat: add persistent label to Filter Memories... input in memory side panel
2025-12-11 16:35:40 -05:00

98 lines
3.1 KiB
TypeScript

import { ThemeSelector } from '@librechat/client';
import { TStartupConfig } from 'librechat-data-provider';
import { ErrorMessage } from '~/components/Auth/ErrorMessage';
import { TranslationKeys, useLocalize } from '~/hooks';
import SocialLoginRender from './SocialLoginRender';
import { BlinkAnimation } from './BlinkAnimation';
import { Banner } from '../Banners';
import Footer from './Footer';
function AuthLayout({
children,
header,
isFetching,
startupConfig,
startupConfigError,
pathname,
error,
}: {
children: React.ReactNode;
header: React.ReactNode;
isFetching: boolean;
startupConfig: TStartupConfig | null | undefined;
startupConfigError: unknown | null | undefined;
pathname: string;
error: TranslationKeys | null;
}) {
const localize = useLocalize();
const hasStartupConfigError = startupConfigError !== null && startupConfigError !== undefined;
const DisplayError = () => {
if (hasStartupConfigError) {
return (
<div className="mx-auto sm:max-w-sm">
<ErrorMessage>{localize('com_auth_error_login_server')}</ErrorMessage>
</div>
);
} else if (error === 'com_auth_error_invalid_reset_token') {
return (
<div className="mx-auto sm:max-w-sm">
<ErrorMessage>
{localize('com_auth_error_invalid_reset_token')}{' '}
<a className="font-semibold text-green-600 hover:underline" href="/forgot-password">
{localize('com_auth_click_here')}
</a>{' '}
{localize('com_auth_to_try_again')}
</ErrorMessage>
</div>
);
} else if (error != null && error) {
return (
<div className="mx-auto sm:max-w-sm">
<ErrorMessage>{localize(error)}</ErrorMessage>
</div>
);
}
return null;
};
return (
<div className="relative flex min-h-screen flex-col bg-white dark:bg-gray-900">
<Banner />
<BlinkAnimation active={isFetching}>
<div className="mt-6 h-10 w-full bg-cover">
<img
src="assets/logo.svg"
className="h-full w-full object-contain"
alt={localize('com_ui_logo', { 0: startupConfig?.appTitle ?? 'LibreChat' })}
/>
</div>
</BlinkAnimation>
<DisplayError />
<div className="absolute bottom-0 left-0 md:m-4">
<ThemeSelector />
</div>
<main className="flex flex-grow items-center justify-center">
<div className="w-authPageWidth overflow-hidden bg-white px-6 py-4 dark:bg-gray-900 sm:max-w-md sm:rounded-lg">
{!hasStartupConfigError && !isFetching && header && (
<h1
className="mb-4 text-center text-3xl font-semibold text-black dark:text-white"
style={{ userSelect: 'none' }}
>
{header}
</h1>
)}
{children}
{!pathname.includes('2fa') &&
(pathname.includes('login') || pathname.includes('register')) && (
<SocialLoginRender startupConfig={startupConfig} />
)}
</div>
</main>
<Footer startupConfig={startupConfig} />
</div>
);
}
export default AuthLayout;