Extract useMachineCreatePage composable and 5 preview/selector components from machines/new.vue, reducing it from 1231 to 196 LOC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
206 lines
7.9 KiB
Vue
206 lines
7.9 KiB
Vue
<template>
|
|
<div v-if="preview" class="space-y-4">
|
|
<div class="border border-base-200 rounded-lg bg-base-100/80">
|
|
<div class="p-4 space-y-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="flex items-center gap-2 text-sm font-semibold text-gray-700">
|
|
<IconLucideEye class="w-4 h-4" aria-hidden="true" />
|
|
<span>Prévisualisation avant création</span>
|
|
</div>
|
|
<span class="badge" :class="getStatusBadgeClass(preview.status)">
|
|
{{ preview.status === 'ready' ? 'Prête à créer' : preview.status === 'warning' ? 'À compléter' : 'Bloquante' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div
|
|
v-for="field in preview.base.fields"
|
|
:key="field.key"
|
|
class="flex flex-col gap-1"
|
|
>
|
|
<span class="text-[11px] uppercase tracking-wide text-gray-500">{{ field.label }}</span>
|
|
<span
|
|
class="text-sm font-medium"
|
|
:class="field.status === 'missing'
|
|
? 'text-error'
|
|
: field.status === 'optional'
|
|
? 'text-gray-500 italic'
|
|
: 'text-gray-900'"
|
|
>
|
|
{{ field.display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2 text-xs text-gray-500">
|
|
<span class="badge badge-ghost badge-sm">Type : {{ preview.type.name }}</span>
|
|
<span v-if="preview.type.category" class="badge badge-ghost badge-sm">Catégorie : {{ preview.type.category }}</span>
|
|
<span class="badge badge-ghost badge-sm">Structure JSON : {{ preview.type.hasStructuredDefinition ? 'Oui' : 'Legacy' }}</span>
|
|
</div>
|
|
|
|
<!-- Base issues -->
|
|
<div v-if="preview.base.issues.length" class="rounded-md bg-warning/10 border border-warning/30 p-3 text-xs text-warning">
|
|
<p class="font-medium mb-1">
|
|
Informations générales incomplètes :
|
|
</p>
|
|
<ul class="space-y-1">
|
|
<li v-for="issue in preview.base.issues" :key="issue.message">
|
|
<button
|
|
type="button"
|
|
class="flex w-full items-start gap-2 text-left hover:underline"
|
|
@click="handleIssueClick(issue)"
|
|
>
|
|
<span class="mt-0.5 text-[8px] leading-none">•</span>
|
|
<span>{{ issue.message }}</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Component groups -->
|
|
<div v-if="preview.componentGroups.length" class="space-y-3">
|
|
<h5 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
Composants hérités
|
|
</h5>
|
|
<PreviewRequirementGroup
|
|
v-for="group in preview.componentGroups"
|
|
:key="group.id"
|
|
:group="group"
|
|
/>
|
|
</div>
|
|
<div v-else class="text-xs text-gray-500">
|
|
Aucun composant n'est requis pour ce type de machine.
|
|
</div>
|
|
|
|
<!-- Piece groups -->
|
|
<div v-if="preview.pieceGroups.length" class="space-y-3">
|
|
<h5 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
Pièces associées
|
|
</h5>
|
|
<PreviewRequirementGroup
|
|
v-for="group in preview.pieceGroups"
|
|
:key="group.id"
|
|
:group="group"
|
|
/>
|
|
</div>
|
|
<div v-else class="text-xs text-gray-500">
|
|
Aucun groupe de pièces à configurer pour ce type.
|
|
</div>
|
|
|
|
<!-- Product groups -->
|
|
<div v-if="preview.productGroups.length" class="space-y-3">
|
|
<h5 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
Produits requis
|
|
</h5>
|
|
<div
|
|
v-for="group in preview.productGroups"
|
|
:key="group.id"
|
|
:id="`product-group-${group.id}`"
|
|
class="border border-base-200 rounded-md p-3 space-y-3"
|
|
>
|
|
<div class="flex flex-wrap items-start justify-between gap-2">
|
|
<div>
|
|
<p class="text-sm font-semibold">
|
|
{{ group.label }}
|
|
</p>
|
|
<p class="text-xs text-gray-500">
|
|
Catégorie : {{ group.typeName }} · Min {{ group.min }} ·
|
|
{{ group.max !== null ? `Max ${group.max}` : 'Max ∞' }}
|
|
</p>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="badge badge-sm" :class="getStatusBadgeClass(group.status)">
|
|
Couverture : {{ group.count }}
|
|
</span>
|
|
<span class="badge badge-ghost badge-sm">
|
|
Direct {{ group.completed }} / {{ group.total || 0 }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="group.issues.length" class="rounded bg-warning/10 border border-warning/30 p-2 text-[11px] text-warning">
|
|
<ul class="list-disc pl-4 space-y-1">
|
|
<li v-for="issue in group.issues" :key="issue.message">
|
|
{{ issue.message }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<ul v-if="group.entries?.length" class="space-y-2">
|
|
<li
|
|
v-for="entry in group.entries"
|
|
:key="entry.key"
|
|
class="flex items-start gap-3"
|
|
>
|
|
<component
|
|
:is="entry.status === 'complete' ? IconLucideCheckCircle2 : IconLucideCircle"
|
|
class="w-4 h-4 mt-0.5"
|
|
:class="entry.status === 'complete' ? 'text-success' : 'text-gray-400'"
|
|
aria-hidden="true"
|
|
/>
|
|
<div class="flex-1">
|
|
<p class="text-sm font-medium" :class="entry.status === 'complete' ? 'text-gray-900' : 'text-gray-500'">
|
|
{{ entry.title }}
|
|
</p>
|
|
<p v-if="entry.subtitle" class="text-xs text-gray-500">
|
|
{{ entry.subtitle }}
|
|
</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<p v-else class="text-xs text-gray-500">
|
|
Couverture assurée via composants ou pièces liés.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Global issues -->
|
|
<div
|
|
v-if="preview.issues.length && preview.status !== 'ready'"
|
|
class="rounded-md border border-warning/30 bg-warning/10 p-3 text-xs text-warning"
|
|
>
|
|
<div class="flex items-start gap-2">
|
|
<IconLucideAlertTriangle class="w-4 h-4 mt-0.5" aria-hidden="true" />
|
|
<div class="space-y-1">
|
|
<p class="font-medium">
|
|
Points à vérifier avant la création :
|
|
</p>
|
|
<ul class="space-y-1">
|
|
<li v-for="issue in preview.issues" :key="`${issue.scope}-${issue.message}`">
|
|
<button
|
|
type="button"
|
|
class="flex w-full items-start gap-2 text-left hover:underline"
|
|
@click="handleIssueClick(issue)"
|
|
>
|
|
<span class="mt-0.5 text-[8px] leading-none">•</span>
|
|
<span>
|
|
<span class="font-medium">{{ issue.scope }} :</span> {{ issue.message }}
|
|
</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
getStatusBadgeClass,
|
|
handleIssueClick,
|
|
} from '~/composables/useMachineCreatePreview'
|
|
import PreviewRequirementGroup from './PreviewRequirementGroup.vue'
|
|
import IconLucideEye from '~icons/lucide/eye'
|
|
import IconLucideAlertTriangle from '~icons/lucide/alert-triangle'
|
|
import IconLucideCheckCircle2 from '~icons/lucide/check-circle-2'
|
|
import IconLucideCircle from '~icons/lucide/circle'
|
|
|
|
defineProps<{
|
|
preview: any
|
|
}>()
|
|
</script>
|