chore: update frontend configuration
This commit is contained in:
@@ -12,14 +12,14 @@
|
||||
class="w-4 h-4 text-purple-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<input
|
||||
<input
|
||||
v-if="isEditMode"
|
||||
:id="`piece-name-${piece.id}`"
|
||||
v-model="pieceData.name"
|
||||
type="text"
|
||||
v-model="pieceData.name"
|
||||
type="text"
|
||||
class="font-semibold text-lg input input-sm input-bordered"
|
||||
@blur="updatePiece"
|
||||
/>
|
||||
>
|
||||
<div v-else class="font-semibold text-lg input input-sm input-bordered bg-base-200">
|
||||
{{ pieceData.name }}
|
||||
</div>
|
||||
@@ -45,19 +45,19 @@
|
||||
|
||||
<div class="space-y-2 text-sm">
|
||||
<div>
|
||||
<span class="font-medium">Référence:</span>
|
||||
<input
|
||||
<span class="font-medium">Référence:</span>
|
||||
<input
|
||||
v-if="isEditMode"
|
||||
:id="`piece-reference-${piece.id}`"
|
||||
v-model="pieceData.reference"
|
||||
type="text"
|
||||
v-model="pieceData.reference"
|
||||
type="text"
|
||||
class="input input-sm input-bordered ml-2"
|
||||
@blur="updatePiece"
|
||||
/>
|
||||
>
|
||||
<span v-else class="ml-2">{{ pieceData.reference || 'Non définie' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Constructeur:</span>
|
||||
<span class="font-medium">Constructeur:</span>
|
||||
<span v-if="!isEditMode" class="ml-2">
|
||||
<span class="font-medium">{{ piece.constructeur?.name || 'Non défini' }}</span>
|
||||
<span v-if="piece.constructeur" class="block text-xs text-gray-500">
|
||||
@@ -68,20 +68,20 @@
|
||||
v-else
|
||||
class="w-full"
|
||||
:model-value="piece.constructeurId || piece.constructeur?.id || null"
|
||||
@update:modelValue="handleConstructeurChange"
|
||||
@update:model-value="handleConstructeurChange"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Prix:</span>
|
||||
<input
|
||||
<span class="font-medium">Prix:</span>
|
||||
<input
|
||||
v-if="isEditMode"
|
||||
:id="`piece-prix-${piece.id}`"
|
||||
v-model="pieceData.prix"
|
||||
type="number"
|
||||
v-model="pieceData.prix"
|
||||
type="number"
|
||||
step="0.01"
|
||||
class="input input-sm input-bordered ml-2"
|
||||
@blur="updatePiece"
|
||||
/>
|
||||
>
|
||||
<span v-else class="ml-2">{{ pieceData.prix ? `${pieceData.prix}€` : 'Non défini' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,7 +90,7 @@
|
||||
v-if="isEditMode && piece.typeMachinePieceRequirement"
|
||||
class="mt-3"
|
||||
>
|
||||
<label class="label">
|
||||
<label class="label">
|
||||
<span class="label-text text-sm font-medium">Modèle de pièce</span>
|
||||
<span class="label-text-alt text-xs">
|
||||
{{ piece.typeMachinePieceRequirement.label || piece.typeMachinePieceRequirement.typePiece?.name || 'Groupe' }}
|
||||
@@ -101,7 +101,9 @@
|
||||
class="select select-bordered select-sm w-full"
|
||||
@change="assignPieceModel($event.target.value)"
|
||||
>
|
||||
<option value="">Définir manuellement</option>
|
||||
<option value="">
|
||||
Définir manuellement
|
||||
</option>
|
||||
<option
|
||||
v-for="model in pieceModelOptions"
|
||||
:key="model.id"
|
||||
@@ -114,10 +116,12 @@
|
||||
|
||||
<!-- Champs personnalisés de la pièce -->
|
||||
<div v-if="piece.customFieldValues && piece.customFieldValues.length > 0" class="mt-4 pt-4 border-t border-gray-200">
|
||||
<h5 class="text-sm font-medium text-gray-700 mb-3">Champs personnalisés</h5>
|
||||
<h5 class="text-sm font-medium text-gray-700 mb-3">
|
||||
Champs personnalisés
|
||||
</h5>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="fieldValue in piece.customFieldValues"
|
||||
<div
|
||||
v-for="fieldValue in piece.customFieldValues"
|
||||
:key="fieldValue.id"
|
||||
class="form-control"
|
||||
>
|
||||
@@ -125,75 +129,77 @@
|
||||
<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
|
||||
<input
|
||||
v-if="fieldValue.customField.type === 'text'"
|
||||
:value="fieldValue.value"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="text"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
/>
|
||||
|
||||
>
|
||||
|
||||
<!-- Champ de type NUMBER -->
|
||||
<input
|
||||
<input
|
||||
v-else-if="fieldValue.customField.type === 'number'"
|
||||
:value="fieldValue.value"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="number"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
/>
|
||||
|
||||
<!-- Champ de type SELECT -->
|
||||
<select
|
||||
v-else-if="fieldValue.customField.type === 'select'"
|
||||
:value="fieldValue.value"
|
||||
@change="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
class="select select-bordered select-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
>
|
||||
<option value="">Sélectionner...</option>
|
||||
<option
|
||||
v-for="option in fieldValue.customField.options"
|
||||
:key="option"
|
||||
|
||||
<!-- Champ de type SELECT -->
|
||||
<select
|
||||
v-else-if="fieldValue.customField.type === 'select'"
|
||||
:value="fieldValue.value"
|
||||
class="select select-bordered select-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@change="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
>
|
||||
<option value="">
|
||||
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
|
||||
<input
|
||||
:value="fieldValue.value"
|
||||
@change="setCustomFieldValue(fieldValue.id, $event.target.checked ? 'true' : 'false')"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
:checked="fieldValue.value === 'true'"
|
||||
@change="setCustomFieldValue(fieldValue.id, $event.target.checked ? 'true' : 'false')"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
/>
|
||||
>
|
||||
<span class="text-sm">{{ fieldValue.value === 'true' ? 'Oui' : 'Non' }}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Champ de type DATE -->
|
||||
<input
|
||||
<input
|
||||
v-else-if="fieldValue.customField.type === 'date'"
|
||||
:value="fieldValue.value"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
type="date"
|
||||
class="input input-bordered input-sm"
|
||||
:required="fieldValue.customField.required"
|
||||
@input="setCustomFieldValue(fieldValue.id, $event.target.value)"
|
||||
@blur="updateCustomFieldValue(fieldValue.id)"
|
||||
/>
|
||||
>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- Mode lecture seule -->
|
||||
<template v-else>
|
||||
<div class="input input-bordered input-sm bg-base-200">
|
||||
@@ -206,13 +212,17 @@
|
||||
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<h5 class="text-sm font-medium text-gray-700">Documents</h5>
|
||||
<h5 class="text-sm font-medium text-gray-700">
|
||||
Documents
|
||||
</h5>
|
||||
<span v-if="isEditMode && selectedFiles.length" class="badge badge-outline">
|
||||
{{ selectedFiles.length }} fichier{{ selectedFiles.length > 1 ? 's' : '' }} sélectionné{{ selectedFiles.length > 1 ? 's' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p v-if="loadingDocuments" class="text-xs text-gray-500">Chargement des documents...</p>
|
||||
<p v-if="loadingDocuments" class="text-xs text-gray-500">
|
||||
Chargement des documents...
|
||||
</p>
|
||||
|
||||
<DocumentUpload
|
||||
v-if="isEditMode"
|
||||
@@ -237,7 +247,9 @@
|
||||
/>
|
||||
</span>
|
||||
<div>
|
||||
<div class="font-medium">{{ document.name }}</div>
|
||||
<div class="font-medium">
|
||||
{{ document.name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
{{ document.mimeType || 'Inconnu' }} • {{ formatSize(document.size) }}
|
||||
</div>
|
||||
@@ -268,13 +280,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else-if="!loadingDocuments" class="text-xs text-gray-500">Aucun document lié à cette pièce.</p>
|
||||
<p v-else-if="!loadingDocuments" class="text-xs text-gray-500">
|
||||
Aucun document lié à cette pièce.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onMounted, watch, ref, computed } from 'vue'
|
||||
import ConstructeurSelect from './ConstructeurSelect.vue'
|
||||
import { useCustomFields } from '~/composables/useCustomFields'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { useDocuments } from '~/composables/useDocuments'
|
||||
@@ -282,22 +297,21 @@ import { getFileIcon } from '~/utils/fileIcons'
|
||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||
import DocumentUpload from '~/components/DocumentUpload.vue'
|
||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||
import ConstructeurSelect from './ConstructeurSelect.vue'
|
||||
import IconLucidePackage from '~icons/lucide/package'
|
||||
|
||||
const props = defineProps({
|
||||
piece: {
|
||||
type: Object,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
isEditMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
pieceModelOptions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update', 'edit', 'custom-field-update', 'assign-model'])
|
||||
@@ -314,7 +328,7 @@ const uploadingDocuments = ref(false)
|
||||
const loadingDocuments = ref(false)
|
||||
const documentsLoaded = ref(!!(props.piece.documents && props.piece.documents.length))
|
||||
const pieceDocuments = computed(() => props.piece.documents || [])
|
||||
const documentIcon = (doc) => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
|
||||
const documentIcon = doc => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
|
||||
const previewDocument = ref(null)
|
||||
const previewVisible = ref(false)
|
||||
const selectedPieceModelId = computed(() => props.piece.pieceModelId || props.piece.pieceModel?.id || '')
|
||||
@@ -328,7 +342,7 @@ const handleConstructeurChange = (value) => {
|
||||
const { uploadDocuments, deleteDocument, loadDocumentsByPiece } = useDocuments()
|
||||
|
||||
const refreshDocuments = async () => {
|
||||
if (!props.piece?.id) return
|
||||
if (!props.piece?.id) { return }
|
||||
loadingDocuments.value = true
|
||||
try {
|
||||
const result = await loadDocumentsByPiece(props.piece.id, { updateStore: false })
|
||||
@@ -342,7 +356,7 @@ const refreshDocuments = async () => {
|
||||
}
|
||||
|
||||
const handleFilesAdded = async (files) => {
|
||||
if (!files.length || !props.piece?.id) return
|
||||
if (!files.length || !props.piece?.id) { return }
|
||||
uploadingDocuments.value = true
|
||||
try {
|
||||
const result = await uploadDocuments(
|
||||
@@ -365,7 +379,7 @@ const handleFilesAdded = async (files) => {
|
||||
}
|
||||
|
||||
const removeDocument = async (documentId) => {
|
||||
if (!documentId) return
|
||||
if (!documentId) { return }
|
||||
const result = await deleteDocument(documentId, { updateStore: false })
|
||||
if (result.success) {
|
||||
props.piece.documents = (props.piece.documents || []).filter(doc => doc.id !== documentId)
|
||||
@@ -373,7 +387,7 @@ const removeDocument = async (documentId) => {
|
||||
}
|
||||
|
||||
const downloadDocument = (doc) => {
|
||||
if (!doc?.path) return
|
||||
if (!doc?.path) { return }
|
||||
|
||||
if (doc.path.startsWith('data:')) {
|
||||
const link = document.createElement('a')
|
||||
@@ -387,7 +401,7 @@ const downloadDocument = (doc) => {
|
||||
}
|
||||
|
||||
const openPreview = (doc) => {
|
||||
if (!canPreviewDocument(doc)) return
|
||||
if (!canPreviewDocument(doc)) { return }
|
||||
previewDocument.value = doc
|
||||
previewVisible.value = true
|
||||
}
|
||||
@@ -398,8 +412,8 @@ const closePreview = () => {
|
||||
}
|
||||
|
||||
const formatSize = (size) => {
|
||||
if (size === undefined || size === null) return '—'
|
||||
if (size === 0) return '0 B'
|
||||
if (size === undefined || size === null) { return '—' }
|
||||
if (size === 0) { return '0 B' }
|
||||
const units = ['B', 'KB', 'MB', 'GB']
|
||||
const index = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024)))
|
||||
const formatted = size / Math.pow(1024, index)
|
||||
@@ -413,7 +427,6 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// Méthodes pour gérer les champs personnalisés
|
||||
const setCustomFieldValue = (fieldValueId, value) => {
|
||||
const fieldValue = props.piece.customFieldValues?.find(fv => fv.id === fieldValueId)
|
||||
@@ -428,7 +441,7 @@ const updatePiece = () => {
|
||||
...props.piece,
|
||||
...pieceData,
|
||||
prix: prixValue && prixValue !== '' ? parseFloat(prixValue) : null,
|
||||
constructeurId: props.piece.constructeurId || null,
|
||||
constructeurId: props.piece.constructeurId || null
|
||||
})
|
||||
}
|
||||
|
||||
@@ -443,7 +456,7 @@ const assignPieceModel = (value) => {
|
||||
pieceId: props.piece.id,
|
||||
pieceModelId: value || null,
|
||||
previousModelId,
|
||||
previousModel,
|
||||
previousModel
|
||||
})
|
||||
}
|
||||
|
||||
@@ -452,7 +465,7 @@ const updateCustomFieldValue = async (fieldValueId) => {
|
||||
if (fieldValue) {
|
||||
const { updateCustomFieldValue } = useCustomFields()
|
||||
const { showSuccess, showError } = useToast()
|
||||
|
||||
|
||||
const result = await updateCustomFieldValue(fieldValueId, { value: fieldValue.value })
|
||||
if (result.success) {
|
||||
showSuccess(`Champ "${fieldValue.customField.name}" mis à jour avec succès`)
|
||||
@@ -473,7 +486,7 @@ watch(
|
||||
pieceData.name = props.piece.name || ''
|
||||
pieceData.reference = props.piece.reference || ''
|
||||
pieceData.prix = props.piece.prix || ''
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
@@ -481,7 +494,7 @@ onMounted(() => {
|
||||
pieceData.name = props.piece.name || ''
|
||||
pieceData.reference = props.piece.reference || ''
|
||||
pieceData.prix = props.piece.prix || ''
|
||||
|
||||
|
||||
// Debug: vérifier si les champs personnalisés sont présents
|
||||
console.log('PieceItem - piece:', props.piece)
|
||||
console.log('PieceItem - customFieldValues:', props.piece.customFieldValues)
|
||||
@@ -490,4 +503,4 @@ onMounted(() => {
|
||||
refreshDocuments()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user