feat: add toast notifications for post save success/failure states

This commit is contained in:
Ender 2025-10-24 15:56:29 +02:00
parent df1a3b726e
commit b36a53fd45

View File

@ -1,4 +1,4 @@
import { Box, Button, Stack, Typography, TextField, MenuItem } from '@mui/material';
import { Box, Button, Stack, Typography, TextField, MenuItem, Snackbar, Alert } from '@mui/material';
import AdminLayout from '../layout/AdminLayout';
import Recorder from '../features/recorder/Recorder';
import RichEditor from './RichEditor';
@ -14,6 +14,7 @@ export default function EditorShell({ onLogout, initialPostId, onBack }: { onLog
const [meta, setMeta] = useState<Metadata>({ title: '', tagsText: '', canonicalUrl: '', featureImage: '' });
const [postClips, setPostClips] = useState<Array<{ id: string; bucket: string; key: string; mime: string; transcript?: string; createdAt: string }>>([]);
const [postStatus, setPostStatus] = useState<'inbox' | 'editing' | 'ready_for_publish' | 'published' | 'archived'>('editing');
const [toast, setToast] = useState<{ open: boolean; message: string; severity: 'success' | 'error' } | null>(null);
useEffect(() => {
const savedId = initialPostId || localStorage.getItem('voxblog_draft_id');
@ -58,7 +59,7 @@ export default function EditorShell({ onLogout, initialPostId, onBack }: { onLog
tags: meta.tagsText ? meta.tagsText.split(',').map(t => t.trim()).filter(Boolean) : undefined,
featureImage: meta.featureImage || undefined,
canonicalUrl: meta.canonicalUrl || undefined,
status: 'editing',
status: postStatus,
}),
});
if (res.ok) {
@ -67,14 +68,30 @@ export default function EditorShell({ onLogout, initialPostId, onBack }: { onLog
setDraftId(data.id);
localStorage.setItem('voxblog_draft_id', data.id);
}
setToast({ open: true, message: 'Post saved', severity: 'success' });
} else {
const text = await res.text();
setToast({ open: true, message: `Save failed: ${text || res.status}`, severity: 'error' });
}
} catch (e: any) {
setToast({ open: true, message: `Save error: ${e?.message || 'unknown error'}` as string, severity: 'error' });
}
} catch {}
};
// No inline post switching here; selection happens on Posts page
return (
<AdminLayout title="VoxBlog Admin" onLogout={onLogout}>
<Snackbar
open={!!toast?.open}
autoHideDuration={2500}
onClose={() => setToast(t => t ? { ...t, open: false } : null)}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
>
<Alert onClose={() => setToast(t => t ? { ...t, open: false } : null)} severity={toast?.severity || 'success'} sx={{ width: '100%' }}>
{toast?.message || ''}
</Alert>
</Snackbar>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 2, gap: 2, flexWrap: 'wrap' }}>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: 1, minWidth: 300 }}>
<Typography variant="h6">Post</Typography>