Fix fournisseur handling across catalog flows
This commit is contained in:
@@ -101,28 +101,44 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="product in filteredProducts" :key="product.id">
|
||||
<tr v-for="row in productRows" :key="row.product.id">
|
||||
<td class="align-middle">
|
||||
<DocumentThumbnail
|
||||
:document="resolvePrimaryDocument(product)"
|
||||
:alt="resolvePreviewAlt(product)"
|
||||
:document="resolvePrimaryDocument(row.product)"
|
||||
:alt="resolvePreviewAlt(row.product)"
|
||||
/>
|
||||
</td>
|
||||
<td class="font-medium">{{ product.name }}</td>
|
||||
<td>{{ product.reference || '—' }}</td>
|
||||
<td>{{ product.typeProduct?.name || '—' }}</td>
|
||||
<td class="font-medium">{{ row.product.name }}</td>
|
||||
<td>{{ row.product.reference || '—' }}</td>
|
||||
<td>{{ row.product.typeProduct?.name || '—' }}</td>
|
||||
<td>
|
||||
<span v-if="product.constructeurs?.length" class="text-sm">
|
||||
{{ formatConstructeurs(product.constructeurs) }}
|
||||
</span>
|
||||
<div
|
||||
v-if="row.suppliers.visible.length"
|
||||
class="flex max-w-[14rem] flex-wrap items-center gap-1 text-sm"
|
||||
:title="row.suppliers.tooltip"
|
||||
>
|
||||
<span
|
||||
v-for="supplier in row.suppliers.visible"
|
||||
:key="supplier"
|
||||
class="badge badge-ghost badge-sm whitespace-nowrap"
|
||||
>
|
||||
{{ supplier }}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.suppliers.overflow"
|
||||
class="badge badge-outline badge-sm"
|
||||
>
|
||||
+{{ row.suppliers.overflow }}
|
||||
</span>
|
||||
</div>
|
||||
<span v-else class="text-sm text-base-content/50">—</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{{ formatPrice(product.supplierPrice) }}
|
||||
{{ formatPrice(row.product.supplierPrice) }}
|
||||
</td>
|
||||
<td class="text-right space-x-2">
|
||||
<NuxtLink
|
||||
:to="`/product/${product.id}/edit`"
|
||||
:to="`/product/${row.product.id}/edit`"
|
||||
class="btn btn-ghost btn-xs"
|
||||
>
|
||||
Modifier
|
||||
@@ -130,7 +146,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
@click="confirmDelete(product)"
|
||||
@click="confirmDelete(row.product)"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
@@ -233,11 +249,91 @@ const formatPrice = (value: any) => {
|
||||
return priceFormatter.format(number)
|
||||
}
|
||||
|
||||
const formatConstructeurs = (constructeurs: Array<Record<string, any>>) =>
|
||||
constructeurs
|
||||
.map((constructeur) => constructeur?.name)
|
||||
.filter((name): name is string => Boolean(name))
|
||||
.join(', ')
|
||||
const MAX_VISIBLE_SUPPLIERS = 3
|
||||
|
||||
const resolveProductSuppliers = (product: Record<string, any>) => {
|
||||
const names: string[] = []
|
||||
const seen = new Set<string>()
|
||||
|
||||
const pushName = (maybeName: unknown) => {
|
||||
if (typeof maybeName !== 'string') {
|
||||
return
|
||||
}
|
||||
const normalized = maybeName.trim().replace(/\s+/g, ' ')
|
||||
if (!normalized.length) {
|
||||
return
|
||||
}
|
||||
const key = normalized.toLowerCase()
|
||||
if (seen.has(key)) {
|
||||
return
|
||||
}
|
||||
seen.add(key)
|
||||
names.push(normalized)
|
||||
}
|
||||
|
||||
const collectConstructeurs = (value: unknown): void => {
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(collectConstructeurs)
|
||||
return
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
pushName(value)
|
||||
return
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
const record = value as Record<string, any>
|
||||
pushName(record?.name ?? record?.label ?? record?.companyName ?? record?.company ?? null)
|
||||
if (record?.constructeur) {
|
||||
collectConstructeurs(record.constructeur)
|
||||
}
|
||||
if (Array.isArray(record?.constructeurs)) {
|
||||
collectConstructeurs(record.constructeurs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const collectFromLabel = (value: unknown): void => {
|
||||
if (typeof value !== 'string') {
|
||||
return
|
||||
}
|
||||
value
|
||||
.split(/[,;\\/•·|]+/)
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean)
|
||||
.forEach(pushName)
|
||||
}
|
||||
|
||||
collectConstructeurs(product?.constructeurs)
|
||||
collectConstructeurs(product?.constructeur)
|
||||
|
||||
collectFromLabel(product?.constructeursLabel)
|
||||
collectFromLabel(product?.supplierLabel)
|
||||
collectFromLabel(product?.suppliers)
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
const buildSuppliersDisplay = (product: Record<string, any>) => {
|
||||
const suppliers = resolveProductSuppliers(product)
|
||||
const visible = suppliers.slice(0, MAX_VISIBLE_SUPPLIERS)
|
||||
const overflow = Math.max(suppliers.length - visible.length, 0)
|
||||
return {
|
||||
suppliers,
|
||||
visible,
|
||||
overflow,
|
||||
tooltip: suppliers.length ? suppliers.join(', ') : '',
|
||||
}
|
||||
}
|
||||
|
||||
const productRows = computed(() =>
|
||||
filteredProducts.value.map((product) => ({
|
||||
product,
|
||||
suppliers: buildSuppliersDisplay(product),
|
||||
})),
|
||||
)
|
||||
|
||||
const resolvePrimaryDocument = (product: Record<string, any>) => {
|
||||
const documents = Array.isArray(product?.documents) ? product.documents : []
|
||||
|
||||
Reference in New Issue
Block a user