refactor(machines) : remove TypeMachine skeleton system, simplify machine creation
- 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>
This commit is contained in:
207
app/components/machine/AddEntityToMachineModal.vue
Normal file
207
app/components/machine/AddEntityToMachineModal.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user