useRescoreJob to reduce duplication
This commit is contained in:
parent
f491f4634e
commit
85976caf9c
@ -46,6 +46,7 @@ import * as api from "../api";
|
||||
import { FitAssessment, JobHeader, TailoredSummary } from ".";
|
||||
import { TailorMode } from "./discovered-panel/TailorMode";
|
||||
import { useProfile } from "../hooks/useProfile";
|
||||
import { useRescoreJob } from "../hooks/useRescoreJob";
|
||||
import type { Job, ResumeProjectCatalogItem } from "../../shared/types";
|
||||
|
||||
type PanelMode = "ready" | "tailor";
|
||||
@ -67,7 +68,7 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
const [mode, setMode] = useState<PanelMode>("ready");
|
||||
const [isMarkingApplied, setIsMarkingApplied] = useState(false);
|
||||
const [isRegenerating, setIsRegenerating] = useState(false);
|
||||
const [isRescoring, setIsRescoring] = useState(false);
|
||||
const { isRescoring, rescoreJob } = useRescoreJob(onJobUpdated);
|
||||
const [catalog, setCatalog] = useState<ResumeProjectCatalogItem[]>([]);
|
||||
const [recentlyApplied, setRecentlyApplied] = useState<{
|
||||
jobId: string;
|
||||
@ -182,21 +183,7 @@ export const ReadyPanel: React.FC<ReadyPanelProps> = ({
|
||||
}
|
||||
}, [job, onJobUpdated]);
|
||||
|
||||
const handleRescore = useCallback(async () => {
|
||||
if (!job) return;
|
||||
|
||||
try {
|
||||
setIsRescoring(true);
|
||||
await api.rescoreJob(job.id);
|
||||
toast.success("Match recalculated");
|
||||
await onJobUpdated();
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to recalculate match";
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setIsRescoring(false);
|
||||
}
|
||||
}, [job, onJobUpdated]);
|
||||
const handleRescore = useCallback(() => rescoreJob(job?.id), [job?.id, rescoreJob]);
|
||||
|
||||
const handleSkip = useCallback(async () => {
|
||||
if (!job) return;
|
||||
|
||||
@ -7,6 +7,7 @@ import { DecideMode } from "./DecideMode";
|
||||
import { EmptyState } from "./EmptyState";
|
||||
import { ProcessingState } from "./ProcessingState";
|
||||
import { TailorMode } from "./TailorMode";
|
||||
import { useRescoreJob } from "../../hooks/useRescoreJob";
|
||||
|
||||
type PanelMode = "decide" | "tailor";
|
||||
|
||||
@ -24,13 +25,12 @@ export const DiscoveredPanel: React.FC<DiscoveredPanelProps> = ({
|
||||
const [mode, setMode] = useState<PanelMode>("decide");
|
||||
const [isSkipping, setIsSkipping] = useState(false);
|
||||
const [isFinalizing, setIsFinalizing] = useState(false);
|
||||
const [isRescoring, setIsRescoring] = useState(false);
|
||||
const { isRescoring, rescoreJob } = useRescoreJob(onJobUpdated);
|
||||
|
||||
useEffect(() => {
|
||||
setMode("decide");
|
||||
setIsSkipping(false);
|
||||
setIsFinalizing(false);
|
||||
setIsRescoring(false);
|
||||
}, [job?.id]);
|
||||
|
||||
const handleSkip = async () => {
|
||||
@ -71,21 +71,7 @@ export const DiscoveredPanel: React.FC<DiscoveredPanelProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleRescore = async () => {
|
||||
if (!job) return;
|
||||
try {
|
||||
setIsRescoring(true);
|
||||
await api.rescoreJob(job.id);
|
||||
toast.success("Match recalculated");
|
||||
await onJobUpdated();
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Failed to recalculate match";
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setIsRescoring(false);
|
||||
}
|
||||
};
|
||||
const handleRescore = () => rescoreJob(job?.id);
|
||||
|
||||
if (!job) {
|
||||
return <EmptyState />;
|
||||
|
||||
38
orchestrator/src/client/hooks/useRescoreJob.test.ts
Normal file
38
orchestrator/src/client/hooks/useRescoreJob.test.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { useRescoreJob } from "./useRescoreJob";
|
||||
import * as api from "../api";
|
||||
import { toast } from "sonner";
|
||||
|
||||
vi.mock("../api", () => ({
|
||||
rescoreJob: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("useRescoreJob", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("rescoring updates the job and shows a toast", async () => {
|
||||
const onJobUpdated = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mocked(api.rescoreJob).mockResolvedValue({} as any);
|
||||
|
||||
const { result } = renderHook(() => useRescoreJob(onJobUpdated));
|
||||
|
||||
await act(async () => {
|
||||
await result.current.rescoreJob("job-1");
|
||||
});
|
||||
|
||||
expect(api.rescoreJob).toHaveBeenCalledWith("job-1");
|
||||
expect(onJobUpdated).toHaveBeenCalled();
|
||||
expect(toast.success).toHaveBeenCalledWith("Match recalculated");
|
||||
});
|
||||
});
|
||||
29
orchestrator/src/client/hooks/useRescoreJob.ts
Normal file
29
orchestrator/src/client/hooks/useRescoreJob.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import * as api from "../api";
|
||||
|
||||
export function useRescoreJob(onJobUpdated: () => void | Promise<void>) {
|
||||
const [isRescoring, setIsRescoring] = useState(false);
|
||||
|
||||
const rescoreJob = useCallback(
|
||||
async (jobId?: string | null) => {
|
||||
if (!jobId) return;
|
||||
|
||||
try {
|
||||
setIsRescoring(true);
|
||||
await api.rescoreJob(jobId);
|
||||
toast.success("Match recalculated");
|
||||
await onJobUpdated();
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to recalculate match";
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setIsRescoring(false);
|
||||
}
|
||||
},
|
||||
[onJobUpdated],
|
||||
);
|
||||
|
||||
return { isRescoring, rescoreJob };
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user