feat: ajout du formulaire de modification complète des types de machines

- Création du composant TypeEditForm.vue pour l'édition complète des types
- Ajout de la page /type/edit/[id].vue pour l'édition complète
- Ajout des boutons d'édition dans les pages types.vue et type/[id].vue
- Gestion complète des champs personnalisés, pièces et composants
- Interface intuitive avec ajout/suppression dynamique d'éléments
This commit is contained in:
Matthieu
2025-07-31 17:42:28 +02:00
parent 74b78137a0
commit c33a04b68e
4 changed files with 1097 additions and 3 deletions

View File

@@ -26,9 +26,17 @@
<h2 class="card-title text-2xl">
Modifier : {{ type.name }}
</h2>
<NuxtLink to="/types" class="btn btn-outline">
Retour
</NuxtLink>
<div class="flex gap-2">
<NuxtLink :to="`/type/edit/${type.id}`" class="btn btn-secondary">
<svg class="w-4 h-4 mr-2" 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>
Éditer complètement
</NuxtLink>
<NuxtLink to="/types" class="btn btn-outline">
Retour
</NuxtLink>
</div>
</div>
<!-- Current Type Info -->

View File

@@ -0,0 +1,221 @@
<template>
<main class="container mx-auto px-6 py-8">
<!-- Hero Section -->
<div class="hero min-h-[30vh] bg-gradient-to-r from-secondary to-primary">
<div class="hero-content text-center text-neutral-content">
<div class="max-w-md">
<h1 class="mb-5 text-4xl font-bold">Modifier le Type</h1>
<p class="mb-5">
Modifiez les informations du type de machine.
</p>
</div>
</div>
</div>
<!-- Loading State -->
<div v-if="loading" class="my-8 text-center">
<div class="loading loading-spinner loading-lg"></div>
<p class="mt-4 text-gray-600">Chargement du type...</p>
</div>
<!-- Edit Form -->
<div v-else-if="type" class="my-8">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center justify-between mb-6">
<h2 class="card-title text-2xl">
Modifier : {{ type.name }}
</h2>
<NuxtLink to="/types" class="btn btn-outline">
Retour
</NuxtLink>
</div>
<form @submit.prevent="saveChanges">
<TypeEditForm
v-model="editedType"
:saving="saving"
@submit="saveChanges"
/>
</form>
</div>
</div>
</div>
<!-- Error State -->
<div v-else class="my-8 text-center">
<div class="alert alert-error">
<div>
<h3 class="font-bold">Type non trouvé</h3>
<p>Le type de machine demandé n'existe pas.</p>
</div>
</div>
<NuxtLink to="/types" class="btn btn-primary mt-4">
Retour aux types
</NuxtLink>
</div>
</main>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
import { useToast } from '~/composables/useToast'
const route = useRoute()
const router = useRouter()
const { getMachineTypeById, updateMachineType } = useMachineTypesApi()
const { showSuccess, showError } = useToast()
const type = ref(null)
const loading = ref(true)
const saving = ref(false)
// Données éditées du type
const editedType = reactive({
name: '',
description: '',
category: '',
maintenanceFrequency: '',
customFields: [],
machinePieces: [],
components: []
})
const saveChanges = async () => {
try {
saving.value = true
// Préparer les données pour l'API
const updatedType = {
...editedType,
// Traiter les champs personnalisés
customFields: editedType.customFields
.filter(field => field.name.trim() !== '')
.map(field => ({
name: field.name,
type: field.type,
required: field.required || false,
defaultValue: field.defaultValue || '',
options: field.type === 'select' && field.optionsText
? field.optionsText.split('\n').filter(opt => opt.trim() !== '')
: []
})),
// Traiter les pièces principales
machinePieces: editedType.machinePieces
.filter(piece => piece.name.trim() !== '')
.map(piece => ({
name: piece.name,
reference: piece.reference || '',
prestataire: piece.prestataire || '',
emplacement: piece.emplacement || '',
prix: piece.prix || null,
customFields: (piece.customFields || [])
.filter(field => field.name.trim() !== '')
.map(field => ({
name: field.name,
type: field.type,
required: field.required || false,
defaultValue: field.defaultValue || '',
options: field.type === 'select' && field.optionsText
? field.optionsText.split('\n').filter(opt => opt.trim() !== '')
: []
}))
})),
// Traiter les composants
components: editedType.components
.filter(comp => comp.name.trim() !== '')
.map(comp => ({
name: comp.name,
reference: comp.reference || '',
prestataire: comp.prestataire || '',
emplacement: comp.emplacement || '',
prix: comp.prix || null,
customFields: (comp.customFields || [])
.filter(field => field.name.trim() !== '')
.map(field => ({
name: field.name,
type: field.type,
required: field.required || false,
defaultValue: field.defaultValue || '',
options: field.type === 'select' && field.optionsText
? field.optionsText.split('\n').filter(opt => opt.trim() !== '')
: []
})),
pieces: (comp.pieces || [])
.filter(piece => piece.name.trim() !== '')
.map(piece => ({
name: piece.name,
reference: piece.reference || '',
prestataire: piece.prestataire || '',
emplacement: piece.emplacement || '',
prix: piece.prix || null,
customFields: (piece.customFields || [])
.filter(field => field.name.trim() !== '')
.map(field => ({
name: field.name,
type: field.type,
required: field.required || false,
defaultValue: field.defaultValue || '',
options: field.type === 'select' && field.optionsText
? field.optionsText.split('\n').filter(opt => opt.trim() !== '')
: []
}))
}))
}))
}
const result = await updateMachineType(type.value.id, updatedType)
if (result.success) {
showSuccess('Type mis à jour avec succès !')
router.push('/types')
} else {
showError('Erreur lors de la mise à jour du type')
}
} catch (error) {
console.error('Erreur lors de la sauvegarde:', error)
showError('Erreur lors de la sauvegarde')
} finally {
saving.value = false
}
}
// Charger le type au montage
onMounted(async () => {
try {
const typeId = route.params.id
console.log('=== EDIT TYPE PAGE LOADING ===')
console.log('Loading type with ID:', typeId)
const result = await getMachineTypeById(typeId)
console.log('API Result:', result)
if (result.success) {
type.value = result.data
console.log('Type loaded successfully:', type.value)
// Initialiser les données éditées
Object.assign(editedType, {
name: type.value.name || '',
description: type.value.description || '',
category: type.value.category || '',
maintenanceFrequency: type.value.maintenanceFrequency || '',
customFields: type.value.customFields || [],
machinePieces: type.value.machinePieces || [],
components: type.value.components || []
})
} else {
console.error('Failed to load type:', result.error)
showError('Type non trouvé')
}
} catch (error) {
console.error('Erreur lors du chargement:', error)
showError('Erreur lors du chargement')
} finally {
loading.value = false
console.log('Loading finished, loading.value:', loading.value)
}
})
</script>

View File

@@ -76,6 +76,12 @@
Supprimer
</button>
<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>
</div>
</div>