import React, { useEffect, useRef, useState } from "react"; import { Alert, Box, Button, Chip, MenuItem, Paper, Stack, TextField, Typography } from "@mui/material"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { api, getApiErrorMessage } from "../../api"; import { useConfirm } from "../../confirm"; import { useI18n } from "../../i18n/I18nProvider"; import { PageSection } from "../../layout/PageLayout"; type Evidence = { id: number; category: string; title: string; statement: string; tags: string[]; sourceReference: string | null }; export default function CareerEvidenceBank() { const { t } = useI18n(); const { confirm } = useConfirm(); const [items, setItems] = useState([]); const [title, setTitle] = useState(""); const [statement, setStatement] = useState(""); const [category, setCategory] = useState("achievement"); const [tags, setTags] = useState(""); const [source, setSource] = useState(""); const [loading, setLoading] = useState(true); const [readAttempt, setReadAttempt] = useState(0); const [loadError, setLoadError] = useState<{ cause: unknown } | null>(null); const [saveError, setSaveError] = useState<{ cause: unknown } | null>(null); const [deleteErrors, setDeleteErrors] = useState>({}); const [busy, setBusy] = useState(false); const [deletingId, setDeletingId] = useState(null); const writePending = useRef(false); const titleInputRef = useRef(null); const focusAfterDelete = useRef(false); useEffect(() => { let cancelled = false; setLoading(true); api.get("/career/evidence") .then((response) => { if (!cancelled) { setItems(response.data); setLoadError(null); } }) .catch((cause) => { if (!cancelled) setLoadError({ cause }); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [readAttempt]); useEffect(() => { if (focusAfterDelete.current) { focusAfterDelete.current = false; titleInputRef.current?.focus(); } }, [items]); const add = async () => { if (writePending.current || loading || loadError || !title.trim() || !statement.trim()) return; writePending.current = true; setBusy(true); try { const response = await api.post("/career/evidence", { category, title, statement, tags: tags.split(","), sourceType: "manual", sourceReference: source, isVerified: true }); setItems((previous) => [response.data, ...previous]); setTitle(""); setStatement(""); setTags(""); setSource(""); setSaveError(null); } catch (cause) { setSaveError({ cause }); } finally { writePending.current = false; setBusy(false); } }; const remove = async (item: Evidence) => { if (writePending.current || loading || loadError) return; writePending.current = true; try { if (!(await confirm({ title: t("careerEvidenceDeleteTitle"), message: t("careerEvidenceDeleteMessage", { title: item.title }), confirmLabel: t("deleteAction"), destructive: true }))) return; setDeletingId(item.id); await api.delete(`/career/evidence/${item.id}`); focusAfterDelete.current = true; setItems((previous) => previous.filter((entry) => entry.id !== item.id)); setDeleteErrors((previous) => { const next = { ...previous }; delete next[item.id]; return next; }); } catch (cause) { setDeleteErrors((previous) => ({ ...previous, [item.id]: { cause } })); } finally { writePending.current = false; setDeletingId(null); } }; const categories = ["achievement", "metric", "project", "technology", "responsibility", "example"]; const categoryLabel = (value: string) => categories.includes(value) ? t(`careerEvidenceCategory_${value}` as "careerEvidenceCategory_achievement") : value; return {loading ? {t("loading")} : loadError ? { titleInputRef.current?.focus(); setReadAttempt((attempt) => attempt + 1); }}>{t("retry")}}>{getApiErrorMessage(loadError.cause, t("careerEvidenceLoadFailed"))} : null} {saveError ? {getApiErrorMessage(saveError.cause, t("careerEvidenceSaveFailed"))} : null} setCategory(e.target.value)}>{categories.map(v => {categoryLabel(v)})} setTitle(e.target.value)} /> setStatement(e.target.value)} multiline minRows={3} helperText={t("careerEvidenceTruthHelp")} /> setTags(e.target.value)} helperText={t("careerEvidenceTagsHelp")} /> setSource(e.target.value)} helperText={t("careerEvidenceSourceHelp")} /> {!loading && !loadError && items.length === 0 ? {t("careerEvidenceEmpty")} : null} {items.map(item => {item.title}{categoryLabel(item.category)} ยท {item.sourceReference || t("careerEvidenceManualSource")} {deleteErrors[item.id] ? void remove(item)}>{t("retry")}}>{getApiErrorMessage(deleteErrors[item.id].cause, t("careerEvidenceDeleteFailed"))} : null} {item.statement}{item.tags.map(tag => )} )} ; }