diff --git a/orchestrator/src/client/components/JobCard.tsx b/orchestrator/src/client/components/JobCard.tsx index a262371..405e813 100644 --- a/orchestrator/src/client/components/JobCard.tsx +++ b/orchestrator/src/client/components/JobCard.tsx @@ -21,7 +21,7 @@ import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; -import { copyTextToClipboard, formatJobForLlmContext } from "@client/lib/jobCopy"; +import { copyTextToClipboard, formatJobForWebhook } from "@client/lib/jobCopy"; import type { Job } from "../../shared/types"; import { ScoreIndicator } from "./ScoreIndicator"; import { StatusBadge } from "./StatusBadge"; @@ -64,6 +64,7 @@ export const JobCard: React.FC = ({ gradcracker: "Gradcracker", indeed: "Indeed", linkedin: "LinkedIn", + ukvisajobs: "UK Visa Jobs", }; const hasPdf = !!job.pdfPath; @@ -78,8 +79,8 @@ export const JobCard: React.FC = ({ const handleCopyInfo = async () => { try { - await copyTextToClipboard(formatJobForLlmContext(job)); - toast.success("Copied job info", { description: "LLM-ready context copied to clipboard." }); + await copyTextToClipboard(formatJobForWebhook(job)); + toast.success("Copied job info", { description: "Webhook payload copied to clipboard." }); } catch { toast.error("Could not copy job info"); } diff --git a/orchestrator/src/client/components/JobTable.tsx b/orchestrator/src/client/components/JobTable.tsx index 759c70b..b9fa0ce 100644 --- a/orchestrator/src/client/components/JobTable.tsx +++ b/orchestrator/src/client/components/JobTable.tsx @@ -29,7 +29,7 @@ import { } from "@/components/ui/dropdown-menu"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { cn } from "@/lib/utils"; -import { copyTextToClipboard, formatJobForLlmContext } from "@client/lib/jobCopy"; +import { copyTextToClipboard, formatJobForWebhook } from "@client/lib/jobCopy"; import type { Job } from "../../shared/types"; import { StatusBadge } from "./StatusBadge"; @@ -67,6 +67,7 @@ const sourceLabel: Record = { gradcracker: "Gradcracker", indeed: "Indeed", linkedin: "LinkedIn", + ukvisajobs: "UK Visa Jobs", }; const defaultSortDirection: Record = { @@ -146,8 +147,8 @@ export const JobTable: React.FC = ({ const handleCopyInfo = async (job: Job) => { try { - await copyTextToClipboard(formatJobForLlmContext(job)); - toast.success("Copied job info", { description: "LLM-ready context copied to clipboard." }); + await copyTextToClipboard(formatJobForWebhook(job)); + toast.success("Copied job info", { description: "Webhook payload copied to clipboard." }); } catch { toast.error("Could not copy job info"); } diff --git a/orchestrator/src/client/lib/jobCopy.ts b/orchestrator/src/client/lib/jobCopy.ts index 3c00ad8..da8e07e 100644 --- a/orchestrator/src/client/lib/jobCopy.ts +++ b/orchestrator/src/client/lib/jobCopy.ts @@ -1,86 +1,15 @@ import type { Job } from "@shared/types"; -const pushLine = (lines: string[], label: string, value: unknown) => { - if (value == null) return; - const normalized = typeof value === "string" ? value.trim() : String(value); - if (!normalized) return; - lines.push(`${label}: ${normalized}`); -}; - -const pushBlock = (lines: string[], heading: string, value: string | null | undefined) => { - const normalized = value?.trim(); - if (!normalized) return; - lines.push(""); - lines.push(`${heading}:`); - lines.push(normalized); -}; - -export const formatJobForLlmContext = (job: Job) => { - const jobLink = job.applicationLink || job.jobUrl; - - const lines: string[] = []; - lines.push("JOB CONTEXT"); - - pushLine(lines, "Title", job.title); - pushLine(lines, "Company", job.employer); - pushLine(lines, "Source", job.source); - pushLine(lines, "Status", job.status); - - pushLine(lines, "Job URL", job.jobUrl); - pushLine(lines, "Application link", job.applicationLink); - pushLine(lines, "Best link", jobLink); - pushLine(lines, "Direct URL", job.jobUrlDirect); - pushLine(lines, "Source job id", job.sourceJobId); - - pushLine(lines, "Location", job.location); - pushLine(lines, "Remote", job.isRemote); - pushLine(lines, "Disciplines", job.disciplines); - pushLine(lines, "Job type", job.jobType); - pushLine(lines, "Job level", job.jobLevel); - pushLine(lines, "Job function", job.jobFunction); - pushLine(lines, "Listing type", job.listingType); - - pushLine(lines, "Salary", job.salary); - if (job.salaryMinAmount != null || job.salaryMaxAmount != null) { - pushLine( - lines, - "Salary range", - [ - job.salaryMinAmount != null ? String(job.salaryMinAmount) : null, - job.salaryMaxAmount != null ? String(job.salaryMaxAmount) : null, - ] - .filter(Boolean) - .join(" - "), - ); - } - pushLine(lines, "Salary interval", job.salaryInterval); - pushLine(lines, "Salary currency", job.salaryCurrency); - pushLine(lines, "Salary source", job.salarySource); - - pushLine(lines, "Degree required", job.degreeRequired); - pushLine(lines, "Starting", job.starting); - pushLine(lines, "Deadline", job.deadline); - pushLine(lines, "Date posted", job.datePosted); - - pushLine(lines, "Skills", job.skills); - pushLine(lines, "Experience", job.experienceRange); - pushLine(lines, "Emails", job.emails); - - pushLine(lines, "Company industry", job.companyIndustry); - pushLine(lines, "Company URL", job.companyUrlDirect || job.employerUrl); - pushLine(lines, "Company employees", job.companyNumEmployees); - pushLine(lines, "Company revenue", job.companyRevenue); - pushLine(lines, "Company rating", job.companyRating); - pushLine(lines, "Company reviews", job.companyReviewsCount); - pushLine(lines, "Company addresses", job.companyAddresses); - - pushLine(lines, "Discovered", job.discoveredAt); - pushLine(lines, "Processed", job.processedAt); - - pushBlock(lines, "Job description", job.jobDescription); - pushBlock(lines, "Company description", job.companyDescription); - - return lines.join("\n").trim() + "\n"; +export const formatJobForWebhook = (job: Job) => { + return JSON.stringify( + { + event: "job.completed", + sentAt: new Date().toISOString(), + job, + }, + null, + 2, + ); }; export async function copyTextToClipboard(text: string) {