jd can be edited

This commit is contained in:
DaKheera47 2026-01-15 22:44:52 +00:00
parent 2af9537516
commit 4fcace18f3
2 changed files with 59 additions and 6 deletions

View File

@ -247,6 +247,7 @@ const TailorMode: React.FC<TailorModeProps> = ({
}) => {
const [catalog, setCatalog] = useState<ResumeProjectCatalogItem[]>([]);
const [summary, setSummary] = useState(job.tailoredSummary || "");
const [jobDescription, setJobDescription] = useState(job.jobDescription || "");
const [selectedIds, setSelectedIds] = useState<Set<string>>(() => {
const saved = job.selectedProjectIds?.split(",").filter(Boolean) ?? [];
return new Set(saved);
@ -265,13 +266,15 @@ const TailorMode: React.FC<TailorModeProps> = ({
// Reset form when job changes
useEffect(() => {
setSummary(job.tailoredSummary || "");
setJobDescription(job.jobDescription || "");
const saved = job.selectedProjectIds?.split(",").filter(Boolean) ?? [];
setSelectedIds(new Set(saved));
setDraftStatus("saved");
}, [job.id, job.tailoredSummary, job.selectedProjectIds]);
}, [job.id, job.tailoredSummary, job.selectedProjectIds, job.jobDescription]);
// Track unsaved changes
const savedSummary = job.tailoredSummary || "";
const savedDescription = job.jobDescription || "";
const savedIds = useMemo(() => {
const saved = job.selectedProjectIds?.split(",").filter(Boolean) ?? [];
return new Set(saved);
@ -279,12 +282,13 @@ const TailorMode: React.FC<TailorModeProps> = ({
const hasChanges = useMemo(() => {
if (summary !== savedSummary) return true;
if (jobDescription !== savedDescription) return true;
if (selectedIds.size !== savedIds.size) return true;
for (const id of selectedIds) {
if (!savedIds.has(id)) return true;
}
return false;
}, [summary, savedSummary, selectedIds, savedIds]);
}, [summary, savedSummary, jobDescription, savedDescription, selectedIds, savedIds]);
// Update draft status when changes are made
useEffect(() => {
@ -302,6 +306,7 @@ const TailorMode: React.FC<TailorModeProps> = ({
setDraftStatus("saving");
await api.updateJob(job.id, {
tailoredSummary: summary,
jobDescription: jobDescription,
selectedProjectIds: Array.from(selectedIds).join(","),
});
setDraftStatus("saved");
@ -311,7 +316,7 @@ const TailorMode: React.FC<TailorModeProps> = ({
}, 1500);
return () => clearTimeout(timeout);
}, [summary, selectedIds, hasChanges, draftStatus, job.id]);
}, [summary, jobDescription, selectedIds, hasChanges, draftStatus, job.id]);
const handleToggleProject = (id: string) => {
const next = new Set(selectedIds);
@ -323,8 +328,19 @@ const TailorMode: React.FC<TailorModeProps> = ({
const handleGenerateWithAI = async () => {
try {
setIsGenerating(true);
// Save any pending changes first so AI uses the latest description
if (hasChanges) {
await api.updateJob(job.id, {
tailoredSummary: summary,
jobDescription: jobDescription,
selectedProjectIds: Array.from(selectedIds).join(","),
});
}
const updatedJob = await api.summarizeJob(job.id, { force: true });
setSummary(updatedJob.tailoredSummary || "");
setJobDescription(updatedJob.jobDescription || "");
if (updatedJob.selectedProjectIds) {
setSelectedIds(
new Set(updatedJob.selectedProjectIds.split(",").filter(Boolean))
@ -348,6 +364,7 @@ const TailorMode: React.FC<TailorModeProps> = ({
setIsSaving(true);
await api.updateJob(job.id, {
tailoredSummary: summary,
jobDescription: jobDescription,
selectedProjectIds: Array.from(selectedIds).join(","),
});
} catch {
@ -441,6 +458,20 @@ const TailorMode: React.FC<TailorModeProps> = ({
</Button>
</div>
{/* Job Description */}
<div className='space-y-2'>
<label className='text-xs font-medium text-muted-foreground'>
Job Description (Edit to help AI tailoring)
</label>
<textarea
className='w-full min-h-[120px] max-h-[250px] rounded-lg border border-border/60 bg-background/50 px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50'
value={jobDescription}
onChange={(e) => setJobDescription(e.target.value)}
placeholder='The raw job description...'
disabled={isGenerating || isFinalizing}
/>
</div>
{/* Tailored Summary */}
<div className='space-y-2'>
<label className='text-xs font-medium text-muted-foreground'>

View File

@ -26,6 +26,7 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
}) => {
const [catalog, setCatalog] = useState<ResumeProjectCatalogItem[]>([]);
const [summary, setSummary] = useState(job.tailoredSummary || "");
const [jobDescription, setJobDescription] = useState(job.jobDescription || "");
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
const [isSummarizing, setIsSummarizing] = useState(false);
const [isGeneratingPdf, setIsGeneratingPdf] = useState(false);
@ -44,7 +45,9 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
return false;
}, [selectedIds, savedSelectedIds]);
const isDirty = summary !== (job.tailoredSummary || "") || hasSelectionDiff;
const isDirty = summary !== (job.tailoredSummary || "") ||
jobDescription !== (job.jobDescription || "") ||
hasSelectionDiff;
useEffect(() => {
onDirtyChange?.(isDirty);
@ -58,7 +61,8 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
if (job.selectedProjectIds) {
setSelectedIds(new Set(job.selectedProjectIds.split(',').filter(Boolean)));
}
}, [job.selectedProjectIds]);
setJobDescription(job.jobDescription || "");
}, [job.selectedProjectIds, job.jobDescription]);
useEffect(() => {
setSummary(job.tailoredSummary || "");
@ -70,6 +74,7 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
setIsSaving(true);
await api.updateJob(job.id, {
tailoredSummary: summary,
jobDescription: jobDescription,
selectedProjectIds: Array.from(selectedIds).join(","),
});
if (showToast) toast.success("Changes saved");
@ -81,7 +86,7 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
setIsSaving(false);
}
},
[job.id, onUpdate, selectedIds, summary],
[job.id, onUpdate, selectedIds, summary, jobDescription],
);
useEffect(() => {
@ -106,8 +111,13 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
const handleSummarize = async () => {
try {
setIsSummarizing(true);
// Save changes first so AI uses latest description
if (isDirty) {
await saveChanges({ showToast: false });
}
const updatedJob = await api.summarizeJob(job.id, { force: true });
setSummary(updatedJob.tailoredSummary || "");
setJobDescription(updatedJob.jobDescription || "");
if (updatedJob.selectedProjectIds) {
setSelectedIds(new Set(updatedJob.selectedProjectIds.split(',').filter(Boolean)));
}
@ -168,6 +178,18 @@ export const TailoringEditor: React.FC<TailoringEditorProps> = ({
</div>
<div className="space-y-4 rounded-lg border bg-card p-4 shadow-sm">
<div className="space-y-2">
<label className="text-sm font-medium">Job Description (Edit to help AI tailoring)</label>
<textarea
className="w-full min-h-[120px] max-h-[250px] rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
value={jobDescription}
onChange={(e) => setJobDescription(e.target.value)}
placeholder="The raw job description..."
/>
</div>
<Separator />
<div className="space-y-2">
<label className="text-sm font-medium">Tailored Summary</label>
<textarea