Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update UI #4

Merged
merged 3 commits into from
Sep 10, 2024
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
32 changes: 28 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions website/app/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import js from '@eslint/js';
import globals from 'globals';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';

export default [
{ ignores: ['dist'] },
Expand All @@ -28,11 +28,13 @@ export default [
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/prop-types': 'off',
'no-unused-vars': 'off',
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]
];
1 change: 1 addition & 0 deletions website/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"react-dom": "^18.3.1",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.26.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0"
},
"devDependencies": {
Expand Down
29 changes: 16 additions & 13 deletions website/app/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';

import ChatPage from "./chat/ChatPage";
import LoginPage from "./auth/LoginPage";
import RegistrationPage from "./auth/RegistrationPage";
import Layout from "./app/Layout";
import { AuthProvider } from "./auth/AuthProvider";
import { ProtectedRoute } from "./auth/ProtectedRoute";
import ChatPage from './chat/ChatPage';
import LoginPage from './auth/LoginPage';
import RegistrationPage from './auth/RegistrationPage';
import Layout from './app/Layout';
import { AuthProvider } from './auth/AuthProvider';
import { ProtectedRoute } from './auth/ProtectedRoute';
import AuthLayout from './app/AuthLayout';

const AppRoutes = () => {
return (
<Routes>
<Route path="" element={<Layout />}>
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegistrationPage />} />
<Route element={<ProtectedRoute loginPath="/login" />}>
<Route path="" element={<Navigate to="/chat" />} />
<Route path="/chat" element={<ChatPage />} />
<Route element={<AuthLayout />}>
<Route path='/login' element={<LoginPage />} />
<Route path='/register' element={<RegistrationPage />} />
</Route>
<Route path='' element={<Layout />}>
<Route element={<ProtectedRoute loginPath='/login' />}>
<Route path='' element={<Navigate to='/chat' />} />
<Route path='/chat' element={<ChatPage />} />
</Route>
</Route>
</Routes>
Expand Down
47 changes: 47 additions & 0 deletions website/app/src/app/AuthLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Outlet, Link, useLocation } from 'react-router-dom';
import { Logo } from '../components/Logo';

const AuthLayout = () => {
const location = useLocation();
const isLoginPage = location.pathname === '/login';

return (
<div className='min-h-screen flex flex-col justify-center items-center space-y-6'>
<Logo />
<div className='w-full max-w-md px-8 py-10 bg-white rounded-lg border flex flex-col space-y-6 items-center animate-fadeIn'>
<div className='w-full max-w-sm mx-auto'>
<Outlet />
</div>
<div className='text-center'>
{isLoginPage ? (
<div>
<span className='text-gray-500'>Don&apos;t have an account?</span>{' '}
<Link to='/register' className=' font-bold'>
Register here.
</Link>
</div>
) : (
<div>
<span className='text-gray-500'>Already have an account?</span>{' '}
<Link to='/login' className=' font-bold'>
Login here.
</Link>
</div>
)}
</div>
</div>
<div className='animate-fadeIn flex flex-col items-center gap-2 text-center '>
<span className='text-xs text-gray-800'>Powered By</span>
<a href='https://serverless.com/' target='_blank'>
<img
src='https://s3.us-east-2.amazonaws.com/assets.public.serverless/general/framework-text-lighting-icon-center-black.svg'
alt='Serverless Framework'
className='h-8 p-0 m-0'
/>
</a>
</div>
</div>
);
};

export default AuthLayout;
67 changes: 47 additions & 20 deletions website/app/src/app/Layout.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,61 @@
import { Outlet, useNavigate } from "react-router-dom";
import { useAuth } from "../auth/AuthProvider";
import { Outlet, useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthProvider';

const Layout = () => {
const navigate = useNavigate();
const { logout, isLoggedIn } = useAuth();

const handleLogout = () => {
logout();
navigate("/login");
navigate('/login');
};

return (
<div className="bg-gray-100 min-h-screen pt-20">
<div className="flex flex-row justify-between fixed top-0 left-0 w-full p-2 bg-white shadow-md">
<img
src="https://assets.serverless-extras.com/general/logo-aws-ai-stack-black.png"
alt="AWS AI Stack"
className="h-10 p-0 m-0"
/>
<div className="m-2">
{isLoggedIn() && (
<div className="cursor-pointer" onClick={handleLogout}>
Logout
</div>
)}
<div className='min-h-screen flex flex-col'>
{isLoggedIn() && (
<div className='flex bg-white items-center sticky top-0 left-0 w-full p-4 z-10 shadow-md gap-2'>
<a href='https://github.com/serverless/aws-ai-stack' target='_blank'>
<img
src='https://assets.serverless-extras.com/general/logo-aws-ai-stack-black.png'
alt='AWS AI Stack'
className='h-10'
/>
</a>
<div className='flex items-center gap-2 text-center ml-auto mr-0 justify-center'>
<span className='text-xs text-gray-800 mt-1.5'>By</span>
<a href='https://serverless.com/' target='_blank'>
<img
src='https://s3.us-east-2.amazonaws.com/assets.public.serverless/general/framework-text-lighting-icon-center-black.svg'
alt='Serverless Framework'
className='h-8'
/>
</a>
</div>
<button
onClick={handleLogout}
className='text-gray-400 bg-transparent px-2 py-2 rounded-md hover:bg-primary hover:text-white focus:outline-none focus:ring-2 focus:ring-primary transition-colors'
>
<svg
xmlns='http://www.w3.org/2000/svg'
width='18'
height='18'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
className='lucide lucide-log-out'
>
<path d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4' />
<polyline points='16 17 21 12 16 7' />
<line x1='21' x2='9' y1='12' y2='12' />
</svg>
</button>
</div>
</div>
<div>
<Outlet />
</div>
)}

<Outlet />
</div>
);
};
Expand Down
22 changes: 11 additions & 11 deletions website/app/src/auth/AuthProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import { createContext, useContext } from "react";
import { createContext, useContext } from 'react';

const authApiUrl = import.meta.env.VITE_AUTH_API_URL;
const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
const getToken = () => localStorage.getItem("token");
const getToken = () => localStorage.getItem('token');

const login = async ({ email, password }) => {
const response = await fetch(`${authApiUrl}/login`, {
method: "POST",
method: 'POST',
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (data.token) {
localStorage.setItem("token", data.token);
localStorage.setItem('token', data.token);
} else if (data.error) {
throw new Error(data.error);
} else {
throw new Error("Unknown error");
throw new Error('Unknown error');
}
};

const register = async ({ email, password }) => {
const response = await fetch(`${authApiUrl}/register`, {
method: "POST",
method: 'POST',
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (data.token) {
localStorage.setItem("token", data.token);
localStorage.setItem('token', data.token);
} else if (data.error) {
throw new Error(data.error);
} else {
throw new Error("Unknown error");
throw new Error('Unknown error');
}
};

const logout = () => {
localStorage.removeItem("token");
localStorage.removeItem('token');
};

const isLoggedIn = () => {
Expand Down
Loading