import {Copy, Play, Plus, ShieldCheck, Trash2} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {createCloudFormationChangeSet, deleteCloudFormationChangeSet, executeCloudFormationChangeSet, getCloudResource, listCloudFormationChangeSets, updateCloudFormationTerminationProtection} from '@/api/cloudProxyClient' import {useState} from 'react' import {CloudFormationTemplateUpload} from './CloudFormationTemplateUpload' import type {CloudResource} from '@/types/resource' export function CloudFormationStackPanel({resource}: {resource?: CloudResource}) { if (!resource || resource.service !== 'iac' || resource.type !== 'stack') return null return } function CloudFormationStackPanelContent({resource}: {resource: CloudResource}) { const detail = useQuery({queryKey: ['cloudformation-stack-detail', resource.id], queryFn: ({signal}) => getCloudResource('aws', 'iac', resource.id, signal), refetchInterval: (query) => /(?:_IN_PROGRESS|_PENDING)$/.test(String(query.state.data?.status ?? resource.status ?? '')) ? 1000 : false}) const stack = detail.data ?? resource const [changeSetName, setChangeSetName] = useState('') const [templateBody, setTemplateBody] = useState(String(resource.metadata.templateBody ?? '')) const [templateError, setTemplateError] = useState(null) const [changeSetParameters, setChangeSetParameters] = useState('{}') const [changeSetCapabilities, setChangeSetCapabilities] = useState('') const queryClient = useQueryClient() const changeSets = useQuery({queryKey: ['cloudformation-change-sets', resource.id], queryFn: ({signal}) => listCloudFormationChangeSets('aws', resource.id, signal)}) const refresh = () => void queryClient.invalidateQueries({queryKey: ['cloudformation-change-sets', resource.id]}) const createChangeSet = useMutation({mutationFn: () => createCloudFormationChangeSet('aws', resource.id, {name: changeSetName.trim(), templateBody, parameters: JSON.parse(changeSetParameters) as Record, capabilities: changeSetCapabilities.split(',').map((item) => item.trim()).filter(Boolean)}), onSuccess: refresh}) const executeChangeSet = useMutation({mutationFn: (name: string) => executeCloudFormationChangeSet('aws', resource.id, name), onSuccess: refresh}) const deleteChangeSet = useMutation({mutationFn: (name: string) => deleteCloudFormationChangeSet('aws', resource.id, name), onSuccess: refresh}) const protection = useMutation({mutationFn: (enabled: boolean) => updateCloudFormationTerminationProtection('aws', resource.id, enabled), onSuccess: () => void queryClient.invalidateQueries({queryKey: ['cloudformation-stack-detail', resource.id]})}) const outputs = array(stack.metadata.outputs) const resources = array(stack.metadata.resources).map((item) => ({...item, consolePath: consolePath(String(item.resourceType ?? ''), String(item.physicalId ?? ''))})) const events = array(stack.metadata.events) const template = typeof stack.metadata.templateBody === 'string' ? stack.metadata.templateBody : null return

Stack workspace

{stack.name}

Stack lifecycle is asynchronous. Refresh the stack list to observe status transitions.

Stack actions

Termination protection: {stack.metadata.enableTerminationProtection === true ? 'Enabled' : 'Disabled'}

{stack.metadata.enableTerminationProtection === true ? 'Enabled: DeleteStack is blocked until this is disabled.' : 'Disabled: the stack can be deleted.'}

Change sets

Preview and deploy

Resource import is unavailable because Floci does not yet implement AWS resource adoption.

{ event.preventDefault(); if (changeSetName.trim() && templateBody.trim()) createChangeSet.mutate() }}>