Skip to content

Commit

Permalink
feat: add zustand
Browse files Browse the repository at this point in the history
Signed-off-by: hansputera <hanifdwyputrasembiring@gmail.com>
  • Loading branch information
hansputera committed May 29, 2023
1 parent 2a28ec0 commit 0bc02e3
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 9 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"formik": "^2.4.0",
"js-cookie": "^3.0.5",
"next": "13.4.4",
"postcss": "8.4.23",
"react": "18.2.0",
"react-dom": "18.2.0",
"swr": "^2.1.5",
"tailwindcss": "3.3.2",
"typescript": "5.0.4",
"yup": "^1.2.0"
"yup": "^1.2.0",
"zustand": "^4.3.8"
},
"devDependencies": {
"@types/js-cookie": "^3.0.3"
}
}
36 changes: 36 additions & 0 deletions pnpm-lock.yaml

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

1 change: 1 addition & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react'
import { Navbar } from '@/components/navbar'
import './globals.css'
import { SessionContextWrap } from '@/components/contexts/sessionContextWrap';
Expand Down
64 changes: 64 additions & 0 deletions src/components/forms/login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import React from 'react'
import { Righteous } from 'next/font/google'
import { Field, Form, Formik } from 'formik'
import * as Yup from 'yup'
import Cookies from 'js-cookie'
import { FormField } from './field';
import { useRouter } from 'next/navigation'

type DataProps = {
isError: boolean;
message?: string;
}

const loginValidation = Yup.object().shape({
email: Yup.string().required().email(),
Expand All @@ -13,6 +21,18 @@ const righteous = Righteous({
subsets: ['latin'],
});
export const LoginForm = () => {
const [data, setData] = React.useState<DataProps>({
isError: false,
});
const router = useRouter();
const savedToken = Cookies.get('ppdb_admin');

React.useEffect(() => {
if (savedToken) {
router.push('/me');
}
}, [savedToken, router]);

return (
<div>
<h1 className={`text-center text-2xl font-sans font-semibold uppercase ${righteous.className}`}>
Expand All @@ -26,6 +46,40 @@ export const LoginForm = () => {
}}
onSubmit={(values, actions) => {
actions.setSubmitting(true);

fetch('https://ppdb.api.sman3palu.sch.id/api/auth/login', {
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
}).then(r => r.json()).then(res => {
if (res.errors || res.error) {
setData({
isError: true,
message: res.error || res.message,
});
actions.setSubmitting(false);
} else {
if (!res?.data) {
setData({
isError: true,
message: 'Login failed, try again',
});
actions.setSubmitting(false);
return;
}

Cookies.set('ppdb_admin', res.data.token);
router.push('/me');
}
}).catch(e => {
actions.setSubmitting(false);
setData({
isError: true,
message: e.message,
});
});
}}
validationSchema={loginValidation}
>
Expand All @@ -43,6 +97,16 @@ export const LoginForm = () => {
{props.errors.password && props.touched.password ? (
<p className="text-red-500">{props.errors.password}</p>
) : null}
{data.message ? (
<div className="text-center mt-2">
<div className={`alert alert-${data.isError ? 'error' : 'success'} shadow-lg`}>
<div>
<svg xmlns="http://www.w3.org/2000/svg" className="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{data.isError ? 'Error' : 'Sukses'}! {data.message}</span>
</div>
</div>
</div>
) : null}
<div className="text-center">
<div className="text-center">
<button disabled={props.isSubmitting} className={`btn border-none mt-2 bg-[#1b30a5]${props.isSubmitting ? ' loading' : ''}`}>login</button>
Expand Down
16 changes: 10 additions & 6 deletions src/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
'use client';
import { SessionContext } from '@/contexts/sessionContext';
import { useContext } from 'react'
import { useSessionStore } from '@/contexts/sessionContext';
import Cookies from 'js-cookie'
import Link from 'next/link'

export const Navbar = () => {
const session = useContext(SessionContext);
const session = useSessionStore();
const tokenAdmin = Cookies.get('ppdb_admin');

if (tokenAdmin) {
session.setToken(tokenAdmin);
}
return (
<div className="navbar bg
import { SessionContext } from '@/contexts/sessionContext';-base-100">
<div className="navbar bg-base-100">
<div className="navbar-start">
<div className="dropdown">
<label tabIndex={0} className="btn btn-ghost btn-circle">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h7" /></svg>
</label>
<ul tabIndex={0} className="menu menu-compact dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52">
<li><a>Homepage</a></li>
{!session ? (
{!session.token ? (
<>
<li><Link href='/login'>Login</Link></li>
</>
Expand Down
29 changes: 27 additions & 2 deletions src/contexts/sessionContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React from 'react';
import { create } from 'zustand';

export enum UserStatus {
FullAdmin = 2,
Expand All @@ -12,6 +12,31 @@ export type UserSession = {
username: string;
email: string;
status: UserStatus;
token?: string;
}

export const SessionContext = React.createContext<UserSession | undefined>(undefined);
export type UserAction = {
updateState: (state: UserSession) => void;
setToken: (token: string) => void;
loadUserInfo: () => void;
}

export const useSessionStore = create<UserSession & UserAction>((set, state) => ({
email: '',
id: 0,
status: UserStatus.NonActive,
username: '',
updateState: (state) => set(() => state),
setToken: (token) => set((st) => ({ ...st, token })),
loadUserInfo: () => {
const st = state();
fetch('https://ppdb.api.sman3palu.sch.id/api/auth', {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${st.token}`,
},
}).then(r => r.json()).then(res => {
set((st) => ({ ...st, ...res.data }));
});
},
}));

0 comments on commit 0bc02e3

Please sign in to comment.