mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-10 23:18:51 +00:00
M-029 Pass 1 batch 1: migrate 4 single-mutation pages to useTrackedMutation
Drains the Bundle 8 useMutation backlog (56 -> 52). Each migration declares
explicit invalidates per the M-009 contract; the wrapper invalidates BEFORE
calling the caller's onSuccess so user code drops the redundant qc.invalidateQueries.
Pages migrated:
- AgentsPage.tsx invalidates: [['agents'], ['agents', 'retired']]
- CertificatesPage.tsx invalidates: [['certificates']]
- DigestPage.tsx invalidates: 'noop' (sendDigest is a server-side email
dispatch — no client query reflects digest-send state)
- IssuerDetailPage.tsx invalidates: [['issuer', id]] (testIssuerConnection
updates last_tested_at server-side)
Verification:
legacy useMutation count 56 -> 52 (-4 sites)
useTrackedMutation count 0 -> 4 (+4 sites)
invalidation surface 82 -> 84 (+2; DigestPage is noop, AgentsPage
collapses 2 invalidates into 1 array, others +1)
Closes 4 of 56 sites toward M-029 Pass 1 completion.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useTrackedMutation } from '../hooks/useTrackedMutation';
|
||||||
import {
|
import {
|
||||||
getAgents,
|
getAgents,
|
||||||
listRetiredAgents,
|
listRetiredAgents,
|
||||||
@@ -45,7 +46,6 @@ type ModalMode =
|
|||||||
|
|
||||||
export default function AgentsPage() {
|
export default function AgentsPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const qc = useQueryClient();
|
|
||||||
const [tab, setTab] = useState<TabKey>('active');
|
const [tab, setTab] = useState<TabKey>('active');
|
||||||
const [modal, setModal] = useState<ModalMode>({ kind: 'closed' });
|
const [modal, setModal] = useState<ModalMode>({ kind: 'closed' });
|
||||||
|
|
||||||
@@ -67,12 +67,11 @@ export default function AgentsPage() {
|
|||||||
// and we invalidate both queries on success so the retired tab refreshes and
|
// and we invalidate both queries on success so the retired tab refreshes and
|
||||||
// the active tab drops the row. 409s are converted into modal.mode=blocked so
|
// the active tab drops the row. 409s are converted into modal.mode=blocked so
|
||||||
// the operator can escalate to force; everything else becomes modal.mode=error.
|
// the operator can escalate to force; everything else becomes modal.mode=error.
|
||||||
const mutation = useMutation({
|
const mutation = useTrackedMutation({
|
||||||
mutationFn: (input: { agent: Agent; force?: boolean; reason?: string }) =>
|
mutationFn: (input: { agent: Agent; force?: boolean; reason?: string }) =>
|
||||||
retireAgent(input.agent.id, { force: input.force, reason: input.reason }),
|
retireAgent(input.agent.id, { force: input.force, reason: input.reason }),
|
||||||
|
invalidates: [['agents'], ['agents', 'retired']],
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
qc.invalidateQueries({ queryKey: ['agents'] });
|
|
||||||
qc.invalidateQueries({ queryKey: ['agents', 'retired'] });
|
|
||||||
setModal({ kind: 'closed' });
|
setModal({ kind: 'closed' });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useTrackedMutation } from '../hooks/useTrackedMutation';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { getCertificates, createCertificate, revokeCertificate, getOwners, getTeams, getRenewalPolicies, getProfiles, getIssuers, bulkRevokeCertificates, bulkRenewCertificates, bulkReassignCertificates } from '../api/client';
|
import { getCertificates, createCertificate, revokeCertificate, getOwners, getTeams, getRenewalPolicies, getProfiles, getIssuers, bulkRevokeCertificates, bulkRenewCertificates, bulkReassignCertificates } from '../api/client';
|
||||||
import { useAuth } from '../components/AuthProvider';
|
import { useAuth } from '../components/AuthProvider';
|
||||||
@@ -73,7 +74,7 @@ function CreateCertificateModal({ onClose, onSuccess }: { onClose: () => void; o
|
|||||||
: `${Math.round(selectedProfile.max_ttl_seconds / 86400)}d`
|
: `${Math.round(selectedProfile.max_ttl_seconds / 86400)}d`
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useTrackedMutation({
|
||||||
mutationFn: () => {
|
mutationFn: () => {
|
||||||
const payload: Record<string, unknown> = { ...form };
|
const payload: Record<string, unknown> = { ...form };
|
||||||
// Convert comma-separated SANs to array
|
// Convert comma-separated SANs to array
|
||||||
@@ -95,6 +96,7 @@ function CreateCertificateModal({ onClose, onSuccess }: { onClose: () => void; o
|
|||||||
}
|
}
|
||||||
return createCertificate(payload);
|
return createCertificate(payload);
|
||||||
},
|
},
|
||||||
|
invalidates: [['certificates']],
|
||||||
onSuccess: () => onSuccess(),
|
onSuccess: () => onSuccess(),
|
||||||
onError: (err: Error) => setError(err.message),
|
onError: (err: Error) => setError(err.message),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useTrackedMutation } from '../hooks/useTrackedMutation';
|
||||||
import { previewDigest, sendDigest } from '../api/client';
|
import { previewDigest, sendDigest } from '../api/client';
|
||||||
import PageHeader from '../components/PageHeader';
|
import PageHeader from '../components/PageHeader';
|
||||||
import ErrorState from '../components/ErrorState';
|
import ErrorState from '../components/ErrorState';
|
||||||
@@ -13,8 +14,10 @@ export default function DigestPage() {
|
|||||||
retry: false,
|
retry: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const sendMutation = useMutation({
|
const sendMutation = useTrackedMutation({
|
||||||
mutationFn: sendDigest,
|
mutationFn: sendDigest,
|
||||||
|
invalidates: 'noop',
|
||||||
|
noopReason: 'sendDigest dispatches an email server-side; no cached client query reflects digest-send state',
|
||||||
onSuccess: () => setShowConfirm(false),
|
onSuccess: () => setShowConfirm(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useTrackedMutation } from '../hooks/useTrackedMutation';
|
||||||
import { getIssuer, testIssuerConnection, getCertificates } from '../api/client';
|
import { getIssuer, testIssuerConnection, getCertificates } from '../api/client';
|
||||||
import PageHeader from '../components/PageHeader';
|
import PageHeader from '../components/PageHeader';
|
||||||
import StatusBadge from '../components/StatusBadge';
|
import StatusBadge from '../components/StatusBadge';
|
||||||
@@ -46,9 +47,9 @@ export default function IssuerDetailPage() {
|
|||||||
enabled: !!id,
|
enabled: !!id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const testMutation = useMutation({
|
const testMutation = useTrackedMutation({
|
||||||
mutationFn: () => testIssuerConnection(id!),
|
mutationFn: () => testIssuerConnection(id!),
|
||||||
onSuccess: () => refetch(),
|
invalidates: [['issuer', id]],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user