feat(tenants): add delete button to tenant list page
Each tenant row now has a Delete button with confirmation dialog. Previously delete was only accessible from the detail page which had no navigation link from the list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6459e5b500
commit
0bc81bbe40
|
|
@ -73,6 +73,7 @@ export default function TenantsPage() {
|
||||||
const [showCreate, setShowCreate] = useState(false);
|
const [showCreate, setShowCreate] = useState(false);
|
||||||
const [expandedId, setExpandedId] = useState<string | null>(null);
|
const [expandedId, setExpandedId] = useState<string | null>(null);
|
||||||
const [editingId, setEditingId] = useState<string | null>(null);
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||||||
|
const [deletingId, setDeletingId] = useState<string | null>(null);
|
||||||
const [editPlan, setEditPlan] = useState<Tenant['plan']>('free');
|
const [editPlan, setEditPlan] = useState<Tenant['plan']>('free');
|
||||||
const [editQuota, setEditQuota] = useState<TenantQuota>({
|
const [editQuota, setEditQuota] = useState<TenantQuota>({
|
||||||
maxServers: 0,
|
maxServers: 0,
|
||||||
|
|
@ -115,6 +116,12 @@ export default function TenantsPage() {
|
||||||
onSuccess: invalidate,
|
onSuccess: invalidate,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
mutationFn: (id: string) =>
|
||||||
|
apiClient(`/api/v1/admin/tenants/${id}`, { method: 'DELETE' }),
|
||||||
|
onSuccess: () => { invalidate(); setDeletingId(null); },
|
||||||
|
});
|
||||||
|
|
||||||
/* ---- helpers ---- */
|
/* ---- helpers ---- */
|
||||||
function resetCreateForm() {
|
function resetCreateForm() {
|
||||||
setShowCreate(false);
|
setShowCreate(false);
|
||||||
|
|
@ -335,6 +342,12 @@ export default function TenantsPage() {
|
||||||
>
|
>
|
||||||
{expandedId === tenant.id ? t('actions.hideQuotas') : t('actions.quotas')}
|
{expandedId === tenant.id ? t('actions.hideQuotas') : t('actions.quotas')}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setDeletingId(tenant.id)}
|
||||||
|
className="px-2 py-1 text-xs rounded bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900 dark:text-red-300"
|
||||||
|
>
|
||||||
|
{tc('delete')}
|
||||||
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -367,6 +380,32 @@ export default function TenantsPage() {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Delete confirmation dialog */}
|
||||||
|
{deletingId && (
|
||||||
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||||
|
<div className="bg-card border rounded-lg p-6 max-w-sm w-full mx-4 space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">{t('deleteDialog.title')}</h2>
|
||||||
|
<p className="text-sm text-muted-foreground">{t('deleteDialog.message')}</p>
|
||||||
|
<div className="flex gap-3 justify-end">
|
||||||
|
<button
|
||||||
|
onClick={() => setDeletingId(null)}
|
||||||
|
disabled={deleteMutation.isPending}
|
||||||
|
className="px-4 py-2 text-sm border rounded-md hover:bg-muted"
|
||||||
|
>
|
||||||
|
{tc('cancel')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => deleteMutation.mutate(deletingId)}
|
||||||
|
disabled={deleteMutation.isPending}
|
||||||
|
className="px-4 py-2 text-sm bg-destructive text-destructive-foreground rounded-md hover:opacity-90 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{deleteMutation.isPending ? tc('deleting') : tc('delete')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue