feat: delete rows via trash icon in all tables
Each row gets a trash can button that appears on hover. Clicking it shows an inline confirm (✓ / ✕) to prevent accidental deletion. On confirm, calls DELETE /endpoint/:id and removes the row from state. If the deleted record is open in the modal, the modal closes too. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import Layout from '../components/Layout';
|
||||
import RecordModal from '../components/RecordModal';
|
||||
import CreateModal, { hasCreateForm } from '../components/CreateModal';
|
||||
import { fetchAll } from '../lib/api';
|
||||
import { fetchAll, apiDelete } from '../lib/api';
|
||||
import { TABLES, STATUS_COLORS } from '../lib/tables';
|
||||
|
||||
function truncate(str, n = 60) {
|
||||
@@ -88,6 +88,8 @@ export default function TableView() {
|
||||
const [highlightId, setHighlightId] = useState(null);
|
||||
const [modalRecord, setModalRecord] = useState(null);
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -130,6 +132,21 @@ export default function TableView() {
|
||||
setRows(prev => [newRecord, ...prev]);
|
||||
}
|
||||
|
||||
const handleDelete = useCallback(async (id, e) => {
|
||||
e.stopPropagation();
|
||||
setDeleting(true);
|
||||
try {
|
||||
await apiDelete(meta.endpoint, id);
|
||||
setRows(prev => prev.filter(r => r.id !== id));
|
||||
setConfirmDeleteId(null);
|
||||
if (modalRecord?.id === id) setModalRecord(null);
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
}
|
||||
}, [meta, modalRecord]);
|
||||
|
||||
if (!meta) return <Layout back="/db"><p className="text-red-500">Unbekannte Tabelle: {tableKey}</p></Layout>;
|
||||
|
||||
return (
|
||||
@@ -198,12 +215,13 @@ export default function TableView() {
|
||||
{col}
|
||||
</th>
|
||||
))}
|
||||
<th className="px-3 py-2.5 w-10" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filtered.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={meta.columns.length} className="text-center py-10 text-slate-400">
|
||||
<td colSpan={meta.columns.length + 1} className="text-center py-10 text-slate-400">
|
||||
Keine Einträge gefunden
|
||||
</td>
|
||||
</tr>
|
||||
@@ -212,7 +230,7 @@ export default function TableView() {
|
||||
<tr
|
||||
key={row.id || i}
|
||||
onClick={() => setModalRecord(row)}
|
||||
className={`border-b border-slate-100 hover:bg-slate-50 transition-colors cursor-pointer
|
||||
className={`group border-b border-slate-100 hover:bg-slate-50 transition-colors cursor-pointer
|
||||
${highlightId && row.id === highlightId ? 'bg-indigo-50 ring-1 ring-indigo-300' : ''}`}
|
||||
>
|
||||
{meta.columns.map(col => (
|
||||
@@ -225,6 +243,34 @@ export default function TableView() {
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
{/* Delete cell */}
|
||||
<td className="px-3 py-2 text-right align-middle" onClick={e => e.stopPropagation()}>
|
||||
{confirmDeleteId === row.id ? (
|
||||
<span className="flex items-center justify-end gap-1">
|
||||
<button
|
||||
onClick={e => handleDelete(row.id, e)}
|
||||
disabled={deleting}
|
||||
className="text-xs text-white bg-red-500 hover:bg-red-600 rounded px-1.5 py-0.5 font-medium disabled:opacity-50"
|
||||
>
|
||||
{deleting ? '…' : '✓'}
|
||||
</button>
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setConfirmDeleteId(null); }}
|
||||
className="text-xs text-slate-500 hover:text-slate-700 bg-slate-100 hover:bg-slate-200 rounded px-1.5 py-0.5"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setConfirmDeleteId(row.id); }}
|
||||
className="opacity-0 group-hover:opacity-100 text-slate-300 hover:text-red-400 transition-colors p-1 rounded"
|
||||
title="Löschen"
|
||||
>
|
||||
🗑
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user