feat: streamline dashboard actions by removing inline add-entry form and manual refresh

This commit is contained in:
root
2025-11-09 18:46:38 +01:00
parent 96eabafea8
commit f5542429b0

View File

@@ -28,6 +28,7 @@ function App() {
const [dirtyDialogMessage, setDirtyDialogMessage] = useState(''); const [dirtyDialogMessage, setDirtyDialogMessage] = useState('');
const [pendingNavigation, setPendingNavigation] = useState(null); const [pendingNavigation, setPendingNavigation] = useState(null);
const [dirtyDialogSaving, setDirtyDialogSaving] = useState(false); const [dirtyDialogSaving, setDirtyDialogSaving] = useState(false);
const [confirmDialog, setConfirmDialog] = useState({ open: false });
const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']; const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
const delay = useCallback((ms) => new Promise((resolve) => setTimeout(resolve, ms)), []); const delay = useCallback((ms) => new Promise((resolve) => setTimeout(resolve, ms)), []);
@@ -543,6 +544,31 @@ function App() {
setPendingNavigation(null); setPendingNavigation(null);
}, []); }, []);
const askConfirmation = useCallback(
(options = {}) =>
new Promise((resolve) => {
setConfirmDialog({
open: true,
title: options.title || 'Bitte bestätigen',
message: options.message || 'Bist du sicher?',
confirmLabel: options.confirmLabel || 'Ja',
cancelLabel: options.cancelLabel || 'Abbrechen',
confirmTone: options.confirmTone || 'primary',
resolve
});
}),
[]
);
const handleConfirmDialog = useCallback((result) => {
setConfirmDialog((prev) => {
if (prev?.resolve) {
prev.resolve(result);
}
return { open: false };
});
}, []);
useEffect(() => { useEffect(() => {
let ticker; let ticker;
let cancelled = false; let cancelled = false;
@@ -651,18 +677,30 @@ function App() {
} }
}; };
const deleteEntry = (entryId) => { const deleteEntry = async (entryId) => {
setIsDirty(true); const confirmed = await askConfirmation({
if (window.confirm('Soll dieser Eintrag dauerhaft gelöscht werden?')) { title: 'Eintrag löschen',
const updatedConfig = config.filter((item) => item.id !== entryId); message: 'Soll dieser Eintrag dauerhaft gelöscht werden?',
setConfig(updatedConfig); confirmLabel: 'Löschen',
setStatus('Eintrag gelöscht!'); confirmTone: 'danger'
setTimeout(() => setStatus(''), 3000); });
if (!confirmed) {
return;
} }
setIsDirty(true);
const updatedConfig = config.filter((item) => item.id !== entryId);
setConfig(updatedConfig);
setStatus('Eintrag gelöscht!');
setTimeout(() => setStatus(''), 3000);
}; };
const hideEntry = async (entryId) => { const hideEntry = async (entryId) => {
if (!window.confirm('Soll dieser Betrieb ausgeblendet werden?')) { const confirmed = await askConfirmation({
title: 'Betrieb ausblenden',
message: 'Soll dieser Betrieb ausgeblendet werden?',
confirmLabel: 'Ausblenden'
});
if (!confirmed) {
return; return;
} }
setIsDirty(true); setIsDirty(true);
@@ -749,9 +787,15 @@ function App() {
if (existing && !existing.hidden) { if (existing && !existing.hidden) {
return; return;
} }
if (!window.confirm(`Soll der Betrieb "${store.name}" zur Liste hinzugefügt werden?`)) { const confirmed = await askConfirmation({
title: 'Betrieb hinzufügen',
message: `Soll der Betrieb "${store.name}" zur Liste hinzugefügt werden?`,
confirmLabel: 'Hinzufügen'
});
if (!confirmed) {
return; return;
} }
setIsDirty(true);
const message = existing ? 'Betrieb wieder eingeblendet.' : 'Betrieb zur Liste hinzugefügt.'; const message = existing ? 'Betrieb wieder eingeblendet.' : 'Betrieb zur Liste hinzugefügt.';
setIsDirty(true); setIsDirty(true);
await persistConfigUpdate( await persistConfigUpdate(
@@ -1409,6 +1453,16 @@ function App() {
onCancel={handleDirtyCancel} onCancel={handleDirtyCancel}
saving={dirtyDialogSaving} saving={dirtyDialogSaving}
/> />
<ConfirmationDialog
open={!!confirmDialog.open}
title={confirmDialog.title}
message={confirmDialog.message}
confirmLabel={confirmDialog.confirmLabel}
cancelLabel={confirmDialog.cancelLabel}
confirmTone={confirmDialog.confirmTone}
onConfirm={() => handleConfirmDialog(true)}
onCancel={() => handleConfirmDialog(false)}
/>
</> </>
</Router> </Router>
); );
@@ -1508,6 +1562,38 @@ function DirtyNavigationDialog({ open, message, onSave, onDiscard, onCancel, sav
); );
} }
function ConfirmationDialog({ open, title, message, confirmLabel, cancelLabel, confirmTone = 'primary', onConfirm, onCancel }) {
if (!open) {
return null;
}
const confirmClasses =
confirmTone === 'danger'
? 'bg-red-600 hover:bg-red-700 focus:ring-red-500'
: 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-500';
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 px-4">
<div className="bg-white rounded-lg shadow-2xl max-w-md w-full p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-2">{title || 'Bitte bestätigen'}</h3>
<p className="text-sm text-gray-600 mb-4">{message || 'Bist du sicher?'}</p>
<div className="flex flex-col gap-2 sm:flex-row sm:justify-end">
<button
onClick={onCancel}
className="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded focus:outline-none focus:ring-2 focus:ring-gray-300"
>
{cancelLabel || 'Abbrechen'}
</button>
<button
onClick={onConfirm}
className={`${confirmClasses} text-white px-4 py-2 rounded focus:outline-none focus:ring-2`}
>
{confirmLabel || 'Ja'}
</button>
</div>
</div>
</div>
);
}
function formatEta(seconds) { function formatEta(seconds) {
if (seconds == null || seconds === Infinity) { if (seconds == null || seconds === Infinity) {
return null; return null;