118 lines
8.3 KiB
TypeScript
118 lines
8.3 KiB
TypeScript
import React from "react";
|
|
import { Box, Button, Chip, CircularProgress, Typography } from "@mui/material";
|
|
|
|
import { useI18n } from "../i18n/I18nProvider";
|
|
import { CandidateFit, FocusPlanResponse, InterviewPrepResponse, MatchScore, ReadinessResponse } from "../types";
|
|
import { DraftCard, ListCard, MatchScoreCard, SectionChips, TwoColumnSection } from "./JobDetailsPanels";
|
|
|
|
type Props = {
|
|
tab: number;
|
|
matchScore: MatchScore | null;
|
|
loadingMatchScore: boolean;
|
|
candidateFit: CandidateFit | null;
|
|
loadingCandidateFit: boolean;
|
|
regenerateCandidateFit: () => void;
|
|
fitLevel: { label: string; color: "success" | "warning" | "default" } | null;
|
|
focusPlan: FocusPlanResponse | null;
|
|
loadingFocusPlan: boolean;
|
|
regenerateFocusPlan: () => void;
|
|
interviewPrep: InterviewPrepResponse | null;
|
|
loadingInterviewPrep: boolean;
|
|
regenerateInterviewPrep: () => void;
|
|
readiness: ReadinessResponse | null;
|
|
loadingReadiness: boolean;
|
|
};
|
|
|
|
export default function JobInsightTabs(props: Props) {
|
|
const { t } = useI18n();
|
|
const { tab, matchScore, loadingMatchScore, candidateFit, loadingCandidateFit, regenerateCandidateFit, fitLevel, focusPlan, loadingFocusPlan, regenerateFocusPlan, interviewPrep, loadingInterviewPrep, regenerateInterviewPrep, readiness, loadingReadiness } = props;
|
|
return <>
|
|
{tab === 5 && (
|
|
<Box>
|
|
<MatchScoreCard score={matchScore} loading={loadingMatchScore} />
|
|
<Box sx={{ display: "flex", justifyContent: "flex-end", my: 1.5 }}>
|
|
<Button size="small" variant="outlined" disabled={loadingCandidateFit} onClick={regenerateCandidateFit}>
|
|
{loadingCandidateFit ? "Regenerating..." : "Regenerate"}
|
|
</Button>
|
|
</Box>
|
|
{loadingCandidateFit ? <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box> : candidateFit ? (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2.5 }}>
|
|
<Typography variant="caption" sx={{ color: "text.secondary", display: "block", mt: -1 }}>{t("jobDetailsAiFitHint")}</Typography>
|
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap" }}>
|
|
<Box><Typography variant="overline">{t("jobDetailsHowYouMatch")}</Typography><Typography sx={{ whiteSpace: "pre-wrap" }}>{candidateFit.matchSummary}</Typography></Box>
|
|
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", alignItems: "center" }}>
|
|
<Chip label={t("jobDetailsMatchPercent", { count: candidateFit.matchScore })} color={candidateFit.matchScore >= 75 ? "success" : candidateFit.matchScore >= 55 ? "warning" : "default"} size="small" />
|
|
{fitLevel ? <Chip label={fitLevel.label} color={fitLevel.color} size="small" /> : null}
|
|
</Box>
|
|
</Box>
|
|
<DraftCard title={t("jobDetailsTailoredPitch")} content={candidateFit.tailoredPitch} />
|
|
<SectionChips title={t("jobDetailsStrongMatches")} items={candidateFit.strengths} color="success" />
|
|
<SectionChips title={t("jobDetailsPossibleGaps")} items={candidateFit.gaps} color="warning" outlined />
|
|
<TwoColumnSection leftTitle={t("jobDetailsWhatToMention")} leftItems={candidateFit.mention} rightTitle={t("jobDetailsWhatNotToOverstate")} rightItems={candidateFit.avoid} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsImproveCv")} leftItems={candidateFit.cvImprovements} rightTitle={t("jobDetailsMissingKeywords")} rightItems={candidateFit.missingKeywords} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsTabInterviewPrep")} leftItems={candidateFit.interviewPrep} rightTitle={t("jobDetailsCvGuidance")} rightItems={candidateFit.guidance.cv} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsCoverLetterGuidance")} leftItems={candidateFit.guidance.coverLetter} rightTitle={t("jobDetailsRecruiterMessageGuidance")} rightItems={candidateFit.guidance.recruiterMessage} />
|
|
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2 }}>
|
|
<DraftCard title={t("jobDetailsCoverLetterDraft")} content={candidateFit.coverLetterDraft ?? t("jobDetailsNoDraftAvailableYet")} />
|
|
<DraftCard title={t("jobDetailsRecruiterMessageDraft")} content={candidateFit.recruiterMessageDraft ?? t("jobDetailsNoDraftAvailableYet")} />
|
|
</Box>
|
|
</Box>
|
|
) : <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsCandidateFitEmpty")}</Typography>}
|
|
</Box>
|
|
)}
|
|
|
|
{tab === 6 && (
|
|
<Box>
|
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1.5 }}>
|
|
<Button size="small" variant="outlined" disabled={loadingFocusPlan} onClick={regenerateFocusPlan}>
|
|
{loadingFocusPlan ? "Regenerating..." : "Regenerate"}
|
|
</Button>
|
|
</Box>
|
|
{loadingFocusPlan ? <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box> : focusPlan ? (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
<DraftCard title={t("jobDetailsFocusSummary")} content={focusPlan.strategicSummary} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsImmediatePriorities")} leftItems={focusPlan.immediatePriorities} rightTitle={t("jobDetailsProofPoints")} rightItems={focusPlan.proofPointsToLeadWith} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsCvBulletIdeas")} leftItems={focusPlan.cvBulletIdeas} rightTitle={t("jobDetailsCoverLetterAngles")} rightItems={focusPlan.coverLetterAngles} />
|
|
<ListCard title={t("jobDetailsFollowUpApproach")} items={focusPlan.followUpApproach} />
|
|
</Box>
|
|
) : <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsNoFocusPlan")}</Typography>}
|
|
</Box>
|
|
)}
|
|
|
|
{tab === 7 && (
|
|
<Box>
|
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1.5 }}>
|
|
<Button size="small" variant="outlined" disabled={loadingInterviewPrep} onClick={regenerateInterviewPrep}>
|
|
{loadingInterviewPrep ? "Regenerating..." : "Regenerate"}
|
|
</Button>
|
|
</Box>
|
|
{loadingInterviewPrep ? <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box> : interviewPrep ? (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
<DraftCard title={t("jobDetailsInterviewPrepBrief")} content={interviewPrep.summary} />
|
|
<TwoColumnSection leftTitle={t("jobDetailsTalkingPoints")} leftItems={interviewPrep.talkingPoints} rightTitle={t("jobDetailsLikelyQuestions")} rightItems={interviewPrep.likelyQuestions} />
|
|
<ListCard title={t("jobDetailsWeakSpots")} items={interviewPrep.weakSpots} />
|
|
</Box>
|
|
) : <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsNoInterviewPrep")}</Typography>}
|
|
</Box>
|
|
)}
|
|
|
|
{tab === 8 && (
|
|
<Box>
|
|
{loadingReadiness ? <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box> : readiness ? (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap" }}>
|
|
<Typography variant="h6">{t("jobDetailsApplicationReadiness")}</Typography>
|
|
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
|
|
<Chip label={t("jobDetailsReadyPercent", { count: readiness.score })} color={readiness.score >= 80 ? "success" : readiness.score >= 60 ? "warning" : "default"} />
|
|
<Chip label={readiness.level} variant="outlined" />
|
|
</Box>
|
|
</Box>
|
|
<TwoColumnSection leftTitle={t("jobDetailsCompleted")} leftItems={readiness.completed} rightTitle={t("jobDetailsStillMissing")} rightItems={readiness.missing} />
|
|
<ListCard title={t("jobDetailsSmartReminders")} items={readiness.reminders} />
|
|
</Box>
|
|
) : <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsNoReadiness")}</Typography>}
|
|
</Box>
|
|
)}
|
|
</>;
|
|
}
|