/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import React, { useState, useEffect, useRef } from 'react'; import { SideSheet, Form, Button, Space, Spin, Typography, Card, InputNumber, Select, Input, Row, Col, Divider, Tag, } from '@douyinfe/semi-ui'; import { Save, X, Server } from 'lucide-react'; import { API, showError, showSuccess } from '../../../../helpers'; import { useTranslation } from 'react-i18next'; import { useIsMobile } from '../../../../hooks/common/useIsMobile'; const { Text, Title } = Typography; const EditDeploymentModal = ({ refresh, editingDeployment, visible, handleClose, }) => { const { t } = useTranslation(); const isMobile = useIsMobile(); const [loading, setLoading] = useState(false); const [models, setModels] = useState([]); const [loadingModels, setLoadingModels] = useState(false); const formRef = useRef(); const isEdit = Boolean(editingDeployment?.id); const title = t('重命名部署'); // Resource configuration options const cpuOptions = [ { label: '0.5 Core', value: '0.5' }, { label: '1 Core', value: '1' }, { label: '2 Cores', value: '2' }, { label: '4 Cores', value: '4' }, { label: '8 Cores', value: '8' }, ]; const memoryOptions = [ { label: '1GB', value: '1Gi' }, { label: '2GB', value: '2Gi' }, { label: '4GB', value: '4Gi' }, { label: '8GB', value: '8Gi' }, { label: '16GB', value: '16Gi' }, { label: '32GB', value: '32Gi' }, ]; const gpuOptions = [ { label: t('无GPU'), value: '' }, { label: '1 GPU', value: '1' }, { label: '2 GPUs', value: '2' }, { label: '4 GPUs', value: '4' }, ]; // Load available models const loadModels = async () => { setLoadingModels(true); try { const res = await API.get('/api/models/?page_size=1000'); if (res.data.success) { const items = res.data.data.items || res.data.data || []; const modelOptions = items.map((model) => ({ label: `${model.model_name} (${model.vendor?.name || 'Unknown'})`, value: model.model_name, model_id: model.id, })); setModels(modelOptions); } } catch (error) { console.error('Failed to load models:', error); showError(t('加载模型列表失败')); } setLoadingModels(false); }; // Form submission const handleSubmit = async (values) => { if (!isEdit || !editingDeployment?.id) { showError(t('无效的部署信息')); return; } setLoading(true); try { // Only handle name update for now const res = await API.put( `/api/deployments/${editingDeployment.id}/name`, { name: values.deployment_name, }, ); if (res.data.success) { showSuccess(t('部署名称更新成功')); handleClose(); refresh(); } else { showError(res.data.message || t('更新失败')); } } catch (error) { console.error('Submit error:', error); showError(t('更新失败,请检查输入信息')); } setLoading(false); }; // Load models when modal opens useEffect(() => { if (visible) { loadModels(); } }, [visible]); // Set form values when editing useEffect(() => { if (formRef.current && editingDeployment && visible && isEdit) { formRef.current.setValues({ deployment_name: editingDeployment.deployment_name || '', }); } }, [editingDeployment, visible, isEdit]); return ( {title} } visible={visible} onCancel={handleClose} width={isMobile ? '100%' : 600} bodyStyle={{ padding: 0 }} maskClosable={false} closeOnEsc={true} > {t('修改部署名称')} {isEdit && ( {t('部署ID')}: {editingDeployment.id} {t('当前状态')}: {editingDeployment.status} )} {t('取消')} formRef.current?.submitForm()} > {isEdit ? t('更新') : t('创建')} ); }; export default EditDeploymentModal;