feat: replis hiérarchie composants et sections type

This commit is contained in:
Matthieu
2025-09-16 17:14:42 +02:00
parent c33a04b68e
commit 95da7c72db
7 changed files with 290 additions and 1879 deletions

View File

@@ -5,6 +5,8 @@
<ComponentItem <ComponentItem
:component="component" :component="component"
:is-edit-mode="isEditMode" :is-edit-mode="isEditMode"
:collapse-all="collapseAll"
:toggle-token="toggleToken"
@update="$emit('update', $event)" @update="$emit('update', $event)"
@edit-piece="$emit('edit-piece', $event)" @edit-piece="$emit('edit-piece', $event)"
/> />
@@ -23,8 +25,16 @@ defineProps({
isEditMode: { isEditMode: {
type: Boolean, type: Boolean,
default: false default: false
},
collapseAll: {
type: Boolean,
default: true
},
toggleToken: {
type: Number,
default: 0
} }
}) })
defineEmits(['update', 'edit-piece']) defineEmits(['update', 'edit-piece'])
</script> </script>

View File

@@ -1,125 +1,107 @@
<template> <template>
<div class="space-y-4"> <div class="space-y-4">
<!-- Component Header --> <!-- Component Header -->
<div class="flex items-center justify-between p-4 bg-base-200 rounded-lg"> <div class="flex items-start justify-between p-4 bg-base-200 rounded-lg">
<div class="flex-1"> <div class="flex items-start gap-3 w-full">
<h3 class="text-lg font-semibold">{{ component.name }}</h3> <button
<div class="flex flex-wrap gap-2 mt-2"> type="button"
<span v-if="component.reference" class="badge badge-outline badge-sm"> class="btn btn-ghost btn-sm btn-circle shrink-0 transition-transform"
{{ component.reference }} :class="{ 'rotate-90': !isCollapsed }"
</span> @click="toggleCollapse"
<span v-if="component.prestataire" class="badge badge-outline badge-sm"> :aria-expanded="!isCollapsed"
{{ component.prestataire }} :title="isCollapsed ? 'Déplier les détails du composant' : 'Replier les détails du composant'"
</span> >
<span v-if="component.emplacement" class="badge badge-outline badge-sm"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{{ component.emplacement }} <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</span> </svg>
<span v-if="component.prix" class="badge badge-primary badge-sm"> <span class="sr-only">{{ isCollapsed ? 'Déplier' : 'Replier' }} le composant</span>
{{ component.prix }} </button>
</span> <div class="flex-1">
<h3 class="text-lg font-semibold">{{ component.name }}</h3>
<div class="flex flex-wrap gap-2 mt-2">
<span v-if="component.reference" class="badge badge-outline badge-sm">{{ component.reference }}</span>
<span v-if="component.prestataire" class="badge badge-outline badge-sm">{{ component.prestataire }}</span>
<span v-if="component.emplacement" class="badge badge-outline badge-sm">{{ component.emplacement }}</span>
<span v-if="component.prix" class="badge badge-primary badge-sm">{{ component.prix }}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Component Info Display - Editable or Read-only --> <div v-show="!isCollapsed" class="space-y-4">
<div class="p-4 bg-base-100 border border-gray-200 rounded-lg"> <!-- Component Info Display - Editable or Read-only -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> <div class="p-4 bg-base-100 border border-gray-200 rounded-lg">
<div class="form-control"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<label class="label"> <div class="form-control">
<span class="label-text font-medium">Nom</span> <label class="label"><span class="label-text font-medium">Nom</span></label>
</label> <input
<input v-if="isEditMode"
v-if="isEditMode" v-model="component.name"
v-model="component.name" type="text"
type="text" class="input input-bordered input-sm"
class="input input-bordered input-sm" @blur="updateComponent"
@blur="updateComponent" />
/> <div v-else class="input input-bordered input-sm bg-base-200">{{ component.name }}</div>
<div v-else class="input input-bordered input-sm bg-base-200">
{{ component.name }}
</div> </div>
</div> <div class="form-control">
<div class="form-control"> <label class="label"><span class="label-text font-medium">Référence</span></label>
<label class="label"> <input
<span class="label-text font-medium">Référence</span> v-if="isEditMode"
</label> v-model="component.reference"
<input type="text"
v-if="isEditMode" class="input input-bordered input-sm"
v-model="component.reference" @blur="updateComponent"
type="text" />
class="input input-bordered input-sm" <div v-else class="input input-bordered input-sm bg-base-200">{{ component.reference || 'Non définie' }}</div>
@blur="updateComponent"
/>
<div v-else class="input input-bordered input-sm bg-base-200">
{{ component.reference || 'Non définie' }}
</div> </div>
</div> <div class="form-control">
<div class="form-control"> <label class="label"><span class="label-text font-medium">Prestataire</span></label>
<label class="label"> <input
<span class="label-text font-medium">Prestataire</span> v-if="isEditMode"
</label> v-model="component.prestataire"
<input type="text"
v-if="isEditMode" class="input input-bordered input-sm"
v-model="component.prestataire" @blur="updateComponent"
type="text" />
class="input input-bordered input-sm" <div v-else class="input input-bordered input-sm bg-base-200">{{ component.prestataire || 'Non défini' }}</div>
@blur="updateComponent"
/>
<div v-else class="input input-bordered input-sm bg-base-200">
{{ component.prestataire || 'Non défini' }}
</div> </div>
</div> <div class="form-control">
<div class="form-control"> <label class="label"><span class="label-text font-medium">Emplacement</span></label>
<label class="label"> <input
<span class="label-text font-medium">Emplacement</span> v-if="isEditMode"
</label> v-model="component.emplacement"
<input type="text"
v-if="isEditMode" class="input input-bordered input-sm"
v-model="component.emplacement" @blur="updateComponent"
type="text" />
class="input input-bordered input-sm" <div v-else class="input input-bordered input-sm bg-base-200">{{ component.emplacement || 'Non défini' }}</div>
@blur="updateComponent"
/>
<div v-else class="input input-bordered input-sm bg-base-200">
{{ component.emplacement || 'Non défini' }}
</div> </div>
</div> <div class="form-control">
<div class="form-control"> <label class="label"><span class="label-text font-medium">Prix</span></label>
<label class="label"> <input
<span class="label-text font-medium">Prix</span> v-if="isEditMode"
</label> v-model="component.prix"
<input type="number"
v-if="isEditMode" step="0.01"
v-model="component.prix" class="input input-bordered input-sm"
type="number" @blur="updateComponent"
step="0.01" />
class="input input-bordered input-sm" <div v-else class="input input-bordered input-sm bg-base-200">{{ component.prix ? `${component.prix}` : 'Non défini' }}</div>
@blur="updateComponent"
/>
<div v-else class="input input-bordered input-sm bg-base-200">
{{ component.prix ? `${component.prix}` : 'Non défini' }}
</div> </div>
</div> </div>
</div> </div>
<!-- Custom Fields Display - Editable or Read-only --> <!-- Custom Fields Display - Editable or Read-only -->
<div v-if="component.customFields && component.customFields.length > 0" class="mt-4 pt-4 border-t border-gray-200"> <div v-if="component.customFields && component.customFields.length > 0" class="mt-4 pt-4 border-t border-gray-200">
<h4 class="font-semibold text-sm text-gray-700 mb-3">Champs personnalisés</h4> <h4 class="font-semibold text-sm text-gray-700 mb-3">Champs personnalisés</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div <div v-for="field in component.customFields" :key="field.id" class="form-control">
v-for="field in component.customFields"
:key="field.id"
class="form-control"
>
<label class="label"> <label class="label">
<span class="label-text text-sm">{{ field.name }}</span> <span class="label-text text-sm">{{ field.name }}</span>
<span v-if="field.required" class="label-text-alt text-error">*</span> <span v-if="field.required" class="label-text-alt text-error">*</span>
</label> </label>
<!-- Mode édition -->
<template v-if="isEditMode"> <template v-if="isEditMode">
<!-- Champ de type TEXT --> <input
<input
v-if="field.type === 'text'" v-if="field.type === 'text'"
v-model="field.value" v-model="field.value"
type="text" type="text"
@@ -128,9 +110,7 @@
:required="field.required" :required="field.required"
@blur="updateComponentCustomField(field)" @blur="updateComponentCustomField(field)"
/> />
<input
<!-- Champ de type NUMBER -->
<input
v-else-if="field.type === 'number'" v-else-if="field.type === 'number'"
v-model="field.value" v-model="field.value"
type="number" type="number"
@@ -139,9 +119,7 @@
:required="field.required" :required="field.required"
@blur="updateComponentCustomField(field)" @blur="updateComponentCustomField(field)"
/> />
<select
<!-- Champ de type SELECT -->
<select
v-else-if="field.type === 'select'" v-else-if="field.type === 'select'"
v-model="field.value" v-model="field.value"
class="select select-bordered select-sm" class="select select-bordered select-sm"
@@ -149,29 +127,20 @@
@change="updateComponentCustomField(field)" @change="updateComponentCustomField(field)"
> >
<option value="">{{ field.defaultValue || 'Sélectionner...' }}</option> <option value="">{{ field.defaultValue || 'Sélectionner...' }}</option>
<option <option v-for="option in field.options" :key="option" :value="option">{{ option }}</option>
v-for="option in field.options"
:key="option"
:value="option"
>
{{ option }}
</option>
</select> </select>
<!-- Champ de type BOOLEAN -->
<div v-else-if="field.type === 'boolean'" class="flex items-center gap-2"> <div v-else-if="field.type === 'boolean'" class="flex items-center gap-2">
<input <input
v-model="field.value" v-model="field.value"
type="checkbox" type="checkbox"
class="checkbox checkbox-sm" class="checkbox checkbox-sm"
:checked="field.value === 'true'" true-value="true"
false-value="false"
@change="updateComponentCustomField(field)" @change="updateComponentCustomField(field)"
/> />
<span class="text-sm">{{ field.value === 'true' ? 'Oui' : 'Non' }}</span> <span class="text-sm">{{ field.value === 'true' ? 'Oui' : 'Non' }}</span>
</div> </div>
<input
<!-- Champ de type DATE -->
<input
v-else-if="field.type === 'date'" v-else-if="field.type === 'date'"
v-model="field.value" v-model="field.value"
type="date" type="date"
@@ -181,53 +150,51 @@
@blur="updateComponentCustomField(field)" @blur="updateComponentCustomField(field)"
/> />
</template> </template>
<!-- Mode lecture seule -->
<template v-else> <template v-else>
<div class="input input-bordered input-sm bg-base-200"> <div class="input input-bordered input-sm bg-base-200">{{ field.value || field.defaultValue || 'Non défini' }}</div>
{{ field.value || field.defaultValue || 'Non défini' }}
</div>
</template> </template>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Component Pieces --> <!-- Component Pieces -->
<div v-if="component.pieces && component.pieces.length > 0" class="space-y-2"> <div v-if="component.pieces && component.pieces.length > 0" class="space-y-2">
<h4 class="font-semibold text-gray-700">Pièces du composant</h4> <h4 class="font-semibold text-gray-700">Pièces du composant</h4>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
<PieceItem <PieceItem
v-for="piece in component.pieces" v-for="piece in component.pieces"
:key="piece.id" :key="piece.id"
:piece="piece" :piece="piece"
:is-edit-mode="isEditMode" :is-edit-mode="isEditMode"
@update="updatePiece" @update="updatePiece"
@edit="editPiece" @edit="editPiece"
@custom-field-update="updatePieceCustomField" @custom-field-update="updatePieceCustomField"
/> />
</div>
</div> </div>
</div>
<!-- Sub Components --> <!-- Sub Components -->
<div v-if="component.subComponents && component.subComponents.length > 0" class="space-y-3"> <div v-if="component.subComponents && component.subComponents.length > 0" class="space-y-3">
<h4 class="font-semibold text-gray-700">Sous-composants</h4> <h4 class="font-semibold text-gray-700">Sous-composants</h4>
<div class="space-y-3 pl-4 border-l-2 border-gray-200"> <div class="space-y-3 pl-4 border-l-2 border-gray-200">
<ComponentItem <ComponentItem
v-for="subComponent in component.subComponents" v-for="subComponent in component.subComponents"
:key="subComponent.id" :key="subComponent.id"
:component="subComponent" :component="subComponent"
:is-edit-mode="isEditMode" :is-edit-mode="isEditMode"
@update="$emit('update', $event)" :collapse-all="collapseAll"
@edit-piece="$emit('edit-piece', $event)" :toggle-token="toggleToken"
/> @update="$emit('update', $event)"
@edit-piece="$emit('edit-piece', $event)"
/>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, watch } from 'vue' import { ref, watch } from 'vue'
import PieceItem from './PieceItem.vue' import PieceItem from './PieceItem.vue'
const props = defineProps({ const props = defineProps({
@@ -238,11 +205,33 @@ const props = defineProps({
isEditMode: { isEditMode: {
type: Boolean, type: Boolean,
default: false default: false
},
collapseAll: {
type: Boolean,
default: true
},
toggleToken: {
type: Number,
default: 0
} }
}) })
const emit = defineEmits(['update', 'edit-piece']) const emit = defineEmits(['update', 'edit-piece'])
const isCollapsed = ref(true)
watch(
() => props.toggleToken,
() => {
isCollapsed.value = props.collapseAll
},
{ immediate: true }
)
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value
}
// Methods // Methods
const updateComponent = () => { const updateComponent = () => {
emit('update', props.component) emit('update', props.component)
@@ -265,4 +254,4 @@ const updatePieceCustomField = (fieldUpdate) => {
// Forward to parent // Forward to parent
emit('edit-piece', { ...fieldUpdate, type: 'custom-field-update' }) emit('edit-piece', { ...fieldUpdate, type: 'custom-field-update' })
} }
</script> </script>

View File

@@ -62,7 +62,25 @@
<div class="card bg-base-100 shadow-lg"> <div class="card bg-base-100 shadow-lg">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="card-title text-lg">Champs personnalisés du type</h3> <div class="flex items-center gap-2">
<button
type="button"
@click="toggleSection('customFields')"
class="btn btn-ghost btn-sm p-1"
>
<svg
class="w-4 h-4 transition-transform duration-200"
:class="{ 'rotate-90': expandedSections.customFields }"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
<h3 class="card-title text-lg">Champs personnalisés du type</h3>
<span class="badge badge-primary">{{ formData.customFields.length }}</span>
</div>
<button <button
type="button" type="button"
@click="addCustomField" @click="addCustomField"
@@ -75,18 +93,24 @@
</button> </button>
</div> </div>
<div class="space-y-4"> <div v-if="expandedSections.customFields" class="space-y-4">
<div <div
v-for="(field, fieldIndex) in formData.customFields" v-for="(field, fieldIndex) in formData.customFields"
:key="fieldIndex" :key="fieldIndex"
class="border border-gray-200 rounded-lg p-4 bg-gray-50" class="border border-gray-200 rounded-lg p-4 bg-gray-50"
> >
<div class="flex items-center justify-between mb-3"> <div class="flex items-center justify-between mb-3">
<h5 class="text-sm font-medium">Champ personnalisé {{ fieldIndex + 1 }}</h5> <div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
<h5 class="text-sm font-medium">Champ personnalisé {{ fieldIndex + 1 }}</h5>
</div>
<button <button
type="button" type="button"
@click="removeCustomField(fieldIndex)" @click="removeCustomField(fieldIndex)"
class="btn btn-square btn-error btn-sm" class="btn btn-square btn-error btn-sm"
title="Supprimer ce champ"
> >
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
@@ -170,7 +194,25 @@
<div class="card bg-base-100 shadow-lg"> <div class="card bg-base-100 shadow-lg">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="card-title text-lg">Pièces principales</h3> <div class="flex items-center gap-2">
<button
type="button"
@click="toggleSection('machinePieces')"
class="btn btn-ghost btn-sm p-1"
>
<svg
class="w-4 h-4 transition-transform duration-200"
:class="{ 'rotate-90': expandedSections.machinePieces }"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
<h3 class="card-title text-lg">Pièces principales</h3>
<span class="badge badge-secondary">{{ formData.machinePieces.length }}</span>
</div>
<button <button
type="button" type="button"
@click="addMachinePiece" @click="addMachinePiece"
@@ -183,18 +225,24 @@
</button> </button>
</div> </div>
<div class="space-y-4"> <div v-if="expandedSections.machinePieces" class="space-y-4">
<div <div
v-for="(piece, pieceIndex) in formData.machinePieces" v-for="(piece, pieceIndex) in formData.machinePieces"
:key="pieceIndex" :key="pieceIndex"
class="border border-gray-200 rounded-lg p-4 bg-gray-50" class="border border-gray-200 rounded-lg p-4 bg-gray-50"
> >
<div class="flex items-center justify-between mb-3"> <div class="flex items-center justify-between mb-3">
<h5 class="text-sm font-medium">Pièce {{ pieceIndex + 1 }}</h5> <div class="flex items-center gap-2">
<svg class="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
<h5 class="text-sm font-medium">Pièce {{ pieceIndex + 1 }}</h5>
</div>
<button <button
type="button" type="button"
@click="removeMachinePiece(pieceIndex)" @click="removeMachinePiece(pieceIndex)"
class="btn btn-square btn-error btn-sm" class="btn btn-square btn-error btn-sm"
title="Supprimer cette pièce"
> >
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
@@ -344,7 +392,25 @@
<div class="card bg-base-100 shadow-lg"> <div class="card bg-base-100 shadow-lg">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="card-title text-lg">Composants</h3> <div class="flex items-center gap-2">
<button
type="button"
@click="toggleSection('components')"
class="btn btn-ghost btn-sm p-1"
>
<svg
class="w-4 h-4 transition-transform duration-200"
:class="{ 'rotate-90': expandedSections.components }"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
<h3 class="card-title text-lg">Composants</h3>
<span class="badge badge-accent">{{ formData.components.length }}</span>
</div>
<button <button
type="button" type="button"
@click="addComponent" @click="addComponent"
@@ -357,18 +423,24 @@
</button> </button>
</div> </div>
<div class="space-y-4"> <div v-if="expandedSections.components" class="space-y-4">
<div <div
v-for="(component, componentIndex) in formData.components" v-for="(component, componentIndex) in formData.components"
:key="componentIndex" :key="componentIndex"
class="border border-gray-200 rounded-lg p-4 bg-gray-50" class="border border-gray-200 rounded-lg p-4 bg-gray-50"
> >
<div class="flex items-center justify-between mb-3"> <div class="flex items-center justify-between mb-3">
<h5 class="text-sm font-medium">Composant {{ componentIndex + 1 }}</h5> <div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
</svg>
<h5 class="text-sm font-medium">Composant {{ componentIndex + 1 }}</h5>
</div>
<button <button
type="button" type="button"
@click="removeComponent(componentIndex)" @click="removeComponent(componentIndex)"
class="btn btn-square btn-error btn-sm" class="btn btn-square btn-error btn-sm"
title="Supprimer ce composant"
> >
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
@@ -549,6 +621,7 @@
type="button" type="button"
@click="addPieceCustomFieldToComponent(componentIndex, pieceIndex)" @click="addPieceCustomFieldToComponent(componentIndex, pieceIndex)"
class="btn btn-xs btn-ghost" class="btn btn-xs btn-ghost"
title="Ajouter un champ personnalisé"
> >
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
@@ -558,6 +631,7 @@
type="button" type="button"
@click="removeComponentPiece(componentIndex, pieceIndex)" @click="removeComponentPiece(componentIndex, pieceIndex)"
class="btn btn-square btn-error btn-xs" class="btn btn-square btn-error btn-xs"
title="Supprimer cette pièce"
> >
<svg class="w-2 h-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-2 h-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
@@ -679,6 +753,13 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue', 'submit']) const emit = defineEmits(['update:modelValue', 'submit'])
// État des sections dépliées/repliées
const expandedSections = reactive({
customFields: true,
machinePieces: true,
components: true
})
// Initialiser les données du formulaire // Initialiser les données du formulaire
const formData = reactive({ const formData = reactive({
name: props.modelValue?.name || '', name: props.modelValue?.name || '',
@@ -695,6 +776,11 @@ watch(formData, (newValue) => {
emit('update:modelValue', newValue) emit('update:modelValue', newValue)
}, { deep: true }) }, { deep: true })
// Méthode pour basculer l'état d'une section
const toggleSection = (sectionName) => {
expandedSections[sectionName] = !expandedSections[sectionName]
}
// Méthodes pour les champs personnalisés // Méthodes pour les champs personnalisés
const addCustomField = () => { const addCustomField = () => {
formData.customFields.push({ formData.customFields.push({

View File

@@ -221,12 +221,33 @@
<div class="card-body"> <div class="card-body">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h2 class="card-title">Composants</h2> <h2 class="card-title">Composants</h2>
<button
type="button"
class="btn btn-ghost btn-sm gap-2"
@click="toggleAllComponents"
:title="componentsCollapsed ? 'Déplier tous les composants' : 'Replier tous les composants'"
>
<svg
class="w-5 h-5 transition-transform"
:class="componentsCollapsed ? 'rotate-0' : 'rotate-90'"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<span class="text-sm">
{{ componentsCollapsed ? 'Tout déplier' : 'Tout replier' }}
</span>
</button>
</div> </div>
<!-- Components Tree --> <!-- Components Tree -->
<ComponentHierarchy <ComponentHierarchy
:components="components" :components="components"
:is-edit-mode="isEditMode" :is-edit-mode="isEditMode"
:collapse-all="componentsCollapsed"
:toggle-token="collapseToggleToken"
@update="updateComponent" @update="updateComponent"
@edit-piece="updatePieceFromComponent" @edit-piece="updatePieceFromComponent"
/> />
@@ -322,6 +343,20 @@ const machineCustomFieldValues = reactive({})
const isEditMode = ref(false) const isEditMode = ref(false)
const debug = ref(false) // Ajout de debug pour afficher les infos de debug const debug = ref(false) // Ajout de debug pour afficher les infos de debug
// Gestion du pliage global des composants
const componentsCollapsed = ref(true)
const collapseToggleToken = ref(0)
const toggleAllComponents = () => {
componentsCollapsed.value = !componentsCollapsed.value
collapseToggleToken.value += 1
}
const collapseAllComponents = () => {
componentsCollapsed.value = true
collapseToggleToken.value += 1
}
// Méthodes pour initialiser les champs // Méthodes pour initialiser les champs
const initMachineFields = () => { const initMachineFields = () => {
if (machine.value) { if (machine.value) {
@@ -456,6 +491,7 @@ const loadMachineData = async () => {
components.value = transformComponentCustomFields(componentsResult.data) components.value = transformComponentCustomFields(componentsResult.data)
console.log('Composants chargés:', components.value.length) console.log('Composants chargés:', components.value.length)
console.log('Composants transformés:', components.value) console.log('Composants transformés:', components.value)
collapseAllComponents()
// Debug: afficher la hiérarchie // Debug: afficher la hiérarchie
console.log('=== HIÉRARCHIE DES COMPOSANTS ===') console.log('=== HIÉRARCHIE DES COMPOSANTS ===')
@@ -643,4 +679,4 @@ onMounted(() => {
console.log('Mode édition activé depuis l\'URL') console.log('Mode édition activé depuis l\'URL')
} }
}) })
</script> </script>

View File

@@ -76,12 +76,6 @@
Supprimer Supprimer
</button> </button>
<NuxtLink :to="`/type/${type.id}`" class="btn btn-sm btn-outline">Voir détails</NuxtLink> <NuxtLink :to="`/type/${type.id}`" class="btn btn-sm btn-outline">Voir détails</NuxtLink>
<NuxtLink :to="`/type/edit/${type.id}`" class="btn btn-sm btn-secondary">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
Modifier
</NuxtLink>
<button class="btn btn-sm btn-primary">Utiliser</button> <button class="btn btn-sm btn-primary">Utiliser</button>
</div> </div>
</div> </div>

View File

@@ -2,6 +2,9 @@ import tailwindcss from "@tailwindcss/vite";
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2025-07-15', compatibilityDate: '2025-07-15',
devtools: { enabled: true }, devtools: { enabled: true },
devServer: {
port: 3001,
},
vite: { vite: {
plugins: [tailwindcss()], plugins: [tailwindcss()],
}, },
@@ -14,4 +17,4 @@ export default defineNuxtConfig({
experimental: { experimental: {
payloadExtraction: false payloadExtraction: false
} }
}) })

1709
package-lock.json generated

File diff suppressed because it is too large Load Diff