Allow component catalog to instantiate components without machine
This commit is contained in:
@@ -153,7 +153,7 @@
|
||||
Instancier un composant
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500">
|
||||
Sélectionnez la machine et le requirement cible puis ajustez les informations d'override avant la création.
|
||||
Renseignez les informations du composant instancié à partir du squelette de la catégorie sélectionnée.
|
||||
</p>
|
||||
<p v-if="selectedType" class="badge badge-outline badge-sm">
|
||||
Catégorie : {{ selectedType.name }}
|
||||
@@ -161,79 +161,6 @@
|
||||
</div>
|
||||
|
||||
<form class="space-y-4" @submit.prevent="submitCreation">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Machine cible</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="creationForm.machineId"
|
||||
class="select select-bordered select-sm md:select-md"
|
||||
:disabled="machinesLoading || submitting"
|
||||
required
|
||||
>
|
||||
<option value="">Sélectionner une machine</option>
|
||||
<option
|
||||
v-for="machine in machines"
|
||||
:key="machine.id"
|
||||
:value="machine.id"
|
||||
>
|
||||
{{ machine.name }}
|
||||
</option>
|
||||
</select>
|
||||
<p v-if="machinesLoading" class="text-xs text-gray-500 mt-1">
|
||||
Chargement des machines...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Requirement</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="creationForm.requirementId"
|
||||
class="select select-bordered select-sm md:select-md"
|
||||
:disabled="requirementLoading || !requirementOptions.length || submitting"
|
||||
required
|
||||
>
|
||||
<option value="">Sélectionner un requirement</option>
|
||||
<option
|
||||
v-for="requirement in requirementOptions"
|
||||
:key="requirement.id"
|
||||
:value="requirement.id"
|
||||
>
|
||||
{{ resolveRequirementLabel(requirement) }}
|
||||
</option>
|
||||
</select>
|
||||
<p v-if="requirementLoading" class="text-xs text-gray-500 mt-1">
|
||||
Chargement des requirements de la machine...
|
||||
</p>
|
||||
<p
|
||||
v-else-if="creationForm.machineId && !requirementOptions.length"
|
||||
class="text-xs text-error mt-1"
|
||||
>
|
||||
Cette machine n'a pas de requirement de composant configuré.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="selectedRequirement"
|
||||
class="rounded-lg border border-base-200 bg-base-200/60 p-4 text-xs text-base-content/80 space-y-1"
|
||||
>
|
||||
<div class="flex flex-wrap gap-2 items-center">
|
||||
<span class="font-medium text-sm">Requirement sélectionné :</span>
|
||||
<span class="badge badge-outline badge-sm">{{ resolveRequirementLabel(selectedRequirement) }}</span>
|
||||
</div>
|
||||
<p v-if="selectedRequirement.typeComposant?.name" class="text-xs">
|
||||
Type attendu : {{ selectedRequirement.typeComposant.name }}
|
||||
</p>
|
||||
<p v-if="selectedRequirement.maxCount !== null && selectedRequirement.maxCount !== undefined" class="text-xs">
|
||||
Capacité : {{ selectedRequirement.minCount ?? (selectedRequirement.required ? 1 : 0) }} -
|
||||
{{ selectedRequirement.maxCount ?? '∞' }} élément(s)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
@@ -311,13 +238,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
||||
import { useComponentTypes } from '~/composables/useComponentTypes'
|
||||
import { useMachines } from '~/composables/useMachines'
|
||||
import { useComposants } from '~/composables/useComposants'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { useApi } from '~/composables/useApi'
|
||||
import { formatStructurePreview, sanitizeDefinitionOverrides } from '~/shared/modelUtils'
|
||||
import type { ComponentModelStructure } from '~/shared/types/inventory'
|
||||
import type { ModelType } from '~/services/modelTypes'
|
||||
@@ -329,54 +254,26 @@ interface ComponentCatalogType extends ModelType {
|
||||
}
|
||||
|
||||
const { componentTypes, loadComponentTypes, loadingComponentTypes } = useComponentTypes()
|
||||
const { machines, loadMachines, loading: machinesLoadingRef } = useMachines()
|
||||
const { createComposant } = useComposants()
|
||||
const toast = useToast()
|
||||
const { apiCall } = useApi()
|
||||
|
||||
const creationModalOpen = ref(false)
|
||||
const selectedType = ref<ComponentCatalogType | null>(null)
|
||||
const submitting = ref(false)
|
||||
const requirementLoading = ref(false)
|
||||
const creationForm = reactive({
|
||||
machineId: '' as string,
|
||||
requirementId: '' as string,
|
||||
name: '' as string,
|
||||
reference: '' as string,
|
||||
constructeurId: null as string | null,
|
||||
prix: '' as string,
|
||||
})
|
||||
|
||||
const machineRequirementCache = reactive<Record<string, { requirements: any[] }>>({})
|
||||
const lastSuggestedName = ref('')
|
||||
let requirementRequestToken = 0
|
||||
|
||||
const loadingTypes = computed(() => loadingComponentTypes.value)
|
||||
const componentTypeList = computed<ComponentCatalogType[]>(() =>
|
||||
(componentTypes.value || [])
|
||||
.filter((item: any) => item?.category === 'COMPONENT') as ComponentCatalogType[],
|
||||
)
|
||||
const machinesLoading = computed(() => machinesLoadingRef.value)
|
||||
|
||||
const requirementOptions = computed(() => {
|
||||
const machineId = creationForm.machineId
|
||||
if (!machineId) {
|
||||
return []
|
||||
}
|
||||
const entry = machineRequirementCache[machineId]
|
||||
if (!entry) {
|
||||
return []
|
||||
}
|
||||
return Array.isArray(entry.requirements) ? entry.requirements : []
|
||||
})
|
||||
|
||||
const selectedRequirement = computed(() => {
|
||||
return requirementOptions.value.find((requirement: any) => requirement.id === creationForm.requirementId) || null
|
||||
})
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return Boolean(creationForm.machineId && creationForm.requirementId && !submitting.value && !requirementLoading.value)
|
||||
})
|
||||
const canSubmit = computed(() => Boolean(selectedType.value && !submitting.value))
|
||||
|
||||
const getCategoryCustomFields = (type: ComponentCatalogType) => {
|
||||
return Array.isArray(type?.customFields) ? type.customFields : []
|
||||
@@ -445,33 +342,22 @@ const resolveSubcomponentLabel = (node: Record<string, any>) => {
|
||||
return parts.length ? parts.join(' • ') : 'Sous-composant'
|
||||
}
|
||||
|
||||
const resolveRequirementLabel = (requirement: any) => {
|
||||
return requirement?.label || requirement?.typeComposant?.name || 'Requirement'
|
||||
}
|
||||
|
||||
const clearCreationForm = () => {
|
||||
creationForm.machineId = ''
|
||||
creationForm.requirementId = ''
|
||||
creationForm.name = ''
|
||||
creationForm.reference = ''
|
||||
creationForm.constructeurId = null
|
||||
creationForm.prix = ''
|
||||
lastSuggestedName.value = ''
|
||||
}
|
||||
|
||||
const resetCreationFormForType = () => {
|
||||
clearCreationForm()
|
||||
creationForm.name = selectedType.value?.name || ''
|
||||
lastSuggestedName.value = creationForm.name
|
||||
}
|
||||
|
||||
const openCreationModal = async (type: ComponentCatalogType) => {
|
||||
selectedType.value = type
|
||||
resetCreationFormForType()
|
||||
creationModalOpen.value = true
|
||||
if (!machines.value?.length) {
|
||||
await loadMachines()
|
||||
}
|
||||
}
|
||||
|
||||
const closeCreationModal = () => {
|
||||
@@ -480,75 +366,14 @@ const closeCreationModal = () => {
|
||||
clearCreationForm()
|
||||
}
|
||||
|
||||
const ensureMachineRequirements = async (machineId: string) => {
|
||||
if (!machineId || machineRequirementCache[machineId]) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = ++requirementRequestToken
|
||||
requirementLoading.value = true
|
||||
try {
|
||||
const result = await apiCall(`/machines/${machineId}`, { method: 'GET' })
|
||||
if (result.success) {
|
||||
const requirements = result.data?.typeMachine?.componentRequirements || []
|
||||
machineRequirementCache[machineId] = { requirements }
|
||||
}
|
||||
} finally {
|
||||
if (requestId === requirementRequestToken) {
|
||||
requirementLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => creationForm.machineId,
|
||||
async (machineId) => {
|
||||
creationForm.requirementId = ''
|
||||
if (!machineId) {
|
||||
return
|
||||
}
|
||||
await ensureMachineRequirements(machineId)
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => creationForm.requirementId,
|
||||
(requirementId) => {
|
||||
if (!selectedType.value) {
|
||||
return
|
||||
}
|
||||
const requirement = requirementId ? selectedRequirement.value : null
|
||||
const suggestion =
|
||||
requirement?.label || requirement?.typeComposant?.name || selectedType.value?.name || ''
|
||||
|
||||
if (!creationForm.name || creationForm.name === lastSuggestedName.value) {
|
||||
creationForm.name = suggestion
|
||||
}
|
||||
lastSuggestedName.value = suggestion
|
||||
},
|
||||
)
|
||||
|
||||
const submitCreation = async () => {
|
||||
if (!creationForm.machineId || !creationForm.requirementId) {
|
||||
toast.showError('Sélectionnez une machine et un requirement avant de continuer.')
|
||||
return
|
||||
}
|
||||
if (!selectedType.value) {
|
||||
toast.showError('Aucune catégorie sélectionnée.')
|
||||
return
|
||||
}
|
||||
|
||||
const payload: Record<string, any> = {
|
||||
machineId: creationForm.machineId,
|
||||
typeMachineComponentRequirementId: creationForm.requirementId,
|
||||
}
|
||||
|
||||
const requirement = selectedRequirement.value
|
||||
if (selectedType.value.id) {
|
||||
const requirementTypeId = requirement?.typeComposantId || null
|
||||
if (!requirementTypeId || requirementTypeId !== selectedType.value.id) {
|
||||
payload.typeComposantId = selectedType.value.id
|
||||
}
|
||||
typeComposantId: selectedType.value.id,
|
||||
}
|
||||
|
||||
const overrides = sanitizeDefinitionOverrides({
|
||||
@@ -579,9 +404,6 @@ const submitCreation = async () => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.allSettled([
|
||||
loadComponentTypes(),
|
||||
loadMachines(),
|
||||
])
|
||||
await loadComponentTypes()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user