feat(interviews): add durable debrief loop
This commit is contained in:
@@ -337,6 +337,7 @@ export const INTERVIEW_PREP_CATEGORIES: { key: string; label: string }[] = [
|
||||
{ key: "star", label: "STAR examples" },
|
||||
{ key: "question", label: "Questions to ask them" },
|
||||
{ key: "note", label: "Notes" },
|
||||
{ key: "debrief", label: "Interview debrief" },
|
||||
];
|
||||
|
||||
export const interviewPrepApi = {
|
||||
|
||||
@@ -91,6 +91,14 @@ export function ApplicationInterviewPrep({ jobId }: { jobId: number }) {
|
||||
return mutate(() => interviewPrepApi.add(jobId, { category, title: value }));
|
||||
};
|
||||
|
||||
const startDebrief = () => mutate(() => interviewPrepApi.add(jobId, {
|
||||
category: "debrief",
|
||||
title: t("interviewDebriefDefaultTitle"),
|
||||
content: t("interviewDebriefTemplate"),
|
||||
}));
|
||||
|
||||
const hasDebrief = board?.groups.some((group) => group.category === "debrief") ?? false;
|
||||
|
||||
return (
|
||||
<Stack spacing={2}>
|
||||
<Shell
|
||||
@@ -151,6 +159,18 @@ export function ApplicationInterviewPrep({ jobId }: { jobId: number }) {
|
||||
))
|
||||
)}
|
||||
|
||||
{board?.isInterviewStage && !hasDebrief ? (
|
||||
<Paper variant="outlined" sx={{ p: 2, bgcolor: "action.hover" }}>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={1.5} alignItems={{ sm: "center" }} justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{t("interviewDebriefTitle")}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">{t("interviewDebriefHelp")}</Typography>
|
||||
</Box>
|
||||
<Button variant="outlined" disabled={busy} onClick={() => void startDebrief()}>{t("interviewDebriefStart")}</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
|
||||
<Box component="form" onSubmit={add}>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={1}>
|
||||
<TextField
|
||||
@@ -485,6 +505,7 @@ function interviewCategoryLabel(t: (key: any) => string, category: string) {
|
||||
star: "interviewCategoryStar",
|
||||
question: "interviewCategoryQuestions",
|
||||
note: "interviewCategoryNotes",
|
||||
debrief: "interviewCategoryDebrief",
|
||||
};
|
||||
return keys[category] ? t(keys[category]) : category;
|
||||
}
|
||||
|
||||
@@ -360,6 +360,12 @@ export const translations = {
|
||||
interviewCategoryStar: "STAR examples",
|
||||
interviewCategoryQuestions: "Questions to ask them",
|
||||
interviewCategoryNotes: "Notes",
|
||||
interviewCategoryDebrief: "Interview debrief",
|
||||
interviewDebriefTitle: "Capture the interview while it is fresh",
|
||||
interviewDebriefHelp: "Record the questions, what went well, what you would improve, and any feedback or next steps.",
|
||||
interviewDebriefStart: "Start debrief",
|
||||
interviewDebriefDefaultTitle: "Interview debrief",
|
||||
interviewDebriefTemplate: "Questions asked:\n\nWhat went well:\n\nWhat I would improve:\n\nFeedback and next steps:",
|
||||
interviewAiTitle: "AI interview coach",
|
||||
interviewAiSubtitle: "Uses the linked CV, job advert and application analysis. Suggestions never overwrite your notes.",
|
||||
interviewAiUsing: "Using",
|
||||
@@ -2735,6 +2741,12 @@ export const translations = {
|
||||
interviewCategoryStar: "STAR-eksempler",
|
||||
interviewCategoryQuestions: "Spørsmål du kan stille",
|
||||
interviewCategoryNotes: "Notater",
|
||||
interviewCategoryDebrief: "Intervjuoppsummering",
|
||||
interviewDebriefTitle: "Noter intervjuet mens det er ferskt",
|
||||
interviewDebriefHelp: "Skriv ned spørsmålene, hva som gikk bra, hva du ville gjort bedre, og eventuelle tilbakemeldinger eller neste steg.",
|
||||
interviewDebriefStart: "Start oppsummering",
|
||||
interviewDebriefDefaultTitle: "Intervjuoppsummering",
|
||||
interviewDebriefTemplate: "Spørsmål som ble stilt:\n\nHva gikk bra:\n\nHva jeg ville gjort bedre:\n\nTilbakemeldinger og neste steg:",
|
||||
interviewAiTitle: "AI-intervjutrening",
|
||||
interviewAiSubtitle: "Bruker tilknyttet CV, stillingsannonse og søknadsanalyse. Forslag overskriver aldri notatene dine.",
|
||||
interviewAiUsing: "Bruker",
|
||||
|
||||
@@ -102,6 +102,24 @@ test("adding a prep item posts the chosen category and title", async () => {
|
||||
));
|
||||
});
|
||||
|
||||
test("starts a structured interview debrief without inventing answers", async () => {
|
||||
routeGet();
|
||||
mockedApi.post.mockResolvedValue({ data: board.groups[0].items[0] } as any);
|
||||
render(<ApplicationInterviewPrep jobId={7} />);
|
||||
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Start debrief" }));
|
||||
|
||||
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(
|
||||
"/jobapplications/7/interview-prep",
|
||||
{
|
||||
category: "debrief",
|
||||
title: "Interview debrief",
|
||||
content: expect.stringContaining("Questions asked:"),
|
||||
},
|
||||
));
|
||||
expect(mockedApi.post.mock.calls[0][1].content).not.toMatch(/Acme|achievement|metric/i);
|
||||
});
|
||||
|
||||
test("marking an item ready patches it", async () => {
|
||||
routeGet();
|
||||
mockedApi.patch.mockResolvedValue({ data: board.groups[1].items[0] } as any);
|
||||
|
||||
Reference in New Issue
Block a user