forked from instructlab/ui
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request instructlab#313 from nerdalert/local-login-page
Align the local dev login page with the Oauth login page
- Loading branch information
Showing
3 changed files
with
174 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,3 +63,7 @@ a { | |
padding-top: 20px; | ||
font-size: large; | ||
} | ||
|
||
.login-label { | ||
color: white; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// src/app/login/LocalLogin.tsx | ||
// 'use client'; | ||
|
||
import React, { useState } from 'react'; | ||
import { signIn } from 'next-auth/react'; | ||
import { Grid, GridItem, Text, TextContent, Form, FormGroup, TextInput, Button, HelperText, HelperTextItem } from '@patternfly/react-core'; | ||
import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon'; | ||
import './githublogin.css'; | ||
|
||
const LocalLogin: React.FunctionComponent = () => { | ||
const [, setShowHelperText] = useState(false); | ||
const [username, setUsername] = useState(''); | ||
const [isValidUsername, setIsValidUsername] = useState(true); | ||
const [password, setPassword] = useState(''); | ||
const [isValidPassword, setIsValidPassword] = useState(true); | ||
|
||
const handleLogin = async (e: React.FormEvent) => { | ||
e.preventDefault(); | ||
const result = await signIn('credentials', { redirect: false, username, password }); | ||
if (result?.error) { | ||
setShowHelperText(true); | ||
setIsValidUsername(false); | ||
setIsValidPassword(false); | ||
} else { | ||
window.location.href = '/'; | ||
} | ||
}; | ||
|
||
const handleUsernameChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => { | ||
setUsername(value); | ||
}; | ||
|
||
const handlePasswordChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => { | ||
setPassword(value); | ||
}; | ||
|
||
const handleGitHubLogin = () => { | ||
signIn('github', { callbackUrl: '/' }); | ||
}; | ||
|
||
return ( | ||
<div className="login-page-background"> | ||
<Grid hasGutter span={12}> | ||
<GridItem span={6} className="login-container"> | ||
<TextContent> | ||
<Text className="sign-in-text">Login locally with a username and password or via GitHub OAuth</Text> | ||
</TextContent> | ||
<TextContent> | ||
<Text className="description-text">Join the novel, community-based movement to create truly open-source LLMs</Text> | ||
</TextContent> | ||
<div className="login-container"> | ||
<Button | ||
variant="primary" | ||
icon={<GithubIcon />} | ||
iconPosition="left" | ||
size="lg" | ||
style={{ backgroundColor: 'black', marginBottom: '1rem' }} | ||
onClick={handleGitHubLogin} | ||
> | ||
Sign in with GitHub | ||
</Button> | ||
<Form onSubmit={handleLogin}> | ||
<FormGroup label="Username" fieldId="username" className="login-label"> | ||
<TextInput | ||
value={username} | ||
onChange={handleUsernameChange} | ||
id="username" | ||
isRequired | ||
validated={isValidUsername ? 'default' : 'error'} | ||
/> | ||
{!isValidUsername && ( | ||
<HelperText> | ||
<HelperTextItem variant="error">Invalid Username</HelperTextItem> | ||
</HelperText> | ||
)} | ||
</FormGroup> | ||
<FormGroup label="Password" fieldId="password" className="login-label"> | ||
<TextInput | ||
value={password} | ||
onChange={handlePasswordChange} | ||
id="password" | ||
type="password" | ||
isRequired | ||
validated={isValidPassword ? 'default' : 'error'} | ||
/> | ||
{!isValidPassword && ( | ||
<HelperText> | ||
<HelperTextItem variant="error">Invalid password</HelperTextItem> | ||
</HelperText> | ||
)} | ||
</FormGroup> | ||
<Button type="submit" style={{ backgroundColor: 'black', color: 'white' }}> | ||
Login | ||
</Button> | ||
</Form> | ||
</div> | ||
<TextContent> | ||
<Text className="urls-text"> | ||
<a | ||
href="https://github.com/instructlab/" | ||
style={{ color: 'white', textDecoration: 'underline' }} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
GitHub | ||
</a>{' '} | ||
|{' '} | ||
<a | ||
href="https://github.com/instructlab/community/blob/main/Collaboration.md" | ||
style={{ color: 'white', textDecoration: 'underline' }} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Collaborate | ||
</a>{' '} | ||
|{' '} | ||
<a | ||
href="https://github.com/instructlab/community/blob/main/CODE_OF_CONDUCT.md" | ||
style={{ color: 'white', textDecoration: 'underline' }} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Code Of Conduct | ||
</a> | ||
</Text> | ||
<Text className="urls-text-medium"> | ||
<a | ||
href="https://www.redhat.com/en/about/terms-use" | ||
style={{ color: 'white', textDecoration: 'underline' }} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Terms of use | ||
</a>{' '} | ||
|{' '} | ||
<a | ||
href="https://www.redhat.com/en/about/privacy-policy" | ||
style={{ color: 'white', textDecoration: 'underline' }} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Privacy Policy | ||
</a> | ||
</Text> | ||
</TextContent> | ||
</GridItem> | ||
</Grid> | ||
</div> | ||
); | ||
}; | ||
|
||
export default LocalLogin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,34 @@ | ||
// src/app/login/page.tsx | ||
'use client'; | ||
|
||
import React, { useState } from 'react'; | ||
import { signIn } from 'next-auth/react'; | ||
import { LoginFooterItem, LoginForm, LoginMainFooterLinksItem, LoginPage } from '@patternfly/react-core/dist/dynamic/components/LoginPage'; | ||
import { ListItem, ListVariant } from '@patternfly/react-core/dist/dynamic/components/List'; | ||
import GithubLogin from './githublogin'; | ||
import React, { useState, useEffect } from 'react'; | ||
import './githublogin.css'; | ||
import LocalLogin from '@/app/login/locallogin'; | ||
import GithubLogin from '@/app/login/githublogin'; | ||
|
||
const Login: React.FunctionComponent = () => { | ||
const [showHelperText, setShowHelperText] = useState(false); | ||
const [username, setUsername] = useState(''); | ||
const [isValidUsername, setIsValidUsername] = useState(true); | ||
const [password, setPassword] = useState(''); | ||
const [isValidPassword, setIsValidPassword] = useState(true); | ||
const [isRememberMeChecked, setIsRememberMeChecked] = useState(false); | ||
const [isProd, setIsProd] = useState<boolean | null>(null); // Use null for initial load state | ||
const [isProd, setIsProd] = useState<boolean | null>(null); | ||
|
||
React.useEffect(() => { | ||
useEffect(() => { | ||
const chooseLoginPage = async () => { | ||
const res = await fetch('/api/envConfig'); | ||
const envConfig = await res.json(); | ||
setIsProd(envConfig.DEPLOYMENT_TYPE !== 'dev'); | ||
try { | ||
const res = await fetch('/api/envConfig'); | ||
const envConfig = await res.json(); | ||
setIsProd(envConfig.DEPLOYMENT_TYPE !== 'dev'); | ||
} catch (error) { | ||
console.error('Error fetching environment config:', error); | ||
setIsProd(true); | ||
} | ||
}; | ||
chooseLoginPage(); | ||
}, []); | ||
|
||
const handleUsernameChange = (event: React.FormEvent<HTMLInputElement>, value: string) => { | ||
setUsername(value); | ||
}; | ||
|
||
const handlePasswordChange = (event: React.FormEvent<HTMLInputElement>, value: string) => { | ||
setPassword(value); | ||
}; | ||
|
||
const onRememberMeClick = () => { | ||
setIsRememberMeChecked(!isRememberMeChecked); | ||
}; | ||
|
||
const onLoginButtonClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { | ||
event.preventDefault(); | ||
const result = await signIn('credentials', { | ||
redirect: false, | ||
username, | ||
password | ||
}); | ||
|
||
if (result?.error) { | ||
setIsValidUsername(false); | ||
setIsValidPassword(false); | ||
setShowHelperText(true); | ||
} else { | ||
window.location.href = '/'; | ||
} | ||
}; | ||
|
||
const handleGitHubLogin = () => { | ||
signIn('github', { callbackUrl: '/' }); // Redirect to home page after login | ||
}; | ||
|
||
const socialMediaLoginContent = ( | ||
<LoginMainFooterLinksItem href="#" onClick={handleGitHubLogin} linkComponentProps={{ 'aria-label': 'Login with Github' }}> | ||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" width="48" height="48"> | ||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" /> | ||
</svg> | ||
</LoginMainFooterLinksItem> | ||
); | ||
|
||
const listItem = ( | ||
<React.Fragment> | ||
<ListItem> | ||
<LoginFooterItem href="https://instructlab.ai/">Terms of Use </LoginFooterItem> | ||
</ListItem> | ||
<ListItem> | ||
<LoginFooterItem href="https://instructlab.ai/">Help</LoginFooterItem> | ||
</ListItem> | ||
<ListItem> | ||
<LoginFooterItem href="https://instructlab.ai/">Privacy Policy</LoginFooterItem> | ||
</ListItem> | ||
</React.Fragment> | ||
); | ||
|
||
const loginForm = ( | ||
<LoginForm | ||
showHelperText={showHelperText} | ||
helperText="Invalid login credentials." | ||
usernameLabel="Username" | ||
usernameValue={username} | ||
onChangeUsername={handleUsernameChange} | ||
isValidUsername={isValidUsername} | ||
passwordLabel="Password" | ||
passwordValue={password} | ||
onChangePassword={handlePasswordChange} | ||
isValidPassword={isValidPassword} | ||
isRememberMeChecked={isRememberMeChecked} | ||
onChangeRememberMe={onRememberMeClick} | ||
onLoginButtonClick={onLoginButtonClick} | ||
loginButtonLabel="Login" | ||
/> | ||
); | ||
|
||
if (isProd === null) return null; // Render nothing until environment is loaded | ||
|
||
if (isProd) { | ||
return <GithubLogin />; | ||
if (isProd === null) { | ||
// Render a loading indicator or null while determining the environment | ||
return null; | ||
} | ||
|
||
return ( | ||
<LoginPage | ||
suppressHydrationWarning={true} | ||
footerListVariants={ListVariant.inline} | ||
brandImgSrc="/InstructLab-Logo.svg" | ||
brandImgAlt="InstructLab logo" | ||
backgroundImgSrc="/login-bg.svg" | ||
footerListItems={listItem} | ||
textContent="InstructLab Taxonomy Submissions" | ||
loginTitle="Login Securely with admin username and password" | ||
loginSubtitle="Local Account" | ||
socialMediaLoginContent={socialMediaLoginContent} | ||
socialMediaLoginAriaLabel="Log in with GitHub" | ||
> | ||
{loginForm} | ||
</LoginPage> | ||
); | ||
return isProd ? <GithubLogin /> : <LocalLogin />; | ||
}; | ||
|
||
export default Login; |