Add searchbar in catalogue and update custom fuild bug
This commit is contained in:
@@ -26,14 +26,33 @@
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<label class="w-full sm:w-64">
|
||||
<span class="text-xs font-semibold uppercase tracking-wide text-base-content/70">Recherche</span>
|
||||
<input
|
||||
v-model="searchTerm"
|
||||
type="text"
|
||||
class="input input-bordered input-sm w-full mt-1"
|
||||
placeholder="Nom, référence ou catégorie…"
|
||||
/>
|
||||
</label>
|
||||
<p class="text-xs text-base-content/50 sm:text-right">
|
||||
{{ filteredComposants.length }} / {{ composantsTotal }} résultat{{ filteredComposants.length > 1 ? 's' : '' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingComposants" class="flex justify-center py-8">
|
||||
<span class="loading loading-spinner" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<p v-else-if="!composantsList.length" class="text-sm text-base-content/70">
|
||||
<p v-else-if="!composantsTotal" class="text-sm text-base-content/70">
|
||||
Aucun composant n'a encore été créé.
|
||||
</p>
|
||||
|
||||
<p v-else-if="!filteredComposants.length" class="text-sm text-base-content/70">
|
||||
Aucun composant ne correspond à votre recherche.
|
||||
</p>
|
||||
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="table table-sm md:table-md">
|
||||
<thead>
|
||||
@@ -45,7 +64,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="component in composantsList" :key="component.id">
|
||||
<tr v-for="component in filteredComposants" :key="component.id">
|
||||
<td>{{ component.name || 'Composant sans nom' }}</td>
|
||||
<td>{{ component.typeComposant?.name || '—' }}</td>
|
||||
<td>{{ component.reference || '—' }}</td>
|
||||
@@ -78,7 +97,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useComposants } from '~/composables/useComposants'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
|
||||
@@ -86,6 +105,25 @@ const { showError } = useToast()
|
||||
const { composants, loadComposants, loading: loadingComposantsRef, deleteComposant } = useComposants()
|
||||
const loadingComposants = computed(() => loadingComposantsRef.value)
|
||||
const composantsList = computed(() => composants.value || [])
|
||||
const composantsTotal = computed(() => composantsList.value.length)
|
||||
|
||||
const searchTerm = ref('')
|
||||
const filteredComposants = computed(() => {
|
||||
const term = searchTerm.value.trim().toLowerCase()
|
||||
if (!term) {
|
||||
return composantsList.value
|
||||
}
|
||||
return composantsList.value.filter((component) => {
|
||||
const name = (component?.name || '').toLowerCase()
|
||||
const reference = (component?.reference || '').toLowerCase()
|
||||
const category = (component?.typeComposant?.name || '').toLowerCase()
|
||||
return (
|
||||
name.includes(term) ||
|
||||
reference.includes(term) ||
|
||||
category.includes(term)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const handleDeleteComponent = async (component: Record<string, any>) => {
|
||||
const hasLinkedElements =
|
||||
|
||||
@@ -863,7 +863,7 @@ const resolveOptions = (field: any): string[] => {
|
||||
return option.trim()
|
||||
}
|
||||
if (typeof option === 'object') {
|
||||
const record = option as Record<string, unknown>
|
||||
const record = option || {}
|
||||
const keys = ['value', 'label', 'name']
|
||||
for (const key of keys) {
|
||||
const candidate = record[key]
|
||||
|
||||
@@ -970,21 +970,21 @@ const resolveOptions = (field: any): string[] => {
|
||||
const sources = [field?.options, field?.value?.options, field?.value?.choices]
|
||||
for (const source of sources) {
|
||||
if (Array.isArray(source)) {
|
||||
const mapped = source
|
||||
.map((option: unknown) => {
|
||||
if (option === null || option === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (typeof option === 'string') {
|
||||
return option.trim()
|
||||
}
|
||||
if (typeof option === 'object') {
|
||||
const record = option as Record<string, unknown>
|
||||
const keys = ['value', 'label', 'name']
|
||||
for (const key of keys) {
|
||||
const candidate = record[key]
|
||||
if (typeof candidate === 'string' && candidate.trim().length > 0) {
|
||||
return candidate.trim()
|
||||
const mapped = source
|
||||
.map((option: unknown) => {
|
||||
if (option === null || option === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (typeof option === 'string') {
|
||||
return option.trim()
|
||||
}
|
||||
if (typeof option === 'object') {
|
||||
const record = option || {}
|
||||
const keys = ['value', 'label', 'name']
|
||||
for (const key of keys) {
|
||||
const candidate = record[key]
|
||||
if (typeof candidate === 'string' && candidate.trim().length > 0) {
|
||||
return candidate.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ import { useApi } from '~/composables/useApi'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { useDocuments } from '~/composables/useDocuments'
|
||||
import { getFileIcon } from '~/utils/fileIcons'
|
||||
import { sanitizeDefinitionOverrides } from '~/shared/modelUtils'
|
||||
import { sanitizeDefinitionOverrides, normalizeStructureForEditor } from '~/shared/modelUtils'
|
||||
import { canPreviewDocument, isImageDocument } from '~/utils/documentPreview'
|
||||
import ComponentHierarchy from '~/components/ComponentHierarchy.vue'
|
||||
import DocumentUpload from '~/components/DocumentUpload.vue'
|
||||
@@ -1340,7 +1340,24 @@ const flattenComponents = (list = []) => {
|
||||
})
|
||||
}
|
||||
traverse(list)
|
||||
return result
|
||||
const filtered = result.filter((field) => {
|
||||
const key =
|
||||
field.customFieldId
|
||||
|| field.id
|
||||
|| (field.name ? `${field.name}::${field.type}` : null)
|
||||
|
||||
if (!key) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (allowedKeys.size > 0) {
|
||||
return allowedKeys.has(key)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
const flattenedComponents = computed(() => flattenComponents(components.value))
|
||||
@@ -1618,18 +1635,18 @@ const formatCustomFieldValue = (field) => {
|
||||
return 'Non défini'
|
||||
}
|
||||
|
||||
const value = field.value ?? ''
|
||||
if (!value && value !== 0) {
|
||||
const value = field.value ?? field.defaultValue ?? ''
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return 'Non défini'
|
||||
}
|
||||
|
||||
if (field.type === 'boolean') {
|
||||
const normalized = String(value).toLowerCase()
|
||||
if (normalized === 'true') return 'Oui'
|
||||
if (normalized === 'false') return 'Non'
|
||||
if (normalized === 'true' || normalized === '1') return 'Oui'
|
||||
if (normalized === 'false' || normalized === '0') return 'Non'
|
||||
}
|
||||
|
||||
return value
|
||||
return String(value)
|
||||
}
|
||||
|
||||
const shouldDisplayCustomField = (field) => {
|
||||
@@ -1679,35 +1696,249 @@ const summarizeCustomFields = (fields = []) => {
|
||||
}))
|
||||
}
|
||||
|
||||
const extractDefinitionName = (definition = {}) => {
|
||||
if (typeof definition?.name === 'string' && definition.name.trim()) {
|
||||
return definition.name.trim()
|
||||
}
|
||||
if (typeof definition?.key === 'string' && definition.key.trim()) {
|
||||
return definition.key.trim()
|
||||
}
|
||||
if (typeof definition?.label === 'string' && definition.label.trim()) {
|
||||
return definition.label.trim()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const extractDefinitionType = (definition = {}, fallback = 'text') => {
|
||||
const allowed = ['text', 'number', 'select', 'boolean', 'date']
|
||||
const rawType =
|
||||
typeof definition?.type === 'string'
|
||||
? definition.type
|
||||
: typeof definition?.value?.type === 'string'
|
||||
? definition.value.type
|
||||
: typeof fallback === 'string'
|
||||
? fallback
|
||||
: 'text'
|
||||
const normalized = rawType.toLowerCase()
|
||||
return allowed.includes(normalized) ? normalized : 'text'
|
||||
}
|
||||
|
||||
const extractDefinitionRequired = (definition = {}, fallback = false) => {
|
||||
if (typeof definition?.required === 'boolean') {
|
||||
return definition.required
|
||||
}
|
||||
const nested = definition?.value?.required
|
||||
if (typeof nested === 'boolean') {
|
||||
return nested
|
||||
}
|
||||
if (typeof nested === 'string') {
|
||||
const normalized = nested.toLowerCase()
|
||||
if (normalized === 'true' || normalized === '1') {
|
||||
return true
|
||||
}
|
||||
if (normalized === 'false' || normalized === '0') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return !!fallback
|
||||
}
|
||||
|
||||
const extractOptionList = (input) => {
|
||||
if (!Array.isArray(input)) {
|
||||
return undefined
|
||||
}
|
||||
const mapped = input
|
||||
.map((option) => {
|
||||
if (option === null || option === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (typeof option === 'string') {
|
||||
return option.trim()
|
||||
}
|
||||
if (typeof option === 'object') {
|
||||
const record = option || {}
|
||||
const keys = ['value', 'label', 'name']
|
||||
for (const key of keys) {
|
||||
const candidate = record[key]
|
||||
if (typeof candidate === 'string' && candidate.trim().length > 0) {
|
||||
return candidate.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
const fallback = String(option).trim()
|
||||
return fallback === '[object Object]' ? '' : fallback
|
||||
})
|
||||
.filter((option) => option.length > 0)
|
||||
return mapped.length ? mapped : undefined
|
||||
}
|
||||
|
||||
const extractDefinitionOptions = (definition = {}) => {
|
||||
const sources = [definition?.options, definition?.value?.options, definition?.value?.choices]
|
||||
for (const source of sources) {
|
||||
const list = extractOptionList(source)
|
||||
if (list) {
|
||||
return list
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const extractDefinitionDefaultValue = (definition = {}) => {
|
||||
const candidates = [
|
||||
definition?.defaultValue,
|
||||
definition?.value?.defaultValue,
|
||||
definition?.value?.value,
|
||||
definition?.value,
|
||||
definition?.default,
|
||||
]
|
||||
for (const candidate of candidates) {
|
||||
if (candidate === undefined || candidate === null || candidate === '') {
|
||||
continue
|
||||
}
|
||||
if (typeof candidate === 'object') {
|
||||
if (candidate === null) {
|
||||
continue
|
||||
}
|
||||
if (candidate && typeof candidate === 'object') {
|
||||
const nestedDefault =
|
||||
candidate.defaultValue !== undefined && candidate.defaultValue !== null
|
||||
? candidate.defaultValue
|
||||
: candidate.value
|
||||
if (nestedDefault !== undefined && nestedDefault !== null && nestedDefault !== '') {
|
||||
return nestedDefault
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
const coerceValueForType = (type, rawValue) => {
|
||||
if (rawValue === undefined || rawValue === null || rawValue === '') {
|
||||
return ''
|
||||
}
|
||||
if (type === 'boolean') {
|
||||
const normalized = String(rawValue).toLowerCase()
|
||||
if (normalized === 'true' || normalized === '1') {
|
||||
return 'true'
|
||||
}
|
||||
if (normalized === 'false' || normalized === '0') {
|
||||
return 'false'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
return String(rawValue)
|
||||
}
|
||||
|
||||
const normalizeExistingCustomFieldDefinitions = (fields) => {
|
||||
if (!Array.isArray(fields)) {
|
||||
return []
|
||||
}
|
||||
return fields
|
||||
.map((field) => normalizeCustomFieldDefinitionEntry(field))
|
||||
.filter((definition) => definition !== null)
|
||||
}
|
||||
|
||||
const normalizeCustomFieldDefinitionEntry = (definition = {}) => {
|
||||
const name = extractDefinitionName(definition)
|
||||
if (!name) {
|
||||
return null
|
||||
}
|
||||
const type = extractDefinitionType(definition)
|
||||
const required = extractDefinitionRequired(definition)
|
||||
const options = extractDefinitionOptions(definition)
|
||||
const defaultValue = extractDefinitionDefaultValue(definition)
|
||||
const id = typeof definition?.id === 'string' ? definition.id : undefined
|
||||
const customFieldId = typeof definition?.customFieldId === 'string' ? definition.customFieldId : id
|
||||
return {
|
||||
id,
|
||||
customFieldId,
|
||||
name,
|
||||
type,
|
||||
required,
|
||||
options,
|
||||
defaultValue,
|
||||
readOnly: !!definition?.readOnly,
|
||||
}
|
||||
}
|
||||
|
||||
const getStructureCustomFields = (structure) => {
|
||||
if (!structure || typeof structure !== 'object') {
|
||||
return []
|
||||
}
|
||||
const customFields = structure.customFields
|
||||
return Array.isArray(customFields) ? customFields : []
|
||||
const normalized = normalizeStructureForEditor(structure)
|
||||
return Array.isArray(normalized.customFields) ? normalized.customFields : []
|
||||
}
|
||||
|
||||
// Transform custom field values to custom fields format
|
||||
const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionSources) => {
|
||||
const normalizedValues = (Array.isArray(valueEntries) ? valueEntries : []).map((entry) => {
|
||||
const id = entry?.customField?.id ?? entry?.customFieldId ?? null
|
||||
const name = entry?.customField?.name ?? ''
|
||||
const type = entry?.customField?.type ?? 'text'
|
||||
const required = !!entry?.customField?.required
|
||||
const options = Array.isArray(entry?.customField?.options) ? entry.customField.options : []
|
||||
const normalizeCustomFieldValueEntry = (entry = {}) => {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
customFieldValueId: entry?.id ?? null,
|
||||
id,
|
||||
customFieldId: id,
|
||||
name,
|
||||
type,
|
||||
required,
|
||||
options,
|
||||
value: entry?.value ?? '',
|
||||
readOnly: false,
|
||||
}
|
||||
})
|
||||
const normalizedDefinition = normalizeCustomFieldDefinitionEntry(entry)
|
||||
if (!normalizedDefinition) {
|
||||
return null
|
||||
}
|
||||
|
||||
const value = coerceValueForType(
|
||||
normalizedDefinition.type,
|
||||
entry?.value ?? entry?.defaultValue ?? normalizedDefinition.defaultValue ?? '',
|
||||
)
|
||||
|
||||
return {
|
||||
id: entry?.customFieldValueId ?? entry?.id ?? null,
|
||||
customFieldId:
|
||||
entry?.customFieldId
|
||||
?? normalizedDefinition.customFieldId
|
||||
?? normalizedDefinition.id
|
||||
?? null,
|
||||
customField: {
|
||||
id: normalizedDefinition.id ?? normalizedDefinition.customFieldId ?? null,
|
||||
name: normalizedDefinition.name,
|
||||
type: normalizedDefinition.type,
|
||||
required: normalizedDefinition.required,
|
||||
options: normalizedDefinition.options,
|
||||
defaultValue: normalizedDefinition.defaultValue ?? '',
|
||||
},
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionSources) => {
|
||||
const normalizedValues = (Array.isArray(valueEntries) ? valueEntries : [])
|
||||
.map((entry) => {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
const normalizedDefinition = normalizeCustomFieldDefinitionEntry(entry.customField || entry)
|
||||
if (!normalizedDefinition) {
|
||||
return null
|
||||
}
|
||||
|
||||
const value = coerceValueForType(
|
||||
normalizedDefinition.type,
|
||||
entry?.value ?? entry?.defaultValue ?? normalizedDefinition.defaultValue ?? '',
|
||||
)
|
||||
|
||||
return {
|
||||
customFieldValueId: entry?.id ?? entry?.customFieldValueId ?? null,
|
||||
id: normalizedDefinition.id,
|
||||
customFieldId: normalizedDefinition.customFieldId ?? normalizedDefinition.id ?? null,
|
||||
name: normalizedDefinition.name,
|
||||
type: normalizedDefinition.type,
|
||||
required: normalizedDefinition.required,
|
||||
options: normalizedDefinition.options,
|
||||
optionsText: normalizedDefinition.options?.length ? normalizedDefinition.options.join('\n') : '',
|
||||
defaultValue: normalizedDefinition.defaultValue ?? '',
|
||||
value,
|
||||
readOnly: !!entry?.readOnly,
|
||||
}
|
||||
})
|
||||
.filter((entry) => entry !== null)
|
||||
|
||||
const result = [...normalizedValues]
|
||||
const keyFor = (item) => item?.id ?? `${item?.name ?? ''}::${item?.type ?? ''}`
|
||||
@@ -1725,18 +1956,26 @@ const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionS
|
||||
|
||||
const definitions = definitionSources
|
||||
.flatMap((source) => (Array.isArray(source) ? source : []))
|
||||
.filter(Boolean)
|
||||
.map((definition) => normalizeCustomFieldDefinitionEntry(definition))
|
||||
.filter((definition) => definition !== null)
|
||||
|
||||
const allowedKeys = new Set()
|
||||
definitions.forEach((definition) => {
|
||||
const normalizedDefinition = {
|
||||
id: definition?.id ?? definition?.customFieldId ?? null,
|
||||
name: definition?.name ?? '',
|
||||
type: definition?.type ?? 'text',
|
||||
required: !!definition?.required,
|
||||
options: Array.isArray(definition?.options) ? definition.options : [],
|
||||
customFieldId: definition?.id ?? definition?.customFieldId ?? null,
|
||||
readOnly: !!definition?.readOnly,
|
||||
if (!definition) {
|
||||
return
|
||||
}
|
||||
if (definition.id) {
|
||||
allowedKeys.add(definition.id)
|
||||
}
|
||||
if (definition.customFieldId) {
|
||||
allowedKeys.add(definition.customFieldId)
|
||||
}
|
||||
if (definition.name) {
|
||||
allowedKeys.add(`${definition.name}::${definition.type}`)
|
||||
}
|
||||
})
|
||||
|
||||
definitions.forEach((normalizedDefinition) => {
|
||||
const key = normalizedDefinition.id ?? `${normalizedDefinition.name}::${normalizedDefinition.type}`
|
||||
if (!key) {
|
||||
return
|
||||
@@ -1762,12 +2001,21 @@ const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionS
|
||||
if (existing) {
|
||||
existing.name = existing.name || normalizedDefinition.name
|
||||
existing.type = existing.type || normalizedDefinition.type
|
||||
existing.required = existing.required ?? normalizedDefinition.required
|
||||
if (!existing.options?.length) {
|
||||
existing.required = existing.required || normalizedDefinition.required
|
||||
if (!existing.options?.length && normalizedDefinition.options?.length) {
|
||||
existing.options = normalizedDefinition.options
|
||||
}
|
||||
if (!existing.defaultValue && normalizedDefinition.defaultValue) {
|
||||
existing.defaultValue = String(normalizedDefinition.defaultValue)
|
||||
if (!existing.value) {
|
||||
existing.value = coerceValueForType(existing.type, normalizedDefinition.defaultValue)
|
||||
}
|
||||
}
|
||||
existing.customFieldId = existing.customFieldId || normalizedDefinition.id
|
||||
existing.readOnly = existing.readOnly && normalizedDefinition.readOnly
|
||||
if (!existing.optionsText && normalizedDefinition.options?.length) {
|
||||
existing.optionsText = normalizedDefinition.options.join('\n')
|
||||
}
|
||||
if (normalizedDefinition.id) {
|
||||
existingMap.set(normalizedDefinition.id, existing)
|
||||
}
|
||||
@@ -1785,7 +2033,9 @@ const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionS
|
||||
type: normalizedDefinition.type,
|
||||
required: normalizedDefinition.required,
|
||||
options: normalizedDefinition.options,
|
||||
value: '',
|
||||
optionsText: normalizedDefinition.options?.length ? normalizedDefinition.options.join('\n') : '',
|
||||
defaultValue: normalizedDefinition.defaultValue ?? '',
|
||||
value: coerceValueForType(normalizedDefinition.type, normalizedDefinition.defaultValue ?? ''),
|
||||
readOnly: false,
|
||||
}
|
||||
result.push(entry)
|
||||
@@ -1858,23 +2108,47 @@ const transformCustomFields = (pieces) => {
|
||||
const requirement = piece.typeMachinePieceRequirement || {}
|
||||
const typePiece = requirement.typePiece || piece.typePiece || {}
|
||||
|
||||
const normalizeStructureDefinitions = (structure) => (
|
||||
structure ? normalizeStructureForEditor(structure) : null
|
||||
)
|
||||
|
||||
const normalizedStructureDefinitions = [
|
||||
normalizeStructureDefinitions(piece.definition?.structure),
|
||||
normalizeStructureDefinitions(piece.typePiece?.structure),
|
||||
normalizeStructureDefinitions(typePiece.structure),
|
||||
normalizeStructureDefinitions(typePiece.pieceSkeleton),
|
||||
normalizeStructureDefinitions(piece.typePiece?.pieceSkeleton),
|
||||
normalizeStructureDefinitions(requirement.structure),
|
||||
normalizeStructureDefinitions(requirement.pieceSkeleton),
|
||||
]
|
||||
|
||||
const valueEntries = [
|
||||
...(Array.isArray(piece.customFieldValues) ? piece.customFieldValues : []),
|
||||
...(Array.isArray(piece.customFields)
|
||||
? piece.customFields
|
||||
.map(normalizeCustomFieldValueEntry)
|
||||
.filter((entry) => entry !== null)
|
||||
: []),
|
||||
...(Array.isArray(typePiece.customFieldValues)
|
||||
? typePiece.customFieldValues
|
||||
.map(normalizeCustomFieldValueEntry)
|
||||
.filter((entry) => entry !== null)
|
||||
: []),
|
||||
]
|
||||
|
||||
const customFields = dedupeCustomFieldEntries(
|
||||
mergeCustomFieldValuesWithDefinitions(
|
||||
piece.customFieldValues,
|
||||
piece.customFields,
|
||||
piece.definition?.customFields,
|
||||
piece.typePiece?.customFields,
|
||||
typePiece.customFields,
|
||||
requirement.typePiece?.customFields,
|
||||
requirement.customFields,
|
||||
requirement.definition?.customFields,
|
||||
getStructureCustomFields(piece.definition?.structure),
|
||||
getStructureCustomFields(piece.typePiece?.structure),
|
||||
getStructureCustomFields(typePiece.structure),
|
||||
getStructureCustomFields(typePiece.pieceSkeleton),
|
||||
getStructureCustomFields(piece.typePiece?.pieceSkeleton),
|
||||
getStructureCustomFields(requirement.structure),
|
||||
getStructureCustomFields(requirement.pieceSkeleton),
|
||||
valueEntries,
|
||||
normalizeExistingCustomFieldDefinitions(piece.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(piece.definition?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(piece.typePiece?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(typePiece.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.typePiece?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.definition?.customFields),
|
||||
...normalizedStructureDefinitions.map((definition) =>
|
||||
getStructureCustomFields(definition),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1894,26 +2168,53 @@ const transformCustomFields = (pieces) => {
|
||||
|
||||
// Transform custom fields for components (now handles nested structure)
|
||||
const transformComponentCustomFields = (componentsData) => {
|
||||
const normalizeStructureDefinitions = (structure) => (
|
||||
structure ? normalizeStructureForEditor(structure) : null
|
||||
)
|
||||
|
||||
return (componentsData || []).map((component) => {
|
||||
const requirement = component.typeMachineComponentRequirement || {}
|
||||
const type = requirement.typeComposant || component.typeComposant || {}
|
||||
|
||||
const normalizedStructureDefinitions = [
|
||||
normalizeStructureDefinitions(component.definition?.structure),
|
||||
normalizeStructureDefinitions(component.typeComposant?.structure),
|
||||
normalizeStructureDefinitions(type.structure),
|
||||
normalizeStructureDefinitions(type.componentSkeleton),
|
||||
normalizeStructureDefinitions(requirement.structure),
|
||||
normalizeStructureDefinitions(requirement.componentSkeleton),
|
||||
]
|
||||
|
||||
const actualComponent = component.originalComposant || component
|
||||
|
||||
const valueEntries = [
|
||||
...(Array.isArray(component.customFieldValues) ? component.customFieldValues : []),
|
||||
...(Array.isArray(component.customFields)
|
||||
? component.customFields
|
||||
.map(normalizeCustomFieldValueEntry)
|
||||
.filter((entry) => entry !== null)
|
||||
: []),
|
||||
...(Array.isArray(actualComponent?.customFields)
|
||||
? actualComponent.customFields
|
||||
.map(normalizeCustomFieldValueEntry)
|
||||
.filter((entry) => entry !== null)
|
||||
: []),
|
||||
]
|
||||
|
||||
const customFields = dedupeCustomFieldEntries(
|
||||
mergeCustomFieldValuesWithDefinitions(
|
||||
component.customFieldValues,
|
||||
component.customFields,
|
||||
component.definition?.customFields,
|
||||
component.typeComposant?.customFields,
|
||||
type.customFields,
|
||||
requirement.typeComposant?.customFields,
|
||||
requirement.customFields,
|
||||
requirement.definition?.customFields,
|
||||
getStructureCustomFields(component.definition?.structure),
|
||||
getStructureCustomFields(component.typeComposant?.structure),
|
||||
getStructureCustomFields(type.structure),
|
||||
getStructureCustomFields(type.componentSkeleton),
|
||||
getStructureCustomFields(requirement.structure),
|
||||
getStructureCustomFields(requirement.componentSkeleton),
|
||||
valueEntries,
|
||||
normalizeExistingCustomFieldDefinitions(component.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(component.definition?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(component.typeComposant?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(type.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(actualComponent?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.typeComposant?.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(requirement.definition?.customFields),
|
||||
...normalizedStructureDefinitions.map((definition) =>
|
||||
getStructureCustomFields(definition),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1947,14 +2248,25 @@ const transformComponentCustomFields = (componentsData) => {
|
||||
const syncMachineCustomFields = () => {
|
||||
if (!machine.value) {
|
||||
machineCustomFields.value = []
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
const merged = dedupeCustomFieldEntries(mergeCustomFieldValuesWithDefinitions(
|
||||
machine.value.customFieldValues,
|
||||
machine.value.customFields,
|
||||
machine.value.typeMachine?.customFields,
|
||||
)).map((field) => ({
|
||||
const valueEntries = [
|
||||
...(Array.isArray(machine.value.customFieldValues) ? machine.value.customFieldValues : []),
|
||||
...(Array.isArray(machine.value.customFields)
|
||||
? machine.value.customFields
|
||||
.map(normalizeCustomFieldValueEntry)
|
||||
.filter((entry) => entry !== null)
|
||||
: []),
|
||||
]
|
||||
|
||||
const merged = dedupeCustomFieldEntries(
|
||||
mergeCustomFieldValuesWithDefinitions(
|
||||
valueEntries,
|
||||
normalizeExistingCustomFieldDefinitions(machine.value.customFields),
|
||||
normalizeExistingCustomFieldDefinitions(machine.value.typeMachine?.customFields),
|
||||
),
|
||||
).map((field) => ({
|
||||
...field,
|
||||
readOnly: false,
|
||||
}))
|
||||
|
||||
@@ -25,14 +25,33 @@
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<label class="w-full sm:w-64">
|
||||
<span class="text-xs font-semibold uppercase tracking-wide text-base-content/70">Recherche</span>
|
||||
<input
|
||||
v-model="searchTerm"
|
||||
type="text"
|
||||
class="input input-bordered input-sm w-full mt-1"
|
||||
placeholder="Nom, référence ou catégorie…"
|
||||
/>
|
||||
</label>
|
||||
<p class="text-xs text-base-content/50 sm:text-right">
|
||||
{{ filteredPieces.length }} / {{ piecesTotal }} résultat{{ filteredPieces.length > 1 ? 's' : '' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingPieces" class="flex justify-center py-8">
|
||||
<span class="loading loading-spinner" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<p v-else-if="!piecesList.length" class="text-sm text-base-content/70">
|
||||
<p v-else-if="!piecesTotal" class="text-sm text-base-content/70">
|
||||
Aucune pièce n'a encore été créée.
|
||||
</p>
|
||||
|
||||
<p v-else-if="!filteredPieces.length" class="text-sm text-base-content/70">
|
||||
Aucune pièce ne correspond à votre recherche.
|
||||
</p>
|
||||
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="table table-sm md:table-md">
|
||||
<thead>
|
||||
@@ -44,7 +63,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="piece in piecesList" :key="piece.id">
|
||||
<tr v-for="piece in filteredPieces" :key="piece.id">
|
||||
<td>{{ piece.name || 'Pièce sans nom' }}</td>
|
||||
<td>{{ piece.typePiece?.name || '—' }}</td>
|
||||
<td>{{ piece.reference || '—' }}</td>
|
||||
@@ -77,7 +96,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { usePieces } from '~/composables/usePieces'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
|
||||
@@ -85,6 +104,25 @@ const { showError } = useToast()
|
||||
const { pieces, loadPieces, loading: loadingPiecesRef, deletePiece } = usePieces()
|
||||
const loadingPieces = computed(() => loadingPiecesRef.value)
|
||||
const piecesList = computed(() => pieces.value || [])
|
||||
const piecesTotal = computed(() => piecesList.value.length)
|
||||
|
||||
const searchTerm = ref('')
|
||||
const filteredPieces = computed(() => {
|
||||
const term = searchTerm.value.trim().toLowerCase()
|
||||
if (!term) {
|
||||
return piecesList.value
|
||||
}
|
||||
return piecesList.value.filter((piece) => {
|
||||
const name = (piece?.name || '').toLowerCase()
|
||||
const reference = (piece?.reference || '').toLowerCase()
|
||||
const category = (piece?.typePiece?.name || '').toLowerCase()
|
||||
return (
|
||||
name.includes(term) ||
|
||||
reference.includes(term) ||
|
||||
category.includes(term)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const handleDeletePiece = async (piece: Record<string, any>) => {
|
||||
const hasLinkedElements =
|
||||
|
||||
Reference in New Issue
Block a user