refactor(cv): use app-owned confirmations
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
cvBuilderApi, moveItem,
|
||||
} from "../cvBuilder";
|
||||
import { useAccountPlan } from "../accountPlan";
|
||||
import { useDialogActions } from "../dialogs";
|
||||
|
||||
const FONTS = [
|
||||
"'Segoe UI', Roboto, Arial, sans-serif",
|
||||
@@ -49,6 +50,7 @@ export default function CvBuilderEditor() {
|
||||
const variantId = Number(id);
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const { confirmAction } = useDialogActions();
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const [settings, setSettings] = useState<CvVariantSettings | null>(null);
|
||||
@@ -76,6 +78,7 @@ export default function CvBuilderEditor() {
|
||||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const contentHeight = useRef(A4_PAGE_PX);
|
||||
const blockerPromptOpen = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
@@ -186,10 +189,22 @@ export default function CvBuilderEditor() {
|
||||
const blocker = useBlocker(hasUnsavedChanges);
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state !== "blocked") return;
|
||||
if (window.confirm("This CV has unsaved changes. Leave and discard them?")) blocker.proceed();
|
||||
else blocker.reset();
|
||||
}, [blocker]);
|
||||
if (blocker.state !== "blocked") {
|
||||
blockerPromptOpen.current = false;
|
||||
return;
|
||||
}
|
||||
if (blockerPromptOpen.current) return;
|
||||
blockerPromptOpen.current = true;
|
||||
void confirmAction("This CV has unsaved changes. Leave and discard them?", {
|
||||
title: "Discard unsaved CV changes?",
|
||||
confirmLabel: "Discard and leave",
|
||||
destructive: true,
|
||||
}).then((confirmed) => {
|
||||
blockerPromptOpen.current = false;
|
||||
if (confirmed) blocker.proceed();
|
||||
else blocker.reset();
|
||||
});
|
||||
}, [blocker, confirmAction]);
|
||||
|
||||
useEffect(() => {
|
||||
const warnBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||
@@ -403,6 +418,7 @@ function ContentTab({ settings, update, outline }: {
|
||||
update: (p: Partial<CvVariantSettings>) => void;
|
||||
outline: CvOutline | null;
|
||||
}) {
|
||||
const { confirmAction } = useDialogActions();
|
||||
// Full section list = configured order (once touched) else default, always including every known key.
|
||||
const sectionRows: CvSectionSetting[] = useMemo(() => {
|
||||
const base = settings.sections.length ? [...settings.sections] : DEFAULT_SECTION_ORDER.map((key) => ({ key }));
|
||||
@@ -429,8 +445,12 @@ function ContentTab({ settings, update, outline }: {
|
||||
};
|
||||
const updateCustom = (key: string, patch: Partial<CvCustomSectionSetting>) =>
|
||||
update({ customSections: settings.customSections.map((c) => (c.key === key ? { ...c, ...patch } : c)) });
|
||||
const removeCustom = (section: CvCustomSectionSetting) => {
|
||||
if (!window.confirm(`Delete the custom section "${section.title || "Untitled"}"?`)) return;
|
||||
const removeCustom = async (section: CvCustomSectionSetting) => {
|
||||
if (!(await confirmAction(`Delete the custom section "${section.title || "Untitled"}" and all of its entries?`, {
|
||||
title: "Delete custom section",
|
||||
confirmLabel: "Delete section",
|
||||
destructive: true,
|
||||
}))) return;
|
||||
update({ customSections: settings.customSections.filter((c) => c.key !== section.key) });
|
||||
};
|
||||
const moveCustom = (index: number, delta: number) =>
|
||||
@@ -440,9 +460,13 @@ function ContentTab({ settings, update, outline }: {
|
||||
if (!section) return;
|
||||
updateCustom(key, { items: section.items.map((item, itemIndex) => itemIndex === index ? value : item) });
|
||||
};
|
||||
const removeCustomItem = (section: CvCustomSectionSetting, index: number) => {
|
||||
const removeCustomItem = async (section: CvCustomSectionSetting, index: number) => {
|
||||
const value = section.items[index];
|
||||
if (value.trim() && !window.confirm("Delete this custom section entry?")) return;
|
||||
if (value.trim() && !(await confirmAction("Delete this custom section entry?", {
|
||||
title: "Delete custom entry",
|
||||
confirmLabel: "Delete entry",
|
||||
destructive: true,
|
||||
}))) return;
|
||||
updateCustom(section.key, { items: section.items.filter((_, itemIndex) => itemIndex !== index) });
|
||||
};
|
||||
|
||||
@@ -497,7 +521,7 @@ function ContentTab({ settings, update, outline }: {
|
||||
<IconButton size="small" aria-label={c.hidden ? "Show custom section" : "Hide custom section"} onClick={() => updateCustom(c.key, { hidden: !c.hidden })}>
|
||||
{c.hidden ? <VisibilityOffIcon fontSize="small" /> : <VisibilityIcon fontSize="small" />}
|
||||
</IconButton>
|
||||
<IconButton size="small" aria-label="Remove custom section" onClick={() => removeCustom(c)}><DeleteOutlineIcon fontSize="small" /></IconButton>
|
||||
<IconButton size="small" aria-label="Remove custom section" onClick={() => void removeCustom(c)}><DeleteOutlineIcon fontSize="small" /></IconButton>
|
||||
</Stack>
|
||||
{!c.hidden ? (
|
||||
<Stack spacing={1} sx={{ mt: 1 }}>
|
||||
@@ -510,7 +534,7 @@ function ContentTab({ settings, update, outline }: {
|
||||
onClick={() => updateCustom(c.key, { items: moveItem(c.items, itemIndex, itemIndex - 1) })}><ArrowUpwardIcon fontSize="small" /></IconButton>
|
||||
<IconButton size="small" aria-label={`Move custom entry ${itemIndex + 1} down`} disabled={itemIndex === c.items.length - 1}
|
||||
onClick={() => updateCustom(c.key, { items: moveItem(c.items, itemIndex, itemIndex + 1) })}><ArrowDownwardIcon fontSize="small" /></IconButton>
|
||||
<IconButton size="small" aria-label={`Delete custom entry ${itemIndex + 1}`} onClick={() => removeCustomItem(c, itemIndex)}><DeleteOutlineIcon fontSize="small" /></IconButton>
|
||||
<IconButton size="small" aria-label={`Delete custom entry ${itemIndex + 1}`} onClick={() => void removeCustomItem(c, itemIndex)}><DeleteOutlineIcon fontSize="small" /></IconButton>
|
||||
</Stack>
|
||||
))}
|
||||
<Button size="small" startIcon={<AddIcon />} sx={{ alignSelf: "flex-start" }} onClick={() => updateCustom(c.key, { items: [...c.items, ""] })}>Add entry</Button>
|
||||
|
||||
Reference in New Issue
Block a user