feat(machine): allow configuring nested skeleton models
This commit is contained in:
201
app/components/SkeletonComponentNodeSelector.vue
Normal file
201
app/components/SkeletonComponentNodeSelector.vue
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['space-y-4', depth > 0 ? 'border-l border-base-200 pl-4 ml-2' : '']">
|
||||||
|
<div class="bg-base-100 border border-base-200 rounded-lg p-4 space-y-4">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="flex items-start justify-between gap-3">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h4 class="font-semibold text-sm">
|
||||||
|
{{ node.name || node.typeComposantLabel || 'Composant' }}
|
||||||
|
</h4>
|
||||||
|
<p class="text-xs text-gray-500">
|
||||||
|
{{
|
||||||
|
node.typeComposantLabel
|
||||||
|
? `Famille : ${node.typeComposantLabel}`
|
||||||
|
: node.typeComposantId
|
||||||
|
? `Famille : ${node.typeComposantId}`
|
||||||
|
: 'Famille non définie'
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!showSelfSelector && selectedComponentModelLabel" class="badge badge-outline badge-sm">
|
||||||
|
Modèle : {{ selectedComponentModelLabel }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showSelfSelector" class="form-control max-w-xs">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text text-xs">Modèle de composant</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
class="select select-bordered select-xs"
|
||||||
|
:value="selectedComponentModelId"
|
||||||
|
@change="onComponentModelSelect($event.target.value)"
|
||||||
|
>
|
||||||
|
<option value="">Sélectionner un modèle</option>
|
||||||
|
<option v-for="model in componentModels" :key="model.id" :value="model.id">
|
||||||
|
{{ model.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p v-if="loadingComponentModels" class="text-[10px] text-gray-500 mt-1">
|
||||||
|
Chargement des modèles…
|
||||||
|
</p>
|
||||||
|
<p v-else-if="!componentModels.length" class="text-[10px] text-gray-500 mt-1">
|
||||||
|
Aucun modèle disponible pour cette famille.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasPieces" class="space-y-2">
|
||||||
|
<h5 class="text-[11px] font-semibold uppercase text-gray-500">Pièces associées</h5>
|
||||||
|
<div
|
||||||
|
v-for="(piece, pieceIndex) in node.pieces"
|
||||||
|
:key="pieceIndex"
|
||||||
|
class="bg-base-200/60 border border-base-200 rounded-md p-3 space-y-2"
|
||||||
|
>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<span class="font-medium text-sm">{{ piece.name || piece.typePieceLabel || 'Pièce' }}</span>
|
||||||
|
<p class="text-[11px] text-gray-500">
|
||||||
|
{{
|
||||||
|
piece.typePieceLabel
|
||||||
|
? `Famille : ${piece.typePieceLabel}`
|
||||||
|
: piece.typePieceId
|
||||||
|
? `Famille : ${piece.typePieceId}`
|
||||||
|
: 'Famille non définie'
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-control max-w-xs">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text text-xs">Modèle de pièce</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
class="select select-bordered select-xs"
|
||||||
|
:value="piece.__pieceModelId || ''"
|
||||||
|
@change="onPieceModelSelect(piece, $event.target.value)"
|
||||||
|
>
|
||||||
|
<option value="">Sélectionner un modèle</option>
|
||||||
|
<option v-for="model in getPieceModels(piece.typePieceId)" :key="model.id" :value="model.id">
|
||||||
|
{{ model.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p v-if="loadingPieceModels" class="text-[10px] text-gray-500 mt-1">
|
||||||
|
Chargement des modèles…
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-else-if="getPieceModels(piece.typePieceId).length === 0"
|
||||||
|
class="text-[10px] text-gray-500 mt-1"
|
||||||
|
>
|
||||||
|
Aucun modèle disponible pour cette famille.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasSubComponents" class="space-y-3">
|
||||||
|
<h5 class="text-[11px] font-semibold uppercase text-gray-500">Sous-composants</h5>
|
||||||
|
<SkeletonComponentNodeSelector
|
||||||
|
v-for="(subComponent, index) in node.subComponents"
|
||||||
|
:key="index"
|
||||||
|
:node="subComponent"
|
||||||
|
:depth="depth + 1"
|
||||||
|
:get-component-models-for-type="getComponentModelsForType"
|
||||||
|
:get-piece-models-for-type="getPieceModelsForType"
|
||||||
|
:loading-component-models="loadingComponentModels"
|
||||||
|
:loading-piece-models="loadingPieceModels"
|
||||||
|
@component-model-change="forwardComponentModelChange"
|
||||||
|
@piece-model-change="forwardPieceModelChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SkeletonComponentNodeSelector' })
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps({
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
depth: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
getComponentModelsForType: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
getPieceModelsForType: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
loadingComponentModels: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loadingPieceModels: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
showSelfSelector: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
}), {
|
||||||
|
depth: 0,
|
||||||
|
loadingComponentModels: false,
|
||||||
|
loadingPieceModels: false,
|
||||||
|
showSelfSelector: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['component-model-change', 'piece-model-change'])
|
||||||
|
|
||||||
|
const componentModels = computed(() => {
|
||||||
|
if (!props.node?.typeComposantId) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const models = props.getComponentModelsForType(props.node.typeComposantId)
|
||||||
|
return Array.isArray(models) ? models : []
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedComponentModelId = computed(() => props.node?.__componentModelId || '')
|
||||||
|
|
||||||
|
const selectedComponentModelLabel = computed(() => {
|
||||||
|
if (!selectedComponentModelId.value) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const match = componentModels.value.find((model) => model.id === selectedComponentModelId.value)
|
||||||
|
return match?.name || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasPieces = computed(() => Array.isArray(props.node?.pieces) && props.node.pieces.length > 0)
|
||||||
|
const hasSubComponents = computed(() => Array.isArray(props.node?.subComponents) && props.node.subComponents.length > 0)
|
||||||
|
|
||||||
|
const getPieceModels = (typePieceId) => {
|
||||||
|
if (!typePieceId) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const models = props.getPieceModelsForType(typePieceId)
|
||||||
|
return Array.isArray(models) ? models : []
|
||||||
|
}
|
||||||
|
|
||||||
|
const onComponentModelSelect = (value) => {
|
||||||
|
emit('component-model-change', props.node, props.node?.typeComposantId || '', value || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPieceModelSelect = (piece, value) => {
|
||||||
|
emit('piece-model-change', piece, piece?.typePieceId || '', value || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const forwardComponentModelChange = (...args) => {
|
||||||
|
emit('component-model-change', ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const forwardPieceModelChange = (...args) => {
|
||||||
|
emit('piece-model-change', ...args)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -585,33 +585,46 @@
|
|||||||
</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="">Sélectionner un modèle</option>
|
||||||
</option>
|
<option
|
||||||
</select>
|
v-for="model in getComponentModelsForType(requirement.typeComposantId)"
|
||||||
<p v-if="loadingComponentModels" class="text-[10px] text-gray-500 mt-1">Chargement des modèles...</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">
|
||||||
@@ -1125,6 +1138,33 @@ const normalizeComponentNode = (source, context = {}) => {
|
|||||||
return node
|
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) => {
|
const createEmptyComponentDefinition = (requirement) => {
|
||||||
return normalizeComponentNode(
|
return normalizeComponentNode(
|
||||||
{
|
{
|
||||||
@@ -1212,6 +1252,7 @@ const handleNodeComponentModelChange = async (node, typeComposantId, modelId) =>
|
|||||||
}
|
}
|
||||||
applyDefinitionToNode(node, definition)
|
applyDefinitionToNode(node, definition)
|
||||||
node.__componentModelId = selectedModel.id
|
node.__componentModelId = selectedModel.id
|
||||||
|
await ensureModelsForNodeDefinition(node)
|
||||||
} else {
|
} else {
|
||||||
node.__componentModelId = null
|
node.__componentModelId = null
|
||||||
node.pieces = []
|
node.pieces = []
|
||||||
@@ -1226,6 +1267,26 @@ const handleNodePieceModelChange = async (pieceNode, typePieceId, modelId) => {
|
|||||||
pieceNode.__pieceModelId = modelId || null
|
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 getComponentRequirementEntries = (requirementId) => {
|
const getComponentRequirementEntries = (requirementId) => {
|
||||||
return componentRequirementSelections[requirementId] || []
|
return componentRequirementSelections[requirementId] || []
|
||||||
}
|
}
|
||||||
@@ -1319,6 +1380,13 @@ const updateComponentSelectionEntry = (requirementId, index, patch) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(patch, 'name') && updated.definition) {
|
||||||
|
updated.definition = {
|
||||||
|
...updated.definition,
|
||||||
|
name: patch.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return updated
|
return updated
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1377,6 +1445,7 @@ const initializeSkeletonRequirementSelections = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const componentTypeIds = new Set()
|
const componentTypeIds = new Set()
|
||||||
|
const pieceTypeIds = new Set()
|
||||||
;(type.componentRequirements || []).forEach((requirement) => {
|
;(type.componentRequirements || []).forEach((requirement) => {
|
||||||
if (requirement.typeComposantId) {
|
if (requirement.typeComposantId) {
|
||||||
componentTypeIds.add(requirement.typeComposantId)
|
componentTypeIds.add(requirement.typeComposantId)
|
||||||
@@ -1385,20 +1454,30 @@ const initializeSkeletonRequirementSelections = async () => {
|
|||||||
(component) => component.typeMachineComponentRequirementId === requirement.id
|
(component) => component.typeMachineComponentRequirementId === requirement.id
|
||||||
)
|
)
|
||||||
const entries = existingComponents.map((component) => {
|
const entries = existingComponents.map((component) => {
|
||||||
|
const definition = buildDefinitionFromComponent(component, requirement)
|
||||||
|
gatherTypeIdsFromComponentNode(definition, componentTypeIds, pieceTypeIds)
|
||||||
const modelId = component.composantModelId || component.composantModel?.id || null
|
const modelId = component.composantModelId || component.composantModel?.id || null
|
||||||
if (modelId) {
|
if (modelId) {
|
||||||
return { mode: 'model', componentModelId: modelId, name: '' }
|
return { mode: 'model', componentModelId: modelId, name: '', definition }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
mode: 'manual',
|
||||||
|
componentModelId: '',
|
||||||
|
name: component.name || '',
|
||||||
|
definition,
|
||||||
}
|
}
|
||||||
return { mode: 'manual', componentModelId: '', name: component.name || '' }
|
|
||||||
})
|
})
|
||||||
const min = requirement.minCount ?? (requirement.required ? 1 : 0)
|
const min = requirement.minCount ?? (requirement.required ? 1 : 0)
|
||||||
while (entries.length < min) {
|
while (entries.length < min) {
|
||||||
entries.push(createComponentSelectionEntry())
|
const entry = createComponentSelectionEntry(requirement)
|
||||||
|
if (entry.definition) {
|
||||||
|
gatherTypeIdsFromComponentNode(entry.definition, componentTypeIds, pieceTypeIds)
|
||||||
|
}
|
||||||
|
entries.push(entry)
|
||||||
}
|
}
|
||||||
componentRequirementSelections[requirement.id] = entries.length ? entries : []
|
componentRequirementSelections[requirement.id] = entries.length ? entries : []
|
||||||
})
|
})
|
||||||
|
|
||||||
const pieceTypeIds = new Set()
|
|
||||||
const allPieces = collectPiecesForSkeleton()
|
const allPieces = collectPiecesForSkeleton()
|
||||||
;(type.pieceRequirements || []).forEach((requirement) => {
|
;(type.pieceRequirements || []).forEach((requirement) => {
|
||||||
if (requirement.typePieceId) {
|
if (requirement.typePieceId) {
|
||||||
@@ -1433,6 +1512,19 @@ const initializeSkeletonRequirementSelections = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 openSkeletonEditor = async () => {
|
const openSkeletonEditor = async () => {
|
||||||
if (skeletonEditor.open) {
|
if (skeletonEditor.open) {
|
||||||
return
|
return
|
||||||
@@ -1520,16 +1612,31 @@ const validateSkeletonSelections = (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' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1573,6 +1680,132 @@ const validateSkeletonSelections = (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 applySkeletonReconfigurationResult = async (data) => {
|
const applySkeletonReconfigurationResult = async (data) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user