import React, { useEffect, useMemo, useState } from "react"; import { Autocomplete, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, Checkbox, MenuItem, TextField, } from "@mui/material"; import { api } from "../api"; import { Company, JobApplication } from "../types"; import { useToast } from "../toast"; import { useCompanies } from "../hooks/useCompanies"; import TagsInput from "./TagsInput"; 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 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 [loading, setLoading] = useState(false); const { companies } = useCompanies(); const [company, setCompany] = useState(null); const [jobTitle, setJobTitle] = useState(""); const [status, setStatus] = useState("Applied"); 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"); 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(() => { return !!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, 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, }); toast("Saved.", "success"); onSaved(); onClose(); } catch { toast("Save failed.", "error"); } finally { setLoading(false); } }; return ( Edit job c.name} value={company} onChange={(_, v) => setCompany(v)} renderInput={(params) => } /> setStatus(e.target.value)}> {STATUS_OPTIONS.map((s) => ( {s} ))} setJobTitle(e.target.value)} /> setDateApplied(e.target.value)} InputLabelProps={{ shrink: true }} /> setLocation(e.target.value)} /> setSalary(e.target.value)} /> setNextAction(e.target.value)} /> setFollowUpAt(e.target.value)} InputLabelProps={{ shrink: true }} /> setJobUrl(e.target.value)} sx={{ gridColumn: "1 / -1" }} /> setNotes(e.target.value)} multiline rows={4} sx={{ gridColumn: "1 / -1" }} /> setDescription(e.target.value)} multiline rows={6} sx={{ gridColumn: "1 / -1" }} /> setTranslatedDescription(e.target.value)} multiline rows={6} sx={{ gridColumn: "1 / -1" }} /> setDescriptionLanguage(e.target.value)} /> setDeadline(e.target.value)} InputLabelProps={{ shrink: true }} /> setCoverLetterText(e.target.value)} multiline rows={6} sx={{ gridColumn: "1 / -1" }} /> setResponseReceived(e.target.checked)} />} label="Response received" /> setResponseDate(e.target.value)} InputLabelProps={{ shrink: true }} /> setHasResume(e.target.checked)} />} label="Resume" /> setHasCoverLetter(e.target.checked)} />} label="Cover letter" /> setHasPortfolio(e.target.checked)} />} label="Portfolio" /> setHasOtherAttachment(e.target.checked)} />} label="Other attachment" /> ); }