import { createOperationKey } from '@/lib/operation-key'; import { useState } from 'react'; import { Management, button, control } from '@/pages/management/shared'; import { profileJson } from '@/lib/profile-json'; import { Link } from '@inertiajs/react'; import { usePagePreparationGuard } from '@/lib/page-preparation-guard'; type Plan = { technicalSupportUpdateUrl?: string; installed: boolean; consistent: boolean; ready: boolean; target: { database: string; port: number }; hash: string; dependencies: Record; changes: string[]; backupDate: string | null; rollback: string; bootstrap: { initialized: boolean; holders: number[]; version: number | null; rule: string; pending: { approval_id: string; confirmation_url: string; expires_at: string; } | null; }; }; export default function Installation(initial: Plan) { const [plan, setPlan] = useState(initial); const [reason, setReason] = useState(''); const [confirmed, setConfirmed] = useState(false); const [first, setFirst] = useState(''); const [second, setSecond] = useState(''); const [busy, setBusy] = useState(false); const [message, setMessage] = useState(''); const [confirmationUrl, setConfirmationUrl] = useState(null); const [installationKey] = useState(createOperationKey); const [bootstrapKey] = useState(createOperationKey); const [confirmedPreparation, setConfirmedPreparation] = useState({ reason: '', first: '', second: '', }); usePagePreparationGuard( busy || reason !== confirmedPreparation.reason || first !== confirmedPreparation.first || second !== confirmedPreparation.second, 'Existe uma preparação ou uma operação de perfis em curso. Sair desta página?', ); async function run(bootstrap = false) { setBusy(true); setMessage(''); try { const result = await profileJson( '/management/profile-install' + (bootstrap ? '/bootstrap' : ''), { hash: plan.hash, confirm: confirmed, reason, idempotency_key: bootstrap ? bootstrapKey : installationKey, ...(bootstrap ? { first_user_id: Number(first), second_user_id: Number(second), } : {}), }, ); setConfirmedPreparation((previous) => ({ ...previous, reason, ...(bootstrap ? { first, second } : {}), })); setMessage( bootstrap ? 'Pedido registado, sem atribuir privilégios. O segundo titular deve autenticar-se com MFA e confirmar pelo endereço abaixo.' : result.changed ? 'Schema instalado. Perfis ainda não disponibilizados; nenhuma conta ou atribuição criada.' : 'Schema já instalado. Nenhuma reinstalação ou duplicação.', ); if (bootstrap) setConfirmationUrl(result.confirmation_url as string); if (!bootstrap) setPlan( (await profileJson( '/management/profile-install', {}, 'GET', )) as Plan, ); setConfirmed(false); } catch (error) { setMessage( error instanceof Error ? error.message : 'Resposta indisponível; consulte o estado antes de repetir.', ); } finally { setBusy(false); } } return ( {plan.technicalSupportUpdateUrl && ( Actualização do Suporte técnico )}

Base {plan.target.database}:{plan.target.port} ·{' '} {plan.installed ? 'Schema instalado' : 'Schema por instalar'}

Preparação:{' '} {plan.ready ? 'Provas atuais verificadas' : 'Dependências ou provas por completar'} . Preparação não significa instalação ou disponibilização dos perfis.

{!plan.consistent && (

Ledger e schema divergem. Recuperar a consistência antes de instalar.

)}
    {Object.entries(plan.dependencies).map(([key, ok]) => (
  • {key}: {ok ? 'presente' : 'pendente'}
  • ))}
    {plan.changes.map((c) => (
  • {c}
  • ))}

Backup/restauro: {plan.backupDate ?? 'Prova atual pendente'}

{plan.rollback}