feat: streamline dashboard actions by removing inline add-entry form and manual refresh
This commit is contained in:
96
src/App.js
96
src/App.js
@@ -28,6 +28,7 @@ function App() {
|
||||
const [dirtyDialogMessage, setDirtyDialogMessage] = useState('');
|
||||
const [pendingNavigation, setPendingNavigation] = useState(null);
|
||||
const [dirtyDialogSaving, setDirtyDialogSaving] = useState(false);
|
||||
const [confirmDialog, setConfirmDialog] = useState({ open: false });
|
||||
|
||||
const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
|
||||
const delay = useCallback((ms) => new Promise((resolve) => setTimeout(resolve, ms)), []);
|
||||
@@ -543,6 +544,31 @@ function App() {
|
||||
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(() => {
|
||||
let ticker;
|
||||
let cancelled = false;
|
||||
@@ -651,18 +677,30 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteEntry = (entryId) => {
|
||||
const deleteEntry = async (entryId) => {
|
||||
const confirmed = await askConfirmation({
|
||||
title: 'Eintrag löschen',
|
||||
message: 'Soll dieser Eintrag dauerhaft gelöscht werden?',
|
||||
confirmLabel: 'Löschen',
|
||||
confirmTone: 'danger'
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
setIsDirty(true);
|
||||
if (window.confirm('Soll dieser Eintrag dauerhaft gelöscht werden?')) {
|
||||
const updatedConfig = config.filter((item) => item.id !== entryId);
|
||||
setConfig(updatedConfig);
|
||||
setStatus('Eintrag gelöscht!');
|
||||
setTimeout(() => setStatus(''), 3000);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
setIsDirty(true);
|
||||
@@ -749,9 +787,15 @@ function App() {
|
||||
if (existing && !existing.hidden) {
|
||||
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;
|
||||
}
|
||||
setIsDirty(true);
|
||||
const message = existing ? 'Betrieb wieder eingeblendet.' : 'Betrieb zur Liste hinzugefügt.';
|
||||
setIsDirty(true);
|
||||
await persistConfigUpdate(
|
||||
@@ -1409,6 +1453,16 @@ function App() {
|
||||
onCancel={handleDirtyCancel}
|
||||
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>
|
||||
);
|
||||
@@ -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) {
|
||||
if (seconds == null || seconds === Infinity) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user