Commit 85b09001 authored by Jean Rabreau's avatar Jean Rabreau
Browse files

馃帹 update itemDuplicate action for partial copy into other set

parent 0564fc02
......@@ -234,12 +234,15 @@ export const en_US = {
item: {
backToSet: "Back to set",
copy: {
toSet: "Into \"{title}\" set",
move: "Move @:item.designate",
duplicate: "Create a copy",
title: "Copy @:item.designate",
here: "Copy here",
inProject: "Choose set",
projectsLoad: "Load other projets"
projectsLoad: "Load other projets",
prefix: "Copy of ",
},
copyOf: "Copy of",
count: "No item | 1 item | {count} items",
create: "Add item",
submitted: "Submitted by:",
......
......@@ -241,12 +241,15 @@ export const fr_FR = {
item: {
backToSet: "Revenir 脿 l'ensemble",
copy: {
toSet: "Vers le set \"{title}\"",
move: "D茅placer @:item.designate",
duplicate: "Cr茅er une copie",
title: "Copier @:item.designate",
here: "Copier ici",
inProject: "Choisir l'ensemble",
projectsLoad: "Charger les autres projets"
projectsLoad: "Charger les autres projets",
prefix: "Copie de ",
},
copyOf: "Copie de",
count: "Aucun 茅l茅ment | 1 茅l茅ment | {count} 茅l茅ments",
create: "Nouvel 茅l茅ment",
submitted: "D茅pos茅 par :",
......
......@@ -36,7 +36,7 @@ import {
* @typedef {Object} MetaData
* @property {string} id
* @property {string} name
* @property {string|string[]} value
* @property {string|string[]|Array<string[]>} value
* @property {boolean} multiple
* @property {boolean} isOption
* @property {boolean} repeatable
......@@ -143,6 +143,7 @@ const Item = {
getters: {
current: state => state.current,
itemId: state => state.current.id,
set: state => state.current.set,
templateId: state => state.current.set.templateId,
projectId: state => state.current.set.projectId,
canEdit: state => state.current.canEdit,
......@@ -334,20 +335,25 @@ const Item = {
* changing its title and resetting associated files
*
* @param context
* @param {Object} [target]
* @param {string} target.setId
* @param {Metadata} target.metadata
* @returns {Promise<void | *>}
*/
itemDuplicate({commit, dispatch, getters}) {
const {title: oldTitle, description, metadata, set: {id: setId}} = getters['current']
const prefix = i18n.t('item.copyOf')
const title = `${prefix} ${oldTitle}`
itemDuplicate({commit, dispatch, getters}, target) {
const item = getters['current']
const {title: oldTitle, description} = item
const setId = target ? target.setId : item.set.id
const metadata = target
? target.metadata
: item.metadata.map(d => d.name === 'title'
? { ...d, value: title}
: d)
const title = `${i18n.t('item.copyOf')}${oldTitle}`
const payload = {
title,
description,
metadata: metadata.map(
d => d.name === 'title'
? { ...d, value: title}
: d
),
metadata,
viewer: {},
setId,
}
......
......@@ -14,6 +14,7 @@ import {
* @property {string} id
* @property {string} title
* @property {SetCreator} creator
* @property {SetCreator} creator
*/
/**
......
......@@ -3,24 +3,32 @@
<v-card>
<v-card-title>
{{ $t('item.copy.title') }}
<v-spacer/>
<v-btn
tile
elevation="0"
@click="copyHere"
>
<v-icon color="secondary" left>mdi-content-copy</v-icon>
{{ $t('item.copy.here' )}}
</v-btn>
</v-card-title>
<v-card-text>
<v-treeview
open-on-click
:open="openProjects"
:items="projectsTree"
:load-children="addSets"
:load-children="extendTree"
>
<template v-slot:label="{item}">
<p v-if="item.level" @click="analyseForCopy(item.id, item.templateId)">
<p v-if="item.level > 0"
@click="analyseForCopy(item)"
>
<v-icon left color="secondary">
mdi-content-copy
</v-icon>
{{ item.title }}
</p>
<p v-else-if="item.id === 'next'" @click="addProjects">
{{ $t('item.copy.projectsLoad')}}
</p>
<p v-else>
{{ item.title }}
</p>
......@@ -28,26 +36,49 @@
</v-treeview>
</v-card-text>
<v-card-actions>
<v-btn
v-if="!setLoaded"
</v-card>
<v-dialog
v-model="analysis.displayed"
>
<v-card v-if="analysis.set">
<v-card-title>
{{ $t('item.copy.toSet', {title: analysis.set.title})}}
</v-card-title>
<v-card-text>
<div v-if="hasError">
</div>
</v-card-text>
<v-card-actions>
<v-btn
tile
elevation="0"
color="primary"
@click="displayProjectSets"
>
{{ $t('item.copy.inProject')}}
</v-btn>
<v-btn
tile
elevation="0"
@click="duplicate"
>
<v-icon color="secondary" left>mdi-content-copy</v-icon>
{{ $t('item.copy.here' )}}
</v-btn>
</v-card-actions>
</v-card>
@click="moveItem"
>
<v-icon color="secondary" left>mdi-file-move-outline</v-icon>
{{ $t('item.copy.move' )}}
</v-btn>
<v-btn
tile
elevation="0"
@click="remoteCopy"
>
<v-icon color="secondary" left>mdi-content-copy</v-icon>
{{ $t('item.copy.duplicate' )}}
</v-btn>
<v-spacer/>
<v-btn
tile
elevation="0"
color="secondary"
@click="analysis.displayed = false"
>
{{ $t('btn.cancel' )}}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-sheet>
</template>
......@@ -57,7 +88,7 @@ import {mapActions, mapGetters, mapState} from 'vuex'
/**
* @typedef {object} Analysis
* @property {string} setId - targeted set id
* @property {ProjectSet | null} set - targeted set
* @property {boolean} displayed - control modal display
* @property {AnalysisResult | null} result - result details
*/
......@@ -68,8 +99,8 @@ import {mapActions, mapGetters, mapState} from 'vuex'
* @property {ResultDetail[]} option - fields that do no recognize item value as valid option
* @property {ResultDetail[]} tooManyValues - fields for which item value number exceed its limit
* @property {ResultDetail[]} ignoredFields - fields for which repeatable, multiple or isOption itemData mismatch
* @property {ResultDetail[]} missing - remaining item fields that have found no match in target
* @property {ResultDetail[]} fields - item metadata to be copied
* @property {MetaData[]} missing - remaining item fields that have found no match in target
* @property {MetaData[]} metadata - item metadata to be copied
*/
/**
......@@ -97,7 +128,7 @@ export default {
* @type Analysis
*/
analysis: {
setId: '',
set: null,
displayed: false,
result: null
},
......@@ -107,7 +138,11 @@ export default {
}
},
computed: {
...mapGetters('item', ['metadata', 'projectId', 'templateId']),
...mapGetters('item', {
itemSet: 'set',
itemProjectId: 'projectId',
itemMetadata: 'metadata'
}),
...mapState('set', ['projectSets']),
...mapGetters('template', ['inputFields']),
...mapGetters('project',
......@@ -115,129 +150,165 @@ export default {
currentProject: 'current',
managedProjects: 'contributions'
}),
hasError() {
const results = this.analysis.result
return results
&& !!results.required.length
&& !!results.tooManyValues.filter(r => r.field.required).length
},
hasWarning() {
return this.analysis.result && !!this.analysis.result.required.length
},
},
created() {
this.initSetsTree()
},
methods: {
...mapActions('item', ['itemDuplicate']),
...mapActions('item', ['itemDuplicate', 'itemUpdate']),
...mapActions('project', ['loadProject', 'retrieveProjects']),
...mapActions('set', ['loadProjectSets']),
...mapActions('template', ['loadTemplate', 'loadProjectTemplates']),
async analyseForCopy(targetedSetId, targetedTemplateId) {
await this.loadTemplate(targetedTemplateId)
this.analysis.result = this.inputFields.reduce((result, field) => {
const itemFields = result.missing
const itemDataIndex = itemFields.findIndex(
f => f.name === field.name)
if (itemDataIndex === -1) {
if (field.required) {
result.required.push(field)
}
} else {
const itemData = itemFields[itemDataIndex]
if (itemData.repeatable === !!field.repeatable
&& itemData.multiple === !!field.multiple
&& itemData.isOption === !!field.options
) {
let error = false
const itemValue = Array.isArray(itemData) ? itemData.value : [itemData.value]
if (itemData.isOption) {
let unknownOptions
if (field.repeatable && field.multiple) {
async analyseForCopy(set) {
if (set.id !== this.analysis.set.id) {
await this.loadTemplate(set.templateId)
this.analysis.result = this.inputFields.reduce((result, field) => {
const itemFields = result.missing
const itemDataIndex = itemFields.findIndex(
f => f.name === field.name)
if (itemDataIndex === -1) {
if (field.required) {
result.required.push(field)
}
} else {
const itemData = itemFields[itemDataIndex]
if (itemData.repeatable === !!field.repeatable
&& itemData.multiple === !!field.multiple
&& itemData.isOption === !!field.options
) {
let error = false
const itemValue = Array.isArray(itemData) ? itemData.value : [itemData.value]
if (itemData.isOption) {
let unknownOptions
if (field.repeatable && field.multiple) {
unknownOptions = itemValue.reduce((options, valueSet) => {
return [
...options,
...valueSet.filter(v => field.options.every(o => o.value !== v))
]
}, [])
} else {
unknownOptions = itemValue.filter(v => field.options.every(o => o.value !== v))
} else {
unknownOptions = itemValue.filter(v => field.options.every(o => o.value !== v))
}
if (unknownOptions.length) {
error = true
result.option.push({field, unknownOptions})
}
}
if (unknownOptions.length) {
if (field.repeatable && itemValue.length > field.maxRepeat + 1) {
error = true
result.option.push({field, unknownOptions})
result.tooManyValues.push({field, itemValue})
}
result.missing.splice(itemDataIndex, 1)
if (!error) {
result.metadata.push(itemData)
}
} else {
result.ignoredFields.push({
field
})
}
if (field.repeatable && itemValue.length > field.maxRepeat + 1) {
error = true
result.tooManyValues.push({field, itemValue})
}
result.missing.splice(itemDataIndex, 1)
if (!error) {
result.fields.push(itemData)
}
} else {
result.ignoredFields.push({
field
})
}
}
// } else if (field.options) {
// const itemValue = itemFields[itemValueIndex].value
// if (!Array.isArray(itemValue)) {
// if (field.options.every(o => o.value !== itemValue)) {
// result.option.push({...field, itemValue: [itemValue]})
// } else {
// const [matchingField] = result.missing.splice(itemValueIndex, 1)
//
// let value = itemValue
// // single value to comply to repeatable or multiple field
// if (field.repeatable) {
// value = [value]
// matchingField.repeatable = true
// }
// if (field.multiple) {
// value = [value]
// matchingField.multiple = true
// }
// result.fields.push({...matchingField, value, isOption: true})
// }
// } else if (field.multiple) {
// if (field.repeatable) {
// const valuesMatch = itemValue.every(
// value => Array.isArray(value)
// && value.every(v => field.options.every(o => o.value === v))
// )
// } else {
//
// }
// } else if (field.repeatable) {
// const unknownOptions = itemValue.filter(v => field.options.every(o => o.value !== v))
// if (unknownOptions.length) {
// result.option.push({...field, unknownOptions})
// } else {
// const [matchingField] = result.missing.splice(itemValueIndex, 1)
//
// }
// } else {
// result.tooManyValues.push({...field, itemValue})
// }
// }
return result
}, {
required: [],
option: [],
tooManyValues: [],
ignoredFields: [],
missing: this.metadata.map(m => ({...m})),// remaining item fields have found no match in target
fields: [] // received item metadata to be posted
})
this.analysis.targetedSet = targetedSetId
// } else if (field.optioncopyItems) {
// const itemValue = itemFields[itemValueIndex].value
// if (!Array.isArray(itemValue)) {
// if (field.options.every(o => o.value !== itemValue)) {
// result.option.push({...field, itemValue: [itemValue]})
// } else {
// const [matchingField] = result.missing.splice(itemValueIndex, 1)
//
// let value = itemValue
// // single value to comply to repeatable or multiple field
// if (field.repeatable) {
// value = [value]
// matchingField.repeatable = true
// }
// if (field.multiple) {
// value = [value]
// matchingField.multiple = true
// }
// result.fields.push({...matchingField, value, isOption: true})
// }
// } else if (field.multiple) {
// if (field.repeatable) {
// const valuesMatch = itemValue.every(
// value => Array.isArray(value)
// && value.every(v => field.options.every(o => o.value === v))
// )
// } else {
//
// }
// } else if (field.repeatable) {
// const unknownOptions = itemValue.filter(v => field.options.every(o => o.value !== v))
// if (unknownOptions.length) {
// result.option.push({...field, unknownOptions})
// } else {
// const [matchingField] = result.missing.splice(itemValueIndex, 1)
//
// }
// } else {
// result.tooManyValues.push({...field, itemValue})
// }
// }
return result
}, {
required: [],
option: [],
tooManyValues: [],
ignoredFields: [],
missing: this.itemMetadata.map(m => ({...m})),// remaining item fields have found no match in target
metadata: []
})
this.analysis.set = set
}
this.analysis.displayed = true
},
async duplicate() {
async copyHere() {
await this.itemDuplicate()
this.$router.push({
name: 'itemMetadata',
params: {itemId: this.item.id}
params: {itemId: this.itemId}
})
},
async displayProjectSets() {
if (this.projectId !== this.currentProject.id) {
await this.loadProject(this.projectId)
async remoteCopy() {
await this.itemDuplicate({
setId: this.analysis.set.id,
metadata: this.analysis.result.metadata
})
this.$router.push({
name: 'itemMetadata',
params: {itemId: this.itemId}
})
},
async moveItem() {
const {setId, result: {metadata}} = this.analysis
await this.itemUpdate({
setId,
metadata
})
this.analysis.displayed = false
this.$router.push({
name: 'itemMetadata',
params: {itemId: this.itemId}
})
},
async initSetsTree() {
if (this.itemProjectId !== this.currentProject.id) {
await this.loadProject(this.itemProjectId)
}
await this.loadProjectSets({size: 0})
this.projectsTree = [
{
id: this.projectId,
id: this.itemProjectId,
title: this.currentProject.name,
level: 0,
children: this.projectSets.map(
......@@ -249,31 +320,35 @@ export default {
}))
},
{
id: "next",
level: 0,
id: "projects__loader",
title: this.$t('item.copy.projectsLoad'),
level: -1,
children: []
}
]
this.openProjects = [this.projectId]
this.openProjects = [this.itemProjectId]
this.setLoaded = true
},
async addProjects() {
await this.retrieveProjects({manageOnly: true, size: 0})
const otherManagedProjects = this.managedProjects.reduce(
(others, currentProject) => {
if (currentProject.id !== this.projectId) {
others.push({
id: currentProject.id,
title: currentProject.name,
children: [],
})
}
return others
}, [])
this.projectsTree.splice(1,1, ...otherManagedProjects)
},
async addSets(project) {
await this.loadProjectSets({projectId: project.id, size: 0})
project.children = this.projectSets.map(s => ({ level: 1, ...s}))
async extendTree(item) {
if (item.level) { // loads other managed projects
await this.retrieveProjects({manageOnly: true, size: 0})
const otherManagedProjects = this.managedProjects.reduce(
(others, currentProject) => {
if (currentProject.id !== this.itemProjectId) {
others.push({
id: currentProject.id,
title: currentProject.name,
children: [],
})
}
return others
}, [])
this.projectsTree.splice(1,1, ...otherManagedProjects)
}
else { // loads project sets
await this.loadProjectSets({projectId: item.id, size: 0})
item.children = this.projectSets.map(s => ({ level: 1, ...s}))
}
},
onPagination(pagination){
this.loadProjectSets({ page: pagination.page, size: pagination.itemsPerPage});
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment