refactor(frontend): migrate Blueprint dialogs to Modal chrome (#954)

Final phase E pass over the experimental blueprint surfaces (gated by
SENCHO_EXPERIMENTAL).

- BlueprintDetail's destructive delete dialog -> Modal +
  ModalDestructiveHeader. Kicker BLUEPRINT · DELETE · IRREVERSIBLE.
  Keeps the type-to-confirm input pattern in the body and uses a
  destructive-variant Button as the primary footer action
- DeploymentsTab's New Blueprint editor -> Modal + ModalHeader.
  Kicker BLUEPRINTS · NEW. The wide editor sits inside ModalBody;
  cancel/submit live inside the BlueprintEditor itself
This commit is contained in:
Anso
2026-05-06 20:20:27 -04:00
committed by GitHub
parent d76b54201c
commit bc1077c722
2 changed files with 32 additions and 37 deletions
@@ -1,7 +1,7 @@
import { useEffect, useState, useCallback } from 'react';
import { ChevronLeft, MoreHorizontal, Pencil, Play, Trash2 } from 'lucide-react';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog';
import { Modal, ModalDestructiveHeader, ModalBody, ModalFooter } from '@/components/ui/modal';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
@@ -321,20 +321,16 @@ export function BlueprintDetail({ blueprintId, open, onOpenChange, onChanged, ca
/>
)}
{blueprint && (
<Dialog open={deleteOpen} onOpenChange={(o) => { if (!o) { setDeleteOpen(false); setDeleteConfirmText(''); } }}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<div className="flex items-center gap-2 text-brand font-mono text-[10px] uppercase tracking-[0.2em]">
<span className="inline-block w-1 h-3 bg-destructive" />
Delete blueprint
</div>
<DialogTitle className="font-serif italic text-xl tracking-[-0.01em]">
Permanently delete {blueprint.name}?
</DialogTitle>
<DialogDescription>
Stateless deployments will be withdrawn first. Stateful deployments must be withdrawn explicitly through the deployment table before delete; the API will refuse otherwise.
</DialogDescription>
</DialogHeader>
<Modal open={deleteOpen} onOpenChange={(o) => { if (!o) { setDeleteOpen(false); setDeleteConfirmText(''); } }} size="md">
<ModalDestructiveHeader
kicker="BLUEPRINT · DELETE · IRREVERSIBLE"
title={`Delete ${blueprint.name}`}
description="Stateless deployments will be withdrawn first. Stateful deployments must be withdrawn explicitly through the deployment table before delete; the API will refuse otherwise."
/>
<ModalBody>
<p className="text-sm text-stat-subtitle">
Stateless deployments will be withdrawn first. Stateful deployments must be withdrawn explicitly through the deployment table before delete.
</p>
<div className="space-y-2">
<p className="text-xs text-stat-subtitle leading-relaxed">
Type <span className="font-mono text-stat-value">{blueprint.name}</span> to confirm.
@@ -347,21 +343,25 @@ export function BlueprintDetail({ blueprintId, open, onOpenChange, onChanged, ca
disabled={submitting}
/>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => { setDeleteOpen(false); setDeleteConfirmText(''); }} disabled={submitting}>
</ModalBody>
<ModalFooter
secondary={
<Button variant="outline" size="sm" onClick={() => { setDeleteOpen(false); setDeleteConfirmText(''); }} disabled={submitting}>
Cancel
</Button>
}
primary={
<Button
variant="outline"
className="text-destructive border-destructive/40 hover:bg-destructive/10"
variant="destructive"
size="sm"
disabled={!deleteTypedOk || submitting}
onClick={performDelete}
>
Delete blueprint
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
}
/>
</Modal>
)}
</SheetContent>
</Sheet>
@@ -1,7 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import {
Dialog, DialogContent, DialogHeader, DialogTitle,
} from '@/components/ui/dialog';
import { Modal, ModalHeader, ModalBody } from '@/components/ui/modal';
import { toast } from '@/components/ui/toast-store';
import {
type BlueprintListItem,
@@ -117,16 +115,13 @@ export function DeploymentsTab() {
/>
)}
<Dialog open={createOpen} onOpenChange={setCreateOpen}>
<DialogContent className="sm:max-w-3xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<span className="font-mono text-[10px] uppercase tracking-[0.18em] text-stat-icon">
New Blueprint
</span>
<DialogTitle className="font-serif italic text-xl tracking-[-0.01em]">
Declare a fleet-wide compose template
</DialogTitle>
</DialogHeader>
<Modal open={createOpen} onOpenChange={setCreateOpen} className="max-w-3xl max-h-[85vh] overflow-y-auto">
<ModalHeader
kicker="BLUEPRINTS · NEW"
title="Declare a fleet-wide compose template"
description="Create a blueprint that can be deployed across the fleet."
/>
<ModalBody>
<BlueprintEditor
mode="create"
distinctLabels={distinctLabels}
@@ -134,8 +129,8 @@ export function DeploymentsTab() {
onSubmit={handleCreate}
submitting={submitting}
/>
</DialogContent>
</Dialog>
</ModalBody>
</Modal>
</div>
);
}