/* * @Author: ZhaoYing * @Date: 2026-03-13 17:19:13 * @Last Modified by: ZhaoYing * @Last Modified time: 2026-03-18 16:03:46 */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Checkbox, App, Form } from 'antd'; import { useTranslation } from 'react-i18next'; import RbModal from '@/components/RbModal'; import { appSharing, getAppShares } from '@/api/application'; import { formatDateTime } from '@/utils/format'; import type { AppSharingModalRef, Release } from '../types'; import type { SpaceItem } from '@/views/KnowledgeBase/types'; import { getWorkspaces } from '@/api/workspaces'; import RadioGroupCard from '@/components/RadioGroupCard'; /** Props for the AppSharingModal component */ interface AppSharingModalProps { /** ID of the application being shared */ appId: string; /** The release version to share */ version: Release | null; } const AppSharingModal = forwardRef(({ appId, version }, ref) => { const { t } = useTranslation(); const { message } = App.useApp(); const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false); // All workspaces available to share with (excluding the current one) const [spaceList, setSpaceList] = useState([]); // IDs of workspaces that already have access to this app const [sharedIds, setSharedIds] = useState([]); const [form] = Form.useForm<{ target_workspace_ids: string[]; permission: 'readonly' | 'editable' }>(); // Reactively track the currently selected workspace IDs in the form const selectedIds: string[] = Form.useWatch('target_workspace_ids', form) ?? []; /** * Fetch workspaces and existing share records in parallel, * sort already-shared spaces to the top, then open the modal. * Shows a warning if the user has no shareable workspaces. */ const handleOpen = () => { Promise.all([getWorkspaces({ include_current: false }), getAppShares(appId)]).then(([spaces, shared]) => { // Normalise the shared workspace ID field across different API response shapes const ids = ((shared as any[]) || []).map((s: any) => s.workspace_id || s.target_workspace_id || s.id); // Sort: already-shared workspaces appear first const sorted = (spaces as SpaceItem[]).sort((a, b) => ids.includes(b.id) ? 1 : ids.includes(a.id) ? -1 : 0 ); setSpaceList(sorted); setSharedIds(ids); if (sorted.length > 0) { setVisible(true); } else { message.warning(t('application.noShareAuth')); } }); }; /** Close the modal and reset form fields */ const handleClose = () => { setVisible(false); form.resetFields(); }; // Expose open/close handlers to the parent via ref useImperativeHandle(ref, () => ({ handleOpen, handleClose })); /** * Toggle a workspace in the selected list. * Already-shared workspaces are read-only and cannot be toggled. */ const handleToggle = (id: string, isShared: boolean) => { if (isShared) return; const prev: string[] = form.getFieldValue('target_workspace_ids') ?? []; form.setFieldValue( 'target_workspace_ids', prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id] ); }; /** Validate the form then submit the sharing request */ const handleConfirm = () => { form.validateFields().then(values => { setLoading(true); appSharing(appId, values) .then(() => { message.success(t('common.operateSuccess')); handleClose(); }) .finally(() => setLoading(false)); }); }; // Normalise the version label to always start with "v" const versionLabel = version?.version_name ? (version.version_name[0].toLowerCase() === 'v' ? version.version_name : `v${version.version_name}`) : `v${version?.version}`; return ( {t('application.confirmSharing')}({selectedIds.length})} onOk={handleConfirm} confirmLoading={loading} width={600} >
{/* Version info: displays version number, release time and publisher */}
{t('application.VersionInformation')}
{t('application.versionList').replace('列表', '号')}
{versionLabel}
{t('application.releaseTime')}
{formatDateTime(version?.published_at || 0, 'YYYY-MM-DD HH:mm:ss')}
{t('application.publisher')}
{version?.publisher_name}
{/* Target space: scrollable list of workspaces with checkbox selection */}
{spaceList.map(space => { const isShared = sharedIds.includes(space.id); return (
handleToggle(space.id, isShared)}> e.stopPropagation()} onChange={() => handleToggle(space.id, isShared)} /> {space.name} {isShared && ( {t('application.alreadyShared')} )}
); })}
{/* Permission mode: readonly (use only) or editable (full copy) */} ({ value: type, label: t(`application.${type}Mode`), labelDesc: t(`application.${type}ModeDesc`), }))} />
); }); export default AppSharingModal;