mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-25 01:37:05 +00:00
refactor(frontend): migrate Routing and Security dialogs to Modal chrome (#948)
NotificationRoutingSection: - Routing rule create/edit form -> Modal at lg with kicker ROUTING · NEW RULE or ROUTING · EDIT RULE - Per-row delete AlertDialog -> a single parent-level destructive ConfirmModal driven by deleteRouteId state, opened by each row's trash button (no more inline AlertDialogTrigger pattern) - handleDelete tightened to close from finally so the dialog clears on errors too SecuritySection: - Policy create/edit form -> Modal at md with kicker SECURITY · NEW POLICY or SECURITY · EDIT POLICY - Policy delete confirm -> destructive ConfirmModal with kicker SECURITY · DELETE · IRREVERSIBLE - Trivy uninstall confirm -> destructive ConfirmModal with kicker TRIVY · REMOVE · IRREVERSIBLE - handleDelete already used finally; handleUninstallTrivy already closes the dialog before awaiting, so it works with the ConfirmModal Promise-aware path
This commit is contained in:
@@ -6,24 +6,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { TogglePill } from '@/components/ui/toggle-pill';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, ConfirmModal } from '@/components/ui/modal';
|
||||
import { toast } from '@/components/ui/toast-store';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { ShieldCheck, Plus, Trash2, Pencil, Download, RefreshCw, Loader2, Info } from 'lucide-react';
|
||||
@@ -462,120 +445,108 @@ export function SecuritySection({ isPaid }: { isPaid: boolean }) {
|
||||
|
||||
{isPaid && (
|
||||
<>
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingId ? 'Edit Policy' : 'New Policy'}</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
Configure the severity threshold and scope for this scan policy.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="policy-name">Name</Label>
|
||||
<Input
|
||||
id="policy-name"
|
||||
placeholder="Production block on critical"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="policy-pattern">Stack pattern (optional)</Label>
|
||||
<Input
|
||||
id="policy-pattern"
|
||||
placeholder="e.g. prod-* or leave blank for all"
|
||||
value={form.stack_pattern}
|
||||
onChange={(e) => setForm({ ...form, stack_pattern: e.target.value })}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Glob-style pattern matched against stack names. Leave blank to apply to all stacks.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Max severity</Label>
|
||||
<Combobox
|
||||
options={SEVERITY_OPTIONS}
|
||||
value={form.max_severity}
|
||||
onValueChange={(v) => setForm({ ...form, max_severity: v as VulnSeverity })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-lg border border-glass-border px-3 py-2.5">
|
||||
<div>
|
||||
<Label className="text-sm">Block on deploy</Label>
|
||||
<Modal open={dialogOpen} onOpenChange={setDialogOpen} size="md">
|
||||
<ModalHeader
|
||||
kicker={editingId ? 'SECURITY · EDIT POLICY' : 'SECURITY · NEW POLICY'}
|
||||
title={editingId ? 'Edit policy' : 'New policy'}
|
||||
description="Configure the severity threshold and scope for this scan policy."
|
||||
/>
|
||||
<ModalBody>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="policy-name">Name</Label>
|
||||
<Input
|
||||
id="policy-name"
|
||||
placeholder="Production block on critical"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="policy-pattern">Stack pattern (optional)</Label>
|
||||
<Input
|
||||
id="policy-pattern"
|
||||
placeholder="e.g. prod-* or leave blank for all"
|
||||
value={form.stack_pattern}
|
||||
onChange={(e) => setForm({ ...form, stack_pattern: e.target.value })}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Emit a critical alert when this policy is violated after a deploy.
|
||||
Glob-style pattern matched against stack names. Leave blank to apply to all stacks.
|
||||
</p>
|
||||
</div>
|
||||
<TogglePill
|
||||
checked={form.block_on_deploy}
|
||||
onChange={(c) => setForm({ ...form, block_on_deploy: c })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-lg border border-glass-border px-3 py-2.5">
|
||||
<div>
|
||||
<Label className="text-sm">Enabled</Label>
|
||||
<p className="text-xs text-muted-foreground">Disabled policies are skipped during evaluation.</p>
|
||||
<div className="space-y-2">
|
||||
<Label>Max severity</Label>
|
||||
<Combobox
|
||||
options={SEVERITY_OPTIONS}
|
||||
value={form.max_severity}
|
||||
onValueChange={(v) => setForm({ ...form, max_severity: v as VulnSeverity })}
|
||||
/>
|
||||
</div>
|
||||
<TogglePill
|
||||
checked={form.enabled}
|
||||
onChange={(c) => setForm({ ...form, enabled: c })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<SettingsPrimaryButton onClick={handleSave} disabled={saving}>
|
||||
{saving ? 'Saving...' : editingId ? 'Update' : 'Create'}
|
||||
</SettingsPrimaryButton>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<div className="flex items-center justify-between rounded-lg border border-glass-border px-3 py-2.5">
|
||||
<div>
|
||||
<Label className="text-sm">Block on deploy</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Emit a critical alert when this policy is violated after a deploy.
|
||||
</p>
|
||||
</div>
|
||||
<TogglePill
|
||||
checked={form.block_on_deploy}
|
||||
onChange={(c) => setForm({ ...form, block_on_deploy: c })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-lg border border-glass-border px-3 py-2.5">
|
||||
<div>
|
||||
<Label className="text-sm">Enabled</Label>
|
||||
<p className="text-xs text-muted-foreground">Disabled policies are skipped during evaluation.</p>
|
||||
</div>
|
||||
<TogglePill
|
||||
checked={form.enabled}
|
||||
onChange={(c) => setForm({ ...form, enabled: c })}
|
||||
/>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter
|
||||
secondary={
|
||||
<Button variant="outline" size="sm" onClick={() => setDialogOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
}
|
||||
primary={
|
||||
<SettingsPrimaryButton size="sm" onClick={handleSave} disabled={saving}>
|
||||
{saving ? 'Saving...' : editingId ? 'Update' : 'Create'}
|
||||
</SettingsPrimaryButton>
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<AlertDialog open={deleteId != null} onOpenChange={(open) => !open && setDeleteId(null)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete scan policy?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This removes the policy immediately. Existing scans are not affected.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleDelete}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<ConfirmModal
|
||||
open={deleteId != null}
|
||||
onOpenChange={(open) => !open && setDeleteId(null)}
|
||||
variant="destructive"
|
||||
kicker="SECURITY · DELETE · IRREVERSIBLE"
|
||||
title="Delete scan policy"
|
||||
confirmLabel="Delete"
|
||||
onConfirm={handleDelete}
|
||||
>
|
||||
<p className="text-sm text-stat-subtitle">
|
||||
Removes the policy immediately. Existing scans are not affected.
|
||||
</p>
|
||||
</ConfirmModal>
|
||||
</>
|
||||
)}
|
||||
|
||||
<AlertDialog open={uninstallConfirm} onOpenChange={setUninstallConfirm}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Remove Trivy?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This removes the managed Trivy binary. Vulnerability scanning will stop working until
|
||||
Trivy is reinstalled or a host binary is provided.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleUninstallTrivy}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
Remove
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<ConfirmModal
|
||||
open={uninstallConfirm}
|
||||
onOpenChange={setUninstallConfirm}
|
||||
variant="destructive"
|
||||
kicker="TRIVY · REMOVE · IRREVERSIBLE"
|
||||
title="Remove Trivy"
|
||||
confirmLabel="Remove"
|
||||
onConfirm={handleUninstallTrivy}
|
||||
>
|
||||
<p className="text-sm text-stat-subtitle">
|
||||
Removes the managed Trivy binary. Vulnerability scanning stops working until Trivy is reinstalled or a host binary is provided.
|
||||
</p>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user