186 lines
7.2 KiB
TypeScript
Executable File
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>
|
|
);
|
|
}
|