- Remove TypeEdit*, TypeInfoDisplay, MachineSkeletonSummary, MachineCreatePreview components - Remove machine-skeleton pages and type pages - Remove useMachineTypesApi, useMachineSkeletonEditor, useMachineCreateSelections composables - Add AddEntityToMachineModal for direct entity linking - Update machine detail/create pages for direct custom fields - Fix SearchSelect, category display, and ipartial search filters Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
208 lines
6.5 KiB
Vue
208 lines
6.5 KiB
Vue
<template>
|
|
<div v-if="open" class="modal modal-open">
|
|
<div class="modal-box max-w-xl w-full" style="overflow: visible">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-circle btn-ghost absolute right-3 top-3"
|
|
@click="handleClose"
|
|
>
|
|
×
|
|
</button>
|
|
|
|
<h3 class="font-bold text-lg mb-6">
|
|
{{ title }}
|
|
</h3>
|
|
|
|
<!-- Step 1: Choose category -->
|
|
<div class="form-control mb-5" style="position: relative; z-index: 20">
|
|
<label class="label pb-1">
|
|
<span class="label-text font-medium">Catégorie</span>
|
|
</label>
|
|
<SearchSelect
|
|
v-model="selectedTypeId"
|
|
:options="types"
|
|
:loading="loadingTypes"
|
|
:max-visible="8"
|
|
placeholder="Rechercher une catégorie..."
|
|
empty-text="Aucune catégorie disponible"
|
|
:option-label="(t: any) => t.name"
|
|
:option-description="(t: any) => t.code"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Step 2: Choose entity (visible only after category selected) -->
|
|
<div v-if="selectedTypeName" class="form-control mb-5" style="position: relative; z-index: 10">
|
|
<label class="label pb-1">
|
|
<span class="label-text font-medium">{{ entityLabel }}</span>
|
|
</label>
|
|
<SearchSelect
|
|
v-model="selectedEntityId"
|
|
:options="entities"
|
|
:loading="loadingEntities"
|
|
:max-visible="8"
|
|
:placeholder="`Rechercher ${entityLabelLower}...`"
|
|
:empty-text="`Aucun ${entityLabelLower} disponible dans cette catégorie`"
|
|
:option-label="entityOptionLabel"
|
|
:option-description="entityOptionDescription"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Summary of selection -->
|
|
<div v-if="selectedEntitySummary" class="bg-base-200 rounded-lg p-3 mb-4">
|
|
<p class="text-sm font-medium">{{ selectedEntitySummary.name }}</p>
|
|
<p v-if="selectedEntitySummary.reference" class="text-xs text-base-content/60">
|
|
Réf : {{ selectedEntitySummary.reference }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="modal-action mt-4 pt-4 border-t border-base-200" style="position: relative; z-index: 0">
|
|
<button type="button" class="btn btn-ghost" @click="handleClose">
|
|
Annuler
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
:disabled="!selectedEntityId"
|
|
@click="handleConfirm"
|
|
>
|
|
Ajouter
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-backdrop" @click="handleClose" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, watch } from 'vue'
|
|
import SearchSelect from '~/components/common/SearchSelect.vue'
|
|
import { useComponentTypes } from '~/composables/useComponentTypes'
|
|
import { usePieceTypes } from '~/composables/usePieceTypes'
|
|
import { useProductTypes } from '~/composables/useProductTypes'
|
|
import { useComposants } from '~/composables/useComposants'
|
|
import { usePieces } from '~/composables/usePieces'
|
|
import { useProducts } from '~/composables/useProducts'
|
|
|
|
type EntityKind = 'component' | 'piece' | 'product'
|
|
|
|
const props = defineProps<{
|
|
open: boolean
|
|
entityKind: EntityKind
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
close: []
|
|
confirm: [entityId: string]
|
|
}>()
|
|
|
|
const selectedTypeId = ref('')
|
|
const selectedEntityId = ref('')
|
|
const loadingEntities = ref(false)
|
|
const entities = ref<any[]>([])
|
|
|
|
const { componentTypes, loadingComponentTypes, loadComponentTypes } = useComponentTypes()
|
|
const { pieceTypes, loadingPieceTypes, loadPieceTypes } = usePieceTypes()
|
|
const { productTypes, loadingProductTypes, loadProductTypes } = useProductTypes()
|
|
const { loadComposants } = useComposants()
|
|
const { loadPieces } = usePieces()
|
|
const { loadProducts } = useProducts()
|
|
|
|
const title = computed(() => {
|
|
const labels: Record<EntityKind, string> = {
|
|
component: 'Ajouter un composant',
|
|
piece: 'Ajouter une pièce',
|
|
product: 'Ajouter un produit',
|
|
}
|
|
return labels[props.entityKind]
|
|
})
|
|
|
|
const entityLabel = computed(() => {
|
|
const labels: Record<EntityKind, string> = {
|
|
component: 'Composant',
|
|
piece: 'Pièce',
|
|
product: 'Produit',
|
|
}
|
|
return labels[props.entityKind]
|
|
})
|
|
|
|
const entityLabelLower = computed(() => entityLabel.value.toLowerCase())
|
|
|
|
const types = computed(() => {
|
|
if (props.entityKind === 'component') return componentTypes.value
|
|
if (props.entityKind === 'piece') return pieceTypes.value
|
|
return productTypes.value
|
|
})
|
|
|
|
const loadingTypes = computed(() => {
|
|
if (props.entityKind === 'component') return loadingComponentTypes.value
|
|
if (props.entityKind === 'piece') return loadingPieceTypes.value
|
|
return loadingProductTypes.value
|
|
})
|
|
|
|
const selectedTypeName = computed(() => {
|
|
if (!selectedTypeId.value) return ''
|
|
const found = types.value.find((t: any) => t.id === selectedTypeId.value)
|
|
return found?.name || ''
|
|
})
|
|
|
|
const entityOptionLabel = (e: any) => e.name || '(sans nom)'
|
|
const entityOptionDescription = (e: any) => e.reference || ''
|
|
|
|
const selectedEntitySummary = computed(() => {
|
|
if (!selectedEntityId.value || !entities.value.length) return null
|
|
const found = entities.value.find((e: any) => e.id === selectedEntityId.value)
|
|
if (!found) return null
|
|
return { name: found.name || '(sans nom)', reference: found.reference || null }
|
|
})
|
|
|
|
// Load types when modal opens
|
|
watch(() => props.open, async (isOpen) => {
|
|
if (!isOpen) return
|
|
if (props.entityKind === 'component') await loadComponentTypes()
|
|
else if (props.entityKind === 'piece') await loadPieceTypes()
|
|
else await loadProductTypes()
|
|
})
|
|
|
|
// Load entities when type changes
|
|
watch(selectedTypeId, async () => {
|
|
selectedEntityId.value = ''
|
|
entities.value = []
|
|
|
|
if (!selectedTypeName.value) return
|
|
|
|
loadingEntities.value = true
|
|
try {
|
|
if (props.entityKind === 'component') {
|
|
const result = await loadComposants({ typeName: selectedTypeName.value, itemsPerPage: 200 })
|
|
entities.value = result?.data?.items || []
|
|
} else if (props.entityKind === 'piece') {
|
|
const result = await loadPieces({ typeName: selectedTypeName.value, itemsPerPage: 200 })
|
|
entities.value = result?.data?.items || []
|
|
} else {
|
|
const result = await loadProducts({ typeName: selectedTypeName.value, itemsPerPage: 200 })
|
|
entities.value = result?.data?.items || []
|
|
}
|
|
} finally {
|
|
loadingEntities.value = false
|
|
}
|
|
})
|
|
|
|
const handleClose = () => {
|
|
resetState()
|
|
emit('close')
|
|
}
|
|
|
|
const handleConfirm = () => {
|
|
if (!selectedEntityId.value) return
|
|
emit('confirm', selectedEntityId.value)
|
|
resetState()
|
|
emit('close')
|
|
}
|
|
|
|
const resetState = () => {
|
|
selectedTypeId.value = ''
|
|
selectedEntityId.value = ''
|
|
entities.value = []
|
|
}
|
|
</script>
|