"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import type { ApplicationStage, InterviewOutcome, SalaryRateBasis } from "@prisma/client"; import { AdminDialog } from "@/components/ui/admin-dialog"; import { advanceStage, agreeSalary, approveSalary, returnSalary, verifyDocuments, recordReferenceCheck, recordInterviewResult, requestInterviewWaiver, approveInterviewWaiver, selectCandidate, returnSelection, rejectApplication, } from "./actions"; const INPUT = "w-full rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"; const PRIMARY = "rounded-lg bg-primary-600 px-4 py-2 text-sm font-semibold text-white hover:bg-primary-700 disabled:opacity-60"; const SECONDARY = "rounded-lg border border-neutral-300 px-4 py-2 text-sm font-medium text-neutral-700 hover:bg-neutral-50 disabled:opacity-60"; const DANGER = "rounded-lg border border-danger-300 px-4 py-2 text-sm font-medium text-danger-700 hover:bg-danger-50 disabled:opacity-60"; export type ActionCardProps = { id: string; stage: ApplicationStage; isExHand: boolean; interviewResult: InterviewOutcome; interviewWaived: boolean; rejectedReason: string | null; salaryPending: boolean; waiverPending: boolean; selectionPending: boolean; salary: { rateBasis: SalaryRateBasis; basic: number; victualingPerDay: number; currency: string; approved: boolean } | null; perms: { manage: boolean; recordReference: boolean; recordInterview: boolean; requestWaiver: boolean; approveSalary: boolean; approveWaiver: boolean; select: boolean; }; }; function useAction() { const router = useRouter(); const [pending, setPending] = useState(false); const [error, setError] = useState(""); async function run(fn: () => Promise<{ ok: true } | { error: string }>) { setPending(true); setError(""); const res = await fn(); setPending(false); if ("error" in res) setError(res.error); else router.refresh(); return res; } return { pending, error, run }; } function Card({ title, sub, children }: { title: string; sub?: string; children: React.ReactNode }) { return (
{sub}
}{msg}
: null; } export function ApplicationActionCard(p: ActionCardProps) { const { run, pending, error } = useAction(); const canReject = p.perms.manage && !["SELECTED", "ONBOARDED", "REJECTED"].includes(p.stage); // Reference-check form state (COMPETENCY_AND_REFERENCES). const [ref, setRef] = useState({ refereeName: "", refereeContact: "", outcome: "positive", note: "" }); // Bank/EPF form state (DOC_VERIFICATION). const [docs, setDocs] = useState({ accountName: "", accountNumber: "", ifsc: "", bankName: "", uan: "", aadhaarLast4: "", pfNumber: "" }); // Salary form state (SALARY_AGREEMENT). const [sal, setSal] = useState({ rateBasis: "MONTHLY", basic: "", victualingPerDay: "0", currency: "INR" }); function fdFrom(obj: RecordAdd a reference check
setRef({ ...ref, refereeName: e.target.value })} /> setRef({ ...ref, refereeContact: e.target.value })} /> setRef({ ...ref, note: e.target.value })} />Awaiting document verification by the MPO.
)} {footer}Proposed: {p.salary?.currency} {p.salary?.basic} / {p.salary?.rateBasis.toLowerCase()} · victualing {p.salary?.currency} {p.salary?.victualingPerDay}/day
{p.perms.approveSalary ? (Awaiting Manager approval.
)} {footer}Awaiting the MPO to agree the salary.
)} {footer}Interview waiver awaiting Manager approval.
) )} {/* Selection row */} {(p.interviewResult === "ACCEPTED" || p.interviewWaived) && ( p.perms.select ? ({p.interviewWaived ? "Interview waived" : "Interview passed"} — awaiting Manager selection.
) )} {footer}Candidate selected.
{p.rejectedReason ?? "This candidate was rejected."}
This candidate has been onboarded.