{
const user = getCurrentUser()
const params = useParams()
const history = useHistory()
+ const location = useLocation()
+ const copyFromProjectURL = React.useMemo(() => {
+ const queryParams = new URLSearchParams(location.search)
+ return queryParams.get('copyFrom')
+ }, [location.search])
+
const bridgeRef = React.useRef()
const facetsRequestsRef = React.useRef({})
const latestFacetRequestRef = React.useRef({})
@@ -304,10 +310,15 @@ const MapProject = () => {
React.useEffect(() => {
fetchMapTypes()
+ fetchAIModels()
+
if(params.projectId && params.owner) {
fetchAndSetProject()
+ return
+ }
+ if(copyFromProjectURL) {
+ copyFromProject()
}
- fetchAIModels()
}, [])
React.useEffect(() => {
@@ -324,6 +335,27 @@ const MapProject = () => {
}
}, [repoVersion, project])
+
+ const copyFromProject = () => {
+ setLoadingProject(true)
+ APIService.new().overrideURL(copyFromProjectURL).appendToUrl('configurations/').get().then(response => {
+ const copiedProject = response.data
+ setProject(null)
+ setFilters(copiedProject.filters || {})
+ setLookupConfig(copiedProject.lookup_config || {})
+ setCandidatesScore(copiedProject.score_configuration || {recommended: 99, available: 70})
+ setRetired(Boolean(copiedProject.include_retired || false))
+ setAlgosSelected(copiedProject.algorithms || [])
+ setEncoderModel(copiedProject.encoder_model || DEFAULT_ENCODER_MODEL)
+ if(copiedProject.target_repo_url) {
+ const repoParams = URIToParentParams(copiedProject.target_repo_url, true)
+ fetchRepo(dropVersion(copiedProject.target_repo_url))
+ fetchVersions(copiedProject.target_repo_url, repoParams?.repoVersion || 'HEAD')
+ }
+ setConfigure(true)
+ }).finally(() => setLoadingProject(false))
+ }
+
const fetchAndSetProject = () => {
setLoadingProject(true)
let url = ['', params.ownerType, params.owner, 'map-projects', params.projectId, ''].join('/')
@@ -2707,6 +2739,15 @@ const MapProject = () => {
/>
)
+
+ const onCopyClick = event => {
+ event.preventDefault()
+ event.stopPropagation()
+ if(project?.url) {
+ window.open(`/#/map-projects/new?copyFrom=${encodeURIComponent(project.url)}`, '_blank', 'noopener,noreferrer')
+ }
+ }
+
return permissionDenied ? : (
{
@@ -2860,6 +2901,7 @@ const MapProject = () => {
isProjectsLogOpen={showProjectLogs}
configure={configure}
setConfigure={setConfigure}
+ onCopyClick={onCopyClick}
/>
}
diff --git a/src/components/map-projects/MapProjects.jsx b/src/components/map-projects/MapProjects.jsx
index f511823..76bc7a8 100644
--- a/src/components/map-projects/MapProjects.jsx
+++ b/src/components/map-projects/MapProjects.jsx
@@ -1,4 +1,7 @@
import React from 'react'
+import { useHistory } from 'react-router-dom'
+import { useTranslation } from 'react-i18next';
+
import moment from 'moment'
import reject from 'lodash/reject'
import Button from '@mui/material/Button'
@@ -12,6 +15,7 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Skeleton from '@mui/material/Skeleton';
import ListItemText from '@mui/material/ListItemText'
+import Tooltip from '@mui/material/Tooltip'
import AddIcon from '@mui/icons-material/Add'
import times from 'lodash/times'
import APIService from '../../services/APIService'
@@ -19,14 +23,16 @@ import { getCurrentUser } from '../../common/utils'
import OwnerIcon from '../common/OwnerIcon'
import NoResults from '../search/NoResults';
import MapProjectDeleteConfirmDialog from './MapProjectDeleteConfirmDialog';
-import { useTranslation } from 'react-i18next';
const MapProjects = () => {
const { t } = useTranslation();
+ const history = useHistory()
+
const user = getCurrentUser()
const [loading, setLoading] = React.useState([])
const [projects, setProjects] = React.useState([])
const [deleteProject, setDeleteProject] = React.useState(null)
+
const fetchProjects = () => {
fetchUserProjects()
fetchOrgProjects()
@@ -53,6 +59,14 @@ const MapProjects = () => {
setDeleteProject(null)
}
+ const onCopyClick = (event, project) => {
+ event.preventDefault()
+ event.stopPropagation()
+ if(project?.url) {
+ history.push(`/map-projects/new?copyFrom=${encodeURIComponent(project.url)}`)
+ }
+ }
+
const isSplitView = false
const loaded = loading.length === 2
return (
@@ -141,10 +155,15 @@ const MapProjects = () => {
-
diff --git a/src/i18n/locales/en/translations.json b/src/i18n/locales/en/translations.json
index a05bc05..a23c37e 100644
--- a/src/i18n/locales/en/translations.json
+++ b/src/i18n/locales/en/translations.json
@@ -83,6 +83,7 @@
"draft": "Draft",
"highlights": "Highlights",
"click_to_copy": "Click to Copy",
+ "copy": "Copy",
"submit": "Submit",
"external_id": "External ID",
"access_level": "Access Level",
@@ -589,7 +590,8 @@
"alternates": "Alternates",
"model": "Model",
"bridge_source_url": "Bridge Source URL",
- "bridge_source_url_description": "The interface terminology to search through for bridge matching"
+ "bridge_source_url_description": "The interface terminology to search through for bridge matching",
+ "copy_project": "Creates a new project by copying this project's configuration only. No candidates, decisions, or input files will be copied."
},
"app": {
"web_version": "Web Version",
diff --git a/src/i18n/locales/es/translations.json b/src/i18n/locales/es/translations.json
index b0029b9..2c91027 100644
--- a/src/i18n/locales/es/translations.json
+++ b/src/i18n/locales/es/translations.json
@@ -91,6 +91,7 @@
"draft": "Borrador",
"highlights": "Destacados",
"click_to_copy": "Clic para copiar",
+ "copy": "Copiar",
"submit": "Enviar",
"external_id": "ID externo",
"access_level": "Nivel de acceso",
@@ -564,7 +565,8 @@
"alternates": "Alternativas",
"model": "Modelo",
"bridge_source_url": "URL de la fuente de Bridge",
- "bridge_source_url_description": "La terminología de interfaz para buscar candidatos de coincidencia"
+ "bridge_source_url_description": "La terminología de interfaz para buscar candidatos de coincidencia",
+ "copy_project": "Crea un nuevo proyecto copiando solo la configuración de este proyecto. No se copiarán candidatos, decisiones ni archivos de entrada."
},
"app": {
"web_version": "Versión Web",
diff --git a/src/i18n/locales/zh/translations.json b/src/i18n/locales/zh/translations.json
index b9dcf20..c1e2b48 100644
--- a/src/i18n/locales/zh/translations.json
+++ b/src/i18n/locales/zh/translations.json
@@ -82,6 +82,7 @@
"draft": "草案",
"highlights": "亮点",
"click_to_copy": "单击复制",
+ "copy": "复制",
"submit": "提交",
"external_id": "外部 ID",
"access_level": "访问权限级别",
@@ -589,7 +590,8 @@
"alternates": "备选",
"model": "模型",
"bridge_source_url": "桥接源 URL",
- "bridge_source_url_description": "用于搜索匹配候选对象的接口术语"
+ "bridge_source_url_description": "用于搜索匹配候选对象的接口术语",
+ "copy_project": "通过仅复制此项目的配置来创建一个新项目。不会复制候选项、决策或输入文件。"
},
"app": {
"web_version": "Web 版本",