allow manual editing JD
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
Copy,
|
Copy,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
Download,
|
Download,
|
||||||
|
Edit2,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
Loader2,
|
Loader2,
|
||||||
@@ -32,6 +33,7 @@ interface JobCardProps {
|
|||||||
onApply: (id: string) => void | Promise<void>;
|
onApply: (id: string) => void | Promise<void>;
|
||||||
onReject: (id: string) => void | Promise<void>;
|
onReject: (id: string) => void | Promise<void>;
|
||||||
onProcess: (id: string) => void | Promise<void>;
|
onProcess: (id: string) => void | Promise<void>;
|
||||||
|
onEditDescription?: (id: string) => void;
|
||||||
isProcessing: boolean;
|
isProcessing: boolean;
|
||||||
highlightedJobId?: string | null;
|
highlightedJobId?: string | null;
|
||||||
onHighlightChange?: (jobId: string | null) => void;
|
onHighlightChange?: (jobId: string | null) => void;
|
||||||
@@ -78,6 +80,7 @@ export const JobCard: React.FC<JobCardProps> = ({
|
|||||||
onApply,
|
onApply,
|
||||||
onReject,
|
onReject,
|
||||||
onProcess,
|
onProcess,
|
||||||
|
onEditDescription,
|
||||||
isProcessing,
|
isProcessing,
|
||||||
highlightedJobId,
|
highlightedJobId,
|
||||||
onHighlightChange,
|
onHighlightChange,
|
||||||
@@ -184,6 +187,13 @@ export const JobCard: React.FC<JobCardProps> = ({
|
|||||||
Copy info
|
Copy info
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{onEditDescription && (
|
||||||
|
<Button variant="outline" size="sm" onClick={() => onEditDescription(job.id)}>
|
||||||
|
<Edit2 className="mr-2 h-4 w-4" />
|
||||||
|
Edit JD
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
{onHighlightChange && (
|
{onHighlightChange && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Copy,
|
Copy,
|
||||||
Download,
|
Download,
|
||||||
|
Edit2,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
RefreshCcw,
|
RefreshCcw,
|
||||||
@@ -58,6 +59,7 @@ export interface JobTableProps {
|
|||||||
onApply: (id: string) => void | Promise<void>;
|
onApply: (id: string) => void | Promise<void>;
|
||||||
onReject: (id: string) => void | Promise<void>;
|
onReject: (id: string) => void | Promise<void>;
|
||||||
onProcess: (id: string) => void | Promise<void>;
|
onProcess: (id: string) => void | Promise<void>;
|
||||||
|
onEditDescription?: (id: string) => void;
|
||||||
processingJobId: string | null;
|
processingJobId: string | null;
|
||||||
highlightedJobId?: string | null;
|
highlightedJobId?: string | null;
|
||||||
onHighlightChange?: (jobId: string | null) => void;
|
onHighlightChange?: (jobId: string | null) => void;
|
||||||
@@ -145,6 +147,7 @@ export const JobTable: React.FC<JobTableProps> = ({
|
|||||||
onApply,
|
onApply,
|
||||||
onReject,
|
onReject,
|
||||||
onProcess,
|
onProcess,
|
||||||
|
onEditDescription,
|
||||||
processingJobId,
|
processingJobId,
|
||||||
highlightedJobId,
|
highlightedJobId,
|
||||||
onHighlightChange,
|
onHighlightChange,
|
||||||
@@ -304,6 +307,13 @@ export const JobTable: React.FC<JobTableProps> = ({
|
|||||||
Copy info
|
Copy info
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
{onEditDescription && (
|
||||||
|
<DropdownMenuItem onSelect={() => onEditDescription(job.id)}>
|
||||||
|
<Edit2 className="mr-2 h-4 w-4" />
|
||||||
|
Edit description
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
{onHighlightChange && (
|
{onHighlightChange && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={() => onHighlightChange(isHighlighted ? null : job.id)}
|
onSelect={() => onHighlightChange(isHighlighted ? null : job.id)}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
Copy,
|
Copy,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
|
Edit2,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
FileText,
|
FileText,
|
||||||
Filter,
|
Filter,
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Play,
|
Play,
|
||||||
RefreshCcw,
|
RefreshCcw,
|
||||||
|
Save,
|
||||||
Search,
|
Search,
|
||||||
Settings,
|
Settings,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
@@ -1016,6 +1018,15 @@ export const OrchestratorPage: React.FC = () => {
|
|||||||
: "Generate PDF"}
|
: "Generate PDF"}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() => {
|
||||||
|
setDetailTab("description");
|
||||||
|
setIsEditingDescription(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Edit2 className="mr-2 h-4 w-4" />
|
||||||
|
Edit description
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onSelect={() => void handleCopyInfo(selectedJob)}>
|
<DropdownMenuItem onSelect={() => void handleCopyInfo(selectedJob)}>
|
||||||
<Copy className="mr-2 h-4 w-4" />
|
<Copy className="mr-2 h-4 w-4" />
|
||||||
Copy info
|
Copy info
|
||||||
@@ -1088,6 +1099,39 @@ export const OrchestratorPage: React.FC = () => {
|
|||||||
<div className="font-medium">{selectedJob.jobType || "Not set"}</div>
|
<div className="font-medium">{selectedJob.jobType || "Not set"}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Separator className="my-2" />
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
|
Description Preview
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
className="h-auto p-0 text-xs text-primary"
|
||||||
|
onClick={() => {
|
||||||
|
setDetailTab("description");
|
||||||
|
setIsEditingDescription(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Edit2 className="mr-1 h-3 w-3" />
|
||||||
|
Edit full JD
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-md border border-border/40 bg-muted/5 p-3 text-xs text-muted-foreground line-clamp-6 whitespace-pre-wrap leading-relaxed">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="w-full h-8 text-xs text-muted-foreground"
|
||||||
|
onClick={() => setDetailTab("description")}
|
||||||
|
>
|
||||||
|
Read full description
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="tailoring" className="pt-3">
|
<TabsContent value="tailoring" className="pt-3">
|
||||||
@@ -1107,45 +1151,103 @@ export const OrchestratorPage: React.FC = () => {
|
|||||||
<div className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
<div className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
Job description
|
Job description
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<div className="flex items-center gap-1">
|
||||||
<DropdownMenuTrigger asChild>
|
{!isEditingDescription ? (
|
||||||
<Button size="icon" variant="ghost" aria-label="Description actions">
|
<Button
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setIsEditingDescription(true)}
|
||||||
|
className="h-8 px-2 text-xs"
|
||||||
|
>
|
||||||
|
<Edit2 className="mr-1.5 h-3.5 w-3.5" />
|
||||||
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
) : (
|
||||||
<DropdownMenuContent align="end">
|
<>
|
||||||
{!isEditingDescription ? (
|
<Button
|
||||||
<DropdownMenuItem onSelect={() => setIsEditingDescription(true)}>
|
size="sm"
|
||||||
Edit description
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
setIsEditingDescription(false);
|
||||||
|
setEditedDescription(selectedJob.jobDescription || "");
|
||||||
|
}}
|
||||||
|
className="h-8 px-2 text-xs text-muted-foreground"
|
||||||
|
disabled={isSavingDescription}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={handleSaveDescription}
|
||||||
|
className="h-8 px-3 text-xs"
|
||||||
|
disabled={isSavingDescription}
|
||||||
|
>
|
||||||
|
{isSavingDescription ? (
|
||||||
|
<Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Save className="mr-1.5 h-3.5 w-3.5" />
|
||||||
|
)}
|
||||||
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="icon" variant="ghost" className="h-8 w-8" aria-label="Description actions">
|
||||||
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() => {
|
||||||
|
void copyTextToClipboard(selectedJob.jobDescription || "");
|
||||||
|
toast.success("Copied raw description");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Copy className="mr-2 h-4 w-4" />
|
||||||
|
Copy raw text
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
) : (
|
</DropdownMenuContent>
|
||||||
<>
|
</DropdownMenu>
|
||||||
<DropdownMenuItem onSelect={handleSaveDescription} disabled={isSavingDescription}>
|
</div>
|
||||||
{isSavingDescription ? "Saving..." : "Save changes"}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onSelect={() => {
|
|
||||||
setIsEditingDescription(false);
|
|
||||||
setEditedDescription(selectedJob.jobDescription || "");
|
|
||||||
}}
|
|
||||||
disabled={isSavingDescription}
|
|
||||||
>
|
|
||||||
Cancel edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-lg border border-border/60 bg-muted/10 p-3 text-sm text-muted-foreground">
|
<div className="rounded-lg border border-border/60 bg-muted/10 p-3 text-sm text-muted-foreground">
|
||||||
{isEditingDescription ? (
|
{isEditingDescription ? (
|
||||||
<Textarea
|
<div className="space-y-3">
|
||||||
value={editedDescription}
|
<Textarea
|
||||||
onChange={(event) => setEditedDescription(event.target.value)}
|
value={editedDescription}
|
||||||
className="min-h-[240px] font-mono text-sm leading-relaxed"
|
onChange={(event) => setEditedDescription(event.target.value)}
|
||||||
placeholder="Enter job description..."
|
className="min-h-[400px] font-mono text-sm leading-relaxed focus-visible:ring-1"
|
||||||
/>
|
placeholder="Enter job description..."
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsEditingDescription(false);
|
||||||
|
setEditedDescription(selectedJob.jobDescription || "");
|
||||||
|
}}
|
||||||
|
disabled={isSavingDescription}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={handleSaveDescription}
|
||||||
|
disabled={isSavingDescription}
|
||||||
|
>
|
||||||
|
{isSavingDescription ? (
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<CheckCircle2 className="mr-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
Save Description
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="whitespace-pre-wrap leading-relaxed">
|
<div className="whitespace-pre-wrap leading-relaxed">
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{description}</ReactMarkdown>
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{description}</ReactMarkdown>
|
||||||
|
|||||||
Reference in New Issue
Block a user