Files
dify/web/app/components/workflow/dsl-export-confirm-modal.tsx
yyh 808a32c457 fix: add pending state to export button to prevent duplicate clicks
Use useTransition to disable the export button and show loading state
in the DSL export confirm modal during async export operations.
2026-02-02 15:52:03 +08:00

91 lines
3.8 KiB
TypeScript

'use client'
import type { EnvironmentVariable } from '@/app/components/workflow/types'
import { RiCloseLine, RiLock2Line } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import * as React from 'react'
import { useState, useTransition } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Checkbox from '@/app/components/base/checkbox'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import Modal from '@/app/components/base/modal'
import { cn } from '@/utils/classnames'
export type DSLExportConfirmModalProps = {
envList: EnvironmentVariable[]
onConfirm: (state: boolean) => void | Promise<void>
onClose: () => void
}
const DSLExportConfirmModal = ({
envList = [],
onConfirm,
onClose,
}: DSLExportConfirmModalProps) => {
const { t } = useTranslation()
const [exportSecrets, setExportSecrets] = useState<boolean>(false)
const [exporting, startExport] = useTransition()
const submit = () => {
startExport(async () => {
await onConfirm(exportSecrets)
onClose()
})
}
return (
<Modal
isShow={true}
onClose={noop}
className={cn('w-[480px] max-w-[480px]')}
>
<div className="title-2xl-semi-bold relative pb-6 text-text-primary">{t('env.export.title', { ns: 'workflow' })}</div>
<div className={cn('absolute right-4 top-4 p-2', exporting ? 'pointer-events-none opacity-50' : 'cursor-pointer')} onClick={onClose}>
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
</div>
<div className="relative">
<table className="radius-md w-full border-separate border-spacing-0 border border-divider-regular shadow-xs">
<thead className="system-xs-medium-uppercase text-text-tertiary">
<tr>
<td width={220} className="h-7 border-b border-r border-divider-regular pl-3">NAME</td>
<td className="h-7 border-b border-divider-regular pl-3">VALUE</td>
</tr>
</thead>
<tbody>
{envList.map((env, index) => (
<tr key={env.name}>
<td className={cn('system-xs-medium h-7 border-r pl-3', index + 1 !== envList.length && 'border-b')}>
<div className="flex w-[200px] items-center gap-1">
<Env className="h-4 w-4 shrink-0 text-util-colors-violet-violet-600" />
<div className="truncate text-text-primary">{env.name}</div>
<div className="shrink-0 text-text-tertiary">Secret</div>
<RiLock2Line className="h-3 w-3 shrink-0 text-text-tertiary" />
</div>
</td>
<td className={cn('h-7 pl-3', index + 1 !== envList.length && 'border-b')}>
<div className="system-xs-regular truncate text-text-secondary">{env.value}</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="mt-4 flex gap-2">
<Checkbox
className="shrink-0"
checked={exportSecrets}
onCheck={() => setExportSecrets(!exportSecrets)}
/>
<div className="system-sm-medium cursor-pointer text-text-primary" onClick={() => setExportSecrets(!exportSecrets)}>{t('env.export.checkbox', { ns: 'workflow' })}</div>
</div>
<div className="flex flex-row-reverse pt-6">
<Button className="ml-2" variant="primary" loading={exporting} disabled={exporting} onClick={submit}>{exportSecrets ? t('env.export.export', { ns: 'workflow' }) : t('env.export.ignore', { ns: 'workflow' })}</Button>
<Button disabled={exporting} onClick={onClose}>{t('operation.cancel', { ns: 'common' })}</Button>
</div>
</Modal>
)
}
export default DSLExportConfirmModal