A project settings danger zone: pause deployments with an undo window, transfer the project in a dialog that shows what changes, and delete it in a dialog that asks for the project name.
@sureui
components/blocks/danger-zone-01/danger-zone.tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { ConfirmButton } from "@/components/ui/sureui/confirm-button"
import { ConfirmDialog } from "@/components/ui/sureui/confirm-dialog"
import { Consequences } from "@/components/ui/sureui/consequences"
type DangerZoneProps = {
project?: string
team?: string
transferTo?: string
className?: string
}
function request() {
return Promise.resolve()
}
function DangerZone({
project = "acme-prod",
team = "Acme",
transferTo = "Design",
className,
}: DangerZoneProps) {
const [paused, setPaused] = React.useState(false)
const [resuming, setResuming] = React.useState(false)
const [owner, setOwner] = React.useState(team)
const [deleted, setDeleted] = React.useState(false)
const transferred = owner !== team
if (deleted) {
return (
<Card role="status" className={cn("w-full", className)}>
<CardHeader>
<CardTitle>{project} was deleted</CardTitle>
<CardDescription>
Its deployments, domains and data are gone.
</CardDescription>
</CardHeader>
</Card>
)
}
const rows = [
{
title: "Pause deployments",
description: paused
? "Paused. New pushes wait until you resume."
: "Stop deploying new pushes.",
action: paused ? (
<Button
variant="outline"
size="sm"
disabled={resuming}
onClick={async () => {
setResuming(true)
await request()
setPaused(false)
setResuming(false)
}}
>
Resume
</Button>
) : (
<ConfirmButton
undo
variant="outline"
size="sm"
onConfirm={async () => {
await request()
setPaused(true)
}}
>
Pause
</ConfirmButton>
),
},
{
title: "Transfer project",
description: transferred
? `Transferred to ${owner}.`
: `Move ${project} to ${transferTo}.`,
action: transferred ? null : (
<ConfirmDialog
title={`Transfer ${project} to ${transferTo}?`}
description={`${transferTo} gets the project, its deployments and domains.`}
consequences={
<Consequences
title="What changes"
items={[
{ label: "Owner", from: team, to: transferTo },
{ label: `${team} members`, to: "No access" },
]}
/>
}
confirmLabel="Transfer"
onConfirm={async () => {
await request()
setOwner(transferTo)
}}
>
<Button variant="outline" size="sm">
Transfer
</Button>
</ConfirmDialog>
),
},
{
title: "Delete project",
description: "Remove its deployments, domains and data.",
action: (
<ConfirmDialog
title={`Delete ${project}?`}
description="This removes the project, its deployments, domains and data. It can't be undone."
phrase={project}
acknowledgements={["Active deployments will go offline."]}
confirmLabel="Delete project"
variant="destructive"
onConfirm={async () => {
await request()
setDeleted(true)
}}
>
<Button variant="destructive" size="sm">
Delete
</Button>
</ConfirmDialog>
),
},
]
return (
<Card className={cn("@container/danger w-full", className)}>
<CardHeader>
<CardTitle>Danger zone</CardTitle>
</CardHeader>
<CardContent className="divide-y">
{rows.map((row) => (
<div
key={row.title}
className="flex flex-col items-start gap-3 py-4 first:pt-0 last:pb-0 @sm/danger:flex-row @sm/danger:items-center @sm/danger:justify-between @sm/danger:gap-4"
>
<div className="grid min-w-0 gap-1">
<p className="font-medium">{row.title}</p>
<p className="break-words text-muted-foreground">
{row.description}
</p>
</div>
{row.action}
</div>
))}
</CardContent>
</Card>
)
}
export { DangerZone, type DangerZoneProps }