diff --git a/src/components/Invitation/ScheduleInterview.tsx b/src/components/Invitation/ScheduleInterview.tsx index f4d63247..158e9945 100644 --- a/src/components/Invitation/ScheduleInterview.tsx +++ b/src/components/Invitation/ScheduleInterview.tsx @@ -22,7 +22,7 @@ const ScheduleInterview: React.FC = ({ status, onClose, technicalInterviews = [], - availableStatuses = ["Scheduled", "Completed", "No show", "cancelled"], + availableStatuses = ["Scheduled", "Completed", "No show", "Cancelled"], }) => { const dispatch = useAppDispatch(); const [isModelOpen, setIsModelOpen] = useState(false); @@ -60,10 +60,8 @@ const ScheduleInterview: React.FC = ({ useEffect(() => { // Ensure technicalInterviews is an array and has length if (Array.isArray(technicalInterviews) && technicalInterviews.length > 0) { - // Set the ID of the first interview in the array setSelectedInterviewId(technicalInterviews[0]._id); } else { - // Ensure we have a fallback setSelectedInterviewId(""); } }, [technicalInterviews]); @@ -120,35 +118,6 @@ const ScheduleInterview: React.FC = ({ }); }; - // const handleStatusUpdate = async (event: React.FormEvent) => { - // event.preventDefault(); - - // console.log("Current technicalInterviews:", technicalInterviews); - // console.log("Current selectedInterviewId:", selectedInterviewId); - - // if (!newStatus) { - // setError("Please select a status"); - // return; - // } - - // // If no interview exists, prevent status update - // if (!selectedInterviewId) { - // setError("No interview found. Please schedule an interview first."); - // return; - // } - - // try { - // setIsLoading(true); - // await dispatch(updateInterviewStatuses(selectedInterviewId, newStatus)); - // onClose(); - // setIsLoading(false); - // } catch (err) { - // console.error("Error updating interview status:", err); - // setError("Failed to update status"); - // setIsLoading(false); - // } - // }; - const handleStatusUpdate = async (event: React.FormEvent) => { event.preventDefault(); @@ -157,13 +126,11 @@ const ScheduleInterview: React.FC = ({ return; } - // If no interview exists, prevent status update if (!selectedInterviewId) { setError("No interview found. Please schedule an interview first."); return; } - // Get the current interview's status const currentInterview = technicalInterviews.find( (interview) => interview._id === selectedInterviewId ); @@ -171,12 +138,11 @@ const ScheduleInterview: React.FC = ({ // Define valid status transitions const validStatusTransitions = { Scheduled: ["Completed", "No show", "Cancelled"], - Completed: [], // No further transitions from Completed - Cancelled: [], // No further transitions from Cancelled - "No show": ["Scheduled"], // Can reschedule after a no-show + Completed: [], + Cancelled: [], + "No show": [], }; - // Check if the status transition is valid if (currentInterview) { const currentStatus = currentInterview.status; const allowedNextStatuses = validStatusTransitions[currentStatus] || []; @@ -209,11 +175,13 @@ const ScheduleInterview: React.FC = ({ stage === "Rejected" || stage === "Admitted" || (technicalInterviews.length > 0 && - technicalInterviews[0].status === "Completed") + (technicalInterviews[0].status === "Completed" || + technicalInterviews[0].status === "Cancelled")) } className={`px-4 py-2 w-full text-sm font-medium text-white ${ technicalInterviews.length > 0 && - technicalInterviews[0].status === "Completed" + (technicalInterviews[0].status === "Completed" || + technicalInterviews[0].status === "Cancelled") ? "bg-gray-400 cursor-not-allowed" : " bg-[#0c6a0c] hover:bg-[#367a4e] dark:bg-[#1bf84b8d] dark:hover:bg-emerald-700 dark:hover:text-gray-100 cursor-pointer" } focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500`} @@ -338,7 +306,30 @@ const ScheduleInterview: React.FC = ({ type="submit" className="text-white bg-[#0c6a0c] dark:bg-[#56C870] hover:bg-[#4ab862] focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full px-5 py-2.5 text-center" > - {isLoading ? "Sending..." : "Send Invite"} + {isLoading ? ( + <> + + sending... + + ) : ( + "Send Invitation" + )} )} @@ -374,7 +365,30 @@ const ScheduleInterview: React.FC = ({ type="submit" className="text-white bg-[#0c6a0c] dark:bg-[#56C870] hover:bg-[#4ab862] focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full px-5 py-2.5 text-center" > - {isLoading ? "Updating..." : "Update"} + {isLoading ? ( + <> + + updating... + + ) : ( + "Update" + )} )} diff --git a/src/pages/ApplicationCycle/myApplication.tsx b/src/pages/ApplicationCycle/myApplication.tsx index 4cde7d53..9b1bfb50 100644 --- a/src/pages/ApplicationCycle/myApplication.tsx +++ b/src/pages/ApplicationCycle/myApplication.tsx @@ -1,324 +1,473 @@ import { useEffect, useState } from "react"; import { useAppDispatch, useAppSelector } from "../../hooks/hooks"; import { toast, ToastContainer } from "react-toastify"; -import { AiFillFilePdf } from "react-icons/ai" +import { AiFillFilePdf } from "react-icons/ai"; import { - getApplicantCyclesApplications, - getCyclesApplicationAttributes, - getCyclesStages + getApplicantCyclesApplications, + getCyclesApplicationAttributes, + getCyclesStages, } from "../../redux/actions/applications"; import { Link } from "react-router-dom"; export const MyApplication = () => { - const dispatch = useAppDispatch(); - const [isLoading, setIsLoading] = useState(true); - const [application, setApplication] = useState(null); - const [attributes, setAttributes] = useState(null); - const [stages, setStages] = useState(null); + const dispatch = useAppDispatch(); + const [isLoading, setIsLoading] = useState(true); + const [application, setApplication] = useState(null); + const [attributes, setAttributes] = useState(null); + const [stages, setStages] = useState(null); - useEffect(() => { - const fetchApplications = async () => { - try { - const response = await getApplicantCyclesApplications(); - if (response?.data?.getTraineeCyclesApplications) { - setApplication(response.data.getTraineeCyclesApplications); - console.log("Appl", response.data.getTraineeCyclesApplications) - } else { - throw new Error("No applications found"); - } - } catch (error) { - toast.error("No applications found!"); - } finally { - setIsLoading(false); - } - }; - - if (!application) { - fetchApplications(); + useEffect(() => { + const fetchApplications = async () => { + try { + const response = await getApplicantCyclesApplications(); + if (response?.data?.getTraineeCyclesApplications) { + setApplication(response.data.getTraineeCyclesApplications); + console.log("Appl", response.data.getTraineeCyclesApplications); } else { - const fetchAttributesAndStages = async () => { - try { - setIsLoading(true); - if (application?._id) { - const attributesResponse = await getCyclesApplicationAttributes(application._id); - if (attributesResponse?.data?.getApplicationsAttributes) { - setAttributes(attributesResponse.data.getApplicationsAttributes); - } - const stagesResponse = await getCyclesStages(application._id); - if (stagesResponse?.data?.getApplicationStages) { - console.log(stagesResponse.data.getApplicationStages); - setStages(stagesResponse.data.getApplicationStages); - } - } - } catch (error) { - toast.error("Something went wrong while fetching details!"); - } finally { - setIsLoading(false); - } - }; + throw new Error("No applications found"); + } + } catch (error) { + toast.error("No applications found!"); + } finally { + setIsLoading(false); + } + }; - fetchAttributesAndStages(); + if (!application) { + fetchApplications(); + } else { + const fetchAttributesAndStages = async () => { + try { + setIsLoading(true); + if (application?._id) { + const attributesResponse = await getCyclesApplicationAttributes( + application._id + ); + if (attributesResponse?.data?.getApplicationsAttributes) { + setAttributes(attributesResponse.data.getApplicationsAttributes); + } + const stagesResponse = await getCyclesStages(application._id); + if (stagesResponse?.data?.getApplicationStages) { + console.log(stagesResponse.data.getApplicationStages); + setStages(stagesResponse.data.getApplicationStages); + } + } + } catch (error) { + toast.error("Something went wrong while fetching details!"); + } finally { + setIsLoading(false); } - }, [application]); + }; - const formatDate = (timestamp) => { - const date = new Date(Number(timestamp)); - console.log("DDD", date) - if (isNaN(date.getTime())) return ''; + fetchAttributesAndStages(); + } + }, [application]); - return new Intl.DateTimeFormat('en-US', { - day: '2-digit', - month: 'short', - year: 'numeric', - }).format(date); - }; + const formatDate = (timestamp) => { + const date = new Date(Number(timestamp)); + console.log("DDD", date); + if (isNaN(date.getTime())) return ""; - if (isLoading) { - return ( - <> -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - ); - } + return new Intl.DateTimeFormat("en-US", { + day: "2-digit", + month: "short", + year: "numeric", + }).format(date); + }; - if (!application) { - return ( -
-
-
-

- No applications found -

-
-
-
- ); - } - console.log("APPJJA", application) + if (isLoading) { + return ( + <> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + ); + } + if (!application) { return ( - <> - -
-

- My Application -

-
-
-
-

- Personal Information -

-
    - {application?.firstName &&
  • Firstname: {application.firstName}
  • } - {application?.lastName &&
  • Lastname: {application.lastName}
  • } - {application?.email &&
  • Email: {application.email}
  • } - {application?.cycle_id?.name &&
  • Application Cycle: {application.cycle_id.name}
  • } - {application?.cycle_id?.createdAt && ( -
  • Published date: {formatDate(application.cycle_id.createdAt)}
  • - )} - {application?.createdAt && ( -
  • Application date: {formatDate(application.createdAt)}
  • - )} -
-
-
- {attributes?.Address && ( -

- Address -

)} -
    - {attributes?.province &&
  • Province: {attributes.province}
  • } - {attributes?.district &&
  • District: {attributes.district}
  • } - {attributes?.sector &&
  • Sector: {attributes.sector}
  • } - {attributes?.Address &&
  • Address: {attributes.Address}
  • } -
-
-
- {(application?.coverLetterUrl || application?.resumeUrl || application?.idDocumentUrl) && ( -
-

- Supporting Documents -

-
    - {application?.idDocumentUrl && ( -
  • - - ID Card - - - - View ID Card - -
  • - )} - {application?.resumeUrl && ( -
  • - - Resume - - - - View Resume - -
  • - )} - {application?.coverLetterUrl && ( -
  • - - Cover Letter - - - - View Cover Letter - -
  • - )} -
-
- )} -
-

Application Stages

-

- Current Stage: - {application?.applicationPhase === "Rejected" ? ( - Rejected - ) : ( - {application?.applicationPhase} - )} -

+
+
+
+

+ No applications found +

+
+
+
+ ); + } + console.log("APPJJA", application); -
-

Stages History

- {!stages?.dismissed && !stages?.interview && !stages?.shortlist && ( -

- No stages history found -

- )} -
    - {stages?.dismissed && ( -
  • - Rejected + return ( + <> + +
    +

    + My Application +

    +
    +
    +
    +

    + Personal Information +

    +
      + {application?.firstName && ( +
    • + Firstname: {application.firstName} +
    • + )} + {application?.lastName && ( +
    • + Lastname: {application.lastName} +
    • + )} + {application?.email && ( +
    • + Email: {application.email} +
    • + )} + {application?.cycle_id?.name && ( +
    • + Application Cycle:{" "} + {application.cycle_id.name} +
    • + )} + {application?.cycle_id?.createdAt && ( +
    • + Published date:{" "} + {formatDate(application.cycle_id.createdAt)} +
    • + )} + {application?.createdAt && ( +
    • + Application date:{" "} + {formatDate(application.createdAt)} +
    • + )} +
    +
    +
    + {attributes?.Address && ( +

    + Address +

    + )} +
      + {attributes?.province && ( +
    • + Province: {attributes.province} +
    • + )} + {attributes?.district && ( +
    • + District: {attributes.district} +
    • + )} + {attributes?.sector && ( +
    • + Sector: {attributes.sector} +
    • + )} + {attributes?.Address && ( +
    • + Address: {attributes.Address} +
    • + )} +
    +
    +
    + {(application?.coverLetterUrl || + application?.resumeUrl || + application?.idDocumentUrl) && ( +
    +

    + Supporting Documents +

    +
      + {application?.idDocumentUrl && ( +
    • + + ID Card + + + + View ID Card + +
    • + )} + {application?.resumeUrl && ( +
    • + + Resume + + + + View Resume + +
    • + )} + {application?.coverLetterUrl && ( +
    • + + Cover Letter + + + + View Cover Letter + +
    • + )} +
    +
    + )} +
    +

    + Application Stages +

    +

    + Current Stage: + {application?.applicationPhase === "Rejected" ? ( + Rejected + ) : ( + + {" "} + {application?.applicationPhase} + + )} +

    - {stages.dismissed?.status && ( -

    Status: {stages.dismissed.status}

    - )} +
    +

    Stages History

    + {!stages?.dismissed && + !stages?.interview && + !stages?.shortlist && ( +

    No stages history found

    + )} +
      + {stages?.dismissed && ( +
    • + + Rejected + - {stages.dismissed?.stageDismissedFrom && ( -

      Stage dismissed from: {stages.dismissed.stageDismissedFrom}

      - )} + {stages.dismissed?.status && ( +

      + Status: {stages.dismissed.status} +

      + )} - {stages.dismissed?.comments && ( -

      Comments: {stages.dismissed.comments}

      - )} + {stages.dismissed?.stageDismissedFrom && ( +

      + Stage dismissed from:{" "} + {stages.dismissed.stageDismissedFrom} +

      + )} - {stages.dismissed?.createdAt && ( -

      Date: {formatDate(stages.dismissed.createdAt)}

      - )} -
    • - )} + {stages.dismissed?.comments && ( +

      + Comments: {stages.dismissed.comments} +

      + )} - {stages?.admitted && ( -
    • - Admitted - {stages?.allStages?.history - .filter((history: any) => history.stage === "Admitted") - .map((history: any, index: any) => ( - -   ({formatDate(history.enteredAt)} - {formatDate(history.exitedAt)}) - - ))} - {stages.admitted?.status &&

      Status: {stages.admitted.status}

      } - {stages.admitted?.stageDismissedFrom &&

      Score: {stages.admitted.stageDismissedFrom}

      } - {stages.admitted?.comments &&

      Comments: {stages.admitted.comments}

      } - {stages.admitted?.createdAt &&

      Date: {formatDate(stages.admitted.createdAt)}

      } -
    • - )} - {stages?.interview && ( -
    • - Interview Assessment - {stages?.allStages?.history - .filter((history: any) => history.stage === "Technical Assessment") - .map((history: any, index: any) => ( - -   ({formatDate(history.enteredAt)} - {formatDate(history.exitedAt)}) - - ))} - {stages.interview?.interviewScore &&

      Score: {stages.interview.interviewScore}

      } - {stages.interview?.status &&

      Status: {stages.interview.status}

      } - {stages.interview?.comments &&

      Comments: {stages.interview.comments}

      } - {stages.interview?.createdAt &&

      Date: {formatDate(stages.interview.createdAt)}

      } -
    • - )} - {stages?.technical && ( -
    • - Technical Assessment - {stages?.allStages?.history - .filter((history: any) => history.stage === "Technical Assessment") - .map((history: any, index: any) => ( - -   ({formatDate(history.enteredAt)} - {formatDate(history.exitedAt)}) - - ))} - {stages.technical?.score &&

      Score: {stages.technical.score}%

      } - {stages.technical?.status &&

      Status: {stages.technical.status}

      } - {stages.technical?.comments &&

      Comments: {stages.technical.comments}

      } - {stages.technical?.createdAt &&

      Date: {formatDate(stages.technical.createdAt)}

      } -
    • - )} - {stages?.shortlist && ( -
    • - Shortlist - {stages?.allStages?.history - .filter((history: any) => history.stage === "Shortlisted") - .map((history: any, index: any) => ( - -   ({formatDate(history.enteredAt)} - {formatDate(history.exitedAt)}) - - ))} - {stages.shortlist?.status &&

      Status: {stages.shortlist.status}

      } - {stages.shortlist?.comments &&

      Comments: {stages.shortlist.comments}

      } - {stages.shortlist?.createdAt &&

      Date: {formatDate(stages.shortlist.createdAt)}

      } -
    • - )} + {stages.dismissed?.createdAt && ( +

      + Date:{" "} + {formatDate(stages.dismissed.createdAt)} +

      + )} + + )} -
    -
    -
    -
    + {stages?.admitted && ( +
  • + Admitted + {stages?.allStages?.history + .filter((history: any) => history.stage === "Admitted") + .map((history: any, index: any) => ( + +   ({formatDate(history.enteredAt)} -{" "} + {formatDate(history.exitedAt)}) + + ))} + {stages.admitted?.status && ( +

    + Status: {stages.admitted.status} +

    + )} + {stages.admitted?.stageDismissedFrom && ( +

    + Score: {stages.admitted.stageDismissedFrom} +

    + )} + {stages.admitted?.comments && ( +

    + Comments: {stages.admitted.comments} +

    + )} + {stages.admitted?.createdAt && ( +

    + Date: {formatDate(stages.admitted.createdAt)} +

    + )} +
  • + )} + {stages?.interview && ( +
  • + Interview Assessment + {stages?.allStages?.history + .filter( + (history: any) => + history.stage === "Technical Assessment" + ) + .map((history: any, index: any) => ( + +   ({formatDate(history.enteredAt)} -{" "} + {formatDate(history.exitedAt)}) + + ))} + {stages.interview?.interviewScore && ( +

    + Score: {stages.interview.interviewScore} +

    + )} + {stages.interview?.status && ( +

    + Status: {stages.interview.status} +

    + )} + {stages.interview?.comments && ( +

    + Comments: {stages.interview.comments} +

    + )} + {stages.interview?.createdAt && ( +

    + Date: {formatDate(stages.interview.createdAt)} +

    + )} +
  • + )} + {stages?.technical && ( +
  • + Technical Assessment + {stages?.allStages?.history + .filter( + (history: any) => + history.stage === "Technical Assessment" + ) + .map((history: any, index: any) => ( + +   ({formatDate(history.enteredAt)} -{" "} + {formatDate(history.exitedAt)}) + + ))} + {stages.technical?.score && ( +

    + Score: {stages.technical.score}% +

    + )} + {stages.technical?.status && ( +

    + Status: {stages.technical.status} +

    + )} + {stages.technical?.comments && ( +

    + Comments: {stages.technical.comments} +

    + )} + {stages.technical?.createdAt && ( +

    + Date: {formatDate(stages.technical.createdAt)} +

    + )} +
  • + )} + {stages?.shortlist && ( +
  • + Shortlist + {stages?.allStages?.history + .filter((history: any) => history.stage === "Shortlisted") + .map((history: any, index: any) => ( + +   ({formatDate(history.enteredAt)} -{" "} + {formatDate(history.exitedAt)}) + + ))} + {stages.shortlist?.status && ( +

    + Status: {stages.shortlist.status} +

    + )} + {stages.shortlist?.comments && ( +

    + Comments: {stages.shortlist.comments} +

    + )} + {stages.shortlist?.createdAt && ( +

    + Date: {formatDate(stages.shortlist.createdAt)} +

    + )} +
  • + )} +
- - ); +
+
+
+ + ); }; -export default MyApplication; \ No newline at end of file +export default MyApplication; diff --git a/src/pages/TraineApplicant/ApplicantStages.tsx b/src/pages/TraineApplicant/ApplicantStages.tsx index c5400745..a591ec7a 100644 --- a/src/pages/TraineApplicant/ApplicantStages.tsx +++ b/src/pages/TraineApplicant/ApplicantStages.tsx @@ -183,6 +183,17 @@ const ApplicantStages = (props: any) => { return dateObj.toLocaleDateString(); }; + const handleConvertInterviewDate = (date: string | number) => { + const timestamp = + typeof date === "string" && /^\d+$/.test(date) ? parseInt(date) : date; + + const dateObj = new Date(timestamp); + + return !isNaN(dateObj.getTime()) + ? dateObj.toLocaleDateString() + : "Invalid Date"; + }; + const handleTurnCutText = (text: string) => { if (text.length > 10) { return text.slice(0, 10) + "..."; @@ -324,7 +335,10 @@ const ApplicantStages = (props: any) => {