mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:01:30 +00:00
fix(gui): wire create modal onSuccess callbacks and fix short-lived profile UX
- All 5 create modals (Profiles, Teams, Owners, Policies, Agent Groups) had no-op onSuccess callbacks — API call fired but modal never closed and list never refreshed. Wired invalidateQueries + setShowCreate. - Removed silent try/catch error swallowing so API errors surface in UI. - Profile create: auto-set TTL to 300s when short-lived checkbox enabled with TTL >= 3600, added validation hint and warning text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,8 +29,7 @@ function CreateAgentGroupModal({ isOpen, onClose, onSuccess, isLoading, error }:
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
try {
|
||||
await createAgentGroup({
|
||||
await createAgentGroup({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
match_os: matchOs.trim() || undefined,
|
||||
@@ -45,11 +44,8 @@ function CreateAgentGroupModal({ isOpen, onClose, onSuccess, isLoading, error }:
|
||||
setMatchArch('');
|
||||
setMatchIpCidr('');
|
||||
setMatchVersion('');
|
||||
setEnabled(true);
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
console.error('Create agent group error:', err);
|
||||
}
|
||||
setEnabled(true);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
@@ -249,7 +245,10 @@ export default function AgentGroupsPage() {
|
||||
<CreateAgentGroupModal
|
||||
isOpen={showCreate}
|
||||
onClose={() => setShowCreate(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['agent-groups'] });
|
||||
setShowCreate(false);
|
||||
}}
|
||||
isLoading={createMutation.isPending}
|
||||
error={createMutation.error ? (createMutation.error as Error).message : null}
|
||||
/>
|
||||
|
||||
@@ -25,19 +25,15 @@ function CreateOwnerModal({ isOpen, onClose, onSuccess, isLoading, error, teamsD
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim() || !email.trim()) return;
|
||||
try {
|
||||
await createOwner({
|
||||
name: name.trim(),
|
||||
email: email.trim(),
|
||||
team_id: teamId || undefined,
|
||||
});
|
||||
setName('');
|
||||
setEmail('');
|
||||
setTeamId('');
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
console.error('Create owner error:', err);
|
||||
}
|
||||
await createOwner({
|
||||
name: name.trim(),
|
||||
email: email.trim(),
|
||||
team_id: teamId || undefined,
|
||||
});
|
||||
setName('');
|
||||
setEmail('');
|
||||
setTeamId('');
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
@@ -203,7 +199,10 @@ export default function OwnersPage() {
|
||||
<CreateOwnerModal
|
||||
isOpen={showCreate}
|
||||
onClose={() => setShowCreate(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['owners'] });
|
||||
setShowCreate(false);
|
||||
}}
|
||||
isLoading={createMutation.isPending}
|
||||
error={createMutation.error ? (createMutation.error as Error).message : null}
|
||||
teamsData={teamsData}
|
||||
|
||||
@@ -40,18 +40,14 @@ function CreatePolicyModal({ isOpen, onClose, onSuccess, isLoading, error }: Cre
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
try {
|
||||
const config = JSON.parse(configStr);
|
||||
await createPolicy({ name: name.trim(), type, severity, config, enabled });
|
||||
setName('');
|
||||
setType('key_algorithm');
|
||||
setSeverity('medium');
|
||||
setConfigStr('{}');
|
||||
setEnabled(true);
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
console.error('Create policy error:', err);
|
||||
}
|
||||
const config = JSON.parse(configStr);
|
||||
await createPolicy({ name: name.trim(), type, severity, config, enabled });
|
||||
setName('');
|
||||
setType('key_algorithm');
|
||||
setSeverity('medium');
|
||||
setConfigStr('{}');
|
||||
setEnabled(true);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
@@ -269,7 +265,10 @@ export default function PoliciesPage() {
|
||||
<CreatePolicyModal
|
||||
isOpen={showCreate}
|
||||
onClose={() => setShowCreate(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['policies'] });
|
||||
setShowCreate(false);
|
||||
}}
|
||||
isLoading={createMutation.isPending}
|
||||
error={createMutation.error ? (createMutation.error as Error).message : null}
|
||||
/>
|
||||
|
||||
@@ -34,22 +34,18 @@ function CreateProfileModal({ isOpen, onClose, onSuccess, isLoading, error }: Cr
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
try {
|
||||
await createProfile({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
max_ttl_seconds: parseInt(ttl) || 86400,
|
||||
allow_short_lived: shortLived,
|
||||
enabled: true,
|
||||
});
|
||||
setName('');
|
||||
setDescription('');
|
||||
setTtl('86400');
|
||||
setShortLived(false);
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
console.error('Create profile error:', err);
|
||||
}
|
||||
await createProfile({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
max_ttl_seconds: parseInt(ttl) || 86400,
|
||||
allow_short_lived: shortLived,
|
||||
enabled: true,
|
||||
});
|
||||
setName('');
|
||||
setDescription('');
|
||||
setTtl('86400');
|
||||
setShortLived(false);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
@@ -89,14 +85,26 @@ function CreateProfileModal({ isOpen, onClose, onSuccess, isLoading, error }: Cr
|
||||
className="w-full bg-white border border-surface-border rounded px-3 py-2 text-sm text-ink focus:outline-none focus:border-brand-400"
|
||||
placeholder="86400"
|
||||
/>
|
||||
<p className="text-xs text-ink-muted mt-1">e.g. 86400 = 1 day, 2592000 = 30 days</p>
|
||||
<p className="text-xs text-ink-muted mt-1">
|
||||
{shortLived
|
||||
? 'Short-lived certs require TTL under 3600 (1 hour). e.g. 300 = 5m, 1800 = 30m'
|
||||
: 'e.g. 86400 = 1 day, 2592000 = 30 days'}
|
||||
</p>
|
||||
{shortLived && parseInt(ttl) >= 3600 && (
|
||||
<p className="text-xs text-amber-600 mt-1">TTL must be under 3600 for short-lived certs</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="shortLived"
|
||||
checked={shortLived}
|
||||
onChange={e => setShortLived(e.target.checked)}
|
||||
onChange={e => {
|
||||
setShortLived(e.target.checked);
|
||||
if (e.target.checked && parseInt(ttl) >= 3600) {
|
||||
setTtl('300');
|
||||
}
|
||||
}}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<label htmlFor="shortLived" className="text-sm text-ink">Allow short-lived certs</label>
|
||||
@@ -251,7 +259,10 @@ export default function ProfilesPage() {
|
||||
<CreateProfileModal
|
||||
isOpen={showCreate}
|
||||
onClose={() => setShowCreate(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['profiles'] });
|
||||
setShowCreate(false);
|
||||
}}
|
||||
isLoading={createMutation.isPending}
|
||||
error={createMutation.error ? (createMutation.error as Error).message : null}
|
||||
/>
|
||||
|
||||
+11
-12
@@ -23,17 +23,13 @@ function CreateTeamModal({ isOpen, onClose, onSuccess, isLoading, error }: Creat
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
try {
|
||||
await createTeam({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
});
|
||||
setName('');
|
||||
setDescription('');
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
console.error('Create team error:', err);
|
||||
}
|
||||
await createTeam({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
});
|
||||
setName('');
|
||||
setDescription('');
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
@@ -167,7 +163,10 @@ export default function TeamsPage() {
|
||||
<CreateTeamModal
|
||||
isOpen={showCreate}
|
||||
onClose={() => setShowCreate(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['teams'] });
|
||||
setShowCreate(false);
|
||||
}}
|
||||
isLoading={createMutation.isPending}
|
||||
error={createMutation.error ? (createMutation.error as Error).message : null}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user