refactor(front): extract shared utils and rewire pages

This commit is contained in:
Matthieu
2026-02-06 17:16:16 +01:00
parent 1fbd1d1b2e
commit 9ee348fff0
36 changed files with 1686 additions and 2194 deletions

View File

@@ -246,6 +246,16 @@ const emitSelection = (ids: string[]) => {
emit('update:modelValue', normalized)
}
const extractDataArray = (data: unknown): ConstructeurSummary[] => {
if (Array.isArray(data)) {
return data as ConstructeurSummary[]
}
if (data && typeof data === 'object' && 'id' in data) {
return [data as ConstructeurSummary]
}
return []
}
const ensureOptionsLoaded = async (force = false) => {
if (!force && !searchTerm.value && constructeurs.value.length) {
applyOptions(constructeurs.value as ConstructeurSummary[])
@@ -262,7 +272,7 @@ const ensureOptionsLoaded = async (force = false) => {
const result = await searchConstructeurs(searchTerm.value)
if (result.success) {
applyOptions(result.data || [])
applyOptions(extractDataArray(result.data))
lastSearchTerm = searchTerm.value
}
}
@@ -283,7 +293,7 @@ const onSearch = () => {
}
const result = await searchConstructeurs(searchTerm.value)
if (result.success) {
applyOptions(result.data || [])
applyOptions(extractDataArray(result.data))
lastSearchTerm = searchTerm.value
}
}, 250)
@@ -310,16 +320,18 @@ const closeCreateModal = () => {
const handleCreate = async () => {
creating.value = true
const payload = { ...createForm.value }
if (!payload.phone) {
delete payload.phone
const payload: { name: string; email?: string; phone?: string } = {
name: createForm.value.name,
}
if (!payload.email) {
delete payload.email
if (createForm.value.email) {
payload.email = createForm.value.email
}
if (createForm.value.phone) {
payload.phone = createForm.value.phone
}
const result = await createConstructeur(payload)
creating.value = false
if (result.success) {
if (result.success && result.data && !Array.isArray(result.data)) {
emitSelection([...selectedIds.value, result.data.id])
searchTerm.value = ''
closeCreateModal()

View File

@@ -1,7 +1,7 @@
<template>
<div class="space-y-1">
<SearchSelect
:model-value="modelValue"
:model-value="modelValue ?? undefined"
:options="productOptions"
:loading="loading"
:placeholder="placeholder"

View File

@@ -825,10 +825,9 @@ const customFieldReorderClass = (index: number) => {
const addCustomField = () => {
ensureArray('customFields')
const nextIndex = Array.isArray(props.node.customFields)
? props.node.customFields.length
: 0
props.node.customFields.push({
const fields = props.node.customFields!
const nextIndex = fields.length
fields.push({
name: '',
type: 'text',
required: false,
@@ -847,7 +846,7 @@ const removeCustomField = (index: number) => {
const addPiece = () => {
ensureArray('pieces')
props.node.pieces.push({
props.node.pieces!.push({
typePieceId: '',
typePieceLabel: '',
reference: '',
@@ -863,7 +862,7 @@ const removePiece = (index: number) => {
const addProduct = () => {
ensureArray('products')
props.node.products.push({
props.node.products!.push({
typeProductId: '',
typeProductLabel: '',
familyCode: '',
@@ -911,6 +910,7 @@ const moveItemInPlace = <T,>(list: T[], from: number, to: number) => {
}
const updated = list.slice()
const [item] = updated.splice(from, 1)
if (item === undefined) return
updated.splice(to, 0, item)
list.splice(0, list.length, ...updated)
}

View File

@@ -66,7 +66,7 @@
type="text"
class="input input-bordered input-sm"
:placeholder="labels.labelPlaceholder"
@input="updateRequirement(index, { label: $event.target.value })"
@input="handleLabelInput(index, $event)"
/>
</div>
@@ -79,7 +79,7 @@
type="number"
min="0"
class="input input-bordered input-sm"
@input="updateRequirement(index, { minCount: parseNumber($event.target.value) })"
@input="handleMinInput(index, $event)"
/>
</div>
@@ -93,7 +93,7 @@
type="number"
min="0"
class="input input-bordered input-sm"
@input="updateRequirement(index, { maxCount: parseOptionalNumber($event.target.value) })"
@input="handleMaxInput(index, $event)"
/>
</div>
</div>
@@ -113,7 +113,7 @@
type="checkbox"
class="checkbox checkbox-sm"
:checked="(requirement.required ?? requiredFallback) === true"
@change="updateRequirement(index, { required: $event.target.checked })"
@change="handleRequiredChange(index, $event)"
/>
{{ labels.requiredLabel }}
</label>
@@ -123,7 +123,7 @@
type="checkbox"
class="checkbox checkbox-sm"
:checked="(requirement.allowNewModels ?? allowNewModelsFallback) === true"
@change="updateRequirement(index, { allowNewModels: $event.target.checked })"
@change="handleAllowNewModelsChange(index, $event)"
/>
{{ labels.allowNewModelsLabel }}
</label>
@@ -277,6 +277,37 @@ const parseOptionalNumber = (value: string) => {
return Number.isFinite(parsed) ? parsed : null
}
// Type-safe event handlers
const getInputValue = (event: Event): string => {
const target = event.target as HTMLInputElement | null
return target?.value ?? ''
}
const getCheckboxValue = (event: Event): boolean => {
const target = event.target as HTMLInputElement | null
return target?.checked ?? false
}
const handleLabelInput = (index: number, event: Event) => {
updateRequirement(index, { label: getInputValue(event) })
}
const handleMinInput = (index: number, event: Event) => {
updateRequirement(index, { minCount: parseNumber(getInputValue(event)) })
}
const handleMaxInput = (index: number, event: Event) => {
updateRequirement(index, { maxCount: parseOptionalNumber(getInputValue(event)) })
}
const handleRequiredChange = (index: number, event: Event) => {
updateRequirement(index, { required: getCheckboxValue(event) })
}
const handleAllowNewModelsChange = (index: number, event: Event) => {
updateRequirement(index, { allowNewModels: getCheckboxValue(event) })
}
const draggingRequirementIndex = ref<number | null>(null)
const requirementDropTargetIndex = ref<number | null>(null)
@@ -297,6 +328,10 @@ const reorderRequirements = (from: number, to: number) => {
}
const updated = list.slice() as Requirement[]
const [moved] = updated.splice(from, 1)
if (!moved) {
resetRequirementDragState()
return
}
updated.splice(to, 0, moved)
requirements.value = applyOrderIndex(updated)
resetRequirementDragState()