Files
Inventory/app/components/machine/MachineInfoCard.vue
Matthieu 7e67b124f3 feat(machine) : allow site editing on machine detail page and align card buttons
- Add site select field in MachineInfoCard (edit mode)
- Include siteId in machine PATCH payload
- Align action buttons (Modifier/Supprimer/Détails) consistently at card bottom
- Use mt-auto + flex-col to push buttons to bottom across all machine cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:09:14 +01:00

243 lines
9.4 KiB
Vue

<template>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title tracking-tight">Informations de la machine</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control">
<label class="label">
<span class="label-text">Nom</span>
</label>
<input
v-if="isEditMode"
:id="getMachineFieldId('name')"
:value="machineName"
type="text"
class="input input-bordered"
@input="$emit('update:machine-name', ($event.target as HTMLInputElement).value)"
@blur="$emit('blur-field')"
/>
<div v-else class="input input-bordered bg-base-200">
{{ machineName }}
</div>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Site</span>
</label>
<select
v-if="isEditMode"
:value="machineSiteId"
class="select select-bordered"
@change="$emit('update:machine-site-id', ($event.target as HTMLSelectElement).value); $emit('blur-field')"
>
<option value="">Sélectionner un site</option>
<option
v-for="site in sites"
:key="site.id"
:value="site.id"
>
{{ site.name }}
</option>
</select>
<div v-else class="input input-bordered bg-base-200">
{{ machineSiteName || 'Non défini' }}
</div>
</div>
<div v-if="isEditMode || machineReference" class="form-control">
<label class="label">
<span class="label-text">Référence</span>
</label>
<input
v-if="isEditMode"
:id="getMachineFieldId('reference')"
:value="machineReference"
type="text"
class="input input-bordered"
@input="$emit('update:machine-reference', ($event.target as HTMLInputElement).value)"
@blur="$emit('blur-field')"
/>
<div v-else class="input input-bordered bg-base-200">
{{ machineReference }}
</div>
</div>
<div v-if="isEditMode || hasMachineConstructeur" class="form-control">
<label class="label">
<span class="label-text">Fournisseur</span>
</label>
<ConstructeurSelect
v-if="isEditMode"
class="w-full"
:model-value="machineConstructeurIds"
:initial-options="machineConstructeursDisplay"
placeholder="Rechercher un ou plusieurs fournisseurs..."
@update:modelValue="$emit('update:constructeur-ids', $event)"
/>
<div v-else class="input input-bordered bg-base-200">
<div v-if="machineConstructeursDisplay.length" class="space-y-1">
<div
v-for="constructeur in machineConstructeursDisplay"
:key="constructeur.id"
class="flex flex-col"
>
<span class="font-medium">{{ constructeur.name }}</span>
<span
v-if="formatConstructeurContactSummary(constructeur)"
class="text-xs text-base-content/50"
>
{{ formatConstructeurContactSummary(constructeur) }}
</span>
</div>
</div>
<span v-else class="font-medium">Non défini</span>
</div>
</div>
</div>
<!-- Champs personnalisés -->
<div v-if="visibleCustomFields.length" class="mt-6 pt-4 border-t border-base-200">
<h4 class="font-semibold text-base-content/80 mb-3">Champs personnalisés de la machine</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div
v-for="field in visibleCustomFields"
:key="field.customFieldValueId || field.id || field.name"
class="form-control"
>
<label class="label">
<span class="label-text text-sm">{{ field.name }}</span>
<span v-if="field.required" class="label-text-alt text-error">*</span>
</label>
<template v-if="isEditMode">
<input
v-if="field.type === 'text'"
:value="field.value ?? ''"
type="text"
class="input input-bordered input-sm"
:required="field.required"
@input="$emit('set-custom-field-value', field, ($event.target as HTMLInputElement).value)"
@blur="$emit('update-custom-field', field)"
/>
<input
v-else-if="field.type === 'number'"
:value="field.value ?? ''"
type="number"
class="input input-bordered input-sm"
:required="field.required"
@input="$emit('set-custom-field-value', field, ($event.target as HTMLInputElement).value)"
@blur="$emit('update-custom-field', field)"
/>
<select
v-else-if="field.type === 'select'"
:value="field.value ?? ''"
class="select select-bordered select-sm"
:required="field.required"
@change="$emit('set-custom-field-value', field, ($event.target as HTMLSelectElement).value)"
@blur="$emit('update-custom-field', field)"
>
<option value="">Sélectionner...</option>
<option
v-for="option in field.options"
:key="option"
:value="option"
>
{{ option }}
</option>
</select>
<label v-else-if="field.type === 'boolean'" class="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
class="toggle toggle-primary toggle-sm"
:checked="String(field.value).toLowerCase() === 'true'"
@change="$emit('set-custom-field-value', field, ($event.target as HTMLInputElement).checked ? 'true' : 'false')"
@blur="$emit('update-custom-field', field)"
>
<span class="text-sm" :class="String(field.value).toLowerCase() === 'true' ? 'text-success font-medium' : 'text-base-content/60'">{{ String(field.value).toLowerCase() === 'true' ? 'Oui' : 'Non' }}</span>
</label>
<input
v-else-if="field.type === 'date'"
:value="field.value ?? ''"
type="date"
class="input input-bordered input-sm"
:required="field.required"
@input="$emit('set-custom-field-value', field, ($event.target as HTMLInputElement).value)"
@blur="$emit('update-custom-field', field)"
/>
<div v-else class="text-xs text-error">
Type de champ non pris en charge
</div>
</template>
<template v-else>
<div class="input input-bordered input-sm bg-base-200">
{{ formatCustomFieldValue(field) }}
</div>
</template>
</div>
</div>
</div>
<div v-if="isEditMode" class="mt-6 pt-4 border-t border-base-200">
<MachineCustomFieldDefEditor
:fields="fieldDefs.fields.value"
:saving="fieldDefs.saving.value"
:reorder-class="fieldDefs.reorderClass"
:on-drag-start="fieldDefs.onDragStart"
:on-drag-enter="fieldDefs.onDragEnter"
:on-drop="fieldDefs.onDrop"
:on-drag-end="fieldDefs.onDragEnd"
@save="fieldDefs.saveDefinitions()"
@add-field="fieldDefs.addField()"
@remove-field="fieldDefs.removeField($event)"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue'
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
import MachineCustomFieldDefEditor from '~/components/machine/MachineCustomFieldDefEditor.vue'
import {
formatConstructeurContact as formatConstructeurContactSummary,
} from '~/shared/constructeurUtils'
import { formatCustomFieldValue } from '~/shared/utils/customFieldUtils'
import { useMachineCustomFieldDefs } from '~/composables/useMachineCustomFieldDefs'
const props = defineProps<{
isEditMode: boolean
machineName: string
machineReference: string
machineSiteId: string
machineSiteName: string
sites: any[]
machineConstructeurIds: string[]
machineConstructeursDisplay: any[]
hasMachineConstructeur: boolean
visibleCustomFields: any[]
getMachineFieldId: (fieldName: string) => string
machineId: string
machineCustomFieldDefs: any[]
}>()
const emit = defineEmits<{
'update:machine-name': [value: string]
'update:machine-reference': [value: string]
'update:machine-site-id': [value: string]
'update:constructeur-ids': [ids: unknown]
'blur-field': []
'set-custom-field-value': [field: any, value: unknown]
'update-custom-field': [field: any]
'custom-fields-saved': []
}>()
const fieldDefs = useMachineCustomFieldDefs({
machineId: props.machineId,
initialDefs: props.machineCustomFieldDefs,
onSaved: () => emit('custom-fields-saved'),
})
watch(() => props.machineCustomFieldDefs, (newDefs) => {
fieldDefs.reinit(newDefs)
}, { deep: true })
</script>