feat(frontend): améliorer éditeurs de structure
This commit is contained in:
@@ -74,30 +74,57 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(piece, index) in localStructure.pieces"
|
v-for="(piece, index) in localStructure.pieces"
|
||||||
:key="`root-piece-${index}`"
|
:key="`root-piece-${index}`"
|
||||||
class="border border-base-200 rounded-md p-3"
|
class="border border-base-200 rounded-md p-3 space-y-3"
|
||||||
>
|
>
|
||||||
<div class="flex items-start justify-between gap-2">
|
<div class="flex items-start justify-between gap-2">
|
||||||
<div class="flex-1 grid grid-cols-1 md:grid-cols-3 gap-2">
|
<div class="flex-1 space-y-3">
|
||||||
<input
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
|
||||||
v-model="piece.name"
|
<input
|
||||||
type="text"
|
v-model="piece.name"
|
||||||
class="input input-bordered input-xs"
|
type="text"
|
||||||
placeholder="Nom de la pièce"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Nom de la pièce"
|
||||||
<input
|
/>
|
||||||
v-model="piece.reference"
|
<input
|
||||||
type="text"
|
v-model="piece.reference"
|
||||||
class="input input-bordered input-xs"
|
type="text"
|
||||||
placeholder="Référence"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Référence"
|
||||||
<input
|
/>
|
||||||
v-model.number="piece.quantity"
|
<input
|
||||||
type="number"
|
v-model.number="piece.quantity"
|
||||||
min="0"
|
type="number"
|
||||||
step="1"
|
min="0"
|
||||||
class="input input-bordered input-xs"
|
step="1"
|
||||||
placeholder="Quantité"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Quantité"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Famille de pièce</span></label>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
:list="`component-piece-type-options-${index}`"
|
||||||
|
v-model="piece.typePieceLabel"
|
||||||
|
type="search"
|
||||||
|
autocomplete="off"
|
||||||
|
class="input input-bordered input-xs"
|
||||||
|
placeholder="Rechercher une famille"
|
||||||
|
@change="handlePieceTypeChange(piece)"
|
||||||
|
@blur="handlePieceTypeChange(piece)"
|
||||||
|
/>
|
||||||
|
<datalist :id="`component-piece-type-options-${index}`">
|
||||||
|
<option
|
||||||
|
v-for="type in availablePieceTypes"
|
||||||
|
:key="type.id"
|
||||||
|
:value="formatPieceTypeOption(type)"
|
||||||
|
/>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
<p class="mt-1 text-[11px] text-gray-500">
|
||||||
|
{{ piece.typePieceId ? `Sélection : ${getPieceTypeLabel(piece.typePieceId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
|
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
|
||||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||||
@@ -124,6 +151,7 @@
|
|||||||
:key="`root-sub-${index}`"
|
:key="`root-sub-${index}`"
|
||||||
:node="subComponent"
|
:node="subComponent"
|
||||||
:depth="0"
|
:depth="0"
|
||||||
|
:piece-types="availablePieceTypes"
|
||||||
@remove="removeSubComponent(index)"
|
@remove="removeSubComponent(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,7 +160,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, watch } from 'vue'
|
import { reactive, watch, computed, onMounted } from 'vue'
|
||||||
import IconLucidePlus from '~icons/lucide/plus'
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
import IconLucideTrash from '~icons/lucide/trash'
|
import IconLucideTrash from '~icons/lucide/trash'
|
||||||
import StructureSubComponentEditor from '~/components/StructureSubComponentEditor.vue'
|
import StructureSubComponentEditor from '~/components/StructureSubComponentEditor.vue'
|
||||||
@@ -141,6 +169,7 @@ import {
|
|||||||
hydrateStructureForEditor,
|
hydrateStructureForEditor,
|
||||||
normalizeStructureForSave,
|
normalizeStructureForSave,
|
||||||
} from '~/shared/modelUtils'
|
} from '~/shared/modelUtils'
|
||||||
|
import { usePieceTypes } from '~/composables/usePieceTypes'
|
||||||
|
|
||||||
defineOptions({ name: 'ComponentModelStructureEditor' })
|
defineOptions({ name: 'ComponentModelStructureEditor' })
|
||||||
|
|
||||||
@@ -186,6 +215,138 @@ watch(
|
|||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PieceTypeOption = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
code?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
||||||
|
|
||||||
|
const availablePieceTypes = computed<PieceTypeOption[]>(() => pieceTypes.value ?? [])
|
||||||
|
|
||||||
|
const pieceTypeMap = computed(() => {
|
||||||
|
const map = new Map<string, PieceTypeOption>()
|
||||||
|
availablePieceTypes.value.forEach((type) => {
|
||||||
|
if (type && typeof type.id === 'string') {
|
||||||
|
map.set(type.id, type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatPieceTypeOption = (type: PieceTypeOption | undefined | null) => {
|
||||||
|
if (!type) return ''
|
||||||
|
return type.code ? `${type.name} (${type.code})` : type.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvePieceType = (input: string) => {
|
||||||
|
const normalized = input.trim().toLowerCase()
|
||||||
|
if (!normalized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
availablePieceTypes.value.find((type) => {
|
||||||
|
const formatted = formatPieceTypeOption(type).toLowerCase()
|
||||||
|
const name = (type?.name ?? '').toLowerCase()
|
||||||
|
const code = (type?.code ?? '').toLowerCase()
|
||||||
|
return (
|
||||||
|
formatted === normalized
|
||||||
|
|| name === normalized
|
||||||
|
|| (!!code && code === normalized)
|
||||||
|
)
|
||||||
|
}) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPieceTypeLabel = (id?: string) => {
|
||||||
|
if (!id) return ''
|
||||||
|
const option = pieceTypeMap.value.get(id)
|
||||||
|
return formatPieceTypeOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePieceTypeLabel = (piece: any) => {
|
||||||
|
if (!piece) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (piece.typePieceId) {
|
||||||
|
const option = pieceTypeMap.value.get(piece.typePieceId)
|
||||||
|
piece.typePieceLabel = option ? formatPieceTypeOption(option) : piece.typePieceLabel || ''
|
||||||
|
} else if (piece.typePieceLabel) {
|
||||||
|
const match = resolvePieceType(piece.typePieceLabel)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePieceTypeChange = (piece: any) => {
|
||||||
|
if (!piece) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const value = typeof piece.typePieceLabel === 'string' ? piece.typePieceLabel.trim() : ''
|
||||||
|
if (!value) {
|
||||||
|
piece.typePieceId = ''
|
||||||
|
piece.typePieceLabel = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const match = resolvePieceType(value)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
} else {
|
||||||
|
piece.typePieceId = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyPieceLabels = (pieces?: any[]) => {
|
||||||
|
if (!Array.isArray(pieces)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pieces.forEach((piece) => {
|
||||||
|
if (piece?.typePieceId) {
|
||||||
|
updatePieceTypeLabel(piece)
|
||||||
|
} else if (piece?.typePieceLabel) {
|
||||||
|
const match = resolvePieceType(piece.typePieceLabel)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const traverseSubComponents = (components?: any[]) => {
|
||||||
|
if (!Array.isArray(components)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
components.forEach((component) => {
|
||||||
|
applyPieceLabels(component?.pieces)
|
||||||
|
traverseSubComponents(component?.subComponents)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncAllPieceTypeLabels = () => {
|
||||||
|
applyPieceLabels(localStructure.pieces)
|
||||||
|
traverseSubComponents(localStructure.subComponents)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!availablePieceTypes.value.length) {
|
||||||
|
await loadPieceTypes()
|
||||||
|
}
|
||||||
|
syncAllPieceTypeLabels()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => availablePieceTypes.value,
|
||||||
|
() => {
|
||||||
|
syncAllPieceTypeLabels()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
const ensureArray = (key: 'customFields' | 'pieces' | 'subComponents') => {
|
const ensureArray = (key: 'customFields' | 'pieces' | 'subComponents') => {
|
||||||
if (!Array.isArray(localStructure[key])) {
|
if (!Array.isArray(localStructure[key])) {
|
||||||
localStructure[key] = []
|
localStructure[key] = []
|
||||||
@@ -215,6 +376,8 @@ const addPiece = () => {
|
|||||||
name: '',
|
name: '',
|
||||||
reference: '',
|
reference: '',
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
|
typePieceId: '',
|
||||||
|
typePieceLabel: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,30 +116,57 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(piece, pieceIndex) in node.pieces"
|
v-for="(piece, pieceIndex) in node.pieces"
|
||||||
:key="`piece-${depth}-${pieceIndex}`"
|
:key="`piece-${depth}-${pieceIndex}`"
|
||||||
class="border border-base-200 rounded-md p-3"
|
class="border border-base-200 rounded-md p-3 space-y-3"
|
||||||
>
|
>
|
||||||
<div class="flex items-start justify-between gap-2">
|
<div class="flex items-start justify-between gap-2">
|
||||||
<div class="flex-1 grid grid-cols-1 md:grid-cols-3 gap-2">
|
<div class="flex-1 space-y-3">
|
||||||
<input
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
|
||||||
v-model="piece.name"
|
<input
|
||||||
type="text"
|
v-model="piece.name"
|
||||||
class="input input-bordered input-xs"
|
type="text"
|
||||||
placeholder="Nom de la pièce"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Nom de la pièce"
|
||||||
<input
|
/>
|
||||||
v-model="piece.reference"
|
<input
|
||||||
type="text"
|
v-model="piece.reference"
|
||||||
class="input input-bordered input-xs"
|
type="text"
|
||||||
placeholder="Référence"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Référence"
|
||||||
<input
|
/>
|
||||||
v-model.number="piece.quantity"
|
<input
|
||||||
type="number"
|
v-model.number="piece.quantity"
|
||||||
min="0"
|
type="number"
|
||||||
step="1"
|
min="0"
|
||||||
class="input input-bordered input-xs"
|
step="1"
|
||||||
placeholder="Quantité"
|
class="input input-bordered input-xs"
|
||||||
/>
|
placeholder="Quantité"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Famille de pièce</span></label>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
:list="getPieceTypeListId(pieceIndex)"
|
||||||
|
v-model="piece.typePieceLabel"
|
||||||
|
type="search"
|
||||||
|
autocomplete="off"
|
||||||
|
class="input input-bordered input-xs"
|
||||||
|
placeholder="Rechercher une famille"
|
||||||
|
@change="handlePieceTypeChange(piece)"
|
||||||
|
@blur="handlePieceTypeChange(piece)"
|
||||||
|
/>
|
||||||
|
<datalist :id="getPieceTypeListId(pieceIndex)">
|
||||||
|
<option
|
||||||
|
v-for="type in pieceTypes"
|
||||||
|
:key="type.id"
|
||||||
|
:value="formatPieceTypeOption(type)"
|
||||||
|
/>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
<p class="mt-1 text-[11px] text-gray-500">
|
||||||
|
{{ piece.typePieceId ? `Sélection : ${getPieceTypeLabel(piece.typePieceId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(pieceIndex)">
|
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(pieceIndex)">
|
||||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||||
@@ -168,6 +195,7 @@
|
|||||||
:key="`sub-${depth}-${index}`"
|
:key="`sub-${depth}-${index}`"
|
||||||
:node="sub"
|
:node="sub"
|
||||||
:depth="depth + 1"
|
:depth="depth + 1"
|
||||||
|
:piece-types="pieceTypes"
|
||||||
@remove="removeSubComponent(index)"
|
@remove="removeSubComponent(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,26 +205,155 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
import IconLucidePlus from '~icons/lucide/plus'
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
import IconLucideTrash from '~icons/lucide/trash'
|
import IconLucideTrash from '~icons/lucide/trash'
|
||||||
|
|
||||||
defineOptions({ name: 'StructureSubComponentEditor' })
|
defineOptions({ name: 'StructureSubComponentEditor' })
|
||||||
|
|
||||||
const props = defineProps({
|
type PieceTypeOption = {
|
||||||
node: {
|
id: string
|
||||||
type: Object,
|
name: string
|
||||||
required: true,
|
code?: string | null
|
||||||
},
|
}
|
||||||
depth: {
|
|
||||||
type: Number,
|
const props = withDefaults(defineProps<{
|
||||||
default: 0,
|
node: Record<string, any>
|
||||||
},
|
depth?: number
|
||||||
|
pieceTypes?: PieceTypeOption[]
|
||||||
|
}>(), {
|
||||||
|
depth: 0,
|
||||||
|
pieceTypes: () => [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['remove'])
|
const emit = defineEmits(['remove'])
|
||||||
|
|
||||||
|
const pieceTypeMap = computed(() => {
|
||||||
|
const map = new Map<string, PieceTypeOption>()
|
||||||
|
;(props.pieceTypes ?? []).forEach((type) => {
|
||||||
|
if (type && typeof type.id === 'string') {
|
||||||
|
map.set(type.id, type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatPieceTypeOption = (type: PieceTypeOption | undefined | null) => {
|
||||||
|
if (!type) return ''
|
||||||
|
return type.code ? `${type.name} (${type.code})` : type.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvePieceType = (input: string) => {
|
||||||
|
const normalized = input.trim().toLowerCase()
|
||||||
|
if (!normalized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(props.pieceTypes ?? []).find((type) => {
|
||||||
|
const formatted = formatPieceTypeOption(type).toLowerCase()
|
||||||
|
const name = (type?.name ?? '').toLowerCase()
|
||||||
|
const code = (type?.code ?? '').toLowerCase()
|
||||||
|
return (
|
||||||
|
formatted === normalized
|
||||||
|
|| name === normalized
|
||||||
|
|| (!!code && code === normalized)
|
||||||
|
)
|
||||||
|
}) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPieceTypeLabel = (id?: string) => {
|
||||||
|
if (!id) return ''
|
||||||
|
const option = pieceTypeMap.value.get(id)
|
||||||
|
return formatPieceTypeOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePieceTypeLabel = (piece: any) => {
|
||||||
|
if (!piece) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (piece.typePieceId) {
|
||||||
|
const option = pieceTypeMap.value.get(piece.typePieceId)
|
||||||
|
piece.typePieceLabel = option ? formatPieceTypeOption(option) : piece.typePieceLabel || ''
|
||||||
|
} else if (piece.typePieceLabel) {
|
||||||
|
const match = resolvePieceType(piece.typePieceLabel)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePieceTypeChange = (piece: any) => {
|
||||||
|
if (!piece) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const value = typeof piece.typePieceLabel === 'string' ? piece.typePieceLabel.trim() : ''
|
||||||
|
if (!value) {
|
||||||
|
piece.typePieceId = ''
|
||||||
|
piece.typePieceLabel = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const match = resolvePieceType(value)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
} else {
|
||||||
|
piece.typePieceId = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPieceTypeListId = (pieceIndex: number) => `sub-piece-type-options-${props.depth ?? 0}-${pieceIndex}`
|
||||||
|
|
||||||
|
const applyPieceLabels = (pieces?: any[]) => {
|
||||||
|
if (!Array.isArray(pieces)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pieces.forEach((piece) => {
|
||||||
|
if (piece?.typePieceId) {
|
||||||
|
updatePieceTypeLabel(piece)
|
||||||
|
} else if (piece?.typePieceLabel) {
|
||||||
|
const match = resolvePieceType(piece.typePieceLabel)
|
||||||
|
if (match) {
|
||||||
|
piece.typePieceId = match.id
|
||||||
|
piece.typePieceLabel = formatPieceTypeOption(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const traverseSubComponents = (components?: any[]) => {
|
||||||
|
if (!Array.isArray(components)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
components.forEach((component) => {
|
||||||
|
applyPieceLabels(component?.pieces)
|
||||||
|
traverseSubComponents(component?.subComponents)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncPieceTypeLabels = () => {
|
||||||
|
applyPieceLabels(props.node?.pieces)
|
||||||
|
traverseSubComponents(props.node?.subComponents)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.pieceTypes,
|
||||||
|
() => {
|
||||||
|
syncPieceTypeLabels()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.node,
|
||||||
|
() => {
|
||||||
|
syncPieceTypeLabels()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
const expanded = ref(true)
|
const expanded = ref(true)
|
||||||
const depthPadding = computed(() => (props.depth > 0 ? 'ml-4' : ''))
|
const depthPadding = computed(() => (props.depth > 0 ? 'ml-4' : ''))
|
||||||
|
|
||||||
@@ -233,6 +390,8 @@ const addPiece = () => {
|
|||||||
name: '',
|
name: '',
|
||||||
reference: '',
|
reference: '',
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
|
typePieceId: '',
|
||||||
|
typePieceLabel: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,20 @@ const sanitizePieces = (pieces: any[]): any[] => {
|
|||||||
const quantity = Number(piece?.quantity)
|
const quantity = Number(piece?.quantity)
|
||||||
const normalizedQuantity = Number.isFinite(quantity) && quantity > 0 ? quantity : undefined
|
const normalizedQuantity = Number.isFinite(quantity) && quantity > 0 ? quantity : undefined
|
||||||
|
|
||||||
|
const rawTypePieceId = typeof piece?.typePieceId === 'string'
|
||||||
|
? piece.typePieceId.trim()
|
||||||
|
: typeof piece?.typePiece?.id === 'string'
|
||||||
|
? piece.typePiece.id.trim()
|
||||||
|
: ''
|
||||||
|
const typePieceId = rawTypePieceId.length > 0 ? rawTypePieceId : undefined
|
||||||
|
|
||||||
|
const rawTypePieceLabel = typeof piece?.typePieceLabel === 'string'
|
||||||
|
? piece.typePieceLabel.trim()
|
||||||
|
: typeof piece?.typePiece?.name === 'string'
|
||||||
|
? piece.typePiece.name.trim()
|
||||||
|
: ''
|
||||||
|
const typePieceLabel = rawTypePieceLabel.length > 0 ? rawTypePieceLabel : undefined
|
||||||
|
|
||||||
const result: Record<string, unknown> = { name }
|
const result: Record<string, unknown> = { name }
|
||||||
if (reference !== undefined) {
|
if (reference !== undefined) {
|
||||||
result.reference = reference
|
result.reference = reference
|
||||||
@@ -92,6 +106,12 @@ const sanitizePieces = (pieces: any[]): any[] => {
|
|||||||
if (normalizedQuantity !== undefined) {
|
if (normalizedQuantity !== undefined) {
|
||||||
result.quantity = normalizedQuantity
|
result.quantity = normalizedQuantity
|
||||||
}
|
}
|
||||||
|
if (typePieceId) {
|
||||||
|
result.typePieceId = typePieceId
|
||||||
|
}
|
||||||
|
if (typePieceLabel) {
|
||||||
|
result.typePieceLabel = typePieceLabel
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -173,6 +193,8 @@ const hydratePieces = (pieces: any[]): any[] => {
|
|||||||
name: piece?.name ?? '',
|
name: piece?.name ?? '',
|
||||||
reference: piece?.reference ?? '',
|
reference: piece?.reference ?? '',
|
||||||
quantity: piece?.quantity ?? undefined,
|
quantity: piece?.quantity ?? undefined,
|
||||||
|
typePieceId: piece?.typePieceId ?? piece?.typePiece?.id ?? '',
|
||||||
|
typePieceLabel: piece?.typePieceLabel ?? piece?.typePiece?.name ?? '',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user