feat(front): allow recursive skeleton selection for machines
This commit is contained in:
@@ -1114,7 +1114,7 @@ const normalizeComponentNode = (source, context = {}) => {
|
|||||||
customFields: isRoot && Array.isArray(source?.customFields)
|
customFields: isRoot && Array.isArray(source?.customFields)
|
||||||
? cloneStructure(source.customFields)
|
? cloneStructure(source.customFields)
|
||||||
: [],
|
: [],
|
||||||
pieces: isRoot && Array.isArray(source?.pieces)
|
pieces: Array.isArray(source?.pieces)
|
||||||
? source.pieces.map((piece) => normalizePieceNode(piece))
|
? source.pieces.map((piece) => normalizePieceNode(piece))
|
||||||
: [],
|
: [],
|
||||||
subComponents: Array.isArray(source?.subComponents || source?.sousComposants)
|
subComponents: Array.isArray(source?.subComponents || source?.sousComposants)
|
||||||
|
|||||||
@@ -177,37 +177,51 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="entry.mode === 'model'" class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div v-if="entry.mode === 'model'" class="space-y-4">
|
||||||
<div class="form-control">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<label class="label">
|
<div class="form-control">
|
||||||
<span class="label-text text-xs">Modèle de composant</span>
|
<label class="label">
|
||||||
</label>
|
<span class="label-text text-xs">Modèle de composant</span>
|
||||||
<select
|
</label>
|
||||||
class="select select-bordered select-sm"
|
<select
|
||||||
:value="entry.componentModelId || ''"
|
class="select select-bordered select-sm"
|
||||||
@change="updateComponentSelectionEntry(requirement.id, entryIndex, { componentModelId: $event.target.value || '' })"
|
:value="entry.componentModelId || ''"
|
||||||
>
|
@change="handleRequirementComponentModelChange(requirement, entryIndex, $event.target.value || '')"
|
||||||
<option value="">
|
|
||||||
Sélectionner un modèle
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
v-for="model in getComponentModelsForType(requirement.typeComposantId)"
|
|
||||||
:key="model.id"
|
|
||||||
:value="model.id"
|
|
||||||
>
|
>
|
||||||
{{ model.name }}
|
<option value="">
|
||||||
</option>
|
Sélectionner un modèle
|
||||||
</select>
|
</option>
|
||||||
<p v-if="loadingComponentModels" class="text-[10px] text-gray-500 mt-1">
|
<option
|
||||||
Chargement des modèles...
|
v-for="model in getComponentModelsForType(requirement.typeComposantId)"
|
||||||
</p>
|
:key="model.id"
|
||||||
<p
|
:value="model.id"
|
||||||
v-else-if="getComponentModelsForType(requirement.typeComposantId).length === 0"
|
>
|
||||||
class="text-[10px] text-gray-500 mt-1"
|
{{ model.name }}
|
||||||
>
|
</option>
|
||||||
Aucun modèle disponible pour ce type.
|
</select>
|
||||||
</p>
|
<p v-if="loadingComponentModels" class="text-[10px] text-gray-500 mt-1">
|
||||||
|
Chargement des modèles...
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-else-if="getComponentModelsForType(requirement.typeComposantId).length === 0"
|
||||||
|
class="text-[10px] text-gray-500 mt-1"
|
||||||
|
>
|
||||||
|
Aucun modèle disponible pour ce type.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<SkeletonComponentNodeSelector
|
||||||
|
v-if="entry.definition && entry.componentModelId"
|
||||||
|
:node="entry.definition"
|
||||||
|
:show-self-selector="false"
|
||||||
|
:get-component-models-for-type="getComponentModelsForType"
|
||||||
|
:get-piece-models-for-type="getPieceModelsForType"
|
||||||
|
:loading-component-models="loadingComponentModels"
|
||||||
|
:loading-piece-models="loadingPieceModels"
|
||||||
|
@component-model-change="handleNodeComponentModelChange"
|
||||||
|
@piece-model-change="handleNodePieceModelChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
@@ -684,13 +698,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
|
||||||
import { useMachines } from '~/composables/useMachines'
|
import { useMachines } from '~/composables/useMachines'
|
||||||
import { useSites } from '~/composables/useSites'
|
import { useSites } from '~/composables/useSites'
|
||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useComponentModels } from '~/composables/useComponentModels'
|
import { useComponentModels } from '~/composables/useComponentModels'
|
||||||
import { usePieceModels } from '~/composables/usePieceModels'
|
import { usePieceModels } from '~/composables/usePieceModels'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import SkeletonComponentNodeSelector from '~/components/SkeletonComponentNodeSelector.vue'
|
||||||
|
import { defaultStructure, cloneStructure } from '~/shared/modelUtils'
|
||||||
import IconLucidePlus from '~icons/lucide/plus'
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
import IconLucideX from '~icons/lucide/x'
|
import IconLucideX from '~icons/lucide/x'
|
||||||
import IconLucideEye from '~icons/lucide/eye'
|
import IconLucideEye from '~icons/lucide/eye'
|
||||||
@@ -757,6 +773,240 @@ const getStatusBadgeClass = (status) => {
|
|||||||
const getComponentRequirementEntries = requirementId => componentRequirementSelections[requirementId] || []
|
const getComponentRequirementEntries = requirementId => componentRequirementSelections[requirementId] || []
|
||||||
const getPieceRequirementEntries = requirementId => pieceRequirementSelections[requirementId] || []
|
const getPieceRequirementEntries = requirementId => pieceRequirementSelections[requirementId] || []
|
||||||
|
|
||||||
|
const getComponentRequirementById = (id) => {
|
||||||
|
const type = selectedMachineType.value
|
||||||
|
if (!type?.componentRequirements) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return type.componentRequirements.find(requirement => requirement.id === id) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizePieceNode = (source, context = {}) => {
|
||||||
|
const name = source?.name || context.name || source?.typePieceLabel || source?.typePiece?.name || ''
|
||||||
|
const typePieceId = source?.typePieceId
|
||||||
|
|| source?.typePiece?.id
|
||||||
|
|| context.typePieceId
|
||||||
|
|| ''
|
||||||
|
const typePieceLabel = source?.typePieceLabel
|
||||||
|
|| source?.typePiece?.name
|
||||||
|
|| context.typePieceLabel
|
||||||
|
|| ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
typePieceId,
|
||||||
|
typePieceLabel,
|
||||||
|
__pieceModelId: source?.__pieceModelId
|
||||||
|
?? source?.pieceModelId
|
||||||
|
?? source?.pieceModel?.id
|
||||||
|
?? context.pieceModelId
|
||||||
|
?? null,
|
||||||
|
customFields: Array.isArray(source?.customFields)
|
||||||
|
? cloneStructure(source.customFields)
|
||||||
|
: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeComponentNode = (source, context = {}) => {
|
||||||
|
const isRoot = context.isRoot ?? false
|
||||||
|
const typeComposantId = source?.typeComposantId
|
||||||
|
|| source?.typeComposant?.id
|
||||||
|
|| context.typeComposantId
|
||||||
|
|| ''
|
||||||
|
|
||||||
|
const node = {
|
||||||
|
name: source?.name || context.name || '',
|
||||||
|
description: source?.description || context.description || '',
|
||||||
|
typeComposantId,
|
||||||
|
typeComposantLabel: source?.typeComposantLabel
|
||||||
|
|| source?.typeComposant?.name
|
||||||
|
|| context.typeComposantLabel
|
||||||
|
|| '',
|
||||||
|
__componentModelId: source?.__componentModelId
|
||||||
|
?? source?.componentModelId
|
||||||
|
?? source?.composantModelId
|
||||||
|
?? source?.composantModel?.id
|
||||||
|
?? context.componentModelId
|
||||||
|
?? null,
|
||||||
|
__requirementId: source?.__requirementId ?? context.requirementId ?? null,
|
||||||
|
customFields: isRoot && Array.isArray(source?.customFields)
|
||||||
|
? cloneStructure(source.customFields)
|
||||||
|
: [],
|
||||||
|
pieces: Array.isArray(source?.pieces)
|
||||||
|
? source.pieces.map(piece => normalizePieceNode(piece))
|
||||||
|
: [],
|
||||||
|
subComponents: Array.isArray(source?.subComponents || source?.sousComposants)
|
||||||
|
? (source.subComponents || source.sousComposants).map((sub) => normalizeComponentNode(sub, {
|
||||||
|
name: sub?.name || '',
|
||||||
|
description: sub?.description || '',
|
||||||
|
typeComposantId: sub?.typeComposantId || sub?.typeComposant?.id || '',
|
||||||
|
typeComposantLabel: sub?.typeComposantLabel || sub?.typeComposant?.name || '',
|
||||||
|
componentModelId: sub?.__componentModelId
|
||||||
|
?? sub?.componentModelId
|
||||||
|
?? sub?.composantModelId
|
||||||
|
?? sub?.composantModel?.id
|
||||||
|
?? null,
|
||||||
|
requirementId: sub?.__requirementId ?? null,
|
||||||
|
isRoot: false,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.name) {
|
||||||
|
node.name = node.typeComposantLabel || 'Composant'
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
const gatherTypeIdsFromComponentNode = (node, componentTypeIds, pieceTypeIds) => {
|
||||||
|
if (!node || typeof node !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.typeComposantId) {
|
||||||
|
componentTypeIds.add(node.typeComposantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(node.pieces)) {
|
||||||
|
node.pieces.forEach((piece) => {
|
||||||
|
const typeId = piece?.typePieceId
|
||||||
|
|| piece?.typePiece?.id
|
||||||
|
|| null
|
||||||
|
if (typeId) {
|
||||||
|
pieceTypeIds.add(typeId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(node.subComponents)) {
|
||||||
|
node.subComponents.forEach((subNode) => {
|
||||||
|
gatherTypeIdsFromComponentNode(subNode, componentTypeIds, pieceTypeIds)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createEmptyComponentDefinition = (requirement) => {
|
||||||
|
return normalizeComponentNode(
|
||||||
|
{
|
||||||
|
name: requirement?.typeComposant?.name || 'Composant',
|
||||||
|
typeComposantId: requirement?.typeComposantId || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
requirementId: requirement?.id || null,
|
||||||
|
isRoot: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createDefinitionFromModel = (model, requirement) => {
|
||||||
|
const structure = cloneStructure(model?.structure || defaultStructure())
|
||||||
|
if (!structure.name) {
|
||||||
|
structure.name = model?.name || requirement?.typeComposant?.name || 'Composant'
|
||||||
|
}
|
||||||
|
if (!structure.typeComposantId) {
|
||||||
|
structure.typeComposantId = model?.typeComposantId || requirement?.typeComposantId || ''
|
||||||
|
}
|
||||||
|
const definition = normalizeComponentNode(structure, {
|
||||||
|
requirementId: requirement?.id || null,
|
||||||
|
componentModelId: model?.id || null,
|
||||||
|
isRoot: true,
|
||||||
|
})
|
||||||
|
definition.__componentModelId = model?.id || null
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyDefinitionToNode = (target, definition) => {
|
||||||
|
target.name = definition.name
|
||||||
|
target.description = definition.description
|
||||||
|
target.typeComposantId = definition.typeComposantId
|
||||||
|
target.typeComposantLabel = definition.typeComposantLabel
|
||||||
|
target.__componentModelId = definition.__componentModelId ?? null
|
||||||
|
target.__requirementId = definition.__requirementId ?? null
|
||||||
|
target.customFields = Array.isArray(definition.customFields)
|
||||||
|
? cloneStructure(definition.customFields)
|
||||||
|
: []
|
||||||
|
target.pieces = Array.isArray(definition.pieces)
|
||||||
|
? definition.pieces.map(piece => normalizePieceNode(piece))
|
||||||
|
: []
|
||||||
|
target.subComponents = Array.isArray(definition.subComponents)
|
||||||
|
? definition.subComponents.map(sub => normalizeComponentNode(sub, { isRoot: false }))
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNodeComponentModelChange = async (node, typeComposantId, modelId) => {
|
||||||
|
const requirement = node.__requirementId
|
||||||
|
? getComponentRequirementById(node.__requirementId)
|
||||||
|
: null
|
||||||
|
|
||||||
|
const previousModelId = node.__componentModelId || null
|
||||||
|
if (previousModelId === modelId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeComposantId) {
|
||||||
|
await loadComponentModels(typeComposantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const models = typeComposantId ? getComponentModelsForType(typeComposantId) : []
|
||||||
|
const selectedModel = models.find(model => model.id === modelId)
|
||||||
|
|
||||||
|
if (selectedModel) {
|
||||||
|
const definition = createDefinitionFromModel(selectedModel, requirement)
|
||||||
|
if (!definition.name && node.name) {
|
||||||
|
definition.name = node.name
|
||||||
|
}
|
||||||
|
applyDefinitionToNode(node, definition)
|
||||||
|
node.__componentModelId = selectedModel.id
|
||||||
|
await ensureModelsForNodeDefinition(node)
|
||||||
|
} else {
|
||||||
|
node.__componentModelId = null
|
||||||
|
node.customFields = []
|
||||||
|
node.pieces = []
|
||||||
|
node.subComponents = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNodePieceModelChange = async (pieceNode, typePieceId, modelId) => {
|
||||||
|
if (typePieceId) {
|
||||||
|
await loadPieceModels(typePieceId)
|
||||||
|
}
|
||||||
|
pieceNode.__pieceModelId = modelId || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureModelsForNodeDefinition = async (node) => {
|
||||||
|
if (!node) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const componentTypeIds = new Set()
|
||||||
|
const pieceTypeIds = new Set()
|
||||||
|
gatherTypeIdsFromComponentNode(node, componentTypeIds, pieceTypeIds)
|
||||||
|
|
||||||
|
const loaders = [
|
||||||
|
...Array.from(componentTypeIds).filter(Boolean).map(id => loadComponentModels(id)),
|
||||||
|
...Array.from(pieceTypeIds).filter(Boolean).map(id => loadPieceModels(id)),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!loaders.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(loaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRequirementComponentModelChange = async (requirement, entryIndex, value) => {
|
||||||
|
if (!requirement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateComponentSelectionEntry(requirement.id, entryIndex, { componentModelId: value })
|
||||||
|
await nextTick()
|
||||||
|
const entries = getComponentRequirementEntries(requirement.id)
|
||||||
|
const entry = entries[entryIndex]
|
||||||
|
if (value && entry?.definition) {
|
||||||
|
await ensureModelsForNodeDefinition(entry.definition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const machinePreview = computed(() => {
|
const machinePreview = computed(() => {
|
||||||
const type = selectedMachineType.value
|
const type = selectedMachineType.value
|
||||||
if (!type) {
|
if (!type) {
|
||||||
@@ -1019,10 +1269,11 @@ const clearRequirementSelections = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createComponentSelectionEntry = () => ({
|
const createComponentSelectionEntry = (requirement) => ({
|
||||||
mode: 'model',
|
mode: 'model',
|
||||||
componentModelId: '',
|
componentModelId: '',
|
||||||
name: ''
|
name: requirement?.typeComposant?.name || '',
|
||||||
|
definition: createEmptyComponentDefinition(requirement || null)
|
||||||
})
|
})
|
||||||
|
|
||||||
const createPieceSelectionEntry = () => ({
|
const createPieceSelectionEntry = () => ({
|
||||||
@@ -1036,7 +1287,10 @@ const addComponentSelectionEntry = (requirement) => {
|
|||||||
toast.showError(`Vous ne pouvez pas ajouter plus de ${max} composant(s) pour ${requirement.label || requirement.typeComposant?.name || 'ce groupe'}`)
|
toast.showError(`Vous ne pouvez pas ajouter plus de ${max} composant(s) pour ${requirement.label || requirement.typeComposant?.name || 'ce groupe'}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
componentRequirementSelections[requirement.id] = [...entries, createComponentSelectionEntry()]
|
componentRequirementSelections[requirement.id] = [
|
||||||
|
...entries,
|
||||||
|
createComponentSelectionEntry(requirement)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeComponentSelectionEntry = (requirementId, index) => {
|
const removeComponentSelectionEntry = (requirementId, index) => {
|
||||||
@@ -1046,20 +1300,62 @@ const removeComponentSelectionEntry = (requirementId, index) => {
|
|||||||
|
|
||||||
const setComponentSelectionMode = (requirementId, index, mode) => {
|
const setComponentSelectionMode = (requirementId, index, mode) => {
|
||||||
const entries = getComponentRequirementEntries(requirementId)
|
const entries = getComponentRequirementEntries(requirementId)
|
||||||
|
const requirement = getComponentRequirementById(requirementId)
|
||||||
componentRequirementSelections[requirementId] = entries.map((entry, i) => {
|
componentRequirementSelections[requirementId] = entries.map((entry, i) => {
|
||||||
if (i !== index) { return entry }
|
if (i !== index) {
|
||||||
if (mode === 'model') {
|
return entry
|
||||||
return { ...entry, mode: 'model', componentModelId: entry.componentModelId || '', name: '' }
|
}
|
||||||
|
if (mode === 'model') {
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
mode: 'model',
|
||||||
|
componentModelId: entry.componentModelId || '',
|
||||||
|
name: entry.name || requirement?.typeComposant?.name || '',
|
||||||
|
definition: entry.definition || createEmptyComponentDefinition(requirement),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
mode: 'manual',
|
||||||
|
componentModelId: '',
|
||||||
|
name: entry.name || '',
|
||||||
|
definition: entry.definition || createEmptyComponentDefinition(requirement),
|
||||||
}
|
}
|
||||||
return { ...entry, mode: 'manual', componentModelId: '', name: entry.name || '' }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateComponentSelectionEntry = (requirementId, index, patch) => {
|
const updateComponentSelectionEntry = (requirementId, index, patch) => {
|
||||||
const entries = getComponentRequirementEntries(requirementId)
|
const entries = getComponentRequirementEntries(requirementId)
|
||||||
componentRequirementSelections[requirementId] = entries.map((entry, i) =>
|
const requirement = getComponentRequirementById(requirementId)
|
||||||
i === index ? { ...entry, ...patch } : entry
|
componentRequirementSelections[requirementId] = entries.map((entry, i) => {
|
||||||
)
|
if (i !== index) {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = { ...entry, ...patch }
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(patch, 'componentModelId')) {
|
||||||
|
const newModelId = patch.componentModelId || ''
|
||||||
|
const typeId = requirement?.typeComposantId || ''
|
||||||
|
const models = typeId ? getComponentModelsForType(typeId) : []
|
||||||
|
const selectedModel = models.find(model => model.id === newModelId)
|
||||||
|
|
||||||
|
if (selectedModel) {
|
||||||
|
updated.definition = createDefinitionFromModel(selectedModel, requirement)
|
||||||
|
} else {
|
||||||
|
updated.definition = createEmptyComponentDefinition(requirement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(patch, 'name') && updated.definition) {
|
||||||
|
updated.definition = {
|
||||||
|
...updated.definition,
|
||||||
|
name: patch.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const addPieceSelectionEntry = (requirement) => {
|
const addPieceSelectionEntry = (requirement) => {
|
||||||
@@ -1115,16 +1411,31 @@ const validateRequirementSelections = (type) => {
|
|||||||
|
|
||||||
usableEntries.forEach((entry) => {
|
usableEntries.forEach((entry) => {
|
||||||
if (entry.mode === 'model') {
|
if (entry.mode === 'model') {
|
||||||
componentSelectionsPayload.push({
|
const definitionPayload = serializeComponentNodeDefinition(entry.definition)
|
||||||
|
const payload = {
|
||||||
requirementId: requirement.id,
|
requirementId: requirement.id,
|
||||||
componentModelId: entry.componentModelId
|
componentModelId: entry.componentModelId
|
||||||
})
|
}
|
||||||
|
if (
|
||||||
|
definitionPayload &&
|
||||||
|
((definitionPayload.pieces && definitionPayload.pieces.length)
|
||||||
|
|| (definitionPayload.subComponents && definitionPayload.subComponents.length)
|
||||||
|
|| (definitionPayload.customFields && definitionPayload.customFields.length))
|
||||||
|
) {
|
||||||
|
payload.definition = definitionPayload
|
||||||
|
}
|
||||||
|
componentSelectionsPayload.push(payload)
|
||||||
} else {
|
} else {
|
||||||
|
const manualName = typeof entry.name === 'string' ? entry.name.trim() : ''
|
||||||
|
const definitionPayload = serializeComponentNodeDefinition(entry.definition) || {}
|
||||||
|
if (manualName && !definitionPayload.name) {
|
||||||
|
definitionPayload.name = manualName
|
||||||
|
}
|
||||||
componentSelectionsPayload.push({
|
componentSelectionsPayload.push({
|
||||||
requirementId: requirement.id,
|
requirementId: requirement.id,
|
||||||
definition: {
|
definition: Object.keys(definitionPayload).length
|
||||||
name: entry.name.trim()
|
? definitionPayload
|
||||||
}
|
: { name: manualName || requirement.typeComposant?.name || 'Composant' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1164,6 +1475,132 @@ const validateRequirementSelections = (type) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseOptionsText = (text) => {
|
||||||
|
return text
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map(option => option.trim())
|
||||||
|
.filter(option => option.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializeCustomFieldDefinition = (field) => {
|
||||||
|
if (!field || typeof field !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const name = typeof field.name === 'string' ? field.name.trim() : ''
|
||||||
|
if (!name) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const type = typeof field.type === 'string' && field.type.trim().length
|
||||||
|
? field.type.trim()
|
||||||
|
: 'text'
|
||||||
|
const result = {
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
required: !!field.required
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = []
|
||||||
|
if (Array.isArray(field.options) && field.options.length) {
|
||||||
|
options = field.options
|
||||||
|
.map(option => (typeof option === 'string' ? option.trim() : ''))
|
||||||
|
.filter(option => option.length > 0)
|
||||||
|
} else if (type === 'select' && typeof field.optionsText === 'string') {
|
||||||
|
options = parseOptionsText(field.optionsText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.length) {
|
||||||
|
result.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializePieceNodeDefinition = (piece) => {
|
||||||
|
if (!piece || typeof piece !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const payload = {}
|
||||||
|
const name = typeof piece.name === 'string' ? piece.name.trim() : ''
|
||||||
|
if (name) {
|
||||||
|
payload.name = name
|
||||||
|
}
|
||||||
|
const typePieceId = piece.typePieceId || piece.typePiece?.id || ''
|
||||||
|
if (typePieceId) {
|
||||||
|
payload.typePieceId = typePieceId
|
||||||
|
}
|
||||||
|
const typePieceLabel = typeof piece.typePieceLabel === 'string' ? piece.typePieceLabel.trim() : ''
|
||||||
|
if (typePieceLabel) {
|
||||||
|
payload.typePieceLabel = typePieceLabel
|
||||||
|
}
|
||||||
|
if (piece.__pieceModelId) {
|
||||||
|
payload.pieceModelId = piece.__pieceModelId
|
||||||
|
}
|
||||||
|
if (Array.isArray(piece.customFields) && piece.customFields.length) {
|
||||||
|
const customFields = piece.customFields
|
||||||
|
.map(field => serializeCustomFieldDefinition(field))
|
||||||
|
.filter(Boolean)
|
||||||
|
if (customFields.length) {
|
||||||
|
payload.customFields = customFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.keys(payload).length > 0 ? payload : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializeComponentNodeDefinition = (node) => {
|
||||||
|
if (!node || typeof node !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {}
|
||||||
|
const name = typeof node.name === 'string' ? node.name.trim() : ''
|
||||||
|
if (name) {
|
||||||
|
payload.name = name
|
||||||
|
}
|
||||||
|
const description = typeof node.description === 'string' ? node.description.trim() : ''
|
||||||
|
if (description) {
|
||||||
|
payload.description = description
|
||||||
|
}
|
||||||
|
const typeComposantId = node.typeComposantId || node.typeComposant?.id || ''
|
||||||
|
if (typeComposantId) {
|
||||||
|
payload.typeComposantId = typeComposantId
|
||||||
|
}
|
||||||
|
const typeComposantLabel = typeof node.typeComposantLabel === 'string'
|
||||||
|
? node.typeComposantLabel.trim()
|
||||||
|
: ''
|
||||||
|
if (typeComposantLabel) {
|
||||||
|
payload.typeComposantLabel = typeComposantLabel
|
||||||
|
}
|
||||||
|
if (node.__componentModelId) {
|
||||||
|
payload.componentModelId = node.__componentModelId
|
||||||
|
}
|
||||||
|
if (Array.isArray(node.customFields) && node.customFields.length) {
|
||||||
|
const customFields = node.customFields
|
||||||
|
.map(field => serializeCustomFieldDefinition(field))
|
||||||
|
.filter(Boolean)
|
||||||
|
if (customFields.length) {
|
||||||
|
payload.customFields = customFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(node.pieces) && node.pieces.length) {
|
||||||
|
const pieces = node.pieces
|
||||||
|
.map(piece => serializePieceNodeDefinition(piece))
|
||||||
|
.filter(Boolean)
|
||||||
|
if (pieces.length) {
|
||||||
|
payload.pieces = pieces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(node.subComponents) && node.subComponents.length) {
|
||||||
|
const subComponents = node.subComponents
|
||||||
|
.map(sub => serializeComponentNodeDefinition(sub))
|
||||||
|
.filter(Boolean)
|
||||||
|
if (subComponents.length) {
|
||||||
|
payload.subComponents = subComponents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(payload).length > 0 ? payload : null
|
||||||
|
}
|
||||||
|
|
||||||
const openCreateComponentModelModal = (requirement) => {
|
const openCreateComponentModelModal = (requirement) => {
|
||||||
createComponentModelModal.open = true
|
createComponentModelModal.open = true
|
||||||
createComponentModelModal.requirement = requirement
|
createComponentModelModal.requirement = requirement
|
||||||
@@ -1201,19 +1638,18 @@ const submitCreateComponentModel = async () => {
|
|||||||
const entries = getComponentRequirementEntries(createComponentModelModal.requirement.id)
|
const entries = getComponentRequirementEntries(createComponentModelModal.requirement.id)
|
||||||
const targetIndex = entries.findIndex(entry => entry.mode === 'model' && !entry.componentModelId)
|
const targetIndex = entries.findIndex(entry => entry.mode === 'model' && !entry.componentModelId)
|
||||||
if (targetIndex !== -1) {
|
if (targetIndex !== -1) {
|
||||||
updateComponentSelectionEntry(createComponentModelModal.requirement.id, targetIndex, {
|
await handleRequirementComponentModelChange(
|
||||||
mode: 'model',
|
createComponentModelModal.requirement,
|
||||||
componentModelId: result.data.id
|
targetIndex,
|
||||||
})
|
result.data.id
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
addComponentSelectionEntry(createComponentModelModal.requirement)
|
addComponentSelectionEntry(createComponentModelModal.requirement)
|
||||||
updateComponentSelectionEntry(
|
const newIndex = getComponentRequirementEntries(createComponentModelModal.requirement.id).length - 1
|
||||||
createComponentModelModal.requirement.id,
|
await handleRequirementComponentModelChange(
|
||||||
getComponentRequirementEntries(createComponentModelModal.requirement.id).length - 1,
|
createComponentModelModal.requirement,
|
||||||
{
|
newIndex,
|
||||||
mode: 'model',
|
result.data.id
|
||||||
componentModelId: result.data.id
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
toast.showSuccess(`Modèle "${result.data.name}" créé`)
|
toast.showSuccess(`Modèle "${result.data.name}" créé`)
|
||||||
@@ -1299,7 +1735,7 @@ const initializeRequirementSelections = async (type) => {
|
|||||||
const initialCount = Math.max(min, requirement.required ? 1 : 0)
|
const initialCount = Math.max(min, requirement.required ? 1 : 0)
|
||||||
const entries = []
|
const entries = []
|
||||||
for (let index = 0; index < initialCount; index += 1) {
|
for (let index = 0; index < initialCount; index += 1) {
|
||||||
entries.push(createComponentSelectionEntry())
|
entries.push(createComponentSelectionEntry(requirement))
|
||||||
}
|
}
|
||||||
componentRequirementSelections[requirement.id] = entries
|
componentRequirementSelections[requirement.id] = entries
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user