132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
|
|
import {
|
|
Box,
|
|
Button,
|
|
Chip,
|
|
Paper,
|
|
Typography,
|
|
} from "@mui/material";
|
|
|
|
import { api } from "../api";
|
|
import { JobApplication } from "../types";
|
|
import { useToast } from "../toast";
|
|
|
|
import JobDetailsDialog from "./JobDetailsDialog";
|
|
|
|
export default function RemindersView() {
|
|
const { toast } = useToast();
|
|
const [items, setItems] = useState<JobApplication[]>([]);
|
|
const [openJobId, setOpenJobId] = useState<number | null>(null);
|
|
|
|
const load = async () => {
|
|
const res = await api.get<JobApplication[]>("/jobapplications/reminders", {
|
|
params: { upcomingDays: 14 },
|
|
});
|
|
setItems(res.data);
|
|
};
|
|
|
|
useEffect(() => {
|
|
void load();
|
|
}, []);
|
|
|
|
const setFollowUp = async (id: number, daysFromNow: number | null) => {
|
|
try {
|
|
const d =
|
|
daysFromNow === null
|
|
? null
|
|
: new Date(Date.now() + daysFromNow * 24 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 10);
|
|
await api.patch(`/jobapplications/${id}/followup`, {
|
|
followUpAt: d,
|
|
});
|
|
toast(daysFromNow === null ? "Follow-up cleared." : "Follow-up set.", "success");
|
|
await load();
|
|
} catch {
|
|
toast("Failed to set follow-up.", "error");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Paper sx={{ mt: 0, p: 2 }}>
|
|
<Typography variant="h6" sx={{ mb: 1 }}>
|
|
Needs Follow-up
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ color: "text.secondary", mb: 2 }}>
|
|
Based on your rules and upcoming follow-up dates.
|
|
</Typography>
|
|
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
|
{items.map((j) => (
|
|
<Paper
|
|
key={j.id}
|
|
sx={{
|
|
p: 1.5,
|
|
display: "grid",
|
|
gridTemplateColumns: "1fr auto",
|
|
gap: 1,
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
<Box>
|
|
<Typography sx={{ fontWeight: 900, lineHeight: 1.25 }}>
|
|
{j.company?.name ?? ""}{" "}
|
|
<span style={{ fontWeight: 700, opacity: 0.7 }}>�</span>{" "}
|
|
{j.jobTitle}
|
|
</Typography>
|
|
<Box sx={{ display: "flex", gap: 1, mt: 0.5, flexWrap: "wrap" }}>
|
|
{j.needsFollowUp ? (
|
|
<Chip size="small" label="Follow up" />
|
|
) : null}
|
|
{j.followUpReason ? (
|
|
<Chip size="small" label={j.followUpReason} variant="outlined" />
|
|
) : null}
|
|
{j.followUpAt ? (
|
|
<Chip
|
|
size="small"
|
|
label={`Follow-up: ${new Date(j.followUpAt).toLocaleDateString()}`}
|
|
variant="outlined"
|
|
/>
|
|
) : null}
|
|
<Chip size="small" label={j.status} variant="outlined" />
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", justifyContent: "flex-end" }}>
|
|
<Button size="small" variant="outlined" onClick={() => setOpenJobId(j.id)}>
|
|
Open
|
|
</Button>
|
|
<Button size="small" variant="outlined" onClick={() => void setFollowUp(j.id, 1)}>
|
|
Tomorrow
|
|
</Button>
|
|
<Button size="small" variant="outlined" onClick={() => void setFollowUp(j.id, 3)}>
|
|
+3d
|
|
</Button>
|
|
<Button size="small" variant="outlined" onClick={() => void setFollowUp(j.id, 7)}>
|
|
+7d
|
|
</Button>
|
|
<Button size="small" onClick={() => void setFollowUp(j.id, null)}>
|
|
Clear
|
|
</Button>
|
|
</Box>
|
|
</Paper>
|
|
))}
|
|
|
|
{items.length === 0 && (
|
|
<Typography sx={{ color: "text.secondary", textAlign: "center", py: 3 }}>
|
|
Nothing to follow up right now.
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
|
|
<JobDetailsDialog
|
|
open={openJobId !== null}
|
|
jobId={openJobId}
|
|
onClose={() => setOpenJobId(null)}
|
|
/>
|
|
</Paper>
|
|
);
|
|
}
|
|
|