From d9187acb379c6536e5c7453053c0a3dd4c28beaa Mon Sep 17 00:00:00 2001 From: DaKheera47 Date: Thu, 15 Jan 2026 18:14:00 +0000 Subject: [PATCH] fit assessment UI --- .../src/client/components/DiscoveredPanel.tsx | 96 +------------------ .../src/client/components/FitAssessment.tsx | 30 ++++++ .../src/client/components/ReadyPanel.tsx | 15 +-- orchestrator/src/client/components/index.ts | 1 + 4 files changed, 36 insertions(+), 106 deletions(-) create mode 100644 orchestrator/src/client/components/FitAssessment.tsx diff --git a/orchestrator/src/client/components/DiscoveredPanel.tsx b/orchestrator/src/client/components/DiscoveredPanel.tsx index 02d5325..1ac58dc 100644 --- a/orchestrator/src/client/components/DiscoveredPanel.tsx +++ b/orchestrator/src/client/components/DiscoveredPanel.tsx @@ -30,6 +30,7 @@ import { Checkbox } from "@/components/ui/checkbox"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/lib/utils"; import * as api from "../api"; +import { FitAssessment } from "."; import type { Job, ResumeProjectCatalogItem } from "../../shared/types"; // ───────────────────────────────────────────────────────────────────────────── @@ -61,46 +62,6 @@ const formatDate = (dateStr: string | null) => { } }; -const getScoreLabel = ( - score: number | null -): { label: string; color: string; description: string } => { - if (score == null) - return { - label: "Unscored", - color: "text-muted-foreground", - description: "No AI assessment yet", - }; - if (score >= 80) - return { - label: "Excellent fit", - color: "text-emerald-400", - description: "Strong match for your profile", - }; - if (score >= 65) - return { - label: "Good fit", - color: "text-emerald-400/80", - description: "Solid match worth considering", - }; - if (score >= 50) - return { - label: "Possible fit", - color: "text-amber-400", - description: "Some relevant aspects", - }; - if (score >= 35) - return { - label: "Weak fit", - color: "text-orange-400", - description: "Limited alignment", - }; - return { - label: "Poor fit", - color: "text-rose-400", - description: "May not be worth pursuing", - }; -}; - const stripHtml = (value: string) => value .replace(/<[^>]*>/g, " ") @@ -114,44 +75,6 @@ const sourceLabel: Record = { ukvisajobs: "UK Visa Jobs", }; -// ───────────────────────────────────────────────────────────────────────────── -// Fit Summary Component (for Decide mode) -// ───────────────────────────────────────────────────────────────────────────── - -interface FitSummaryProps { - job: Job; -} - -const FitSummary: React.FC = ({ job }) => { - return ( -
- {/* AI Assessment */} - {job.suitabilityReason && ( -
-
- AI Assessment -
-

- {job.suitabilityReason} -

-
- )} - - {/* No assessment fallback */} - {!job.suitabilityReason && !job.suitabilityScore && ( -
-

- No AI assessment available yet. -

-

- Review the job description to decide if you want to tailor. -

-
- )} -
- ); -}; - // ───────────────────────────────────────────────────────────────────────────── // Decide Mode Panel // ───────────────────────────────────────────────────────────────────────────── @@ -172,7 +95,6 @@ const DecideMode: React.FC = ({ const [showDescription, setShowDescription] = useState(false); const deadline = formatDate(job.deadline); const jobLink = job.applicationLink || job.jobUrl; - const scoreInfo = getScoreLabel(job.suitabilityScore); const description = useMemo(() => { if (!job.jobDescription) return "No description available."; @@ -195,25 +117,13 @@ const DecideMode: React.FC = ({

-
+
{sourceLabel[job.source]} - {job.suitabilityScore != null && ( -
- - {job.suitabilityScore}% - -
- )}
@@ -244,7 +154,7 @@ const DecideMode: React.FC = ({ {/* Fit Summary - the core content */}
- + {/* Collapsible full description */}
diff --git a/orchestrator/src/client/components/FitAssessment.tsx b/orchestrator/src/client/components/FitAssessment.tsx new file mode 100644 index 0000000..aacb42a --- /dev/null +++ b/orchestrator/src/client/components/FitAssessment.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { Sparkles } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { Job } from "../../shared/types"; + +interface FitAssessmentProps { + job: Job; + className?: string; +} + +export const FitAssessment: React.FC = ({ + job, + className, +}) => { + if (!job.suitabilityReason) return null; + + return ( +
+
+
+ + Fit Assessment +
+

+ {job.suitabilityReason} +

+
+
+ ); +}; diff --git a/orchestrator/src/client/components/ReadyPanel.tsx b/orchestrator/src/client/components/ReadyPanel.tsx index 9a3ebbf..5d9de59 100644 --- a/orchestrator/src/client/components/ReadyPanel.tsx +++ b/orchestrator/src/client/components/ReadyPanel.tsx @@ -22,7 +22,6 @@ import { Briefcase, Building2, FolderKanban, - Sparkles, } from "lucide-react"; import { toast } from "sonner"; @@ -43,6 +42,7 @@ import { import { cn } from "@/lib/utils"; import { copyTextToClipboard, formatJobForWebhook } from "@client/lib/jobCopy"; import * as api from "../api"; +import { FitAssessment } from "."; import type { Job, ResumeProjectCatalogItem } from "../../shared/types"; interface ReadyPanelProps { @@ -275,18 +275,7 @@ export const ReadyPanel: React.FC = ({
{/* Job identity - confirm this is the right role */}
- {/* AI Suitability Reasoning - Why you're a fit */} - {job.suitabilityReason && ( -
-
- - Fit Assessment -
-

- {job.suitabilityReason} -

-
- )} + {/* Tailored summary snippet - shows what's in the PDF */} {tailoredSummary && ( diff --git a/orchestrator/src/client/components/index.ts b/orchestrator/src/client/components/index.ts index 76fdba3..6478036 100644 --- a/orchestrator/src/client/components/index.ts +++ b/orchestrator/src/client/components/index.ts @@ -2,6 +2,7 @@ export { Header } from './Header'; export { Stats } from './Stats'; export { StatusBadge } from './StatusBadge'; export { ScoreIndicator } from './ScoreIndicator'; +export { FitAssessment } from './FitAssessment'; export { JobCard } from './JobCard'; export { JobTable } from './JobTable'; export { JobList } from './JobList';