feat: add toast notifications for post save success/failure states
This commit is contained in:
parent
df1a3b726e
commit
b36a53fd45
@ -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 AdminLayout from '../layout/AdminLayout';
|
||||||
import Recorder from '../features/recorder/Recorder';
|
import Recorder from '../features/recorder/Recorder';
|
||||||
import RichEditor from './RichEditor';
|
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 [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 [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 [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(() => {
|
useEffect(() => {
|
||||||
const savedId = initialPostId || localStorage.getItem('voxblog_draft_id');
|
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,
|
tags: meta.tagsText ? meta.tagsText.split(',').map(t => t.trim()).filter(Boolean) : undefined,
|
||||||
featureImage: meta.featureImage || undefined,
|
featureImage: meta.featureImage || undefined,
|
||||||
canonicalUrl: meta.canonicalUrl || undefined,
|
canonicalUrl: meta.canonicalUrl || undefined,
|
||||||
status: 'editing',
|
status: postStatus,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@ -67,14 +68,30 @@ export default function EditorShell({ onLogout, initialPostId, onBack }: { onLog
|
|||||||
setDraftId(data.id);
|
setDraftId(data.id);
|
||||||
localStorage.setItem('voxblog_draft_id', 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 {}
|
} catch (e: any) {
|
||||||
|
setToast({ open: true, message: `Save error: ${e?.message || 'unknown error'}` as string, severity: 'error' });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// No inline post switching here; selection happens on Posts page
|
// No inline post switching here; selection happens on Posts page
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminLayout title="VoxBlog Admin" onLogout={onLogout}>
|
<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" alignItems="center" justifyContent="space-between" sx={{ mb: 2, gap: 2, flexWrap: 'wrap' }}>
|
||||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: 1, minWidth: 300 }}>
|
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: 1, minWidth: 300 }}>
|
||||||
<Typography variant="h6">Post</Typography>
|
<Typography variant="h6">Post</Typography>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user