Merges the full git history of Inventory_frontend into the monorepo under frontend/. Removes the submodule in favor of a unified repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
182 lines
5.6 KiB
TypeScript
182 lines
5.6 KiB
TypeScript
/**
|
|
* Reactive custom field management for entity items (ComponentItem, PieceItem).
|
|
*
|
|
* Wraps the pure logic from entityCustomFieldLogic.ts with Vue reactivity,
|
|
* watchers, and API calls for updating/upserting custom field values.
|
|
*/
|
|
|
|
import { computed, watch } from 'vue'
|
|
import { useCustomFields } from '~/composables/useCustomFields'
|
|
import { useToast } from '~/composables/useToast'
|
|
import {
|
|
buildDefinitionSources,
|
|
buildCandidateCustomFields,
|
|
mergeFieldDefinitionsWithValues,
|
|
dedupeMergedFields,
|
|
ensureCustomFieldId,
|
|
resolveFieldId,
|
|
resolveFieldName,
|
|
resolveFieldType,
|
|
resolveFieldReadOnly,
|
|
resolveCustomFieldId,
|
|
buildCustomFieldMetadata,
|
|
} from '~/shared/utils/entityCustomFieldLogic'
|
|
|
|
export interface EntityCustomFieldsDeps {
|
|
entity: () => any
|
|
entityType: 'composant' | 'piece'
|
|
}
|
|
|
|
export function useEntityCustomFields(deps: EntityCustomFieldsDeps) {
|
|
const { entity, entityType } = deps
|
|
const {
|
|
updateCustomFieldValue: updateCustomFieldValueApi,
|
|
upsertCustomFieldValue,
|
|
} = useCustomFields()
|
|
const { showSuccess, showError } = useToast()
|
|
|
|
const definitionSources = computed(() =>
|
|
buildDefinitionSources(entity(), entityType),
|
|
)
|
|
|
|
const displayedCustomFields = computed(() =>
|
|
dedupeMergedFields(
|
|
mergeFieldDefinitionsWithValues(
|
|
definitionSources.value,
|
|
entity().customFieldValues,
|
|
),
|
|
),
|
|
)
|
|
|
|
const candidateCustomFields = computed(() =>
|
|
buildCandidateCustomFields(entity(), definitionSources.value),
|
|
)
|
|
|
|
// Watchers to ensure field IDs are resolved
|
|
watch(
|
|
candidateCustomFields,
|
|
() => {
|
|
const candidates = candidateCustomFields.value
|
|
;(displayedCustomFields.value || []).forEach((field: any) => {
|
|
if (field) ensureCustomFieldId(field, candidates)
|
|
})
|
|
},
|
|
{ immediate: true, deep: true },
|
|
)
|
|
|
|
watch(
|
|
displayedCustomFields,
|
|
(fields) => {
|
|
const candidates = candidateCustomFields.value
|
|
;(fields || []).forEach((field: any) => {
|
|
if (field) ensureCustomFieldId(field, candidates)
|
|
})
|
|
},
|
|
{ immediate: true, deep: true },
|
|
)
|
|
|
|
const updateCustomField = async (field: any) => {
|
|
if (!field || resolveFieldReadOnly(field)) return
|
|
|
|
const e = entity()
|
|
const fieldValueId = resolveFieldId(field)
|
|
|
|
// Update existing field value
|
|
if (fieldValueId) {
|
|
const result: any = await updateCustomFieldValueApi(fieldValueId, { value: field.value ?? '' })
|
|
if (result.success) {
|
|
const existingValue = e.customFieldValues?.find((v: any) => v.id === fieldValueId)
|
|
if (existingValue?.customField?.id) {
|
|
field.customFieldId = existingValue.customField.id
|
|
field.customField = existingValue.customField
|
|
}
|
|
showSuccess(`Champ "${resolveFieldName(field)}" mis à jour avec succès`)
|
|
} else {
|
|
showError(`Erreur lors de la mise à jour du champ "${resolveFieldName(field)}"`)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Create new field value
|
|
const customFieldId = ensureCustomFieldId(field, candidateCustomFields.value)
|
|
const fieldName = resolveFieldName(field)
|
|
if (!e?.id) {
|
|
showError(`Impossible de créer la valeur pour ce champ`)
|
|
return
|
|
}
|
|
if (!customFieldId && (!fieldName || fieldName === 'Champ')) {
|
|
showError(`Impossible de créer la valeur pour ce champ`)
|
|
return
|
|
}
|
|
|
|
const metadata = customFieldId ? undefined : buildCustomFieldMetadata(field)
|
|
const result: any = await upsertCustomFieldValue(
|
|
customFieldId,
|
|
entityType,
|
|
e.id,
|
|
field.value ?? '',
|
|
metadata,
|
|
)
|
|
|
|
if (result.success) {
|
|
const newValue = result.data
|
|
if (newValue?.id) {
|
|
field.customFieldValueId = newValue.id
|
|
field.value = newValue.value ?? field.value ?? ''
|
|
if (newValue.customField?.id) {
|
|
field.customFieldId = newValue.customField.id
|
|
field.customField = newValue.customField
|
|
}
|
|
|
|
if (Array.isArray(e.customFieldValues)) {
|
|
const index = e.customFieldValues.findIndex((v: any) => v.id === newValue.id)
|
|
if (index !== -1) {
|
|
e.customFieldValues.splice(index, 1, newValue)
|
|
} else {
|
|
e.customFieldValues.push(newValue)
|
|
}
|
|
} else {
|
|
e.customFieldValues = [newValue]
|
|
}
|
|
}
|
|
showSuccess(`Champ "${resolveFieldName(field)}" créé avec succès`)
|
|
|
|
// Update definitions list
|
|
const definitions = Array.isArray(e.customFields) ? [...e.customFields] : []
|
|
const fieldIdentifier = ensureCustomFieldId(field, candidateCustomFields.value)
|
|
const existingIndex = definitions.findIndex((definition: any) => {
|
|
const definitionId = resolveCustomFieldId(definition)
|
|
if (fieldIdentifier && definitionId) return definitionId === fieldIdentifier
|
|
return definition?.name === resolveFieldName(field)
|
|
})
|
|
|
|
const updatedDefinition = {
|
|
...(existingIndex !== -1 ? definitions[existingIndex] : {}),
|
|
customFieldValueId: field.customFieldValueId,
|
|
customFieldId: fieldIdentifier,
|
|
name: resolveFieldName(field),
|
|
type: resolveFieldType(field),
|
|
required: field.required ?? false,
|
|
options: field.options ?? [],
|
|
value: field.value ?? '',
|
|
customField: field.customField ?? null,
|
|
}
|
|
|
|
if (existingIndex !== -1) {
|
|
definitions.splice(existingIndex, 1, updatedDefinition)
|
|
} else {
|
|
definitions.push(updatedDefinition)
|
|
}
|
|
e.customFields = definitions
|
|
} else {
|
|
showError(`Erreur lors de la sauvegarde du champ "${resolveFieldName(field)}"`)
|
|
}
|
|
}
|
|
|
|
return {
|
|
displayedCustomFields,
|
|
candidateCustomFields,
|
|
updateCustomField,
|
|
}
|
|
}
|