fix(i18n): use singular Gmail review counts

This commit is contained in:
cesnimda
2026-11-09 22:36:32 +01:00
parent 2af1502594
commit e8e7d83958
3 changed files with 46 additions and 3 deletions
@@ -226,6 +226,37 @@ describe('GmailReviewPage', () => {
expect(mockedApi.put).not.toHaveBeenCalled();
});
test.each([
{ count: 0, candidatesEn: '0 candidate threads', candidatesNb: '0 kandidattråder', jobsEn: '', jobsNb: '', messagesEn: '', messagesNb: '' },
{ count: 1, candidatesEn: '1 candidate thread', candidatesNb: '1 kandidattråd', jobsEn: '1 suggested job', jobsNb: '1 foreslått jobb', messagesEn: '1 message', messagesNb: '1 melding' },
{ count: 2, candidatesEn: '2 candidate threads', candidatesNb: '2 kandidattråder', jobsEn: '2 suggested jobs', jobsNb: '2 foreslåtte jobber', messagesEn: '2 messages', messagesNb: '2 meldinger' },
])('uses the correct review counts for $count on a live language switch', async ({ count, candidatesEn, candidatesNb, jobsEn, jobsNb, messagesEn, messagesNb }) => {
const threads = Array.from({ length: count }, (_, index) => reviewThread({ threadId: `thread-${index}`, subject: `Synthetic review ${index}`, messageCount: count }));
const items = threads.map(thread => ({ threadId: thread.threadId, companyName: 'Synthetic laboratory', suggestedJobTitle: 'Research technician' }));
mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: url === '/gmail/review-candidates' ? reviewQueue(threads) : { count, items } } as any));
renderPage();
await screen.findByText(candidatesEn);
if (count) {
expect(screen.getByText(jobsEn)).toBeVisible();
expect(threadRow('Synthetic review 0').getByText(`${messagesEn} · ${copy('gmailReviewRouteReview')}`)).toBeVisible();
} else {
expect(screen.getByText(copy('gmailReviewNoCandidates'))).toBeVisible();
expect(screen.queryByText('0 suggested jobs')).not.toBeInTheDocument();
}
await changeLanguage('nb');
expect(screen.getByText(candidatesNb)).toBeVisible();
if (count) {
expect(screen.getByText(jobsNb)).toBeVisible();
expect(threadRow('Synthetic review 0').getByText(`${messagesNb} · ${copy('gmailReviewRouteReview')}`)).toBeVisible();
} else {
expect(screen.getByText(copy('gmailReviewNoCandidates'))).toBeVisible();
expect(screen.queryByText('0 foreslåtte jobber')).not.toBeInTheDocument();
}
expect(mockedApi.get).toHaveBeenCalledTimes(2);
expect(mockedApi.post).not.toHaveBeenCalled();
});
test('decision, manual sync and suggested creation each own their captured request against duplicate and competing actions', async () => {
const second = reviewThread({ threadId: 'thread-2', subject: 'Other owned thread' });
const suggestion = { threadId: 'thread-1', companyName: 'Acme', suggestedJobTitle: 'Backend Developer', subject: 'Suggestion subject', recruiterName: 'Maria', recruiterEmail: 'maria@acme.test', preview: 'Recorded preview' };
+6
View File
@@ -2529,12 +2529,15 @@ export const translations = {
gmailReviewRouteSuggested: "Suggested",
gmailReviewRouteLinked: "Linked",
gmailReviewRouteRejected: "Rejected",
gmailReviewCandidateThread: "{count} candidate thread",
gmailReviewCandidateThreads: "{count} candidate threads",
gmailReviewAutoLinkCount: "{count} auto-link",
gmailReviewReviewCount: "{count} review",
gmailReviewUnmatchedCount: "{count} unmatched",
gmailReviewSuggestedJobCount: "{count} suggested job",
gmailReviewSuggestedJobsCount: "{count} suggested jobs",
gmailReviewNoCandidates: "No Gmail review candidates match the current filter.",
gmailReviewSingleMessageSummary: "{count} message · {route}",
gmailReviewThreadSummary: "{count} messages · {route}",
gmailReviewHasImported: "Has imported messages",
gmailReviewNotes: "Review notes",
@@ -5749,12 +5752,15 @@ export const translations = {
gmailReviewRouteSuggested: "Foreslått",
gmailReviewRouteLinked: "Tilknyttet",
gmailReviewRouteRejected: "Avvist",
gmailReviewCandidateThread: "{count} kandidattråd",
gmailReviewCandidateThreads: "{count} kandidattråder",
gmailReviewAutoLinkCount: "{count} automatisk tilknytning",
gmailReviewReviewCount: "{count} til kontroll",
gmailReviewUnmatchedCount: "{count} uten treff",
gmailReviewSuggestedJobCount: "{count} foreslått jobb",
gmailReviewSuggestedJobsCount: "{count} foreslåtte jobber",
gmailReviewNoCandidates: "Ingen kandidater til Gmail-kontroll samsvarer med gjeldende filter.",
gmailReviewSingleMessageSummary: "{count} melding · {route}",
gmailReviewThreadSummary: "{count} meldinger · {route}",
gmailReviewHasImported: "Har importerte meldinger",
gmailReviewNotes: "Kontrollnotater",
+9 -3
View File
@@ -241,11 +241,15 @@ export default function GmailReviewPage({ embedded = false, active = true }: { e
{!unavailable && data ? (
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mb: 2 }}>
<Chip label={t("gmailReviewCandidateThreads", { count: data.candidateThreadCount })} variant="outlined" sx={chipSx} />
<Chip label={data.candidateThreadCount === 1
? t("gmailReviewCandidateThread", { count: data.candidateThreadCount })
: t("gmailReviewCandidateThreads", { count: data.candidateThreadCount })} variant="outlined" sx={chipSx} />
<Chip label={t("gmailReviewAutoLinkCount", { count: data.autoLinkThreadCount })} color="success" variant="outlined" sx={chipSx} />
<Chip label={t("gmailReviewReviewCount", { count: data.reviewThreadCount })} color="warning" variant="outlined" sx={chipSx} />
<Chip label={t("gmailReviewUnmatchedCount", { count: data.unmatchedThreadCount })} variant="outlined" sx={chipSx} />
{suggestions?.count ? <Chip label={t("gmailReviewSuggestedJobsCount", { count: suggestions.count })} color="secondary" variant="outlined" sx={chipSx} /> : null}
{suggestions?.count ? <Chip label={suggestions.count === 1
? t("gmailReviewSuggestedJobCount", { count: suggestions.count })
: t("gmailReviewSuggestedJobsCount", { count: suggestions.count })} color="secondary" variant="outlined" sx={chipSx} /> : null}
</Box>
) : null}
@@ -261,7 +265,9 @@ export default function GmailReviewPage({ embedded = false, active = true }: { e
<Box sx={{ minWidth: 0, flex: "1 1 420px" }}>
<Typography id={headingId} component={embedded ? "h3" : "h2"} variant="subtitle1" sx={{ fontWeight: 700, overflowWrap: "anywhere" }}>{thread.subject}</Typography>
<Typography variant="body2" sx={{ color: "text.secondary" }}>
{t("gmailReviewThreadSummary", { count: thread.messageCount, route: routeLabel(thread.routing) })}
{thread.messageCount === 1
? t("gmailReviewSingleMessageSummary", { count: thread.messageCount, route: routeLabel(thread.routing) })
: t("gmailReviewThreadSummary", { count: thread.messageCount, route: routeLabel(thread.routing) })}
</Typography>
<Box sx={{ display: "flex", gap: 0.75, flexWrap: "wrap", mt: 0.75 }}>
{thread.matchedQueries.slice(0, 3).map((query) => (