Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/login/githublogin.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ a {
padding-top: 20px;
font-size: large;
}

.login-label {
color: white;
}
152 changes: 152 additions & 0 deletions src/app/login/locallogin.tsx
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;
129 changes: 18 additions & 111 deletions src/app/login/page.tsx
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;