fix(i18n): localize job email workflows
This commit is contained in:
@@ -357,7 +357,7 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
try {
|
||||
await api.post(`/jobapplications/${response.data.id}/generate-tailored-cv-draft`);
|
||||
} catch (error: any) {
|
||||
toast(getApiErrorMessage(error, "Job created, but the tailored CV could not be generated."), "warning");
|
||||
toast(getApiErrorMessage(error, t("addJobModalTailoredCvFailed")), "warning");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +387,14 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
};
|
||||
|
||||
const canSave = normalizedCompanyName.length > 0 && jobTitle.trim().length > 0;
|
||||
const steps = ["Add job", "Review details", "CV", "Cover letter", "Portfolio", "Additional files"];
|
||||
const steps = [
|
||||
t("addJobModalStepAddJob"),
|
||||
t("addJobModalStepReview"),
|
||||
t("addJobModalResume"),
|
||||
t("addJobModalCoverLetter"),
|
||||
t("addJobModalPortfolio"),
|
||||
t("addJobModalStepAdditionalFiles"),
|
||||
];
|
||||
const optionalStep = activeStep >= 2 && activeStep <= 5;
|
||||
|
||||
const setFilesForBucket = (bucket: AttachmentBucketKey, files: FileList | null) => {
|
||||
@@ -438,14 +445,14 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
{steps.map((label) => <Step key={label}><StepLabel>{label}</StepLabel></Step>)}
|
||||
</Stepper>
|
||||
<Typography variant="subtitle2" sx={{ display: { xs: "block", sm: "none" }, mb: 2 }}>
|
||||
Step {activeStep + 1} of {steps.length}: {steps[activeStep]}
|
||||
{t("addJobModalStepProgress", { current: activeStep + 1, total: steps.length, step: steps[activeStep] })}
|
||||
</Typography>
|
||||
|
||||
{activeStep === 0 ? <Box sx={{ display: "grid", gap: 2 }}>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900 }}>Start with the job advert</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900 }}>{t("addJobModalStartWithAdvert")}</Typography>
|
||||
<Typography variant="body2" sx={{ color: "text.secondary", mt: 0.5 }}>
|
||||
Paste a job URL and Jobbjakt will try to fill the details. You can continue with manual entry whenever extraction is unavailable.
|
||||
{t("addJobModalStartWithAdvertBody")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<TextField label={t("addJobModalJobUrl")} value={jobUrl} onChange={(e) => setJobUrl(e.target.value)} fullWidth sx={FIELD_SX} />
|
||||
@@ -453,7 +460,7 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
<Button variant="contained" onClick={() => void importFromUrl()} disabled={importing || !jobUrl.trim()}>
|
||||
{importing ? t("addJobModalImporting") : t("addJobModalImportFromUrl")}
|
||||
</Button>
|
||||
<Button onClick={() => setActiveStep(1)}>Enter details manually</Button>
|
||||
<Button onClick={() => setActiveStep(1)}>{t("addJobModalEnterManually")}</Button>
|
||||
</Box>
|
||||
</Box> : null}
|
||||
|
||||
@@ -570,7 +577,7 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
rows={6}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
helperText={`${description.length} characters`}
|
||||
helperText={t("characterCount", { count: description.length })}
|
||||
sx={{ gridColumn: "1 / -1" }}
|
||||
/>
|
||||
|
||||
@@ -581,7 +588,7 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
rows={6}
|
||||
value={translatedDescription}
|
||||
onChange={(e) => setTranslatedDescription(e.target.value)}
|
||||
helperText={`${translatedDescription.length} characters`}
|
||||
helperText={t("characterCount", { count: translatedDescription.length })}
|
||||
sx={{ gridColumn: "1 / -1" }}
|
||||
/>
|
||||
) : null}
|
||||
@@ -594,7 +601,7 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
sx={{ gridColumn: "1 / -1" }}
|
||||
/>
|
||||
|
||||
<TextField label={t("addJobModalNotes")} multiline rows={3} value={notes} onChange={(e) => setNotes(e.target.value)} helperText={`${notes.length} characters`} sx={{ gridColumn: "1 / -1" }} />
|
||||
<TextField label={t("addJobModalNotes")} multiline rows={3} value={notes} onChange={(e) => setNotes(e.target.value)} helperText={t("characterCount", { count: notes.length })} sx={{ gridColumn: "1 / -1" }} />
|
||||
|
||||
</Box>
|
||||
</> : null}
|
||||
@@ -603,15 +610,15 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900 }}>{steps[activeStep]}</Typography>
|
||||
<Typography variant="body2" sx={{ color: "text.secondary", mt: 0.5 }}>
|
||||
{activeStep === 2 ? "Use your master CV later in Career Workspace, or attach the version prepared for this application now." : null}
|
||||
{activeStep === 3 ? "Attach a drafted cover letter now, or add/generate one from the application workspace later." : null}
|
||||
{activeStep === 4 ? "Add supporting portfolio material now, or select lightweight profile-linked projects later." : null}
|
||||
{activeStep === 5 ? "Certificates, references, and other supporting documents are optional." : null}
|
||||
{activeStep === 2 ? t("addJobModalCvStepHelp") : null}
|
||||
{activeStep === 3 ? t("addJobModalCoverLetterStepHelp") : null}
|
||||
{activeStep === 4 ? t("addJobModalPortfolioStepHelp") : null}
|
||||
{activeStep === 5 ? t("addJobModalOtherFilesStepHelp") : null}
|
||||
</Typography>
|
||||
</Box>
|
||||
{activeStep === 2 ? <>
|
||||
<FormControlLabel control={<Checkbox disabled={!canUseAi} checked={generateTailoredCv && canUseAi} onChange={(event) => setGenerateTailoredCv(event.target.checked)} />} label={canUseAi ? "Generate a tailored CV draft after creating this job" : "Tailored CV generation requires Pro"} />
|
||||
<Typography variant="caption" sx={{ color: "text.secondary", mt: -1 }}>{canUseAi ? "Uses your reviewed Career Profile and keeps the result as an editable suggestion." : "Create and track the job normally; no AI operation will be started."}</Typography>
|
||||
<FormControlLabel control={<Checkbox disabled={!canUseAi} checked={generateTailoredCv && canUseAi} onChange={(event) => setGenerateTailoredCv(event.target.checked)} />} label={canUseAi ? t("addJobModalGenerateTailoredCv") : t("addJobModalTailoredCvRequiresPro")} />
|
||||
<Typography variant="caption" sx={{ color: "text.secondary", mt: -1 }}>{canUseAi ? t("addJobModalTailoredCvHelp") : t("addJobModalNoAiHelp")}</Typography>
|
||||
{uploadField("resume", t("addJobModalResume"), t("addJobModalResumeHelp"))}
|
||||
</> : null}
|
||||
{activeStep === 3 ? uploadField("coverLetter", t("addJobModalCoverLetter"), t("addJobModalCoverLetterHelp")) : null}
|
||||
@@ -626,9 +633,9 @@ export default function AddJobModal({ open, onClose, onCreated, initialUrl }: Pr
|
||||
<DialogActions sx={{ px: 3, py: 2, justifyContent: "space-between", flexWrap: "wrap", gap: 1.5 }}>
|
||||
<Button variant="outlined" onClick={onClose}>{t("close")}</Button>
|
||||
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
|
||||
{activeStep > 0 ? <Button onClick={() => setActiveStep((step) => step - 1)} disabled={saving}>Back</Button> : null}
|
||||
{activeStep > 0 ? <Button onClick={() => setActiveStep((step) => step - 1)} disabled={saving}>{t("back")}</Button> : null}
|
||||
{activeStep < steps.length - 1 ? <Button variant="contained" onClick={() => setActiveStep((step) => step + 1)} disabled={saving || (activeStep === 1 && !canSave)}>
|
||||
{optionalStep ? "Skip and continue" : "Continue"}
|
||||
{optionalStep ? t("skipAndContinue") : t("continue")}
|
||||
</Button> : <>
|
||||
<Button variant="outlined" onClick={() => void createJob(true)} disabled={saving || !canSave}>
|
||||
{saving ? t("rulesSaving") : t("createAndAddAnother")}
|
||||
|
||||
Reference in New Issue
Block a user