mirror of
https://github.com/langgenius/dify.git
synced 2026-01-31 08:01:46 +08:00
feat(sandbox-provider): update UI to match Figma design
- Update settings icon to RiEqualizer2Line - Add 4px rounded container for provider icons in config modal - Update section titles to uppercase style - Change switch modal confirm button to warning variant - Add i18n keys for setAsActive, readDocLink, securityTip
This commit is contained in:
parent
9eafe982ee
commit
a81d0327d2
@ -2,7 +2,7 @@
|
||||
|
||||
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
|
||||
import type { SandboxProvider } from '@/service/use-sandbox-provider'
|
||||
import { RiExternalLinkLine } from '@remixicon/react'
|
||||
import { RiExternalLinkLine, RiLock2Fill } from '@remixicon/react'
|
||||
import { memo, useCallback, useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
@ -22,6 +22,27 @@ type ConfigModalProps = {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const PROVIDER_ICONS: Record<string, string> = {
|
||||
e2b: '/sandbox-providers/e2b.svg',
|
||||
daytona: '/sandbox-providers/daytona.svg',
|
||||
docker: '/sandbox-providers/docker.svg',
|
||||
local: '/sandbox-providers/local.svg',
|
||||
}
|
||||
|
||||
const ProviderIcon = ({ providerType }: { providerType: string }) => {
|
||||
const iconSrc = PROVIDER_ICONS[providerType] || PROVIDER_ICONS.e2b
|
||||
|
||||
return (
|
||||
<div className="flex h-4 w-4 shrink-0 items-center justify-center text-clip rounded border-[0.5px] border-divider-subtle">
|
||||
<img
|
||||
src={iconSrc}
|
||||
alt={`${providerType} icon`}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ConfigModal = ({
|
||||
provider,
|
||||
onClose,
|
||||
@ -91,54 +112,78 @@ const ConfigModal = ({
|
||||
<Modal
|
||||
isShow
|
||||
onClose={onClose}
|
||||
title={t('sandboxProvider.configModal.title', { ns: 'common', provider: provider.label })}
|
||||
title={t('sandboxProvider.configModal.title', { ns: 'common' })}
|
||||
closable
|
||||
className="w-[480px]"
|
||||
>
|
||||
<div className="mt-4">
|
||||
<BaseForm
|
||||
formSchemas={formSchemas}
|
||||
ref={formRef}
|
||||
labelClassName="system-sm-semibold mb-1 flex items-center gap-1 text-text-secondary"
|
||||
formClassName="space-y-4"
|
||||
/>
|
||||
{/* Provider subtitle */}
|
||||
<div className="-mt-2 mb-4 flex items-center gap-2">
|
||||
<ProviderIcon providerType={provider.provider_type} />
|
||||
<span className="system-md-regular text-text-secondary">{provider.label}</span>
|
||||
</div>
|
||||
|
||||
{/* Footer Actions */}
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<div>
|
||||
{docLink && (
|
||||
<a
|
||||
href={docLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="system-sm-medium inline-flex items-center gap-1 text-text-accent hover:underline"
|
||||
>
|
||||
{t('sandboxProvider.configModal.readDoc', { ns: 'common' })}
|
||||
<RiExternalLinkLine className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{isConfigured && (
|
||||
<Button
|
||||
variant="warning"
|
||||
size="medium"
|
||||
onClick={handleRevoke}
|
||||
disabled={isDeleting || isSaving}
|
||||
>
|
||||
{t('sandboxProvider.configModal.revoke', { ns: 'common' })}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="primary"
|
||||
size="medium"
|
||||
onClick={handleSave}
|
||||
disabled={isSaving || isDeleting}
|
||||
<BaseForm
|
||||
formSchemas={formSchemas}
|
||||
ref={formRef}
|
||||
labelClassName="system-sm-medium mb-1 flex items-center gap-1 text-text-secondary"
|
||||
formClassName="space-y-4"
|
||||
/>
|
||||
|
||||
{/* Footer Actions */}
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<div>
|
||||
{docLink && (
|
||||
<a
|
||||
href={docLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="system-xs-regular inline-flex items-center gap-1 text-text-accent hover:underline"
|
||||
>
|
||||
{t('sandboxProvider.configModal.confirm', { ns: 'common' })}
|
||||
</Button>
|
||||
</div>
|
||||
{t('sandboxProvider.configModal.readDocLink', { ns: 'common', provider: provider.label })}
|
||||
<RiExternalLinkLine className="h-3 w-3" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{isConfigured && (
|
||||
<Button
|
||||
variant="warning"
|
||||
size="medium"
|
||||
onClick={handleRevoke}
|
||||
disabled={isDeleting || isSaving}
|
||||
>
|
||||
{t('sandboxProvider.configModal.revoke', { ns: 'common' })}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
onClick={onClose}
|
||||
disabled={isSaving || isDeleting}
|
||||
>
|
||||
{t('sandboxProvider.configModal.cancel', { ns: 'common' })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="medium"
|
||||
onClick={handleSave}
|
||||
disabled={isSaving || isDeleting}
|
||||
>
|
||||
{t('sandboxProvider.configModal.save', { ns: 'common' })}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Security tip */}
|
||||
<div className="-mx-6 -mb-6 mt-4 flex items-start justify-center gap-1 rounded-b-2xl border-t border-divider-subtle bg-background-soft px-2 py-3">
|
||||
<RiLock2Fill className="h-3 w-3 shrink-0 text-text-primary" />
|
||||
<p className="system-xs-regular text-text-tertiary">
|
||||
{t('sandboxProvider.configModal.securityTip', { ns: 'common' })}
|
||||
{' '}
|
||||
<span className="text-text-accent">PKCS1_OAEP</span>
|
||||
{' '}
|
||||
{t('sandboxProvider.configModal.securityTipTechnology', { ns: 'common' })}
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -41,7 +41,7 @@ const SandboxProviderPage = () => {
|
||||
{/* Current Provider Section */}
|
||||
{currentProvider && (
|
||||
<div>
|
||||
<div className="system-sm-semibold mb-2 text-text-secondary">
|
||||
<div className="system-sm-semibold-uppercase mb-2 text-text-secondary">
|
||||
{t('sandboxProvider.currentProvider', { ns: 'common' })}
|
||||
</div>
|
||||
<ProviderCard
|
||||
@ -56,7 +56,7 @@ const SandboxProviderPage = () => {
|
||||
{/* Other Providers Section */}
|
||||
{otherProviders.length > 0 && (
|
||||
<div>
|
||||
<div className="system-sm-semibold mb-2 text-text-secondary">
|
||||
<div className="system-sm-semibold-uppercase mb-2 text-text-secondary">
|
||||
{t('sandboxProvider.otherProvider', { ns: 'common' })}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { SandboxProvider } from '@/service/use-sandbox-provider'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
@ -17,6 +18,7 @@ type ProviderCardProps = {
|
||||
|
||||
const PROVIDER_ICONS: Record<string, string> = {
|
||||
e2b: '/sandbox-providers/e2b.svg',
|
||||
daytona: '/sandbox-providers/daytona.svg',
|
||||
docker: '/sandbox-providers/docker.svg',
|
||||
local: '/sandbox-providers/local.svg',
|
||||
}
|
||||
@ -28,7 +30,7 @@ const ProviderIcon = ({ providerType }: { providerType: string }) => {
|
||||
<img
|
||||
src={iconSrc}
|
||||
alt={`${providerType} icon`}
|
||||
className="h-5 w-5"
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -47,50 +49,62 @@ const ProviderCard = ({
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'flex items-center justify-between rounded-xl p-4',
|
||||
'flex items-center gap-3 rounded-[15px] py-3 pl-3 pr-4',
|
||||
'border-[0.5px] border-components-panel-border shadow-xs',
|
||||
isCurrent ? 'bg-background-section' : 'bg-background-section-burn',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{/* Icon */}
|
||||
<div className="mr-3 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-divider-subtle bg-background-default-subtle">
|
||||
<ProviderIcon providerType={provider.provider_type} />
|
||||
</div>
|
||||
{/* Icon - 40x40 with 10px rounded */}
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center text-clip rounded-[10px] border-[0.5px] border-divider-subtle bg-background-default-subtle">
|
||||
<ProviderIcon providerType={provider.provider_type} />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="system-md-semibold text-text-primary">
|
||||
{provider.label}
|
||||
{/* Content */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="system-md-semibold text-text-primary">
|
||||
{provider.label}
|
||||
</span>
|
||||
{provider.is_system_configured && (
|
||||
<span className="system-2xs-medium rounded-[5px] border border-divider-deep px-[5px] py-[3px] text-text-tertiary">
|
||||
{t('sandboxProvider.managedBySaas', { ns: 'common' })}
|
||||
</span>
|
||||
{provider.is_system_configured && (
|
||||
<span className="system-2xs-medium-uppercase rounded border border-divider-regular px-1.5 py-0.5 text-text-tertiary">
|
||||
{t('sandboxProvider.managedBySaas', { ns: 'common' })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{provider.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{provider.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right side: Connected Badge + Actions */}
|
||||
{/* Right side: Connected Badge + Divider + Settings Icon + Enable Button */}
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
{/* Connected Status */}
|
||||
{isConfigured && (
|
||||
<span className="system-xs-medium flex items-center gap-1 rounded-md bg-util-colors-green-green-50 px-1.5 py-0.5 text-util-colors-green-green-600">
|
||||
<span className="flex items-center gap-1">
|
||||
<Indicator color="green" />
|
||||
{t('sandboxProvider.connected', { ns: 'common' })}
|
||||
<span className="system-xs-semibold-uppercase text-util-colors-green-green-600">
|
||||
{t('sandboxProvider.connected', { ns: 'common' })}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
|
||||
{/* Divider */}
|
||||
{isConfigured && (
|
||||
<div className="pl-1">
|
||||
<div className="h-3 w-px bg-divider-regular" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Settings Icon Button */}
|
||||
<button
|
||||
onClick={onConfig}
|
||||
disabled={disabled}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{t('sandboxProvider.config', { ns: 'common' })}
|
||||
</Button>
|
||||
<RiEqualizer2Line className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{/* Set as Active Button */}
|
||||
{showEnableButton && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
@ -98,7 +112,7 @@ const ProviderCard = ({
|
||||
onClick={onEnable}
|
||||
disabled={disabled}
|
||||
>
|
||||
{t('sandboxProvider.enable', { ns: 'common' })}
|
||||
{t('sandboxProvider.setAsActive', { ns: 'common' })}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -79,7 +79,7 @@ const SwitchModal = ({
|
||||
{t('sandboxProvider.switchModal.cancel', { ns: 'common' })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
variant="warning"
|
||||
size="medium"
|
||||
onClick={handleConfirm}
|
||||
disabled={isPending}
|
||||
|
||||
@ -571,11 +571,14 @@
|
||||
"sandboxProvider.configModal.e2bTemplate": "E2B Template",
|
||||
"sandboxProvider.configModal.e2bTemplatePlaceholder": "code-interpreter-v1",
|
||||
"sandboxProvider.configModal.readDoc": "Read Documentation",
|
||||
"sandboxProvider.configModal.readDocLink": "Read {{provider}} Docs",
|
||||
"sandboxProvider.configModal.revoke": "Revoke",
|
||||
"sandboxProvider.configModal.save": "Save",
|
||||
"sandboxProvider.configModal.title": "Configure - {{provider}}",
|
||||
"sandboxProvider.connected": "Connected",
|
||||
"sandboxProvider.currentProvider": "Current Provider",
|
||||
"sandboxProvider.configModal.securityTip": "Your API Token will be encrypted and stored using",
|
||||
"sandboxProvider.configModal.securityTipTechnology": "technology.",
|
||||
"sandboxProvider.configModal.title": "Configure Sandbox Provider",
|
||||
"sandboxProvider.connected": "CONNECTED",
|
||||
"sandboxProvider.currentProvider": "CURRENT ACTIVE",
|
||||
"sandboxProvider.daytona.description": "Secure sandboxed cloud environments for AI agents. learn more",
|
||||
"sandboxProvider.daytona.label": "Daytona",
|
||||
"sandboxProvider.docker.description": "Secure sandboxed cloud environments for AI agents. learn more",
|
||||
@ -588,9 +591,10 @@
|
||||
"sandboxProvider.managedBySaas": "Managed by SaaS",
|
||||
"sandboxProvider.noPermission": "Contact the workspace administrator to make changes.",
|
||||
"sandboxProvider.notConfigured": "Not Configured",
|
||||
"sandboxProvider.otherProvider": "Other Provider",
|
||||
"sandboxProvider.otherProvider": "OTHER PROVIDERS",
|
||||
"sandboxProvider.setAsActive": "Set as Active",
|
||||
"sandboxProvider.switchModal.cancel": "Cancel",
|
||||
"sandboxProvider.switchModal.confirm": "Switch Provider",
|
||||
"sandboxProvider.switchModal.confirm": "Switch",
|
||||
"sandboxProvider.switchModal.confirmText": "You are about to switch the active sandbox provider to {{provider}}.",
|
||||
"sandboxProvider.switchModal.title": "Switch Active Provider?",
|
||||
"sandboxProvider.switchModal.warning": "Impact on running agents",
|
||||
|
||||
@ -569,11 +569,14 @@
|
||||
"sandboxProvider.configModal.e2bTemplate": "E2B 模板",
|
||||
"sandboxProvider.configModal.e2bTemplatePlaceholder": "code-interpreter-v1",
|
||||
"sandboxProvider.configModal.readDoc": "查看文档",
|
||||
"sandboxProvider.configModal.readDocLink": "查看 {{provider}} 文档",
|
||||
"sandboxProvider.configModal.revoke": "撤销",
|
||||
"sandboxProvider.configModal.save": "保存",
|
||||
"sandboxProvider.configModal.title": "配置 - {{provider}}",
|
||||
"sandboxProvider.configModal.securityTip": "您的 API Token 将使用",
|
||||
"sandboxProvider.configModal.securityTipTechnology": "技术加密存储。",
|
||||
"sandboxProvider.configModal.title": "配置 Sandbox Provider",
|
||||
"sandboxProvider.connected": "已连接",
|
||||
"sandboxProvider.currentProvider": "当前供应商",
|
||||
"sandboxProvider.currentProvider": "当前激活",
|
||||
"sandboxProvider.daytona.description": "为 AI 代理提供安全的沙箱云环境。了解更多",
|
||||
"sandboxProvider.daytona.label": "Daytona",
|
||||
"sandboxProvider.docker.description": "为 AI 代理提供安全的沙箱云环境。了解更多",
|
||||
@ -587,8 +590,9 @@
|
||||
"sandboxProvider.noPermission": "请联系工作空间管理员进行更改。",
|
||||
"sandboxProvider.notConfigured": "未配置",
|
||||
"sandboxProvider.otherProvider": "其他供应商",
|
||||
"sandboxProvider.setAsActive": "设为激活",
|
||||
"sandboxProvider.switchModal.cancel": "取消",
|
||||
"sandboxProvider.switchModal.confirm": "切换供应商",
|
||||
"sandboxProvider.switchModal.confirm": "切换",
|
||||
"sandboxProvider.switchModal.confirmText": "您即将将活动沙箱供应商切换为 {{provider}}。",
|
||||
"sandboxProvider.switchModal.title": "切换活动供应商?",
|
||||
"sandboxProvider.switchModal.warning": "对运行中的代理的影响",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user