Fix in-progress jobs in command bar (#275)

This commit is contained in:
Shaheer Sarfaraz 2026-03-15 22:08:34 +00:00 committed by GitHub
parent a0220df17f
commit ec89a02e4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 93 additions and 2 deletions

View File

@ -148,10 +148,29 @@ describe("JobCommandBar", () => {
expect(screen.getByText("Lock to @ready")).toBeInTheDocument();
expect(screen.getByText("Lock to @discovered")).toBeInTheDocument();
expect(screen.getByText("Lock to @applied")).toBeInTheDocument();
expect(screen.getByText("Lock to @in-progress")).toBeInTheDocument();
expect(screen.getByText("Lock to @skipped")).toBeInTheDocument();
expect(screen.getByText("Lock to @expired")).toBeInTheDocument();
});
it("creates in-progress lock from @prog + Tab", () => {
render(
<JobCommandBar
jobs={[createJob({ id: "job-1", status: "in_progress" })]}
onSelectJob={vi.fn()}
/>,
);
openWithKeyboard();
const input = screen.getByPlaceholderText(
"Search jobs by job title or company name...",
);
fireEvent.change(input, { target: { value: "@prog" } });
fireEvent.keyDown(input, { key: "Tab" });
expect(screen.getByText("@in-progress")).toBeInTheDocument();
});
it("searches by company name and routes to the matched state", () => {
const onSelectJob = vi.fn();
const jobs: Job[] = [
@ -377,11 +396,44 @@ describe("JobCommandBar", () => {
fireEvent.keyDown(input, { key: "Tab" });
expect(
screen.queryByText(/^@(ready|discovered|applied|skipped|expired)$/),
screen.queryByText(
/^@(ready|discovered|applied|in-progress|skipped|expired)$/,
),
).not.toBeInTheDocument();
expect((input as HTMLInputElement).value).toBe("@all");
});
it("routes in-progress jobs to the all jobs view", () => {
const onSelectJob = vi.fn();
render(
<JobCommandBar
jobs={[
createJob({
id: "in-progress-job",
title: "Staff Engineer",
employer: "Globex",
status: "in_progress",
}),
]}
onSelectJob={onSelectJob}
/>,
);
openWithKeyboard();
fireEvent.change(
screen.getByPlaceholderText(
"Search jobs by job title or company name...",
),
{
target: { value: "Globex" },
},
);
fireEvent.click(screen.getByText("Staff Engineer"));
expect(onSelectJob).toHaveBeenCalledWith("all", "in-progress-job");
});
it("excludes processing jobs from every lock scope", () => {
const jobs: Job[] = [
createJob({

View File

@ -51,6 +51,8 @@ export const JobCommandBar: React.FC<JobCommandBarProps> = ({
"border-sky-500/50 shadow-[0_0_0_1px_rgba(14,165,233,0.2),0_0_36px_-12px_rgba(14,165,233,0.55)]",
applied:
"border-emerald-500/50 shadow-[0_0_0_1px_rgba(16,185,129,0.2),0_0_36px_-12px_rgba(16,185,129,0.55)]",
in_progress:
"border-cyan-500/50 shadow-[0_0_0_1px_rgba(6,182,212,0.2),0_0_36px_-12px_rgba(6,182,212,0.55)]",
skipped:
"border-rose-500/50 shadow-[0_0_0_1px_rgba(244,63,94,0.2),0_0_36px_-12px_rgba(244,63,94,0.55)]",
expired:

View File

@ -6,6 +6,7 @@ export type StatusLock =
| "ready"
| "discovered"
| "applied"
| "in_progress"
| "skipped"
| "expired";
@ -21,6 +22,7 @@ const lockAliases: Record<StatusLock, string[]> = {
ready: ["ready", "rdy"],
discovered: ["discovered", "discover", "disc"],
applied: ["applied", "apply", "app"],
in_progress: ["in-progress", "inprogress", "progress", "prog"],
skipped: ["skipped", "skip", "skp"],
expired: ["expired", "expire", "exp"],
};
@ -29,6 +31,7 @@ export const lockLabel: Record<StatusLock, string> = {
ready: "ready",
discovered: "discovered",
applied: "applied",
in_progress: "in-progress",
skipped: "skipped",
expired: "expired",
};
@ -124,6 +127,7 @@ export const jobMatchesLock = (job: JobListItem, lock: StatusLock) => {
if (lock === "ready") return job.status === "ready";
if (lock === "discovered") return job.status === "discovered";
if (lock === "applied") return job.status === "applied";
if (lock === "in_progress") return job.status === "in_progress";
if (lock === "skipped") return job.status === "skipped";
if (lock === "expired") return job.status === "expired";
return false;

View File

@ -15,6 +15,35 @@ const baseJob = createJob({
});
describe("useFilteredJobs", () => {
it("keeps in-progress jobs in the all jobs tab", () => {
const jobs: Job[] = [
{ ...baseJob, id: "in-progress", status: "in_progress" },
{ ...baseJob, id: "processing", status: "processing" },
{
...baseJob,
id: "closed",
status: "in_progress",
closedAt: 1741996800,
},
];
const { result } = renderHook(() =>
useFilteredJobs(
jobs,
"all",
"all",
"all",
{ mode: "at_least", min: null, max: null },
{
key: "score",
direction: "desc",
},
),
);
expect(result.current.map((job) => job.id)).toEqual(["in-progress"]);
});
it("filters by sponsor status categories", () => {
const jobs: Job[] = [
{ ...baseJob, id: "confirmed", sponsorMatchScore: 99 },

View File

@ -24,7 +24,7 @@ export const useFilteredJobs = (
sort: JobSort,
) =>
useMemo(() => {
let filtered = jobs.filter((job) => job.status !== "in_progress");
let filtered = [...jobs];
if (activeTab === "ready") {
filtered = filtered.filter((job) => job.status === "ready");
@ -34,6 +34,10 @@ export const useFilteredJobs = (
);
} else if (activeTab === "applied") {
filtered = filtered.filter((job) => job.status === "applied");
} else if (activeTab === "all") {
filtered = filtered.filter(
(job) => job.status !== "processing" && job.closedAt == null,
);
}
if (activeTab !== "all") {