fix(email): share application context
This commit is contained in:
@@ -107,7 +107,30 @@ interface PagedResult<T> {
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export default function Correspondence({ jobId, job }: { jobId: number; job: JobApplication | null }) {
|
||||
export type CorrespondenceJobContext = {
|
||||
companyName?: string | null;
|
||||
recruiterEmail?: string | null;
|
||||
jobTitle?: string | null;
|
||||
};
|
||||
|
||||
export function buildSuggestedCorrespondenceQueries(
|
||||
context: CorrespondenceJobContext,
|
||||
subjects: Array<string | null | undefined>,
|
||||
) {
|
||||
const uniqueSubjects = Array.from(new Set(subjects.filter(Boolean) as string[])).slice(0, 2);
|
||||
const companyName = context.companyName?.trim();
|
||||
const recruiterEmail = context.recruiterEmail?.trim();
|
||||
const jobTitle = context.jobTitle?.trim();
|
||||
|
||||
return [
|
||||
recruiterEmail ? { label: "Recruiter mailbox", value: `(from:${recruiterEmail} OR to:${recruiterEmail}) newer_than:365d` } : null,
|
||||
companyName && jobTitle ? { label: "Company + role", value: `"${companyName}" "${jobTitle}" newer_than:365d` } : null,
|
||||
companyName ? { label: "Company mail", value: `"${companyName}" (application OR interview OR recruiter) newer_than:365d` } : null,
|
||||
...uniqueSubjects.map((subject) => ({ label: `Subject: ${subject}`, value: `subject:"${subject}" newer_than:365d` })),
|
||||
].filter(Boolean).slice(0, 6) as Array<{ label: string; value: string }>;
|
||||
}
|
||||
|
||||
export default function Correspondence({ jobId, jobContext }: { jobId: number; jobContext: CorrespondenceJobContext }) {
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { t } = useI18n();
|
||||
@@ -274,20 +297,7 @@ export default function Correspondence({ jobId, job }: { jobId: number; job: Job
|
||||
|
||||
const canSend = useMemo(() => text.trim().length > 0, [text]);
|
||||
|
||||
const suggestedQueries = useMemo(() => {
|
||||
const fromSubjects = messages.map((m) => m.subject).filter(Boolean) as string[];
|
||||
const uniqueSubjects = Array.from(new Set(fromSubjects)).slice(0, 2);
|
||||
const companyName = job?.company?.name?.trim();
|
||||
const recruiterEmail = job?.company?.recruiterEmail?.trim();
|
||||
const jobTitle = job?.jobTitle?.trim();
|
||||
|
||||
return [
|
||||
recruiterEmail ? { label: "Recruiter mailbox", value: `(from:${recruiterEmail} OR to:${recruiterEmail}) newer_than:365d` } : null,
|
||||
companyName && jobTitle ? { label: "Company + role", value: `"${companyName}" "${jobTitle}" newer_than:365d` } : null,
|
||||
companyName ? { label: "Company mail", value: `"${companyName}" (application OR interview OR recruiter) newer_than:365d` } : null,
|
||||
...uniqueSubjects.map((subject) => ({ label: `Subject: ${subject}`, value: `subject:"${subject}" newer_than:365d` })),
|
||||
].filter(Boolean).slice(0, 6) as Array<{ label: string; value: string }>;
|
||||
}, [job?.company?.name, job?.company?.recruiterEmail, job?.jobTitle, messages]);
|
||||
const suggestedQueries = buildSuggestedCorrespondenceQueries(jobContext, messages.map((message) => message.subject));
|
||||
|
||||
const send = async () => {
|
||||
if (!canSend) return;
|
||||
@@ -595,9 +605,9 @@ export default function Correspondence({ jobId, job }: { jobId: number; job: Job
|
||||
<Typography variant="body2" sx={{ color: "text.secondary" }}>
|
||||
{gmailLoading ? t("correspondenceCheckingConnection") : gmailStatus?.connected ? t("correspondenceConnectedAs", { email: gmailStatus.gmailAddress || "" }) : t("correspondenceConnectGmailHint")}
|
||||
</Typography>
|
||||
{job ? (
|
||||
{jobContext.companyName || jobContext.jobTitle ? (
|
||||
<Typography variant="body2" sx={{ color: "text.secondary", mt: 0.5 }}>
|
||||
Matching against {job.company?.name || "this company"} / {job.jobTitle}
|
||||
Matching against {jobContext.companyName || "this company"} / {jobContext.jobTitle || "this role"}
|
||||
</Typography>
|
||||
) : null}
|
||||
</Box>
|
||||
|
||||
@@ -919,7 +919,7 @@ export default function JobDetailsDialog({ open, jobId, onClose, initialTab = 0,
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{tab === 1 && jobId && <Correspondence jobId={jobId} job={job} />}
|
||||
{tab === 1 && jobId && <Correspondence jobId={jobId} jobContext={{ companyName: job?.company?.name, recruiterEmail: job?.company?.recruiterEmail, jobTitle: job?.jobTitle }} />}
|
||||
{tab === 2 && jobId && <Attachments jobId={jobId} />}
|
||||
|
||||
{tab === 3 && (
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { buildSuggestedCorrespondenceQueries } from "./components/Correspondence";
|
||||
|
||||
test("builds contextual suggestions for the shared application correspondence view", () => {
|
||||
expect(buildSuggestedCorrespondenceQueries(
|
||||
{ companyName: "Acme AS", recruiterEmail: "recruiter@acme.test", jobTitle: "Utvikler" },
|
||||
["Interview invitation", "Interview invitation"],
|
||||
)).toEqual([
|
||||
{ label: "Recruiter mailbox", value: "(from:recruiter@acme.test OR to:recruiter@acme.test) newer_than:365d" },
|
||||
{ label: "Company + role", value: "\"Acme AS\" \"Utvikler\" newer_than:365d" },
|
||||
{ label: "Company mail", value: "\"Acme AS\" (application OR interview OR recruiter) newer_than:365d" },
|
||||
{ label: "Subject: Interview invitation", value: "subject:\"Interview invitation\" newer_than:365d" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("does not invent weak context when the workspace has no company details", () => {
|
||||
expect(buildSuggestedCorrespondenceQueries({}, [])).toEqual([]);
|
||||
});
|
||||
@@ -111,7 +111,9 @@ export default function ApplicationWorkspacePage() {
|
||||
<Paper sx={{ p: 2, borderRadius: 3 }}><Attachments jobId={jobId} /></Paper>
|
||||
)}
|
||||
{section === "communication" && jobId > 0 && (
|
||||
<Paper sx={{ p: 2, borderRadius: 3 }}><Correspondence jobId={jobId} job={null as any} /></Paper>
|
||||
<Paper sx={{ p: 2, borderRadius: 3 }}>
|
||||
<Correspondence jobId={jobId} jobContext={{ companyName: overview?.company, jobTitle: overview?.jobTitle }} />
|
||||
</Paper>
|
||||
)}
|
||||
{section === "checklist" && jobId > 0 && (
|
||||
<ApplicationChecklist jobId={jobId} onChanged={load} />
|
||||
|
||||
Reference in New Issue
Block a user