UpdateVersionModal.tsx139 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { ChevronDown } from 'lucide-react' |
| 3 | import { toast } from 'sonner' |
| 4 | import { Collapsible, CollapsibleContent, CollapsibleTrigger, DialogSectionSeparator } from 'ui' |
| 5 | import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' |
| 6 | |
| 7 | import { getStatusName } from './Pipeline.utils' |
| 8 | import { PipelineStatusName, STATUS_REFRESH_FREQUENCY_MS } from './Replication.constants' |
| 9 | import { useReplicationPipelineStatusQuery } from '@/data/replication/pipeline-status-query' |
| 10 | import { useReplicationPipelineVersionQuery } from '@/data/replication/pipeline-version-query' |
| 11 | import { Pipeline } from '@/data/replication/pipelines-query' |
| 12 | import { useRestartPipelineHelper } from '@/data/replication/restart-pipeline-helper' |
| 13 | import { useUpdatePipelineVersionMutation } from '@/data/replication/update-pipeline-version-mutation' |
| 14 | import { |
| 15 | PipelineStatusRequestStatus, |
| 16 | usePipelineRequestStatus, |
| 17 | } from '@/state/replication-pipeline-request-status' |
| 18 | import { type ResponseError } from '@/types' |
| 19 | |
| 20 | interface UpdateVersionModalProps { |
| 21 | visible: boolean |
| 22 | pipeline?: Pipeline |
| 23 | confirmLabel?: string |
| 24 | confirmLabelLoading?: string |
| 25 | onClose: () => void |
| 26 | } |
| 27 | |
| 28 | export const UpdateVersionModal = ({ |
| 29 | visible, |
| 30 | pipeline, |
| 31 | confirmLabel, |
| 32 | confirmLabelLoading = 'Updating', |
| 33 | onClose, |
| 34 | }: UpdateVersionModalProps) => { |
| 35 | const { ref: projectRef } = useParams() |
| 36 | const { setRequestStatus } = usePipelineRequestStatus() |
| 37 | |
| 38 | const { data: pipelineStatusData } = useReplicationPipelineStatusQuery( |
| 39 | { projectRef, pipelineId: pipeline?.id }, |
| 40 | { refetchInterval: STATUS_REFRESH_FREQUENCY_MS } |
| 41 | ) |
| 42 | const pipelineStatus = pipelineStatusData?.status |
| 43 | const statusName = getStatusName(pipelineStatus) |
| 44 | const isStopped = statusName === PipelineStatusName.STOPPED |
| 45 | |
| 46 | const { data: versionData, isPending: isLoadingVersion } = useReplicationPipelineVersionQuery({ |
| 47 | projectRef, |
| 48 | pipelineId: pipeline?.id, |
| 49 | }) |
| 50 | const currentVersionName = versionData?.version?.name |
| 51 | const newVersionName = versionData?.new_version?.name |
| 52 | |
| 53 | const { mutateAsync: updatePipelineVersion } = useUpdatePipelineVersionMutation() |
| 54 | const { restartPipeline } = useRestartPipelineHelper() |
| 55 | |
| 56 | const onConfirmUpdate = async () => { |
| 57 | if (!projectRef || !pipeline?.id) return |
| 58 | const versionId = versionData?.new_version?.id |
| 59 | if (!versionId) return |
| 60 | |
| 61 | // Step 1: Update to the new version |
| 62 | try { |
| 63 | await updatePipelineVersion({ projectRef, pipelineId: pipeline.id, versionId }) |
| 64 | } catch (e) { |
| 65 | // 404: default changed; version cache will refresh via mutation onError. Keep dialog open. |
| 66 | if ((e as ResponseError)?.code === 404) return |
| 67 | // Other errors are already toasted by the mutation; do not double-toast here. |
| 68 | return |
| 69 | } |
| 70 | |
| 71 | // Step 2: Reflect optimistic restart (only if not stopped) and close any panels |
| 72 | if (!isStopped) { |
| 73 | setRequestStatus(pipeline.id, PipelineStatusRequestStatus.RestartRequested, statusName) |
| 74 | |
| 75 | // Step 3: Restart the pipeline (stop + start) |
| 76 | try { |
| 77 | await restartPipeline({ projectRef, pipelineId: pipeline.id }) |
| 78 | toast.success('Pipeline successfully updated and is currently restarting') |
| 79 | } catch (e) { |
| 80 | // Clear optimistic state and surface a single concise error |
| 81 | setRequestStatus(pipeline.id, PipelineStatusRequestStatus.None) |
| 82 | toast.error(`Failed to restart pipeline: ${(e as ResponseError).message}`) |
| 83 | } |
| 84 | } else { |
| 85 | toast.success('Pipeline successfully updated') |
| 86 | } |
| 87 | |
| 88 | onClose() |
| 89 | } |
| 90 | |
| 91 | return ( |
| 92 | <ConfirmationModal |
| 93 | size="small" |
| 94 | visible={visible} |
| 95 | title="Update pipeline image" |
| 96 | className="p-0!" |
| 97 | confirmLabel={confirmLabel ?? (isStopped ? 'Update image' : 'Update and restart')} |
| 98 | confirmLabelLoading={confirmLabelLoading} |
| 99 | onCancel={onClose} |
| 100 | onConfirm={onConfirmUpdate} |
| 101 | > |
| 102 | <div className="flex flex-col gap-y-3 py-4 px-5"> |
| 103 | <p className="text-sm text-foreground"> |
| 104 | A new pipeline image is available with improvements and bug fixes. Proceed to update? |
| 105 | </p> |
| 106 | {!isStopped && ( |
| 107 | <p className="text-sm text-foreground-light"> |
| 108 | The pipeline will automatically restart when updating. Replication will continue from |
| 109 | where it left off. |
| 110 | </p> |
| 111 | )} |
| 112 | </div> |
| 113 | <DialogSectionSeparator /> |
| 114 | |
| 115 | <Collapsible className="px-5 py-3 group"> |
| 116 | <CollapsibleTrigger className="w-full flex items-center justify-between text-sm text-foreground-light"> |
| 117 | <p>View version update details</p> |
| 118 | <ChevronDown size={14} className="group-data-open:-rotate-180 transition" /> |
| 119 | </CollapsibleTrigger> |
| 120 | <CollapsibleContent> |
| 121 | <div className="flex flex-col gap-y-2 mt-2 pb-2"> |
| 122 | <div className="text-sm text-foreground prose max-w-full"> |
| 123 | <p className="text-foreground-light mb-1">Current version:</p>{' '} |
| 124 | <code className="text-code-inline"> |
| 125 | {isLoadingVersion ? 'Loading...' : (currentVersionName ?? 'Unknown')} |
| 126 | </code> |
| 127 | </div> |
| 128 | <div className="text-sm text-foreground prose max-w-full"> |
| 129 | <p className="text-foreground-light mb-1">New version:</p>{' '} |
| 130 | <code className="text-code-inline"> |
| 131 | {isLoadingVersion ? 'Loading...' : (newVersionName ?? 'Unknown')} |
| 132 | </code> |
| 133 | </div> |
| 134 | </div> |
| 135 | </CollapsibleContent> |
| 136 | </Collapsible> |
| 137 | </ConfirmationModal> |
| 138 | ) |
| 139 | } |