Skip to content

Commit

Permalink
Add the github Oauth option to the dev login page
Browse files Browse the repository at this point in the history
- Break out the local login into a new client side file to avoid
rehydration errors in nextjs v15
Signed-off-by: Brent Salisbury <[email protected]>
  • Loading branch information
nerdalert committed Nov 13, 2024
1 parent fd0e89e commit 1cc5022
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 117 deletions.
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;
133 changes: 16 additions & 117 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,134 +2,33 @@
'use client';

import React, { useState, useEffect } from 'react';
import { signIn } from 'next-auth/react';
import GithubLogin from './githublogin';
import { Grid, GridItem } from '@patternfly/react-core/dist/dynamic/layouts/Grid';
import { Text, TextContent } from '@patternfly/react-core/dist/dynamic/components/Text';
import { Form, FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form';
import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput';
import { Button } from '@patternfly/react-core/dist/dynamic/components/Button';
import { HelperText, HelperTextItem } from '@patternfly/react-core/dist/dynamic/components/HelperText';
import './githublogin.css';
import LocalLogin from '@/app/login/locallogin';
import GithubLogin from '@/app/login/githublogin';

const Login: 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 [isProd, setIsProd] = useState<boolean | null>(null); // Use null for initial load state

const [isProd, setIsProd] = useState<boolean | null>(null);

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 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 loginForm = (
<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>
);

const devModeContent = (
<div className="login-page-background">
<Grid hasGutter span={12}>
<GridItem span={6} className="login-container">
<TextContent>
<Text className="sign-in-text">Login locally with admin username and password</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">{loginForm}</div>
<TextContent>
<Text className="urls-text">
<a href="https://github.com/instructlab/" style={{ color: 'white', textDecoration: 'underline' }} target="_blank">
GitHub
</a>{' '}
|{' '}
<a
href="https://github.com/instructlab/community/blob/main/Collaboration.md"
style={{ color: 'white', textDecoration: 'underline' }}
target="_blank"
>
Collaborate
</a>{' '}
|{' '}
<a
href="https://github.com/instructlab/community/blob/main/CODE_OF_CONDUCT.md"
style={{ color: 'white', textDecoration: 'underline' }}
target="_blank"
>
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">
Terms of use
</a>{' '}
|{' '}
<a href="https://www.redhat.com/en/about/privacy-policy" style={{ color: 'white', textDecoration: 'underline' }} target="_blank">
Privacy Policy
</a>
</Text>
</TextContent>
</GridItem>
</Grid>
</div>
);
if (isProd === null) {
// Render a loading indicator or null while determining the environment
return null;
}

return isProd ? <GithubLogin /> : devModeContent;
return isProd ? <GithubLogin /> : <LocalLogin />;
};

export default Login;

0 comments on commit 1cc5022

Please sign in to comment.