From fde32d2c51c7431ae9e8d14094e9faf53dc602a1 Mon Sep 17 00:00:00 2001 From: DaKheera47 Date: Tue, 20 Jan 2026 11:09:50 +0000 Subject: [PATCH] combined JobHeader --- .../src/client/components/JobHeader.tsx | 90 +++++++++++++++++++ .../src/client/components/ReadyPanel.tsx | 4 +- .../discovered-panel/DecideMode.tsx | 81 +++++------------ orchestrator/src/client/components/index.ts | 1 + .../pages/orchestrator/JobDetailPanel.tsx | 81 +---------------- 5 files changed, 117 insertions(+), 140 deletions(-) create mode 100644 orchestrator/src/client/components/JobHeader.tsx diff --git a/orchestrator/src/client/components/JobHeader.tsx b/orchestrator/src/client/components/JobHeader.tsx new file mode 100644 index 0000000..3d1f9be --- /dev/null +++ b/orchestrator/src/client/components/JobHeader.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import { Calendar, DollarSign, MapPin } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { cn, formatDate, sourceLabel } from "@/lib/utils"; +import type { Job, JobStatus } from "../../shared/types"; +import { defaultStatusToken, statusTokens } from "../pages/orchestrator/constants"; + +interface JobHeaderProps { + job: Job; + className?: string; +} + +const StatusPill: React.FC<{ status: JobStatus }> = ({ status }) => { + const tokens = statusTokens[status] ?? defaultStatusToken; + return ( + + + {tokens.label} + + ); +}; + +const ScoreMeter: React.FC<{ score: number | null }> = ({ score }) => { + if (score == null) { + return -; + } + + return ( +
+
+
+
+ {score} +
+ ); +}; + +export const JobHeader: React.FC = ({ job, className }) => { + const deadline = formatDate(job.deadline); + + return ( +
+ {/* Detail header: lighter weight than list items */} +
+
+
{job.title}
+
{job.employer}
+
+ + {sourceLabel[job.source]} + +
+ + {/* Tertiary metadata - subdued */} +
+ {job.location && ( + + + {job.location} + + )} + {deadline && ( + + + {deadline} + + )} + {job.salary && ( + + + {job.salary} + + )} +
+ + {/* Status and score: single line, subdued */} +
+ + +
+
+ ); +}; diff --git a/orchestrator/src/client/components/ReadyPanel.tsx b/orchestrator/src/client/components/ReadyPanel.tsx index 7097766..3cafb27 100644 --- a/orchestrator/src/client/components/ReadyPanel.tsx +++ b/orchestrator/src/client/components/ReadyPanel.tsx @@ -41,7 +41,7 @@ import { } from "@/components/ui/accordion"; import { cn, copyTextToClipboard, formatJobForWebhook } from "@/lib/utils"; import * as api from "../api"; -import { FitAssessment } from "."; +import { FitAssessment, JobHeader } from "."; import type { Job, ResumeProjectCatalogItem } from "../../shared/types"; interface ReadyPanelProps { @@ -217,6 +217,8 @@ export const ReadyPanel: React.FC = ({ return (
+ + {/* ───────────────────────────────────────────────────────────────────── PRIMARY ACTION CLUSTER All actions in one line: View, Save, Open, and Mark Applied diff --git a/orchestrator/src/client/components/discovered-panel/DecideMode.tsx b/orchestrator/src/client/components/discovered-panel/DecideMode.tsx index 6678fcf..3b5e2c8 100644 --- a/orchestrator/src/client/components/discovered-panel/DecideMode.tsx +++ b/orchestrator/src/client/components/discovered-panel/DecideMode.tsx @@ -1,12 +1,10 @@ import React, { useMemo, useState } from "react"; -import { Calendar, DollarSign, ExternalLink, Loader2, MapPin, Sparkles, XCircle } from "lucide-react"; +import { ExternalLink, Loader2, Sparkles, XCircle } from "lucide-react"; -import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; -import { FitAssessment } from "../FitAssessment"; -import { formatDate, sourceLabel } from "@/lib/utils"; +import { FitAssessment, JobHeader } from ".."; import type { Job } from "../../../shared/types"; import { CollapsibleSection } from "./CollapsibleSection"; import { getPlainDescription } from "./helpers"; @@ -25,7 +23,6 @@ export const DecideMode: React.FC = ({ isSkipping, }) => { const [showDescription, setShowDescription] = useState(false); - const deadline = formatDate(job.deadline); const jobLink = job.applicationLink || job.jobUrl; const description = useMemo( @@ -35,105 +32,66 @@ export const DecideMode: React.FC = ({ return (
-
-
-
-

- {job.title} -

-

{job.employer}

-
+
+ -
- - {sourceLabel[job.source]} - -
-
- - {(job.location || deadline || job.salary) && ( -
- {job.location && ( - - - {job.location} - - )} - {deadline && ( - - - {deadline} - - )} - {job.salary && ( - - - {job.salary} - - )} -
- )} - -
+
- + -
+
setShowDescription((prev) => !prev)} - label={`${showDescription ? "Hide" : "View"} full job description`} + label={`${showDescription ? "Hide" : "View"} Full Job Description`} > -
-

+

+

{description}

- + -
+
{jobLink ? ( ) : null} @@ -141,3 +99,4 @@ export const DecideMode: React.FC = ({
); }; + diff --git a/orchestrator/src/client/components/index.ts b/orchestrator/src/client/components/index.ts index 989ae45..5af0fa5 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 { JobHeader } from './JobHeader'; export { FitAssessment } from './FitAssessment'; export { PipelineProgress } from './PipelineProgress'; export { TailoringEditor } from './TailoringEditor'; diff --git a/orchestrator/src/client/pages/orchestrator/JobDetailPanel.tsx b/orchestrator/src/client/pages/orchestrator/JobDetailPanel.tsx index 63c5ee6..e65beeb 100644 --- a/orchestrator/src/client/pages/orchestrator/JobDetailPanel.tsx +++ b/orchestrator/src/client/pages/orchestrator/JobDetailPanel.tsx @@ -1,14 +1,11 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { - Calendar, CheckCircle2, Copy, - DollarSign, Edit2, ExternalLink, FileText, Loader2, - MapPin, MoreHorizontal, RefreshCcw, Save, @@ -29,9 +26,9 @@ import { } from "@/components/ui/dropdown-menu"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; -import { cn, copyTextToClipboard, formatDate, formatJobForWebhook, sourceLabel, safeFilenamePart, stripHtml } from "@/lib/utils"; +import { cn, copyTextToClipboard, formatJobForWebhook, safeFilenamePart, stripHtml } from "@/lib/utils"; -import { DiscoveredPanel } from "../../components"; +import { DiscoveredPanel, JobHeader } from "../../components"; import { ReadyPanel } from "../../components/ReadyPanel"; import { TailoringEditor } from "../../components/TailoringEditor"; import * as api from "../../api"; @@ -48,40 +45,6 @@ interface JobDetailPanelProps { onSetActiveTab: (tab: FilterTab) => void; } -// Subdued status pill for inspector panel - not competing with list -const StatusPill: React.FC<{ status: JobStatus }> = ({ status }) => { - const tokens = statusTokens[status] ?? defaultStatusToken; - return ( - - - {tokens.label} - - ); -}; - -// Compact score meter for inspector panel -const ScoreMeter: React.FC<{ score: number | null }> = ({ score }) => { - if (score == null) { - return -; - } - - return ( -
-
-
-
- {score} -
- ); -}; - export const JobDetailPanel: React.FC = ({ activeTab, activeJobs, @@ -258,7 +221,6 @@ export const JobDetailPanel: React.FC = ({ const selectedPdfHref = selectedJob ? `/pdfs/resume_${selectedJob.id}.pdf?v=${encodeURIComponent(selectedJob.updatedAt)}` : "#"; - const selectedDeadline = selectedJob ? formatDate(selectedJob.deadline) : null; const canApply = selectedJob?.status === "ready"; const canProcess = selectedJob ? ["discovered", "ready"].includes(selectedJob.status) : false; const canSkip = selectedJob ? ["discovered", "ready"].includes(selectedJob.status) : false; @@ -309,44 +271,7 @@ export const JobDetailPanel: React.FC = ({ return (
- {/* Detail header: lighter weight than list items */} -
-
-
{selectedJob.title}
-
{selectedJob.employer}
-
- - {sourceLabel[selectedJob.source]} - -
- - {/* Tertiary metadata - subdued */} -
- {selectedJob.location && ( - - - {selectedJob.location} - - )} - {selectedDeadline && ( - - - {selectedDeadline} - - )} - {selectedJob.salary && ( - - - {selectedJob.salary} - - )} -
- - {/* Status and score: single line, subdued */} -
- - -
+