Ready panel can edit now
This commit is contained in:
parent
7cc5017e56
commit
164256326f
@ -3,6 +3,8 @@
|
||||
*
|
||||
* Designed for a single, fast, repeatable workflow: verify → download → apply → mark applied.
|
||||
* The PDF is the primary artifact, represented abstractly through an Application Kit summary.
|
||||
*
|
||||
* Now includes inline tailoring mode for editing and regenerating PDFs without switching tabs.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
@ -42,14 +44,15 @@ import {
|
||||
import { cn, copyTextToClipboard, formatJobForWebhook } from "@/lib/utils";
|
||||
import * as api from "../api";
|
||||
import { FitAssessment, JobHeader, TailoredSummary } from ".";
|
||||
import { TailorMode } from "./discovered-panel/TailorMode";
|
||||
import type { Job, ResumeProjectCatalogItem } from "../../shared/types";
|
||||
|
||||
type PanelMode = "ready" | "tailor";
|
||||
|
||||
interface ReadyPanelProps {
|
||||
job: Job | null;
|
||||
onJobUpdated: () => void | Promise<void>;
|
||||
onJobMoved: (jobId: string) => void;
|
||||
onEditTailoring: () => void;
|
||||
onEditDescription: () => void;
|
||||
}
|
||||
|
||||
const safeFilenamePart = (value: string | null | undefined) =>
|
||||
@ -59,9 +62,8 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
job,
|
||||
onJobUpdated,
|
||||
onJobMoved,
|
||||
onEditTailoring,
|
||||
onEditDescription,
|
||||
}) => {
|
||||
const [mode, setMode] = useState<PanelMode>("ready");
|
||||
const [isMarkingApplied, setIsMarkingApplied] = useState(false);
|
||||
const [isRegenerating, setIsRegenerating] = useState(false);
|
||||
const [catalog, setCatalog] = useState<ResumeProjectCatalogItem[]>([]);
|
||||
@ -77,6 +79,11 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
api.getProfileProjects().then(setCatalog).catch(console.error);
|
||||
}, []);
|
||||
|
||||
// Reset mode when job changes
|
||||
useEffect(() => {
|
||||
setMode("ready");
|
||||
}, [job?.id]);
|
||||
|
||||
// Compute derived values
|
||||
const pdfHref = job
|
||||
? `/pdfs/resume_${job.id}.pdf?v=${encodeURIComponent(job.updatedAt)}`
|
||||
@ -198,6 +205,23 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
}
|
||||
}, [job]);
|
||||
|
||||
// Handler for regenerating PDF after tailoring edits
|
||||
const handleTailorFinalize = useCallback(async () => {
|
||||
if (!job) return;
|
||||
try {
|
||||
setIsRegenerating(true);
|
||||
await api.generateJobPdf(job.id);
|
||||
toast.success("PDF regenerated");
|
||||
setMode("ready");
|
||||
await onJobUpdated();
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to regenerate PDF";
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setIsRegenerating(false);
|
||||
}
|
||||
}, [job, onJobUpdated]);
|
||||
|
||||
// Empty state
|
||||
if (!job) {
|
||||
return (
|
||||
@ -213,6 +237,19 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
// Tailor mode - reuse the same TailorMode component with 'ready' variant
|
||||
if (mode === "tailor") {
|
||||
return (
|
||||
<TailorMode
|
||||
job={job}
|
||||
onBack={() => setMode("ready")}
|
||||
onFinalize={handleTailorFinalize}
|
||||
isFinalizing={isRegenerating}
|
||||
variant="ready"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<JobHeader
|
||||
@ -332,7 +369,7 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="center" className="w-56">
|
||||
{/* Fix/Edit actions */}
|
||||
<DropdownMenuItem onSelect={onEditTailoring}>
|
||||
<DropdownMenuItem onSelect={() => setMode("tailor")}>
|
||||
<Edit2 className="mr-2 h-4 w-4" />
|
||||
Edit tailoring
|
||||
</DropdownMenuItem>
|
||||
@ -345,11 +382,6 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
{isRegenerating ? "Regenerating..." : "Regenerate PDF"}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onSelect={onEditDescription}>
|
||||
<Edit2 className="mr-2 h-4 w-4" />
|
||||
Edit job description
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{/* Utility actions */}
|
||||
|
||||
@ -15,6 +15,8 @@ interface TailorModeProps {
|
||||
onBack: () => void;
|
||||
onFinalize: () => void;
|
||||
isFinalizing: boolean;
|
||||
/** Variant controls the finalize button text. Default is 'discovered'. */
|
||||
variant?: 'discovered' | 'ready';
|
||||
}
|
||||
|
||||
export const TailorMode: React.FC<TailorModeProps> = ({
|
||||
@ -22,6 +24,7 @@ export const TailorMode: React.FC<TailorModeProps> = ({
|
||||
onBack,
|
||||
onFinalize,
|
||||
isFinalizing,
|
||||
variant = 'discovered',
|
||||
}) => {
|
||||
const [catalog, setCatalog] = useState<ResumeProjectCatalogItem[]>([]);
|
||||
const [summary, setSummary] = useState(job.tailoredSummary || "");
|
||||
@ -274,7 +277,7 @@ export const TailorMode: React.FC<TailorModeProps> = ({
|
||||
<div className='space-y-2'>
|
||||
{!canFinalize && (
|
||||
<p className='text-[10px] text-center text-muted-foreground'>
|
||||
Add a summary and select at least one project to finalize.
|
||||
Add a summary and select at least one project to {variant === 'ready' ? 'regenerate' : 'finalize'}.
|
||||
</p>
|
||||
)}
|
||||
<Button
|
||||
@ -285,17 +288,19 @@ export const TailorMode: React.FC<TailorModeProps> = ({
|
||||
{isFinalizing ? (
|
||||
<>
|
||||
<Loader2 className='mr-2 h-4 w-4 animate-spin' />
|
||||
Finalizing & generating PDF...
|
||||
{variant === 'ready' ? 'Regenerating PDF...' : 'Finalizing & generating PDF...'}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Check className='mr-2 h-4 w-4' />
|
||||
Finalize & Move to Ready
|
||||
{variant === 'ready' ? 'Regenerate PDF' : 'Finalize & Move to Ready'}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<p className='text-[10px] text-center text-muted-foreground/70'>
|
||||
This will generate your tailored PDF and move the job to Ready.
|
||||
{variant === 'ready'
|
||||
? 'This will save your changes and regenerate the tailored PDF.'
|
||||
: 'This will generate your tailored PDF and move the job to Ready.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -243,17 +243,6 @@ export const JobDetailPanel: React.FC<JobDetailPanelProps> = ({
|
||||
job={selectedJob}
|
||||
onJobUpdated={onJobUpdated}
|
||||
onJobMoved={handleJobMoved}
|
||||
onEditTailoring={() => {
|
||||
onSetActiveTab("discovered");
|
||||
setTimeout(() => setDetailTab("tailoring"), 50);
|
||||
}}
|
||||
onEditDescription={() => {
|
||||
onSetActiveTab("discovered");
|
||||
setTimeout(() => {
|
||||
setDetailTab("description");
|
||||
setIsEditingDescription(true);
|
||||
}, 50);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user