diff --git a/config-ui/src/api/scope-config/index.ts b/config-ui/src/api/scope-config/index.ts index caaa789d762..41547d9c058 100644 --- a/config-ui/src/api/scope-config/index.ts +++ b/config-ui/src/api/scope-config/index.ts @@ -18,6 +18,8 @@ import { request } from '@/utils'; +import { ICheck } from './types'; + export const list = (plugin: string, connectionId: ID) => request(`/plugins/${plugin}/connections/${connectionId}/scope-configs`); @@ -35,3 +37,6 @@ export const update = (plugin: string, connectionId: ID, id: ID, data: any) => method: 'patch', data, }); + +export const check = (plugin: string, id: ID): Promise => + request(`/plugins/${plugin}/scope-config/${id}/projects`); diff --git a/config-ui/src/api/scope-config/types.ts b/config-ui/src/api/scope-config/types.ts new file mode 100644 index 00000000000..4c779c6589b --- /dev/null +++ b/config-ui/src/api/scope-config/types.ts @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export type ICheck = { + count: number; + projects: Array<{ + name: string; + }>; +}; diff --git a/config-ui/src/plugins/components/scope-config/index.tsx b/config-ui/src/plugins/components/scope-config/index.tsx index 7dd5b7e058e..0bc83870db1 100644 --- a/config-ui/src/plugins/components/scope-config/index.tsx +++ b/config-ui/src/plugins/components/scope-config/index.tsx @@ -18,10 +18,11 @@ import { useState } from 'react'; import { LinkOutlined, EditOutlined } from '@ant-design/icons'; -import { Button, Modal } from 'antd'; +import { theme, Button, Modal, Flex, Space } from 'antd'; import styled from 'styled-components'; import API from '@/api'; +import { Message } from '@/components'; import { operator } from '@/utils'; import { PluginName } from '../plugin-name'; @@ -34,16 +35,39 @@ interface Props { plugin: string; connectionId: ID; scopeId: ID; + scopeName: string; id?: ID; name?: string; - onSuccess?: () => void; + onSuccess?: (id?: ID) => void; } -export const ScopeConfig = ({ plugin, connectionId, scopeId, id, name, onSuccess }: Props) => { - const [type, setType] = useState<'associate' | 'update'>(); +export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name, onSuccess }: Props) => { + const [type, setType] = useState<'associate' | 'update' | 'relatedProjects'>(); + const [relatedProjects, setRelatedProjects] = useState>([]); + + const { + token: { colorPrimary }, + } = theme.useToken(); const handleHideDialog = () => setType(undefined); + const handleCheckScopeConfig = async () => { + if (!id) return; + + const [success, res] = await operator(() => API.scopeConfig.check(plugin, id), { hideToast: true }); + + if (success) { + const projects = res.projects.map((it: any) => ({ name: it.name, scopes: [] })); + + if (projects.length !== 1) { + setRelatedProjects(projects); + setType('relatedProjects'); + } else { + setType('update'); + } + } + }; + const handleAssociate = async (trId: ID) => { const [success] = await operator( () => API.scope.update(plugin, connectionId, scopeId, { scopeConfigId: trId !== 'None' ? +trId : null }), @@ -54,13 +78,13 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, id, name, onSuccess if (success) { handleHideDialog(); - onSuccess?.(); + onSuccess?.(id); } }; const handleUpdate = (trId: ID) => { handleHideDialog(); - onSuccess?.(); + onSuccess?.(id); }; return ( @@ -74,17 +98,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, id, name, onSuccess setType('associate'); }} /> - {id && ( - + + + + + )} ); }; diff --git a/config-ui/src/routes/blueprint/connection-detail/index.tsx b/config-ui/src/routes/blueprint/connection-detail/index.tsx index b686d9e815f..08c8d8a06aa 100644 --- a/config-ui/src/routes/blueprint/connection-detail/index.tsx +++ b/config-ui/src/routes/blueprint/connection-detail/index.tsx @@ -263,11 +263,12 @@ export const BlueprintConnectionDetailPage = () => { { title: 'Scope Config', key: 'scopeConfig', - render: (_, { id, scopeConfigId, scopeConfigName }) => ( + render: (_, { id, name, scopeConfigId, scopeConfigName }) => ( { token: { colorPrimary }, } = theme.useToken(); + const [modal, contextHolder] = Modal.useModal(); + const dispatch = useAppDispatch(); const connection = useAppSelector((state) => selectConnection(state, `${plugin}-${connectionId}`)) as IConnection; const navigate = useNavigate(); + const { ready, data } = useRefreshData( () => API.scope.list(plugin, connectionId, { page, pageSize, blueprints: true }), [version, page, pageSize], @@ -227,9 +230,46 @@ export const Connection = () => { } }; - const handleScopeConfigChange = () => { - // TO-DO: check scope config change will effect the scope config - setVersion(version + 1); + const handleScopeConfigChange = async (scopeConfigId?: ID) => { + if (!scopeConfigId) { + return; + } + + const [success, res] = await operator(() => API.scopeConfig.check(plugin, scopeConfigId), { hideToast: true }); + + if (success) { + modal.success({ + closable: true, + centered: true, + width: 830, + title: 'Scope Config Saved', + content: ( + <> +
+ The listed projects are impacted. Please re-transform the data to apply the updated scope config. +
+
    + {res.projects.map((it: any) => ( +
  • + + {it.name} + + +
  • + ))} +
+ + ), + footer: null, + onCancel: () => setVersion(version + 1), + }); + } }; return ( @@ -305,11 +345,12 @@ export const Connection = () => { title: 'Scope Config', key: 'scopeConfig', width: 400, - render: (_, { id, configId, configName }) => ( + render: (_, { id, name, configId, configName }) => ( { )} )} + {contextHolder} ); };