182 lines
7.3 KiB
TypeScript
182 lines
7.3 KiB
TypeScript
|
|
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||
|
|
import { Link } from "@remix-run/react";
|
||
|
|
import { Check, Clock, Lock, X } from "lucide-react";
|
||
|
|
import { useEffect, useState } from "react";
|
||
|
|
import AgreementPDF, { type AgreementProps } from "~/components/document/aggrement-letter.client";
|
||
|
|
import { Button } from "~/components/ui/button";
|
||
|
|
import { useGetAgreementProject } from "~/services/projects/get-agreement-by-project-id";
|
||
|
|
import type { StatusData } from "~/types/constants/status-data";
|
||
|
|
import { formatImagePath } from "~/utils/prefix-file-path";
|
||
|
|
import SignContractDialog from "./modal/sign-contract";
|
||
|
|
|
||
|
|
type StatusType = "SUCCESS" | "FAILED" | "CURRENT" | "UPCOMING" | "PENDING";
|
||
|
|
|
||
|
|
export default function SecondSection({
|
||
|
|
id,
|
||
|
|
data,
|
||
|
|
handleClick,
|
||
|
|
}: { id: string; data: StatusData[]; handleClick: () => void }) {
|
||
|
|
const [contract, setContract] = useState<AgreementProps | null>(null);
|
||
|
|
const { data: contractData } = useGetAgreementProject(data[0].id || "");
|
||
|
|
const steps = [
|
||
|
|
"Proposal Project Terkirim",
|
||
|
|
"Peninjauan Proposal",
|
||
|
|
"Proses Approval dari Komitee Koperasi",
|
||
|
|
"Kontrak Perjanjian",
|
||
|
|
"Proses Penggalangan Penyertaan Modal",
|
||
|
|
];
|
||
|
|
|
||
|
|
const getLatestStatusForStep = (step: string) => {
|
||
|
|
const matchingStatuses = data.filter((status) => status.history === step);
|
||
|
|
return matchingStatuses.sort(
|
||
|
|
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
||
|
|
)[0];
|
||
|
|
};
|
||
|
|
|
||
|
|
const getStepStatus = (index: number): StatusType => {
|
||
|
|
const step = steps[index];
|
||
|
|
const latestStatus = getLatestStatusForStep(step);
|
||
|
|
if (latestStatus) return latestStatus.status as unknown as StatusType;
|
||
|
|
|
||
|
|
const lastCompletedIndex = steps.findIndex((step) => !getLatestStatusForStep(step));
|
||
|
|
if (index === lastCompletedIndex) return "CURRENT";
|
||
|
|
return "UPCOMING";
|
||
|
|
};
|
||
|
|
|
||
|
|
const getStatusColor = (status: StatusType) => {
|
||
|
|
switch (status) {
|
||
|
|
case "SUCCESS":
|
||
|
|
return "bg-green-500 text-green-500";
|
||
|
|
case "FAILED":
|
||
|
|
return "bg-red-500 text-red-500";
|
||
|
|
case "CURRENT":
|
||
|
|
return "bg-blue-500 text-blue-500";
|
||
|
|
case "PENDING":
|
||
|
|
return "bg-yellow-400 text-yellow-500";
|
||
|
|
default:
|
||
|
|
return "bg-gray-300 text-gray-500";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (contractData) {
|
||
|
|
setContract({
|
||
|
|
idProjek: contractData.idProjek,
|
||
|
|
idUser: contractData.idUser,
|
||
|
|
namaProyek: contractData.namaProyek,
|
||
|
|
namaPetugas: contractData.namaPetugas,
|
||
|
|
alamatPetugas: contractData.alamatPetugas,
|
||
|
|
namaPemilikProyek: contractData.namaPemilikProyek,
|
||
|
|
nik: contractData.nik,
|
||
|
|
noHp: contractData.noHp,
|
||
|
|
alamat: contractData.alamat,
|
||
|
|
tandaTangan: formatImagePath(contractData.tandaTangan),
|
||
|
|
signature: formatImagePath(contractData.signature),
|
||
|
|
nominalDisetujui: contractData.nominalDisetujui,
|
||
|
|
tanggal: contractData.createdAt,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}, [contractData]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<section className="order-2 min-w-96 flex-1 space-y-6 rounded-md bg-white p-6 xl:order-1">
|
||
|
|
<h4 className="font-semibold text-lg">Progres Status Pengajuan Project</h4>
|
||
|
|
<div className="space-y-10">
|
||
|
|
{steps.map((step, index) => {
|
||
|
|
const stepData = getLatestStatusForStep(step);
|
||
|
|
const status = getStepStatus(index);
|
||
|
|
const statusColor = getStatusColor(status);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div key={index} className={`relative ${index < steps.length - 1 ? "h-32" : "h-fit"}`}>
|
||
|
|
<div className="flex items-start">
|
||
|
|
<div className="mr-4 flex flex-col items-center">
|
||
|
|
<div
|
||
|
|
className={`flex h-8 w-8 items-center justify-center rounded-full ${
|
||
|
|
// biome-ignore lint/nursery/useSortedClasses: <explanation>
|
||
|
|
statusColor.split(" ")[0]
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{status === "SUCCESS" && <Check className="text-white" size={16} />}
|
||
|
|
{status === "FAILED" && <X className="text-white" size={16} />}
|
||
|
|
{status === "CURRENT" && (
|
||
|
|
<span className="font-bold text-white">{index + 1}</span>
|
||
|
|
)}
|
||
|
|
{status === "PENDING" && <Clock className="text-white" size={16} />}
|
||
|
|
{status === "UPCOMING" && <Lock className="text-white" size={16} />}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex-1">
|
||
|
|
<h3
|
||
|
|
className={`font-semibold ${
|
||
|
|
// biome-ignore lint/nursery/useSortedClasses: <explanation>
|
||
|
|
statusColor.split(" ")[1]
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{step}
|
||
|
|
</h3>
|
||
|
|
{stepData && (
|
||
|
|
<>
|
||
|
|
<p className="text-gray-500 text-sm">
|
||
|
|
{new Date(stepData.created_at).toLocaleString()}
|
||
|
|
</p>
|
||
|
|
<div
|
||
|
|
className={`mt-2 rounded p-3 text-sm ${
|
||
|
|
status === "SUCCESS"
|
||
|
|
? "bg-green-100 text-green-700"
|
||
|
|
: status === "FAILED"
|
||
|
|
? "bg-red-100 text-red-700"
|
||
|
|
: "bg-yellow-100 text-yellow-700"
|
||
|
|
}`}
|
||
|
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
|
||
|
|
dangerouslySetInnerHTML={{
|
||
|
|
__html: stepData.keterangan,
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
{step === "Kontrak Perjanjian" ||
|
||
|
|
steps.indexOf(step) > steps.indexOf("Kontrak Perjanjian") ? (
|
||
|
|
contract ? (
|
||
|
|
<PDFDownloadLink
|
||
|
|
document={<AgreementPDF {...contract} />}
|
||
|
|
fileName="agreement.pdf"
|
||
|
|
>
|
||
|
|
<Button className="mt-2 w-full">Download Kontrak</Button>
|
||
|
|
</PDFDownloadLink>
|
||
|
|
) : (
|
||
|
|
<SignContractDialog id={id} />
|
||
|
|
)
|
||
|
|
) : null}
|
||
|
|
{status === "CURRENT" && step === "Peninjauan Proposal" ? (
|
||
|
|
<button
|
||
|
|
onClick={handleClick}
|
||
|
|
type="button"
|
||
|
|
className="mt-2 rounded border border-blue-500 bg-white px-4 py-2 text-blue-500 transition-colors hover:bg-blue-50"
|
||
|
|
>
|
||
|
|
Tinjau Proposal
|
||
|
|
</button>
|
||
|
|
) : status === "FAILED" ? (
|
||
|
|
<Button
|
||
|
|
asChild
|
||
|
|
type="button"
|
||
|
|
className="mt-2 rounded border border-red-500 bg-white px-4 py-2 text-red-500 transition-colors hover:bg-red-50"
|
||
|
|
>
|
||
|
|
<Link to={`/app/my-projects/${id}/edit`}>Ajukan Ulang</Link>
|
||
|
|
</Button>
|
||
|
|
) : (
|
||
|
|
<div className="mt-2 h-10" />
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{index < steps.length - 1 && (
|
||
|
|
<div className="absolute top-8 bottom-0 left-4 h-full w-0 border-gray-300 border-l-2 border-dashed" />
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
);
|
||
|
|
}
|