ferinesia-schedule-board/src/pages/auth/login.tsx

186 lines
7.2 KiB
TypeScript
Executable File

import ferinesiaLogo from "@/assets/logos/ferinesia-logo-thin.png";
import { endpoints } from "@/config/host";
import { createAxiosInstance } from "@/functions/axios-instance";
import { useEffect, useState } from "react";
import { useLocation, useSearchParams } from "react-router-dom";
import Cookies from "js-cookie";
import { AxiosError } from "axios";
/**
* Copyright: Task Master (https://tailwindflex.com/@task_master)
* URL: https://tailwindflex.com/@task_master/login-form-with-social-login-buttons
*/
export interface InputValidationError {
type: string;
message: string;
}
export default function Login() {
const [searchParams] = useSearchParams();
const [isLoading, setIsLoading] = useState(false);
const [loginToken, setLoginToken] = useState<string | null>(null);
const [terminalID, setTerminalID] = useState<string | null>(null);
const [isSuccessLogin, setIsSuccessLogin] = useState<boolean | null>(null);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [inputValidationMsg, setInputValidationMsg] = useState<InputValidationError[]>([]);
const doLogin = async () => {
let validationErrors = []
if(username.length <= 0){
validationErrors.push({type: "username", message: "Username tidak boleh kosong"})
}
if(password.length <= 0){
validationErrors.push({type: "password", message: "Password tidak boleh kosong"})
}
setInputValidationMsg(validationErrors)
if(validationErrors.length > 0)
return
setIsLoading(true)
try{
const {data} = await createAxiosInstance({}).post(endpoints.login, {
username,
password,
token: loginToken
});
setIsSuccessLogin(true);
return
}
catch(err){
if(err instanceof AxiosError){
if(err.response?.data?.meta?.is_error){
validationErrors.push({type: "username", message: err.response?.data?.meta?.message})
}
}
else
validationErrors.push({type: "username", message: "Terjadi kesalahan saat memproses data"})
}
finally{
setInputValidationMsg(validationErrors)
setIsLoading(false)
}
}
useEffect(() => {
setLoginToken(searchParams.get("state") ?? null);
setTerminalID(searchParams.get("terminal_id") ?? null);
});
return (
<div className="min-h-screen bg-gray-100 flex flex-col justify-center sm:py-12">
<div className="p-10 xs:p-0 mx-auto md:w-full md:max-w-md">
<div className="flex flex-col items-center">
<img src={ferinesiaLogo} width={128} />
<h1 className="font-bold text-center text-2xl mb-5">
<span className="text-blue-500">ferinesia</span>
<span className="text-yellow-500">.com</span>
</h1>
</div>
<div className="bg-white shadow w-full rounded-lg pt-4 pb-4">
{
(isSuccessLogin == null || !isSuccessLogin) && <>
<div className="px-5 py-2">
<label className="font-semibold text-sm text-gray-600 pb-1 block">
Terminal ID
</label>
<input
type="text"
className="border rounded-lg px-3 py-2 mt-1 mb-1 text-sm w-full"
value={terminalID ?? "N/A"}
disabled
/>
{/* {
inputValidationMsg.find(x => x.type == "username") &&
<span className="text-red-500">{inputValidationMsg.find(x => x.type == "username")!.message}</span>
} */}
</div>
<div className="px-5 py-2">
<label className="font-semibold text-sm text-gray-600 pb-1 block">
Username
</label>
<input
type="text"
className="border rounded-lg px-3 py-2 mt-1 mb-1 text-sm w-full"
onInput={(e) => setUsername(e.currentTarget.value)}
/>
{
inputValidationMsg.find(x => x.type == "username") &&
<span className="text-red-500">{inputValidationMsg.find(x => x.type == "username")!.message}</span>
}
</div>
<div className="px-5 py-2">
<label className="font-semibold text-sm text-gray-600 pb-1 block">
Password
</label>
<input
type="password"
className="border rounded-lg px-3 py-2 mt-1 mb-1 text-sm w-full"
onInput={(e) => setPassword(e.currentTarget.value)}
/>
{
inputValidationMsg.find(x => x.type == "password") &&
<span className="text-red-500">{inputValidationMsg.find(x => x.type == "password")!.message}</span>
}
</div>
<div className="px-5 py-2">
{terminalID != null && loginToken != null && (
<button
onClick={doLogin}
disabled={isLoading}
type="button"
className="transition duration-200 bg-blue-500 hover:bg-blue-600 focus:bg-blue-700 focus:shadow-sm focus:ring-4 focus:ring-blue-500 focus:ring-opacity-50 text-white w-full py-2.5 rounded-lg text-sm shadow-sm hover:shadow-md font-semibold text-center flex flex-row justify-center align-middle items-center disabled:bg-gray-300"
>
{isLoading && (
<span
className="animate-spin inline-block size-4 border-[3px] border-current border-t-transparent text-white rounded-full mr-2"
role="status"
aria-label="loading"
>
<span className="sr-only">Loading...</span>
</span>
)}
<span className="inline-block mr-2">
{isLoading ? "Mohon Tunggu" : "Login"}
</span>
{!isLoading && (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
className="w-4 h-4 inline-block"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 8l4 4m0 0l-4 4m4-4H3"
/>
</svg>
)}
</button>
)}
{(terminalID == null || loginToken == null) && <span className="text-[#ff0015]">Terminal ID dan Token Akses tidak valid.</span>}
</div>
</>
}
{
isSuccessLogin && <div className="px-5 py-2">
<h4 className="text-xl text-green-600 text-center">Sukses</h4>
<h5 className="text-lg text-center mt-4">Berhasil melakukan login untuk penampil jadwal. Anda dapat menutup halaman ini.</h5>
</div>
}
</div>
</div>
</div>
);
}