diff --git a/app/apis/admin/login-staff.ts b/app/apis/admin/login-staff.ts index dbdcaf4..64a8381 100644 --- a/app/apis/admin/login-staff.ts +++ b/app/apis/admin/login-staff.ts @@ -1,7 +1,7 @@ import { z } from 'zod' -import { type TLoginSchema } from '~/layouts/news/form-login' import { HttpServer } from '~/libs/http-server' +import type { TLoginSchema } from '~/pages/admin-login' const loginResponseSchema = z.object({ data: z.object({ diff --git a/app/configs/cookies.ts b/app/configs/cookies.ts index 7e832ae..9b78dff 100644 --- a/app/configs/cookies.ts +++ b/app/configs/cookies.ts @@ -2,6 +2,6 @@ export const USER_COOKIES = { token: '__lg-usr-tkn', } -export const ADMIN_COOKIES = { - token: '__lg-adm-tkn', +export const STAFF_COOKIES = { + token: '__lg-stf-tkn', } diff --git a/app/libs/cookie.server.ts b/app/libs/cookie.server.ts index a6c5ce4..557d52e 100644 --- a/app/libs/cookie.server.ts +++ b/app/libs/cookie.server.ts @@ -1,6 +1,6 @@ import { createCookie } from 'react-router' -import { ADMIN_COOKIES, USER_COOKIES } from '~/configs/cookies' +import { STAFF_COOKIES, USER_COOKIES } from '~/configs/cookies' export const userTokenCookieConfig = createCookie(USER_COOKIES.token, { httpOnly: false, @@ -10,7 +10,7 @@ export const userTokenCookieConfig = createCookie(USER_COOKIES.token, { path: '/', }) -export const adminTokenCookieConfig = createCookie(ADMIN_COOKIES.token, { +export const staffTokenCookieConfig = createCookie(STAFF_COOKIES.token, { httpOnly: false, sameSite: 'lax', secure: process.env.NODE_ENV === 'production', diff --git a/app/libs/cookies.ts b/app/libs/cookies.ts index 2a84148..b14ed8e 100644 --- a/app/libs/cookies.ts +++ b/app/libs/cookies.ts @@ -1,16 +1,16 @@ -import { adminTokenCookieConfig, userTokenCookieConfig } from './cookie.server' +import { staffTokenCookieConfig, userTokenCookieConfig } from './cookie.server' export const handleCookie = async (request: Request) => { const headers = request.headers const userToken = (await userTokenCookieConfig.parse( headers.get('Cookie'), )) as string - const adminToken = (await adminTokenCookieConfig.parse( + const staffToken = (await staffTokenCookieConfig.parse( headers.get('Cookie'), )) as string return { userToken, - adminToken, + staffToken, } } diff --git a/app/libs/logout-header.server.ts b/app/libs/logout-header.server.ts index dd8964f..39563de 100644 --- a/app/libs/logout-header.server.ts +++ b/app/libs/logout-header.server.ts @@ -1,4 +1,4 @@ -import { ADMIN_COOKIES, USER_COOKIES } from '~/configs/cookies' +import { STAFF_COOKIES, USER_COOKIES } from '~/configs/cookies' export const setUserLogoutHeaders = () => { const responseHeaders = new Headers() @@ -10,11 +10,11 @@ export const setUserLogoutHeaders = () => { return responseHeaders } -export const setAdminLogoutHeaders = () => { +export const setStaffLogoutHeaders = () => { const responseHeaders = new Headers() responseHeaders.append( 'Set-Cookie', - `${ADMIN_COOKIES.token}=; Path=/lg-admin; HttpOnly; SameSite=Strict; Max-Age=0`, + `${STAFF_COOKIES.token}=; Path=/lg-admin; HttpOnly; SameSite=Strict; Max-Age=0`, ) return responseHeaders diff --git a/app/pages/admin-login/index.tsx b/app/pages/admin-login/index.tsx index 2698423..7389545 100644 --- a/app/pages/admin-login/index.tsx +++ b/app/pages/admin-login/index.tsx @@ -1,12 +1,43 @@ -import { useState } from 'react' -import { Link } from 'react-router' +import { zodResolver } from '@hookform/resolvers/zod' +import { useEffect, useState } from 'react' +import { Link, useFetcher } from 'react-router' +import { RemixFormProvider, useRemixForm } from 'remix-hook-form' +import { z } from 'zod' -import { EyeIcon } from '~/components/icons/eye' import { Button } from '~/components/ui/button' +import { Input } from '~/components/ui/input' import { APP } from '~/configs/meta' +export const loginSchema = z.object({ + email: z.string().email('Email tidak valid'), + password: z.string().min(6, 'Kata sandi minimal 6 karakter'), +}) + +export type TLoginSchema = z.infer + export const AdminLoginPage = () => { - const [showPassword, setShowPassword] = useState(false) + const fetcher = useFetcher() + const formMethods = useRemixForm({ + mode: 'onSubmit', + fetcher, + resolver: zodResolver(loginSchema), + }) + const [error, setError] = useState() + const [disabled, setDisabled] = useState(false) + + const { handleSubmit } = formMethods + + useEffect(() => { + if (!fetcher.data?.success) { + setError(fetcher.data?.message) + setDisabled(false) + return + } + + setDisabled(true) + setError(undefined) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetcher]) return (
@@ -24,70 +55,52 @@ export const AdminLoginPage = () => { Selamat Datang, silakan masukkan akun Anda untuk melanjutkan!

-
- {/* Input Email / No Telepon */} -
- - + + -
- {/* Input Password */} -
- - - -
- {/* Lupa Kata Sandi */} -
- Lupa Kata Sandi? - - Reset Kata Sandi - -
+ {error && ( +
{error}
+ )} - {/* Tombol Masuk */} - -
+ {/* Lupa Kata Sandi */} +
+ Lupa Kata Sandi? + + Reset Kata Sandi + +
+ + + +
{/* Link Daftar */} diff --git a/app/routes/_admin.lg-admin.tsx b/app/routes/_admin.lg-admin.tsx index 5eb7a6f..da8f184 100644 --- a/app/routes/_admin.lg-admin.tsx +++ b/app/routes/_admin.lg-admin.tsx @@ -9,18 +9,18 @@ import { handleCookie } from '~/libs/cookies' import type { Route } from './+types/_admin.lg-admin' export const loader = async ({ request }: Route.LoaderArgs) => { - const { adminToken } = await handleCookie(request) + const { staffToken } = await handleCookie(request) const { pathname } = new URL(request.url) const isAuthPage = AUTH_PAGES.includes(pathname) let adminData - if (!isAuthPage && !adminToken) { + if (!isAuthPage && !staffToken) { throw redirect('/lg-admin/login') } - if (adminToken) { + if (staffToken) { const { data } = await getStaff({ - accessToken: adminToken, + accessToken: staffToken, }) adminData = data } diff --git a/app/routes/actions.admin.login.ts b/app/routes/actions.admin.login.ts index 6923511..778cb7a 100644 --- a/app/routes/actions.admin.login.ts +++ b/app/routes/actions.admin.login.ts @@ -3,10 +3,10 @@ import { data } from 'react-router' import { getValidatedFormData } from 'remix-hook-form' import { XiorError } from 'xior' -import { getUser } from '~/apis/news/get-user' -import { userLoginRequest } from '~/apis/news/login-user' -import { loginSchema, type TLoginSchema } from '~/layouts/news/form-login' -import { generateTokenCookie } from '~/utils/token' +import { getStaff } from '~/apis/admin/get-staff' +import { staffLoginRequest } from '~/apis/admin/login-staff' +import { loginSchema, type TLoginSchema } from '~/pages/admin-login' +import { generateStaffTokenCookie } from '~/utils/token' import type { Route } from './+types/actions.login' @@ -26,12 +26,12 @@ export const action = async ({ request }: Route.ActionArgs) => { return data({ success: false, errors, defaultValues }, { status: 400 }) } - const { data: loginData } = await userLoginRequest(payload) + const { data: loginData } = await staffLoginRequest(payload) const { token } = loginData - const { data: userData } = await getUser({ + const { data: staffData } = await getStaff({ accessToken: token, }) - const tokenCookie = generateTokenCookie({ + const tokenCookie = generateStaffTokenCookie({ token, }) @@ -41,7 +41,7 @@ export const action = async ({ request }: Route.ActionArgs) => { return data( { success: true, - user: userData, + staff: staffData, }, { headers, diff --git a/app/routes/actions.login.ts b/app/routes/actions.login.ts index 6923511..a04d136 100644 --- a/app/routes/actions.login.ts +++ b/app/routes/actions.login.ts @@ -6,7 +6,7 @@ import { XiorError } from 'xior' import { getUser } from '~/apis/news/get-user' import { userLoginRequest } from '~/apis/news/login-user' import { loginSchema, type TLoginSchema } from '~/layouts/news/form-login' -import { generateTokenCookie } from '~/utils/token' +import { generateUserTokenCookie } from '~/utils/token' import type { Route } from './+types/actions.login' @@ -31,7 +31,7 @@ export const action = async ({ request }: Route.ActionArgs) => { const { data: userData } = await getUser({ accessToken: token, }) - const tokenCookie = generateTokenCookie({ + const tokenCookie = generateUserTokenCookie({ token, }) diff --git a/app/routes/actions.register.ts b/app/routes/actions.register.ts index fac144b..f4ba8a2 100644 --- a/app/routes/actions.register.ts +++ b/app/routes/actions.register.ts @@ -9,7 +9,7 @@ import { registerSchema, type TRegisterSchema, } from '~/layouts/news/form-register' -import { generateTokenCookie } from '~/utils/token' +import { generateUserTokenCookie } from '~/utils/token' import type { Route } from './+types/actions.register' @@ -34,7 +34,7 @@ export const action = async ({ request }: Route.ActionArgs) => { const { data: userData } = await getUser({ accessToken: token, }) - const tokenCookie = generateTokenCookie({ + const tokenCookie = generateUserTokenCookie({ token, }) diff --git a/app/utils/token.ts b/app/utils/token.ts index 9bd4ebd..1069738 100644 --- a/app/utils/token.ts +++ b/app/utils/token.ts @@ -1,12 +1,15 @@ import { decodeJwt } from 'jose' -import { userTokenCookieConfig } from '~/libs/cookie.server' +import { + staffTokenCookieConfig, + userTokenCookieConfig, +} from '~/libs/cookie.server' type TTokenCookie = { token: string } -export const generateTokenCookie = (parameters: TTokenCookie) => { +export const generateUserTokenCookie = (parameters: TTokenCookie) => { const { token } = parameters const decodedToken = decodeJwt(token) @@ -19,3 +22,17 @@ export const generateTokenCookie = (parameters: TTokenCookie) => { expires: expirationDate, }) } + +export const generateStaffTokenCookie = (parameters: TTokenCookie) => { + const { token } = parameters + + const decodedToken = decodeJwt(token) + const decodedTokenExp = decodedToken.exp + const expirationDate = decodedTokenExp + ? new Date(decodedTokenExp * 1000) + : undefined + + return staffTokenCookieConfig.serialize(token, { + expires: expirationDate, + }) +}