import React, { useEffect, useMemo, useState } from "react"; import { Autocomplete, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, Checkbox, MenuItem, Paper, TextField, Typography, Chip, } from "@mui/material"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { api } from "../api"; import { Company, JobApplication } from "../types"; import { useToast } from "../toast"; import { useCompanies } from "../hooks/useCompanies"; import TagsInput from "./TagsInput"; import { useI18n } from "../i18n/I18nProvider"; interface Props { open: boolean; jobId: number | null; onClose: () => void; onSaved: () => void; } const STATUS_OPTIONS = ["Applied", "Waiting", "Interview", "Offer", "Rejected", "Ghosted"] as const; function toDateInputValue(isoLike?: string): string { if (!isoLike) return new Date().toISOString().slice(0, 10); const d = new Date(isoLike); if (Number.isNaN(+d)) return new Date().toISOString().slice(0, 10); return d.toISOString().slice(0, 10); } function parsePickerDate(value?: string | null): Date | null { if (!value) return null; const parsed = new Date(value); return Number.isNaN(+parsed) ? null : parsed; } function toPickerIso(value: Date | null): string { if (!value || Number.isNaN(+value)) return ""; return value.toISOString().slice(0, 10); } function parseTags(raw: any): string[] { if (!raw) return []; if (Array.isArray(raw)) return raw.filter((x) => typeof x === "string"); if (typeof raw !== "string") return []; try { const p = JSON.parse(raw); return Array.isArray(p) ? p.filter((x) => typeof x === "string") : []; } catch { return []; } } export default function EditJobDialog({ open, jobId, onClose, onSaved }: Props) { const { toast } = useToast(); const { t } = useI18n(); const [loading, setLoading] = useState(false); const { companies } = useCompanies(); const [company, setCompany] = useState(null); const [jobTitle, setJobTitle] = useState(""); const [status, setStatus] = useState("Applied"); const [initialStatus, setInitialStatus] = useState("Applied"); const [statusChangedAt, setStatusChangedAt] = useState(() => new Date().toISOString().slice(0, 10)); const [dateApplied, setDateApplied] = useState(() => new Date().toISOString().slice(0, 10)); const [location, setLocation] = useState(""); const [salary, setSalary] = useState(""); const [nextAction, setNextAction] = useState(""); const [followUpAt, setFollowUpAt] = useState(""); const [jobUrl, setJobUrl] = useState(""); const [notes, setNotes] = useState(""); const [description, setDescription] = useState(""); const [translatedDescription, setTranslatedDescription] = useState(""); const [descriptionLanguage, setDescriptionLanguage] = useState(""); const [tags, setTags] = useState([]); const [deadline, setDeadline] = useState(""); const [coverLetterText, setCoverLetterText] = useState(""); const [responseReceived, setResponseReceived] = useState(false); const [responseDate, setResponseDate] = useState(""); const [hasResume, setHasResume] = useState(false); const [hasCoverLetter, setHasCoverLetter] = useState(false); const [hasPortfolio, setHasPortfolio] = useState(false); const [hasOtherAttachment, setHasOtherAttachment] = useState(false); useEffect(() => { if (!open || !jobId) return; setLoading(true); api.get(`/jobapplications/${jobId}`).then((r) => { const j = r.data; setCompany(j.company ?? null); setJobTitle(j.jobTitle ?? ""); setStatus(j.status ?? "Applied"); setInitialStatus(j.status ?? "Applied"); setStatusChangedAt(new Date().toISOString().slice(0, 10)); setDateApplied(toDateInputValue(j.dateApplied)); setLocation(j.location ?? ""); setSalary(j.salary ?? ""); setNextAction((j as any).nextAction ?? ""); setFollowUpAt((j as any).followUpAt ? toDateInputValue((j as any).followUpAt) : ""); setJobUrl(j.jobUrl ?? ""); setNotes(j.notes ?? ""); setDescription((j as any).description ?? ""); setTranslatedDescription((j as any).translatedDescription ?? ""); setDescriptionLanguage((j as any).descriptionLanguage ?? ""); setTags(parseTags((j as any).tags)); setDeadline((j as any).deadline ? toDateInputValue((j as any).deadline) : ""); setCoverLetterText(j.coverLetterText ?? ""); setResponseReceived(Boolean(j.responseReceived)); setResponseDate(j.responseDate ? toDateInputValue(j.responseDate) : ""); setHasResume(Boolean((j as any).hasResume)); setHasCoverLetter(Boolean((j as any).hasCoverLetter)); setHasPortfolio(Boolean((j as any).hasPortfolio)); setHasOtherAttachment(Boolean((j as any).hasOtherAttachment)); }).finally(() => setLoading(false)); }, [open, jobId]); const canSave = useMemo(() => !!company?.id && jobTitle.trim().length > 0 && !loading, [company, jobTitle, loading]); const save = async () => { if (!jobId || !company?.id) return; setLoading(true); try { await api.put(`/jobapplications/${jobId}`, { jobTitle: jobTitle.trim(), companyId: company.id, status, statusChangedAt: status !== initialStatus ? statusChangedAt || null : null, responseReceived, responseDate: responseReceived && responseDate ? responseDate : null, location: location.trim() || null, salary: salary.trim() || null, nextAction: nextAction.trim() || null, followUpAt: followUpAt || null, hasResume, hasCoverLetter, hasPortfolio, hasOtherAttachment, notes: notes || null, description: description || null, translatedDescription: translatedDescription || null, descriptionLanguage: descriptionLanguage || null, tags: tags.length ? JSON.stringify(tags) : null, deadline: deadline || null, coverLetterText: coverLetterText || null, dateApplied: dateApplied || null, jobUrl: jobUrl.trim() || null, }); toast(t("save"), "success"); onSaved(); onClose(); } catch { toast(t("editJobSaveFailed"), "error"); } finally { setLoading(false); } }; return ( {t("editJobTitle")} {t("editJobIntro")} {t("editJobApplicationDetails")} c.name} value={company} onChange={(_, v) => setCompany(v)} renderInput={(params) => } /> setJobTitle(e.target.value)} /> setDateApplied(toPickerIso(value))} slotProps={{ textField: { fullWidth: true } }} /> setJobUrl(e.target.value)} /> {t("editJobStatusUpdate")} setStatus(e.target.value)}> {STATUS_OPTIONS.map((s) => {s})} setStatusChangedAt(toPickerIso(value))} slotProps={{ textField: { fullWidth: true, helperText: status === initialStatus ? t("editJobStatusChangedHelpIdle") : t("editJobStatusChangedHelpActive") } }} /> setResponseReceived(e.target.checked)} />} label={t("editJobReplyReceived")} /> setResponseDate(toPickerIso(value))} slotProps={{ textField: { fullWidth: true } }} /> setNextAction(e.target.value)} /> setFollowUpAt(toPickerIso(value))} slotProps={{ textField: { fullWidth: true } }} /> {t("editJobRoleDetails")} setLocation(e.target.value)} /> setSalary(e.target.value)} /> setDeadline(toPickerIso(value))} slotProps={{ textField: { fullWidth: true } }} /> setDescriptionLanguage(e.target.value)} /> setNotes(e.target.value)} multiline rows={4} helperText={t("correspondenceCharacters", { count: notes.length })} sx={{ gridColumn: "1 / -1" }} /> setDescription(e.target.value)} multiline rows={6} helperText={t("correspondenceCharacters", { count: description.length })} sx={{ gridColumn: "1 / -1" }} /> setTranslatedDescription(e.target.value)} multiline rows={6} helperText={t("correspondenceCharacters", { count: translatedDescription.length })} sx={{ gridColumn: "1 / -1" }} /> setCoverLetterText(e.target.value)} multiline rows={6} helperText={t("correspondenceCharacters", { count: coverLetterText.length })} sx={{ gridColumn: "1 / -1" }} /> {t("editJobAttachmentsChecklist")} setHasResume(e.target.checked)} />} label={t("editJobResume")} /> setHasCoverLetter(e.target.checked)} />} label={t("editJobCoverLetter")} /> setHasPortfolio(e.target.checked)} />} label={t("editJobPortfolio")} /> setHasOtherAttachment(e.target.checked)} />} label={t("editJobOtherAttachment")} /> ); }