hvac: add gateway delete (retire the old Modbus gateway config)

There was no way to remove an mhi_gateways row either, alongside the
already-fixed device-delete gap — exposed by the same Modbus->MQTT
switchover. Adds DELETE /api/mhi-gateways/:id (manage_devices cap) and a
'Delete gateway' button. Devices referencing the gateway are NOT deleted
(mhi_gateway_id just goes NULL per the existing ON DELETE SET NULL FK) —
the confirm dialog says so and points at the separate device-delete flow
for retiring the stale mhi_modbus rows too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-08-13 14:19:02 +00:00
parent 7ce95a31da
commit d7cbb0c452
3 changed files with 37 additions and 2 deletions

View file

@ -113,6 +113,9 @@ export function updateMhiGateway(id: number, body: Partial<{ name: string; host:
export function testMhiGatewayConnection(id: number): Promise<{ ok: boolean; note?: string; error?: string }> {
return request(`/mhi-gateways/${id}/test-connection`, { method: 'POST', body: JSON.stringify({}) })
}
export function deleteMhiGateway(id: number): Promise<{ ok: boolean }> {
return request(`/mhi-gateways/${id}`, { method: 'DELETE' })
}
export async function importMhiRegisterMap(id: number, file: File): Promise<MhiGateway> {
const form = new FormData()
form.append('file', file)

View file

@ -1,8 +1,8 @@
import { useEffect, useState } from 'react'
import { Plus, Wifi, WifiOff, Upload, Loader2 } from 'lucide-react'
import { Plus, Wifi, WifiOff, Upload, Loader2, Trash2 } from 'lucide-react'
import type { Zone, MhiGateway } from '../types'
import {
fetchMhiGateways, createMhiGateway, testMhiGatewayConnection, importMhiRegisterMap, assignMhiUnit,
fetchMhiGateways, createMhiGateway, testMhiGatewayConnection, importMhiRegisterMap, assignMhiUnit, deleteMhiGateway,
} from '../api'
// Gateway configuration + MAPS register-map import for MHI aircon units (manual
@ -65,6 +65,22 @@ export default function GatewaysPanel({ zones, onAssigned }: { zones: Zone[]; on
}
}
async function handleDelete(gw: MhiGateway) {
if (!confirm(
`Delete gateway "${gw.name}"? This removes its connection config and register-map import.` +
(gw.assigned_count > 0 ? ` ${gw.assigned_count} assigned device(s) will be unlinked from it (not deleted) — ` +
'if this gateway was switched to native MQTT, remove those Modbus device rows separately in Devices.' : '')
)) return
setError(''); setMsg('')
try {
await deleteMhiGateway(gw.id)
setMsg('Gateway deleted.')
load()
} catch (e) {
setError(e instanceof Error ? e.message : 'Delete failed')
}
}
async function handleImport(id: number, file: File | undefined) {
if (!file) return
setImportingId(id); setError(''); setMsg('')
@ -170,6 +186,9 @@ export default function GatewaysPanel({ zones, onAssigned }: { zones: Zone[]; on
onChange={e => { handleImport(gw.id, e.target.files?.[0]); e.target.value = '' }}
/>
</label>
<button className="btn btn-sm btn-danger" onClick={() => handleDelete(gw)}>
<Trash2 size={13} strokeWidth={1.75} /> Delete gateway
</button>
</div>
</div>