chore: retire legacy model catalogs

This commit is contained in:
MatthieuTD
2025-10-02 16:29:50 +02:00
parent 386a1c9d1b
commit c5f2c568b6
19 changed files with 1234 additions and 3597 deletions

View File

@@ -7,15 +7,9 @@
:is-edit-mode="isEditMode"
:collapse-all="collapseAll"
:toggle-token="toggleToken"
:component-model-options="componentModelOptionsProvider(component)"
:component-model-options-provider="componentModelOptionsProvider"
:piece-model-options-provider="pieceModelOptionsProvider"
@update="$emit('update', $event)"
@edit-piece="$emit('edit-piece', $event)"
@assign-model="$emit('assign-model', $event)"
@assign-piece-model="$emit('assign-piece-model', $event)"
@custom-field-update="$emit('custom-field-update', $event)"
@create-model-from-component="$emit('create-model-from-component', $event)"
/>
</div>
</div>
@@ -40,16 +34,8 @@ defineProps({
toggleToken: {
type: Number,
default: 0
},
componentModelOptionsProvider: {
type: Function,
default: () => []
},
pieceModelOptionsProvider: {
type: Function,
default: () => []
}
})
defineEmits(['update', 'edit-piece', 'assign-model', 'assign-piece-model', 'custom-field-update', 'create-model-from-component'])
defineEmits(['update', 'edit-piece', 'custom-field-update'])
</script>

View File

@@ -34,12 +34,6 @@
>
Groupe : {{ component.typeMachineComponentRequirement.label || component.typeMachineComponentRequirement.typeComposant?.name || 'Non défini' }}
</span>
<span
v-if="component.composantModel"
class="badge badge-outline badge-sm badge-primary"
>
Modèle : {{ component.composantModel.name }}
</span>
</div>
</div>
</div>
@@ -108,45 +102,6 @@
</div>
</div>
<div
v-if="isEditMode && component.typeMachineComponentRequirement"
class="grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div class="form-control">
<label class="label">
<span class="label-text font-medium">Modèle de composant</span>
<span class="label-text-alt text-xs">
{{ component.typeMachineComponentRequirement.label || component.typeMachineComponentRequirement.typeComposant?.name || 'Famille' }}
</span>
</label>
<div class="flex flex-col md:flex-row gap-2 items-start md:items-center">
<select
:value="selectedComponentModelId"
class="select select-bordered select-sm"
@change="assignComponentModel($event.target.value)"
>
<option value="">
Définir manuellement
</option>
<option
v-for="model in componentModelOptionsList"
:key="model.id"
:value="model.id"
>
{{ model.name }}
</option>
</select>
<button
v-if="isEditMode && component.typeMachineComponentRequirement?.typeComposantId"
type="button"
class="btn btn-ghost btn-xs"
@click="emit('create-model-from-component', component)"
>
Sauvegarder comme modèle
</button>
</div>
</div>
</div>
</div>
<!-- Custom Fields Display - Editable or Read-only -->
@@ -314,7 +269,6 @@
@update="updatePiece"
@edit="editPiece"
@custom-field-update="updatePieceCustomField"
@assign-model="emitAssignPieceModel"
/>
</div>
</div>
@@ -332,13 +286,9 @@
:is-edit-mode="isEditMode"
:collapse-all="collapseAll"
:toggle-token="toggleToken"
:component-model-options="componentModelOptionsProvider(subComponent)"
:component-model-options-provider="componentModelOptionsProvider"
:piece-model-options-provider="pieceModelOptionsProvider"
@update="$emit('update', $event)"
@edit-piece="$emit('edit-piece', $event)"
@assign-model="$emit('assign-model', $event)"
@assign-piece-model="$emit('assign-piece-model', $event)"
@custom-field-update="$emit('custom-field-update', $event)"
/>
</div>
</div>
@@ -375,28 +325,13 @@ const props = defineProps({
toggleToken: {
type: Number,
default: 0
},
componentModelOptions: {
type: Array,
default: () => []
},
componentModelOptionsProvider: {
type: Function,
default: () => []
},
pieceModelOptionsProvider: {
type: Function,
default: () => []
}
})
const emit = defineEmits([
'update',
'edit-piece',
'custom-field-update',
'assign-model',
'assign-piece-model',
'create-model-from-component'
'custom-field-update'
])
const isCollapsed = ref(true)
@@ -409,16 +344,19 @@ const documentIcon = doc => getFileIcon({ name: doc.filename || doc.name, mime:
const previewDocument = ref(null)
const previewVisible = ref(false)
const selectedComponentModelId = computed(() => props.component.composantModelId || props.component.composantModel?.id || '')
const componentModelOptionsList = computed(() => {
const provided = props.componentModelOptionsProvider(props.component)
return Array.isArray(provided) && provided.length ? provided : props.componentModelOptions
})
const pieceModelOptionsList = computed(() => props.pieceModelOptionsProvider(props.component) || [])
const childComponents = computed(() => {
const list = props.component.subcomponents || props.component.subComponents || []
return Array.isArray(list) ? list : []
})
const extractStructureCustomFields = (structure) => {
if (!structure || typeof structure !== 'object') {
return []
}
const customFields = structure.customFields
return Array.isArray(customFields) ? customFields : []
}
function fieldKeyFromNameAndType(name, type) {
const normalizedName = typeof name === 'string' ? name : ''
const normalizedType = typeof type === 'string' ? type : ''
@@ -520,24 +458,49 @@ function mergeFieldDefinitionsWithValues(definitions, values) {
return merged
}
const componentDefinitionSources = computed(() => {
const requirement = props.component.typeMachineComponentRequirement || {}
const type = requirement.typeComposant || props.component.typeComposant || {}
const definitions = []
const pushFields = (collection) => {
if (Array.isArray(collection)) {
definitions.push(...collection)
}
}
pushFields(props.component.customFields)
pushFields(props.component.definition?.customFields)
pushFields(type.customFields)
pushFields(requirement.customFields)
pushFields(requirement.definition?.customFields)
;[
props.component.definition?.structure,
type.structure,
type.componentSkeleton,
requirement.structure,
requirement.componentSkeleton,
].forEach((structure) => {
const fields = extractStructureCustomFields(structure)
if (fields.length) {
definitions.push(...fields)
}
})
return definitions
})
const displayedCustomFields = computed(() =>
mergeFieldDefinitionsWithValues(
props.component.customFields,
componentDefinitionSources.value,
props.component.customFieldValues,
),
)
const candidateCustomFields = computed(() => {
const sources = [
props.component.customFieldValues?.map((value) => value?.customField),
props.component.typeComposant?.customFields,
props.component.typeMachineComponentRequirement?.typeComposant?.customFields,
props.component.composantModel?.customFields,
props.component.typeMachineComponentRequirement?.customFields,
props.component.customFields,
]
const candidateCustomFields = computed(() => {
const map = new Map()
sources.forEach((collection) => {
const register = (collection) => {
if (!Array.isArray(collection)) {
return
}
@@ -553,7 +516,10 @@ const candidateCustomFields = computed(() => {
}
map.set(key, item)
})
})
}
register(props.component.customFieldValues?.map((value) => value?.customField))
register(componentDefinitionSources.value)
return Array.from(map.values())
})
@@ -838,25 +804,6 @@ const updatePieceCustomField = (fieldUpdate) => {
emit('edit-piece', { ...fieldUpdate, type: 'custom-field-update' })
}
const assignComponentModel = (value) => {
const previousModelId = props.component.composantModelId || props.component.composantModel?.id || null
const previousModel = props.component.composantModel || null
props.component.composantModelId = value || null
if (!value) {
props.component.composantModel = null
}
emit('assign-model', {
componentId: props.component.id,
composantModelId: value || null,
previousModelId,
previousModel
})
}
const emitAssignPieceModel = (payload) => {
emit('assign-piece-model', payload)
}
const ensureDocumentsLoaded = async () => {
if (documentsLoaded.value || !props.component?.id) { return }
await refreshDocuments()

View File

@@ -36,12 +36,6 @@
"Non défini"
}}
</span>
<span
v-if="piece.pieceModel"
class="badge badge-outline badge-primary badge-sm"
>
Modèle : {{ piece.pieceModel.name }}
</span>
<span
v-if="piece.parentComponentName"
class="badge badge-ghost badge-sm"
@@ -104,33 +98,6 @@
</div>
</div>
<div v-if="isEditMode && piece.typeMachinePieceRequirement" class="mt-3">
<label class="label">
<span class="label-text text-sm font-medium">Modèle de pièce</span>
<span class="label-text-alt text-xs">
{{
piece.typeMachinePieceRequirement.label ||
piece.typeMachinePieceRequirement.typePiece?.name ||
"Groupe"
}}
</span>
</label>
<select
:value="selectedPieceModelId"
class="select select-bordered select-sm w-full"
@change="assignPieceModel($event.target.value)"
>
<option value="">Définir manuellement</option>
<option
v-for="model in pieceModelOptions"
:key="model.id"
:value="model.id"
>
{{ model.name }}
</option>
</select>
</div>
<!-- Champs personnalisés de la pièce -->
<div
v-if="displayedCustomFields.length"
@@ -381,17 +348,12 @@ const props = defineProps({
type: Boolean,
default: false,
},
pieceModelOptions: {
type: Array,
default: () => [],
},
});
const emit = defineEmits([
"update",
"edit",
"custom-field-update",
"assign-model",
]);
// Données locales isolées pour cette pièce
@@ -412,10 +374,13 @@ const documentIcon = (doc) =>
getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType });
const previewDocument = ref(null);
const previewVisible = ref(false);
const selectedPieceModelId = computed(
() => props.piece.pieceModelId || props.piece.pieceModel?.id || ""
);
const pieceModelOptions = computed(() => props.pieceModelOptions || []);
const extractStructureCustomFields = (structure) => {
if (!structure || typeof structure !== "object") {
return [];
}
const customFields = structure.customFields;
return Array.isArray(customFields) ? customFields : [];
};
function fieldKeyFromNameAndType(name, type) {
const normalizedName = typeof name === 'string' ? name : '';
const normalizedType = typeof type === 'string' ? type : '';
@@ -529,25 +494,53 @@ function mergeFieldDefinitionsWithValues(definitions, values) {
return merged;
}
const pieceDefinitionSources = computed(() => {
const requirement = props.piece.typeMachinePieceRequirement || {};
const type = requirement.typePiece || props.piece.typePiece || {};
const definitions = [];
const pushFields = (collection) => {
if (Array.isArray(collection)) {
definitions.push(...collection);
}
};
pushFields(props.piece.customFields);
pushFields(props.piece.definition?.customFields);
pushFields(props.piece.typePiece?.customFields);
pushFields(type.customFields);
pushFields(requirement.typePiece?.customFields);
pushFields(requirement.customFields);
pushFields(requirement.definition?.customFields);
[
props.piece.definition?.structure,
props.piece.typePiece?.structure,
type.structure,
type.pieceSkeleton,
props.piece.typePiece?.pieceSkeleton,
requirement.structure,
requirement.pieceSkeleton,
].forEach((structure) => {
const fields = extractStructureCustomFields(structure);
if (fields.length) {
definitions.push(...fields);
}
});
return definitions;
});
const displayedCustomFields = computed(() =>
mergeFieldDefinitionsWithValues(
props.piece.customFields,
pieceDefinitionSources.value,
props.piece.customFieldValues,
),
);
const candidateCustomFields = computed(() => {
const sources = [
props.piece.customFieldValues?.map((value) => value?.customField),
props.piece.typePiece?.customFields,
props.piece.typeMachinePieceRequirement?.typePiece?.customFields,
props.piece.typeMachinePieceRequirement?.customFields,
props.piece.pieceModel?.customFields,
props.piece.customFields,
];
const map = new Map();
sources.forEach((collection) => {
const register = (collection) => {
if (!Array.isArray(collection)) {
return;
}
@@ -563,7 +556,10 @@ const candidateCustomFields = computed(() => {
}
map.set(key, item);
});
});
};
register(props.piece.customFieldValues?.map((value) => value?.customField));
register(pieceDefinitionSources.value);
return Array.from(map.values());
});
@@ -827,22 +823,6 @@ const updatePiece = () => {
});
};
const assignPieceModel = (value) => {
const previousModelId =
props.piece.pieceModelId || props.piece.pieceModel?.id || null;
const previousModel = props.piece.pieceModel || null;
props.piece.pieceModelId = value || null;
if (!value) {
props.piece.pieceModel = null;
}
emit("assign-model", {
pieceId: props.piece.id,
pieceModelId: value || null,
previousModelId,
previousModel,
});
};
const updateCustomFieldValue = async (fieldValueId, field) => {
if (!field || resolveFieldReadOnly(field)) {
return;

View File

@@ -1,199 +0,0 @@
<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.alias || 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.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 || 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 = 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,
},
})
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(() => {
const list = props.node?.subcomponents || props.node?.subComponents || []
return Array.isArray(list) && list.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>

View File

@@ -9,7 +9,7 @@
Les champs marqués d'un astérisque sont obligatoires.
</p>
<form class="mt-6 space-y-5" @submit.prevent="handleSubmit">
<form class="mt-6 space-y-6" @submit.prevent="handleSubmit">
<div>
<label class="label" for="model-type-name">
<span class="label-text">Nom *</span>
@@ -79,10 +79,59 @@
<p class="mt-1 text-xs text-base-content/70">Saisissez des informations complémentaires (facultatif).</p>
</div>
<div class="divider"></div>
<section class="space-y-4">
<header>
<h3 class="text-lg font-semibold text-base-content">
Structure du squelette
</h3>
<p class="mt-1 text-sm text-base-content/70">
Définissez la structure canonique appliquée lors de la création des composants ou pièces de cette catégorie.
</p>
</header>
<div
v-if="structureLoading"
class="flex items-center justify-center rounded-lg border border-dashed border-base-300 py-12"
>
<span class="loading loading-spinner loading-lg" aria-hidden="true"></span>
<span class="ml-3 text-sm text-base-content/70">Chargement du squelette…</span>
</div>
<template v-else>
<div
v-if="form.category === 'COMPONENT'"
class="space-y-3 rounded-lg border border-base-300 p-4"
>
<p class="text-sm text-base-content/70">
Aperçu :
<span class="font-medium text-base-content">{{ componentStructurePreview }}</span>
</p>
<ComponentModelStructureEditor v-model="componentStructure" />
</div>
<div
v-else
class="space-y-3 rounded-lg border border-base-300 p-4"
>
<p class="text-sm text-base-content/70">
Aperçu :
<span class="font-medium text-base-content">{{ pieceStructurePreview }}</span>
</p>
<PieceModelStructureEditor v-model="pieceStructure" />
</div>
</template>
</section>
<div class="modal-action">
<button type="button" class="btn btn-ghost" @click="close">Annuler</button>
<button type="submit" class="btn btn-primary" :disabled="saving">
<span v-if="saving" class="loading loading-spinner loading-sm" aria-hidden="true"></span>
<button type="submit" class="btn btn-primary" :disabled="isSubmitDisabled">
<span
v-if="saving"
class="loading loading-spinner loading-sm"
aria-hidden="true"
></span>
{{ submitLabel }}
</button>
</div>
@@ -95,6 +144,18 @@
<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch, onBeforeUnmount } from 'vue';
import ComponentModelStructureEditor from '~/components/ComponentModelStructureEditor.vue';
import PieceModelStructureEditor from '~/components/PieceModelStructureEditor.vue';
import {
clonePieceStructure,
cloneStructure,
defaultPieceStructure,
defaultStructure,
formatPieceStructurePreview,
formatStructurePreview,
normalizePieceStructureForSave,
normalizeStructureForSave,
} from '~/shared/modelUtils';
import type { ModelCategory, ModelTypePayload } from '~/services/modelTypes';
const props = defineProps<{
@@ -104,6 +165,7 @@ const props = defineProps<{
initialData?: Partial<ModelTypePayload> | null;
saving?: boolean;
lockCategory?: boolean;
structureLoading?: boolean;
}>();
const emit = defineEmits<{
@@ -112,12 +174,14 @@ const emit = defineEmits<{
}>();
const lockCategory = computed(() => props.lockCategory ?? false);
const structureLoading = computed(() => props.structureLoading ?? false);
const form = reactive<ModelTypePayload>({
name: '',
code: '',
category: 'COMPONENT',
notes: '',
structure: undefined,
});
const errors = reactive<{ name?: string; code?: string }>({});
@@ -126,6 +190,9 @@ const nameInput = ref<HTMLInputElement | null>(null);
const codePattern = /^[a-z0-9\-_.]+$/i;
const componentStructure = ref(normalizeStructureForSave(defaultStructure()));
const pieceStructure = ref(normalizePieceStructureForSave(defaultPieceStructure()));
const resetForm = () => {
form.name = props.initialData?.name ?? '';
form.code = props.initialData?.code ?? '';
@@ -133,6 +200,21 @@ const resetForm = () => {
form.notes = props.initialData?.notes ?? '';
errors.name = undefined;
errors.code = undefined;
const incomingStructure = props.initialData?.structure;
if (form.category === 'COMPONENT') {
componentStructure.value = normalizeStructureForSave(
incomingStructure && props.initialData?.category === 'COMPONENT'
? incomingStructure
: defaultStructure(),
);
} else {
pieceStructure.value = normalizePieceStructureForSave(
incomingStructure && props.initialData?.category === 'PIECE'
? incomingStructure
: defaultPieceStructure(),
);
}
};
const close = () => {
@@ -146,6 +228,7 @@ const modalTitle = computed(() =>
const submitLabel = computed(() => (props.mode === 'edit' ? 'Enregistrer' : 'Créer'));
const saving = computed(() => props.saving ?? false);
const isSubmitDisabled = computed(() => saving.value || structureLoading.value);
const onEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
@@ -179,11 +262,25 @@ const handleSubmit = () => {
return;
}
emit('save', {
const common = {
name: form.name.trim(),
code: form.code.trim(),
category: form.category,
notes: form.notes?.trim() ? form.notes.trim() : undefined,
};
if (form.category === 'COMPONENT') {
emit('save', {
...common,
category: 'COMPONENT',
structure: normalizeStructureForSave(cloneStructure(componentStructure.value)),
});
return;
}
emit('save', {
...common,
category: 'PIECE',
structure: normalizePieceStructureForSave(clonePieceStructure(pieceStructure.value)),
});
};
@@ -200,6 +297,53 @@ watch(
},
);
watch(
() => form.category,
(category, previous) => {
if (category === previous) {
return;
}
if (category === 'COMPONENT' && previous !== 'COMPONENT') {
componentStructure.value = normalizeStructureForSave(defaultStructure());
}
if (category === 'PIECE' && previous !== 'PIECE') {
pieceStructure.value = normalizePieceStructureForSave(defaultPieceStructure());
}
},
);
watch(
() => props.initialData?.structure,
(value) => {
if (!props.visible) {
return;
}
if (form.category === 'COMPONENT') {
componentStructure.value = normalizeStructureForSave(
props.initialData?.category === 'COMPONENT' && value
? value
: componentStructure.value,
);
} else if (form.category === 'PIECE') {
pieceStructure.value = normalizePieceStructureForSave(
props.initialData?.category === 'PIECE' && value
? value
: pieceStructure.value,
);
}
},
);
const componentStructurePreview = computed(() =>
formatStructurePreview(componentStructure.value),
);
const pieceStructurePreview = computed(() =>
formatPieceStructurePreview(pieceStructure.value),
);
watch(
() => props.initialData,
() => {

View File

@@ -41,6 +41,7 @@
:initial-data="modalInitialData"
:saving="saving"
:lock-category="!allowCategorySwitch"
:structure-loading="structureLoading"
@close="closeModal"
@save="handleSave"
/>
@@ -56,6 +57,7 @@ import EditModal from "~/components/model-types/EditModal.vue";
import {
createModelType,
deleteModelType,
getModelType,
listModelTypes,
updateModelType,
type ModelCategory,
@@ -97,6 +99,7 @@ const isModalOpen = ref(false);
const modalMode = ref<"create" | "edit">("create");
const modalInitialData = ref<Partial<ModelTypePayload> | null>(null);
const editingId = ref<string | null>(null);
const structureLoading = ref(false);
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
let activeController: AbortController | null = null;
@@ -234,10 +237,11 @@ const openCreateModal = () => {
modalMode.value = "create";
modalInitialData.value = null;
editingId.value = null;
structureLoading.value = false;
isModalOpen.value = true;
};
const openEditModal = (item: ModelType) => {
const openEditModal = async (item: ModelType) => {
modalMode.value = "edit";
editingId.value = item.id;
modalInitialData.value = {
@@ -245,8 +249,26 @@ const openEditModal = (item: ModelType) => {
code: item.code,
category: item.category,
notes: item.notes ?? item.description ?? "",
structure: item.structure,
};
isModalOpen.value = true;
structureLoading.value = true;
try {
const response = await getModelType(item.id);
modalInitialData.value = {
name: response.name,
code: response.code,
category: response.category,
notes: response.notes ?? response.description ?? "",
structure: response.structure,
};
} catch (error) {
showError(extractErrorMessage(error));
} finally {
structureLoading.value = false;
}
};
const closeModal = () => {