Skip to content

Commit

Permalink
Merge pull request #5 from study-hex/f/signup
Browse files Browse the repository at this point in the history
F/signup
  • Loading branch information
Aya-X authored Sep 9, 2023
2 parents 49bb353 + eccd0d4 commit e3f1400
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 69 deletions.
49 changes: 9 additions & 40 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,15 @@
import React from 'react';
import {
createHashRouter,
RouterProvider,
Link,
Outlet,
Navigate,
} from 'react-router-dom';
import { createHashRouter, RouterProvider, Navigate } from 'react-router-dom';

import { AuthProvider } from './contexts/AuthContext';

import Login from './routes/Login';
import Signup from './routes/Signup';
import Login from './routes/Login';
import Todo from './routes/Todo';
import NotFound from './routes/NotFound';

import ImgHero from './images/hero.webp';

function PublicLayout() {
return (
<div className="container mx-auto min-h-screen px-8 py-12 sm:flex sm:flex-wrap sm:items-center sm:justify-between lg:grid lg:grid-cols-12 lg:gap-[103px] xl:gap-[120px]">
<header className="sm:w-full md:mx-auto md:w-[47%] lg:col-span-5 lg:col-start-2 lg:w-full">
<Link
to="/signup"
className="leading-[3rem] hover:opacity-80"
title="TODOLIST"
>
<h1 className="mb-4 w-full overflow-hidden whitespace-nowrap bg-logo bg-[length:316px_46.9px] bg-center bg-no-repeat indent-[101%]">
ONLINE TODO LIST
</h1>
</Link>

<figure className="hidden w-full lg:inline-block">
<img
src={ImgHero}
alt="hero"
className="mx-auto object-cover lg:block lg:aspect-square"
/>
</figure>
</header>

<main className="sm:w-full md:mx-auto md:w-3/5 lg:col-span-4 lg:w-full">
<Outlet />
</main>
</div>
);
}
import PublicLayout from './routes/PublicLayout';
import ProtectedRoute from './routes/ProtectedRoute';

function App(): React.ReactElement {
const router = createHashRouter([
Expand All @@ -68,7 +33,11 @@ function App(): React.ReactElement {
},
{
path: '/todo',
element: <Todo />,
element: (
<ProtectedRoute>
<Todo />
</ProtectedRoute>
),
},
{
path: '/*',
Expand Down
48 changes: 44 additions & 4 deletions src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,71 @@
import { useState, useMemo, useContext, createContext } from 'react';

const AuthContext = createContext<IAuthContextData | undefined>(undefined);
import { useState, useMemo, useEffect, useContext, createContext } from 'react';

interface IAuthContextData {
token: string;
setToken: (token: string) => void;
saveToken: (resToken: string) => void;
removeToken: () => void;
}

interface IAuthProviderProps {
children: React.ReactNode;
}
// end of interface

const AuthContext = createContext<IAuthContextData | undefined>(undefined);

export function AuthProvider({ children }: IAuthProviderProps) {
const [token, setToken] = useState('');
const [token, setToken] = useState<string>('');

const saveToken = (resToken: string) => {
// console.log('AUTH_TOKEN:::', resToken);
setToken(resToken);
// localStorage.setItem('AUTH_TOKEN', resToken);

const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(1, 0, 0, 0);

document.cookie = `hexschoolTodo=${resToken}; expires=${tomorrow.toUTCString()}`;
};
// end of saveToken(resToken)

function removeToken() {
document.cookie =
'hexschoolTodo=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
setToken('');
}
// end of removeToken

useEffect(() => {
const cookieValue = document.cookie
.split('; ')
.find((row) => row.startsWith('hexschoolTodo='))
?.split('=')[1];

if (cookieValue) {
setToken(cookieValue);
}
}, []);
// end of useEffect()

const authContextData: IAuthContextData = useMemo(() => {
return {
token,
setToken,
saveToken,
removeToken,
};
}, [token, setToken]);
// end of useMemo()

return (
<AuthContext.Provider value={authContextData}>
{children}
</AuthContext.Provider>
);
}
// end of AuthProvider()

export function useAuth() {
const authContextData = useContext(AuthContext);
Expand All @@ -37,3 +76,4 @@ export function useAuth() {

return authContextData;
}
// end of useAuth()
35 changes: 25 additions & 10 deletions src/helpers/api.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios from 'axios';
import axios, { AxiosResponse } from 'axios';

import { Toast } from '../components/Toast';

Expand All @@ -23,30 +23,45 @@ const handleError = ({ err }: any = {}) => {
});
};

const signup = async (data: object) => {
const signup = async (data: object): Promise<any> => {
try {
const res = await req.post('/users/sign_up', data);
const res: AxiosResponse = await req.post('/users/sign_up', data);
if (res?.status !== 201) {
throw Error;
throw new Error();
}

return res?.data;
} catch (error) {
} catch (error: unknown) {
handleError(error);
}
};

const login = async (data: object) => {
const login = async (data: object): Promise<any> => {
try {
const res = await req.post('/users/sign_in', data);
const res: AxiosResponse = await req.post('/users/sign_in', data);
if (res?.status !== 200) {
throw Error;
throw new Error();
}

return res?.data;
} catch (error) {
} catch (error: unknown) {
handleError(error);
}
};

export const api = { signup, login };
const check = async (cookieValue: string): Promise<any> => {
try {
req.defaults.headers.common['Authorization'] = cookieValue;

const res: AxiosResponse = await req.get('/users/checkout');
if (res?.status !== 200) {
throw new Error();
}

return res?.data;
} catch (error: unknown) {
handleError(error);
}
};

export const api = { signup, login, check };
16 changes: 1 addition & 15 deletions src/routes/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,10 @@ const LoginSchema = Yup.object().shape({
});

function Login(): React.ReactElement {
const { token, setToken } = useAuth();
const { saveToken } = useAuth();

const navigate = useNavigate();

const saveToken = (resToken: string) => {
// console.log('AUTH_TOKEN:::', resToken);
setToken(resToken);
// localStorage.setItem('AUTH_TOKEN', resToken);

const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(1, 0, 0, 0);
// const date = new Date(exp * 1000);
// console.log(date.toUTCString());

document.cookie = `hexschoolTodo=${resToken}; expires=${tomorrow.toUTCString()}`;
};

return (
<Formik
initialValues={{
Expand Down
23 changes: 23 additions & 0 deletions src/routes/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
// import { Navigate } from 'react-router-dom';

import { useAuth } from '../contexts/AuthContext';

interface IProtectedRouteProps {
children: JSX.Element;
}
// end of interface

function ProtectedRoute({ children }: IProtectedRouteProps) {
const { token } = useAuth();

if (!token) {
return <Navigate to="/login" />;
// return <Navigate to="/" replace />;
}

return children;
}

export default ProtectedRoute;
71 changes: 71 additions & 0 deletions src/routes/PublicLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useEffect } from 'react';
import { Link, Outlet, useNavigate } from 'react-router-dom';

import { useAuth } from '../contexts/AuthContext';
import { api } from '../helpers/api';
import { Toast } from '../components/Toast';

import ImgHero from '../images/hero.webp';

function PublicLayout(): React.ReactElement {
const { token } = useAuth();
const navigate = useNavigate();

useEffect(() => {
if (token) {
api.check(token).then((res: any) => {
if (!res?.status) {
Toast.fire({
icon: 'warning',
title: '請重新操作',
});
}
// end of !res?.status

if (res?.status) {
Toast.fire({
icon: 'success',
title: '登入成功',
didClose: () => {
setTimeout(() => {
navigate('/todo');
}, 400);
},
});
}
// end of res?.status
});
// end of api
}
}, []);

return (
<div className="container mx-auto min-h-screen px-8 py-12 sm:flex sm:flex-wrap sm:items-center sm:justify-between lg:grid lg:grid-cols-12 lg:gap-[103px] xl:gap-[120px]">
<header className="sm:w-full md:mx-auto md:w-[47%] lg:col-span-5 lg:col-start-2 lg:w-full">
<Link
to="/signup"
className="leading-[3rem] hover:opacity-80"
title="TODOLIST"
>
<h1 className="mb-4 w-full overflow-hidden whitespace-nowrap bg-logo bg-[length:316px_46.9px] bg-center bg-no-repeat indent-[101%]">
ONLINE TODO LIST
</h1>
</Link>

<figure className="hidden w-full lg:inline-block">
<img
src={ImgHero}
alt="hero"
className="mx-auto object-cover lg:block lg:aspect-square"
/>
</figure>
</header>

<main className="sm:w-full md:mx-auto md:w-3/5 lg:col-span-4 lg:w-full">
<Outlet />
</main>
</div>
);
}

export default PublicLayout;

0 comments on commit e3f1400

Please sign in to comment.