From b4dc8cc2ad9da8957c38096494a1889e3d00075b Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+Berry-13@users.noreply.github.com> Date: Mon, 26 Feb 2024 20:21:17 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=96=8C=EF=B8=8F=20style:=20auth=20dark=20?= =?UTF-8?q?theme=20(#1862)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove minLength validation and update login link style * Add theme selector component and update login form styles * Update styling in Login and LoginForm components * Update ResetPassword component styles and text color * Refactor login component and add theme selector * Add ThemeSelector component to Registration, RequestPasswordReset, and ResetPassword pages * chore(Login.tsx): remove unused `useCallback` * chore(Login.tsx) import order * Update ResetPassword.tsx import order * Update RequestPasswordReset.tsx import order * Update Registration.tsx import order --------- Co-authored-by: Danny Avila --- client/src/components/Auth/Login.tsx | 21 +++++++--- client/src/components/Auth/LoginForm.tsx | 11 +++--- client/src/components/Auth/Registration.tsx | 37 ++++++++++-------- .../components/Auth/RequestPasswordReset.tsx | 18 ++++++--- client/src/components/Auth/ResetPassword.tsx | 32 ++++++++------- client/src/components/Auth/SocialButton.tsx | 34 ++++------------ client/src/components/ui/ThemeSelector.tsx | 39 +++++++++++++++++++ client/src/components/ui/index.ts | 1 + 8 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 client/src/components/ui/ThemeSelector.tsx diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx index d38673a6b9..67aa7a5d24 100644 --- a/client/src/components/Auth/Login.tsx +++ b/client/src/components/Auth/Login.tsx @@ -3,10 +3,11 @@ import { useNavigate } from 'react-router-dom'; import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import { GoogleIcon, FacebookIcon, OpenIDIcon, GithubIcon, DiscordIcon } from '~/components'; import { useAuthContext } from '~/hooks/AuthContext'; +import { ThemeSelector } from '~/components/ui'; +import SocialButton from './SocialButton'; import { getLoginError } from '~/utils'; import { useLocalize } from '~/hooks'; import LoginForm from './LoginForm'; -import SocialButton from './SocialButton'; function Login() { const { login, error, isAuthenticated } = useAuthContext(); @@ -91,9 +92,15 @@ function Login() { }; return ( -
-
-

+
+
+ +
+
+

{localize('com_auth_welcome_back')}

{error && ( @@ -106,7 +113,7 @@ function Login() { )} {startupConfig.emailLoginEnabled && } {startupConfig.registrationEnabled && ( -

+

{' '} {localize('com_auth_no_account')}{' '} @@ -119,7 +126,9 @@ function Login() { {startupConfig.emailLoginEnabled && ( <>

diff --git a/client/src/components/Auth/LoginForm.tsx b/client/src/components/Auth/LoginForm.tsx index 92cac25bc2..64e5ea5494 100644 --- a/client/src/components/Auth/LoginForm.tsx +++ b/client/src/components/Auth/LoginForm.tsx @@ -40,17 +40,16 @@ const LoginForm: React.FC = ({ onSubmit }) => { aria-label={localize('com_auth_email')} {...register('email', { required: localize('com_auth_email_required'), - minLength: { value: 3, message: localize('com_auth_email_min_length') }, maxLength: { value: 120, message: localize('com_auth_email_max_length') }, pattern: { value: /\S+@\S+\.\S+/, message: localize('com_auth_email_pattern') }, })} aria-invalid={!!errors.email} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " /> @@ -70,12 +69,12 @@ const LoginForm: React.FC = ({ onSubmit }) => { maxLength: { value: 128, message: localize('com_auth_password_max_length') }, })} aria-invalid={!!errors.password} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " /> @@ -90,7 +89,7 @@ const LoginForm: React.FC = ({ onSubmit }) => { aria-label="Sign in" data-testid="login-button" type="submit" - className="w-full transform rounded-md bg-green-500 px-4 py-3 tracking-wide text-white transition-all duration-300 hover:bg-green-550 focus:bg-green-550 focus:outline-none" + className="w-full transform rounded-md bg-green-500 px-4 py-3 tracking-wide text-white transition-all duration-300 hover:bg-green-550 focus:bg-green-550 focus:outline-none dark:text-white" > {localize('com_auth_continue')} diff --git a/client/src/components/Auth/Registration.tsx b/client/src/components/Auth/Registration.tsx index 759b8593e0..3f3a51f842 100644 --- a/client/src/components/Auth/Registration.tsx +++ b/client/src/components/Auth/Registration.tsx @@ -1,11 +1,12 @@ -import React, { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; +import React, { useState, useEffect } from 'react'; import { useRegisterUserMutation, useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { TRegisterUser } from 'librechat-data-provider'; import { GoogleIcon, FacebookIcon, OpenIDIcon, GithubIcon, DiscordIcon } from '~/components'; -import { useLocalize } from '~/hooks'; +import { ThemeSelector } from '~/components/ui'; import SocialButton from './SocialButton'; +import { useLocalize } from '~/hooks'; const Registration: React.FC = () => { const navigate = useNavigate(); @@ -63,19 +64,19 @@ const Registration: React.FC = () => { validation, )} aria-invalid={!!errors[id]} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " data-testid={id} >
{errors[id] && ( - + {String(errors[id]?.message) ?? ''} )} @@ -147,9 +148,15 @@ const Registration: React.FC = () => { }; return ( -
-
-

+
+
+ +
+
+

{localize('com_auth_create_account')}

{error && ( @@ -191,7 +198,7 @@ const Registration: React.FC = () => { {renderInput('email', 'com_auth_email', 'email', { required: localize('com_auth_email_required'), minLength: { - value: 3, + value: 1, message: localize('com_auth_email_min_length'), }, maxLength: { @@ -228,13 +235,9 @@ const Registration: React.FC = () => {
-

+

{localize('com_auth_already_have_account')}{' '} - + {localize('com_auth_login')}

@@ -243,7 +246,9 @@ const Registration: React.FC = () => { {startupConfig.emailLoginEnabled && ( <>
-
Or
+
+ Or +
diff --git a/client/src/components/Auth/RequestPasswordReset.tsx b/client/src/components/Auth/RequestPasswordReset.tsx index f3249b70f5..fad1fce0dc 100644 --- a/client/src/components/Auth/RequestPasswordReset.tsx +++ b/client/src/components/Auth/RequestPasswordReset.tsx @@ -5,6 +5,7 @@ import { useRequestPasswordResetMutation, } from 'librechat-data-provider/react-query'; import type { TRequestPasswordReset, TRequestPasswordResetResponse } from 'librechat-data-provider'; +import { ThemeSelector } from '~/components/ui'; import { useLocalize } from '~/hooks'; function RequestPasswordReset() { @@ -102,18 +103,18 @@ function RequestPasswordReset() { }, })} aria-invalid={!!errors.email} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " >
{errors.email && ( - + {/* @ts-ignore not sure why */} {errors.email.message} @@ -139,9 +140,14 @@ function RequestPasswordReset() { }; return ( -
-
-

{headerText}

+
+
+ +
+
+

+ {headerText} +

{requestError && (
-
-

+
+
+ +
+
+

{localize('com_auth_reset_password_success')}

-
-

+
+
+ +
+
+

{localize('com_auth_reset_password')}

{resetError && (
{localize('com_auth_error_invalid_reset_token')}{' '} @@ -108,12 +115,12 @@ function ResetPassword() { }, })} aria-invalid={!!errors.password} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " > @@ -142,12 +149,12 @@ function ResetPassword() { value === password || localize('com_auth_password_not_match'), })} aria-invalid={!!errors.confirm_password} - className="peer block w-full appearance-none rounded-md border border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + className="peer block w-full appearance-none rounded-md border border-gray-300 bg-white px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700 dark:bg-gray-900 dark:text-white dark:focus:border-green-500" placeholder=" " > @@ -180,9 +187,6 @@ function ResetPassword() { > {localize('com_auth_continue')} - - {localize('com_auth_back_to_login')} -
diff --git a/client/src/components/Auth/SocialButton.tsx b/client/src/components/Auth/SocialButton.tsx index 80b01cec8f..7e76c6f763 100644 --- a/client/src/components/Auth/SocialButton.tsx +++ b/client/src/components/Auth/SocialButton.tsx @@ -3,8 +3,6 @@ import React, { useState } from 'react'; const SocialButton = ({ id, enabled, serverDomain, oauthPath, Icon, label }) => { const [isHovered, setIsHovered] = useState(false); const [isPressed, setIsPressed] = useState(false); - - // New state to keep track of the currently pressed button const [activeButton, setActiveButton] = useState(null); if (!enabled) { @@ -29,40 +27,24 @@ const SocialButton = ({ id, enabled, serverDomain, oauthPath, Icon, label }) => }; const getButtonStyles = () => { - const baseStyles = { - border: '1px solid #CCCCCC', - transition: 'background-color 0.3s ease, border 0.3s ease', - }; + // Define Tailwind CSS classes based on state + const baseStyles = 'border border-solid border-gray-300 dark:border-gray-800 transition-colors'; - if (isPressed && activeButton === id) { - return { - ...baseStyles, - backgroundColor: '#B9DAE9', - border: '2px solid #B9DAE9', - }; - } + const pressedStyles = 'bg-blue-200 border-blue-200 dark:bg-blue-900 dark:border-blue-600'; + const hoverStyles = 'bg-gray-100 dark:bg-gray-700'; - if (isHovered) { - return { - ...baseStyles, - backgroundColor: '#E5E5E5', - }; - } - - return { - ...baseStyles, - backgroundColor: 'transparent', - }; + return `${baseStyles} ${ + isPressed && activeButton === id ? pressedStyles : isHovered ? hoverStyles : '' + }`; }; return (
void }) => { + const themeIcons = { + system: , + dark: , + light: , + }; + + return ( +
+
onChange(theme === 'dark' ? 'light' : 'dark')}> + {themeIcons[theme]} +
+
+ ); +}; + +const ThemeSelector = () => { + const { theme, setTheme } = useContext(ThemeContext); + const changeTheme = useCallback( + (value: string) => { + setTheme(value); + }, + [setTheme], + ); + + return ( +
+
+ +
+
+ ); +}; + +export default ThemeSelector; diff --git a/client/src/components/ui/index.ts b/client/src/components/ui/index.ts index 3d4ce6262e..59c07b9103 100644 --- a/client/src/components/ui/index.ts +++ b/client/src/components/ui/index.ts @@ -26,3 +26,4 @@ export { default as SelectDropDown } from './SelectDropDown'; export { default as MultiSelectPop } from './MultiSelectPop'; export { default as SelectDropDownPop } from './SelectDropDownPop'; export { default as MultiSelectDropDown } from './MultiSelectDropDown'; +export { default as ThemeSelector } from './ThemeSelector';