'use client'; import {useState} from 'react'; import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog'; import {VStack, HStack} from '@astryxdesign/core/Layout'; import {TextInput} from '@astryxdesign/core/TextInput'; import {Button} from '@astryxdesign/core/Button'; import {Banner} from '@astryxdesign/core/Banner'; import {companyRepository} from '@/features/admin/repositories/companyRepository'; import type {Company} from '@/features/admin/types/company'; /** * Delete a company. Permanently. * * ── Why the typed slug ─────────────────────────────────────────────────── * The platform requires the body to repeat the company's slug, and this dialog * asks for it rather than filling it in. That is the whole safeguard: a * confirm button can be clicked by muscle memory on the wrong row, and typing * "tenext-retail" cannot. The button stays disabled until it matches exactly — * no trimming of case, because the slug is lowercase upstream and a console * that quietly "fixes" what was typed is not confirming anything. * * ── Why the suspended check is NOT enforced here ───────────────────────── * The platform refuses to delete an active company (`409 still_active`), and * this dialog does not pre-empt that. It says so plainly, and the button is * still live: an active company is sent, and the platform's real 409 is what * appears. A console that blocked the request itself would eventually disagree * with the server about what is deletable, and that disagreement surfaces as a * delete that looked like it worked and did not happen. * * ── What is actually destroyed ─────────────────────────────────────────── * Stored face images go from object storage first — a failure there is * `502 storage_error` and nothing else is touched — then the shop PCs' broker * logins, then every row by cascade: face templates, visits, users, sessions, * cameras. The data is biometric. There is no undo and no backup to ask for. */ export function DeleteCompanyDialog({ company, onClose, onDeleted, }: { company: Company; onClose: () => void; onDeleted: () => void; }) { const [confirm, setConfirm] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const matches = confirm === company.slug; async function submit() { setBusy(true); setError(null); const res = await companyRepository.remove(company.id, confirm); setBusy(false); if (!res.ok) { // `still_active` and `storage_error` both name a condition the operator // can act on — suspend it first, or retry once storage is healthy. setError(res.message ?? 'Could not delete that merchant.'); return; } onDeleted(); onClose(); } return ( (open ? undefined : onClose())} purpose="required" width={460} aria-label="Delete merchant" > (open ? undefined : onClose())} /> {error ? : null} {company.isActive ? ( ) : null} ); }