Extract 2 composables (useMachineDetailData, useMachineSkeletonEditor) and 7 UI components from machine/[id].vue, reducing it from 2989 to 219 LOC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
194 lines
8.6 KiB
Vue
194 lines
8.6 KiB
Vue
<template>
|
|
<div
|
|
v-if="componentRequirementGroups.length || pieceRequirementGroups.length || productRequirementGroups.length"
|
|
class="card bg-base-100 shadow-lg"
|
|
>
|
|
<div class="card-body space-y-6">
|
|
<div>
|
|
<h2 class="card-title">Structure sélectionnée</h2>
|
|
<p class="text-sm text-gray-500">
|
|
Synthèse des familles définies dans le type et des modèles utilisés pour cette machine.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Component requirement groups -->
|
|
<div v-if="componentRequirementGroups.length" class="space-y-4">
|
|
<h3 class="text-sm font-semibold text-gray-700">Composants</h3>
|
|
<div
|
|
v-for="group in componentRequirementGroups"
|
|
:key="group.requirement.id"
|
|
class="rounded-lg border border-base-200 p-4"
|
|
>
|
|
<div class="flex flex-wrap items-center justify-between gap-2 mb-3">
|
|
<div>
|
|
<h4 class="font-medium text-sm">
|
|
{{ group.requirement.label || group.requirement.typeComposant?.name || 'Famille de composants' }}
|
|
</h4>
|
|
<p class="text-xs text-gray-500">
|
|
Type : {{ group.requirement.typeComposant?.name || 'Non défini' }} · Min {{ group.requirement.minCount ?? (group.requirement.required ? 1 : 0) }} · Max {{ group.requirement.maxCount ?? '∞' }}
|
|
</p>
|
|
</div>
|
|
<span class="badge badge-outline badge-sm">{{ group.components.length }} composant(s)</span>
|
|
</div>
|
|
|
|
<div v-if="group.components.length" class="space-y-2">
|
|
<div
|
|
v-for="component in group.components"
|
|
:key="component.id"
|
|
class="flex flex-wrap items-center gap-2 text-sm"
|
|
>
|
|
<span class="font-medium">{{ component.name }}</span>
|
|
<span v-if="component.parentComposantId" class="text-xs text-gray-500">
|
|
(Sous-composant)
|
|
</span>
|
|
<div
|
|
v-if="summarizeCustomFields(component.customFields || []).length"
|
|
class="w-full flex flex-wrap gap-2 text-xs text-gray-600"
|
|
>
|
|
<span
|
|
v-for="field in summarizeCustomFields(component.customFields || [])"
|
|
:key="field.key"
|
|
class="badge badge-ghost badge-sm whitespace-pre-wrap"
|
|
>
|
|
<span class="font-medium">{{ field.label }} :</span>
|
|
<span class="ml-1">{{ field.value }}</span>
|
|
</span>
|
|
</div>
|
|
<SkeletonProductDisplay :product-display="component.__productDisplay" />
|
|
</div>
|
|
</div>
|
|
<p v-else class="text-xs text-gray-500">Aucun composant rattaché à ce groupe.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Piece requirement groups -->
|
|
<div v-if="pieceRequirementGroups.length" class="space-y-4">
|
|
<h3 class="text-sm font-semibold text-gray-700">Pièces principales</h3>
|
|
<div
|
|
v-for="group in pieceRequirementGroups"
|
|
:key="group.requirement.id"
|
|
class="rounded-lg border border-base-200 p-4"
|
|
>
|
|
<div class="flex flex-wrap items-center justify-between gap-2 mb-3">
|
|
<div>
|
|
<h4 class="font-medium text-sm">
|
|
{{ group.requirement.label || group.requirement.typePiece?.name || 'Groupe de pièces' }}
|
|
</h4>
|
|
<p class="text-xs text-gray-500">
|
|
Type : {{ group.requirement.typePiece?.name || 'Non défini' }} · Min {{ group.requirement.minCount ?? (group.requirement.required ? 1 : 0) }} · Max {{ group.requirement.maxCount ?? '∞' }}
|
|
</p>
|
|
</div>
|
|
<span class="badge badge-outline badge-sm">{{ group.pieces.length }} pièce(s)</span>
|
|
</div>
|
|
|
|
<div v-if="group.pieces.length" class="space-y-2">
|
|
<div
|
|
v-for="piece in group.pieces"
|
|
:key="piece.id"
|
|
class="flex flex-wrap items-center gap-2 text-sm"
|
|
>
|
|
<span class="font-medium">{{ piece.name }}</span>
|
|
<span v-if="piece.parentComponentName" class="text-xs text-gray-500">
|
|
(Rattachée à {{ piece.parentComponentName }})
|
|
</span>
|
|
<div
|
|
v-if="summarizeCustomFields(piece.customFields || []).length"
|
|
class="w-full flex flex-wrap gap-2 text-xs text-gray-600"
|
|
>
|
|
<span
|
|
v-for="field in summarizeCustomFields(piece.customFields || [])"
|
|
:key="field.key"
|
|
class="badge badge-ghost badge-sm whitespace-pre-wrap"
|
|
>
|
|
<span class="font-medium">{{ field.label }} :</span>
|
|
<span class="ml-1">{{ field.value }}</span>
|
|
</span>
|
|
</div>
|
|
<SkeletonProductDisplay :product-display="piece.__productDisplay" />
|
|
</div>
|
|
</div>
|
|
<p v-else class="text-xs text-gray-500">Aucune pièce rattachée à ce groupe.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Product requirement groups -->
|
|
<div v-if="productRequirementGroups.length" class="space-y-4">
|
|
<h3 class="text-sm font-semibold text-gray-700">Produits requis</h3>
|
|
<div
|
|
v-for="group in productRequirementGroups"
|
|
:key="group.requirement.id"
|
|
class="rounded-lg border border-base-200 p-4"
|
|
>
|
|
<div class="flex flex-wrap items-center justify-between gap-2 mb-3">
|
|
<div>
|
|
<h4 class="font-medium text-sm">
|
|
{{ group.requirement.label || group.requirement.typeProduct?.name || 'Groupe de produits' }}
|
|
</h4>
|
|
<p class="text-xs text-gray-500">
|
|
Catégorie : {{ group.requirement.typeProduct?.name || 'Non définie' }} · Min {{ group.requirement.minCount ?? (group.requirement.required ? 1 : 0) }} · Max {{ group.requirement.maxCount ?? '∞' }}
|
|
</p>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="badge badge-outline badge-sm">Total {{ group.totalCount }}</span>
|
|
<span class="badge badge-ghost badge-sm">Direct {{ group.directProducts.length }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-xs text-gray-500 mb-3">
|
|
Via composants : {{ group.componentCount }} • Via pièces : {{ group.pieceCount }}
|
|
</div>
|
|
|
|
<div v-if="group.directProducts.length" class="space-y-2">
|
|
<div
|
|
v-for="product in group.directProducts"
|
|
:key="product.id || product.name"
|
|
class="rounded border border-base-200 bg-base-200/60 p-3 text-sm"
|
|
>
|
|
<div class="font-medium">{{ product.name }}</div>
|
|
<div v-if="product.reference" class="text-xs text-gray-500">
|
|
Référence : {{ product.reference }}
|
|
</div>
|
|
<div v-if="product.supplierLabel" class="text-xs text-gray-500">
|
|
Fournisseurs : {{ product.supplierLabel }}
|
|
</div>
|
|
<div v-if="product.priceLabel" class="text-xs text-gray-500">
|
|
Prix indicatif : {{ product.priceLabel }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p v-else class="text-xs text-gray-500">
|
|
Aucune sélection directe. Couverture assurée via composants ou pièces associés.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { defineComponent } from 'vue'
|
|
import { summarizeCustomFields } from '~/shared/utils/customFieldUtils'
|
|
|
|
defineProps<{
|
|
componentRequirementGroups: any[]
|
|
pieceRequirementGroups: any[]
|
|
productRequirementGroups: any[]
|
|
}>()
|
|
|
|
const SkeletonProductDisplay = defineComponent({
|
|
name: 'SkeletonProductDisplay',
|
|
props: {
|
|
productDisplay: { type: Object, default: null },
|
|
},
|
|
template: `
|
|
<div v-if="productDisplay" class="w-full text-xs text-gray-600 space-y-1">
|
|
<div><span class="font-medium">Produit :</span> <span>{{ productDisplay.name }}</span></div>
|
|
<div v-if="productDisplay.category"><span class="font-medium">Catégorie :</span> <span>{{ productDisplay.category }}</span></div>
|
|
<div v-if="productDisplay.reference"><span class="font-medium">Référence :</span> <span>{{ productDisplay.reference }}</span></div>
|
|
<div v-if="productDisplay.suppliers"><span class="font-medium">Fournisseurs :</span> <span>{{ productDisplay.suppliers }}</span></div>
|
|
<div v-if="productDisplay.price"><span class="font-medium">Prix indicatif :</span> <span>{{ productDisplay.price }}</span></div>
|
|
</div>
|
|
`,
|
|
})
|
|
</script>
|