feat(frontend): replace inline icons with lucide components
This commit is contained in:
@@ -19,331 +19,58 @@
|
||||
<div class="flex items-center gap-2 print:hidden" data-print-hide>
|
||||
<button
|
||||
@click="toggleEditMode"
|
||||
class="btn btn-primary"
|
||||
class="btn btn-primary btn-sm"
|
||||
:class="{ 'btn-outline': isEditMode }"
|
||||
>
|
||||
<svg v-if="!isEditMode" class="w-5 h-5 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>
|
||||
<svg v-else class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
||||
</svg>
|
||||
{{ isEditMode ? 'Voir détails' : 'Modifier' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!isEditMode"
|
||||
@click="openPrintModal"
|
||||
type="button"
|
||||
class="btn btn-outline btn-secondary"
|
||||
>
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2V9a2 2 0 00-2-2h-2V4a1 1 0 00-1-1H8a1 1 0 00-1 1v3H5a2 2 0 00-2 2v6a2 2 0 002 2h2">
|
||||
</path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17H7v5h10v-5z"></path>
|
||||
</svg>
|
||||
Imprimer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Debug info -->
|
||||
<div v-if="debug" class="bg-yellow-100 p-4 rounded-lg">
|
||||
<p>Debug: Machine trouvée - {{ machine.name }}</p>
|
||||
<p>Components count: {{ components.length }}</p>
|
||||
<p>Pieces count: {{ pieces.length }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<PageHero
|
||||
:title="machine.name"
|
||||
:subtitle="machine.description || machine.typeMachine?.description"
|
||||
min-height="min-h-[20vh]"
|
||||
max-width="max-w-md"
|
||||
rounded
|
||||
>
|
||||
<div class="flex justify-center gap-4">
|
||||
<div class="badge badge-outline">{{ machine.typeMachine?.category || 'N/A' }}</div>
|
||||
<div class="badge badge-outline">{{ machine.site?.name }}</div>
|
||||
<div v-if="machine.reference" class="badge badge-outline">{{ machine.reference }}</div>
|
||||
</div>
|
||||
</PageHero>
|
||||
|
||||
<!-- Machine Info Card -->
|
||||
<div class="card bg-base-100 shadow-lg">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">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')"
|
||||
v-model="machineName"
|
||||
type="text"
|
||||
class="input input-bordered"
|
||||
@blur="updateMachineInfo"
|
||||
/>
|
||||
<div v-else class="input input-bordered bg-base-200">
|
||||
{{ machineName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Référence</span>
|
||||
</label>
|
||||
<input
|
||||
v-if="isEditMode"
|
||||
:id="getMachineFieldId('reference')"
|
||||
v-model="machineReference"
|
||||
type="text"
|
||||
class="input input-bordered"
|
||||
@blur="updateMachineInfo"
|
||||
/>
|
||||
<div v-else class="input input-bordered bg-base-200">
|
||||
{{ machineReference || 'Non définie' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Emplacement</span>
|
||||
</label>
|
||||
<input
|
||||
v-if="isEditMode"
|
||||
:id="getMachineFieldId('emplacement')"
|
||||
v-model="machineEmplacement"
|
||||
type="text"
|
||||
class="input input-bordered"
|
||||
@blur="updateMachineInfo"
|
||||
/>
|
||||
<div v-else class="input input-bordered bg-base-200">
|
||||
{{ machineEmplacement || 'Non défini' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Constructeur</span>
|
||||
</label>
|
||||
<ConstructeurSelect
|
||||
v-if="isEditMode"
|
||||
class="w-full"
|
||||
:key="machine.value?.id"
|
||||
:model-value="machineConstructeurId"
|
||||
placeholder="Rechercher un constructeur..."
|
||||
@update:modelValue="handleMachineConstructeurChange"
|
||||
/>
|
||||
<div v-else class="input input-bordered bg-base-200">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">{{ machineConstructeurDisplay?.name || 'Non défini' }}</span>
|
||||
<span class="text-xs text-gray-500">
|
||||
{{ [machineConstructeurDisplay?.email, machineConstructeurDisplay?.phone].filter(Boolean).join(' • ') || '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Champs personnalisés de la machine -->
|
||||
<div v-if="machine && machine.customFieldValues && machine.customFieldValues.length > 0" class="mt-6 pt-4 border-t border-gray-200">
|
||||
<h4 class="font-semibold text-gray-700 mb-3">Champs personnalisés de la machine</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div
|
||||
v-for="fieldValue in machine.customFieldValues"
|
||||
:key="fieldValue.id"
|
||||
class="form-control"
|
||||
>
|
||||
<label class="label">
|
||||
<span class="label-text text-sm">{{ fieldValue.customField.name }}</span>
|
||||
<span v-if="fieldValue.customField.required" class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
|
||||
<!-- Mode édition -->
|
||||
<template v-if="isEditMode">
|
||||
<!-- Champ de type TEXT -->
|
||||
<input
|
||||
v-if="fieldValue.customField.type === 'text'"
|
||||
:value="fieldValue.value"
|
||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="text"
|
||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@blur="updateMachineCustomField(fieldValue.id)"
|
||||
/>
|
||||
|
||||
<!-- Champ de type NUMBER -->
|
||||
<input
|
||||
v-else-if="fieldValue.customField.type === 'number'"
|
||||
:value="fieldValue.value"
|
||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="number"
|
||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@blur="updateMachineCustomField(fieldValue.id)"
|
||||
/>
|
||||
|
||||
<!-- Champ de type SELECT -->
|
||||
<select
|
||||
v-else-if="fieldValue.customField.type === 'select'"
|
||||
:value="fieldValue.value"
|
||||
@change="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
class="select select-bordered select-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@blur="updateMachineCustomField(fieldValue.id)"
|
||||
>
|
||||
<option value="">{{ fieldValue.customField.defaultValue || 'Sélectionner...' }}</option>
|
||||
<option
|
||||
v-for="option in fieldValue.customField.options"
|
||||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- Champ de type BOOLEAN -->
|
||||
<div v-else-if="fieldValue.customField.type === 'boolean'" class="flex items-center gap-2">
|
||||
<input
|
||||
:value="fieldValue.value"
|
||||
@change="setMachineCustomFieldValue(fieldValue.id, $event.target.checked ? 'true' : 'false')"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
:checked="fieldValue.value === 'true'"
|
||||
@blur="updateMachineCustomField(fieldValue.id)"
|
||||
/>
|
||||
<span class="text-sm">{{ fieldValue.value === 'true' ? 'Oui' : 'Non' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Champ de type DATE -->
|
||||
<input
|
||||
v-else-if="fieldValue.customField.type === 'date'"
|
||||
:value="fieldValue.value"
|
||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="date"
|
||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@blur="updateMachineCustomField(fieldValue.id)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Mode lecture seule -->
|
||||
<template v-else>
|
||||
<div class="input input-bordered input-sm bg-base-200">
|
||||
{{ fieldValue.value || fieldValue.customField.defaultValue || 'Non défini' }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100 shadow-lg mt-6">
|
||||
<div class="card-body space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="card-title">Documents de la machine</h2>
|
||||
<p class="text-xs text-gray-500">Ajoutez ou consultez les documents liés à cette machine.</p>
|
||||
</div>
|
||||
<span v-if="isEditMode && machineDocumentFiles.length" class="badge badge-outline">
|
||||
{{ machineDocumentFiles.length }} fichier{{ machineDocumentFiles.length > 1 ? 's' : '' }} sélectionné{{ machineDocumentFiles.length > 1 ? 's' : '' }}
|
||||
<IconLucideSquarePen
|
||||
v-if="!isEditMode"
|
||||
class="w-4 h-4 mr-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<IconLucideCheck
|
||||
v-else
|
||||
class="w-4 h-4 mr-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="text-sm">
|
||||
{{ isEditMode ? 'Quitter le mode édition' : 'Activer le mode édition' }}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<DocumentUpload
|
||||
v-if="isEditMode"
|
||||
v-model="machineDocumentFiles"
|
||||
title="Déposer des fichiers pour la machine"
|
||||
subtitle="Formats acceptés : PDF, images, documents..."
|
||||
@files-added="handleMachineFilesAdded"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm"
|
||||
@click="toggleAllComponents"
|
||||
>
|
||||
<IconLucideChevronDown
|
||||
class="w-4 h-4 mr-2 transform transition-transform"
|
||||
:class="componentsCollapsed ? '-rotate-90' : 'rotate-0'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="text-sm">
|
||||
{{ componentsCollapsed ? 'Tout déplier' : 'Tout replier' }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div v-if="machineDocumentsList.length" class="space-y-2">
|
||||
<div
|
||||
v-for="document in machineDocumentsList"
|
||||
:key="document.id"
|
||||
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
||||
{{ documentIcon(document).icon }}
|
||||
</span>
|
||||
<div>
|
||||
<div class="font-medium">{{ document.name }}</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
{{ document.mimeType || 'Inconnu' }} • {{ formatSize(document.size) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs"
|
||||
:disabled="!canPreviewDocument(document)"
|
||||
:title="canPreviewDocument(document) ? 'Consulter le document' : 'Aucun aperçu disponible pour ce type'"
|
||||
@click="openPreview(document)"
|
||||
>
|
||||
Consulter
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs" @click="downloadDocument(document)">
|
||||
Télécharger
|
||||
</button>
|
||||
<button
|
||||
v-if="isEditMode"
|
||||
type="button"
|
||||
class="btn btn-error btn-xs"
|
||||
:disabled="machineDocumentsUploading"
|
||||
@click="removeMachineDocument(document.id)"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="text-xs text-gray-500">Aucun document lié à cette machine.</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline btn-sm"
|
||||
@click="openPrintModal"
|
||||
>
|
||||
<IconLucidePrinter class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span class="text-sm">Imprimer</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Components Section -->
|
||||
<div class="card bg-base-100 shadow-lg">
|
||||
<div class="card-body">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<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>
|
||||
|
||||
<!-- Components Tree -->
|
||||
<ComponentHierarchy
|
||||
:components="components"
|
||||
:is-edit-mode="isEditMode"
|
||||
:collapse-all="componentsCollapsed"
|
||||
:toggle-token="collapseToggleToken"
|
||||
@update="updateComponent"
|
||||
@edit-piece="updatePieceFromComponent"
|
||||
/>
|
||||
</div>
|
||||
<!-- Components Tree -->
|
||||
<ComponentHierarchy
|
||||
:components="components"
|
||||
:is-edit-mode="isEditMode"
|
||||
:collapse-all="componentsCollapsed"
|
||||
:toggle-token="collapseToggleToken"
|
||||
@update="updateComponent"
|
||||
@edit-piece="updatePieceFromComponent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Machine Pieces Section -->
|
||||
@@ -371,9 +98,7 @@
|
||||
<!-- Error State -->
|
||||
<div v-else class="text-center py-12">
|
||||
<div class="max-w-md mx-auto">
|
||||
<svg class="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33"></path>
|
||||
</svg>
|
||||
<IconLucideAlertTriangle class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" />
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Machine non trouvée</h3>
|
||||
<p class="text-gray-500 mb-4">La machine avec l'ID "{{ machineId }}" n'existe pas ou a été supprimée.</p>
|
||||
<NuxtLink to="/machines" class="btn btn-primary">
|
||||
@@ -415,6 +140,11 @@ import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||
import PageHero from '~/components/PageHero.vue'
|
||||
import MachinePrintSelectionModal from '~/components/MachinePrintSelectionModal.vue'
|
||||
import { buildMachinePrintContext, buildMachinePrintHtml } from '~/utils/printTemplates/machineReport'
|
||||
import IconLucideAlertTriangle from '~icons/lucide/alert-triangle'
|
||||
import IconLucideSquarePen from '~icons/lucide/square-pen'
|
||||
import IconLucideCheck from '~icons/lucide/check'
|
||||
import IconLucideChevronDown from '~icons/lucide/chevron-down'
|
||||
import IconLucidePrinter from '~icons/lucide/printer'
|
||||
|
||||
const route = useRoute()
|
||||
const machineId = route.params.id
|
||||
|
||||
Reference in New Issue
Block a user