Compare commits
9 Commits
d4fc0f1fee
...
db630e315b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db630e315b | ||
|
|
53530dc16d | ||
|
|
974b74ee9f | ||
|
|
ab05ce589d | ||
|
|
ce3f081a0a | ||
|
|
63fba4138e | ||
|
|
d58a8c2479 | ||
|
|
81f7b1a9ac | ||
|
|
9e303426a7 |
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import SearchSelect from '~/components/common/SearchSelect.vue'
|
||||
import { useComposants } from '~/composables/useComposants'
|
||||
|
||||
@@ -52,43 +52,39 @@ const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | null): void
|
||||
}>()
|
||||
|
||||
const { composants, loading, loadComposants } = useComposants()
|
||||
const { loading: globalLoading, loadComposants } = useComposants()
|
||||
|
||||
const composantOptions = computed(() => {
|
||||
const baseOptions = Array.isArray(composants.value) ? composants.value : []
|
||||
if (!props.typeComposantId) {
|
||||
return baseOptions
|
||||
const localComposants = ref<any[]>([])
|
||||
const localLoading = ref(false)
|
||||
const loading = computed(() => localLoading.value || globalLoading.value)
|
||||
|
||||
const composantOptions = computed(() => localComposants.value)
|
||||
|
||||
const loadFilteredComposants = async () => {
|
||||
if (!props.typeComposantId) return
|
||||
localLoading.value = true
|
||||
try {
|
||||
const result = await loadComposants({ typeComposantId: props.typeComposantId, itemsPerPage: 500, force: true })
|
||||
if (result.success && result.data?.items) {
|
||||
localComposants.value = result.data.items
|
||||
}
|
||||
}
|
||||
|
||||
const allowedTypeId = String(props.typeComposantId)
|
||||
return baseOptions.filter((composant: any) => {
|
||||
const typeId =
|
||||
composant?.typeComposantId ||
|
||||
composant?.typeComposant?.id ||
|
||||
null
|
||||
return typeId ? String(typeId) === allowedTypeId : false
|
||||
})
|
||||
})
|
||||
catch (error: unknown) {
|
||||
console.error('Erreur lors du chargement des composants:', error)
|
||||
}
|
||||
finally {
|
||||
localLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (composantOptions.value.length === 0) {
|
||||
loadComposants({ itemsPerPage: 200 }).catch((error: unknown) => {
|
||||
console.error('Erreur lors du chargement des composants:', error)
|
||||
})
|
||||
}
|
||||
loadFilteredComposants()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
if (typeof value === 'string' && value) {
|
||||
const exists = composantOptions.value.some((c: any) => c.id === value)
|
||||
if (!exists && !loading.value) {
|
||||
loadComposants({ itemsPerPage: 200, force: true }).catch((error: unknown) => {
|
||||
console.error('Erreur lors du chargement des composants:', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
() => props.typeComposantId,
|
||||
() => {
|
||||
loadFilteredComposants()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -102,8 +98,12 @@ const updateValue = (value: string | number | null | undefined) => {
|
||||
|
||||
const formatDescription = (option: any) => {
|
||||
const parts: string[] = []
|
||||
const typeName = option?.typeComposant?.name
|
||||
if (typeName) {
|
||||
parts.push(typeName)
|
||||
}
|
||||
if (option?.reference) {
|
||||
parts.push(option.reference)
|
||||
parts.push(`Ref. ${option.reference}`)
|
||||
}
|
||||
if (option?.prix !== undefined && option.prix !== null) {
|
||||
const price = Number(option.prix)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import SearchSelect from '~/components/common/SearchSelect.vue'
|
||||
import { usePieces } from '~/composables/usePieces'
|
||||
|
||||
@@ -52,43 +52,39 @@ const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | null): void
|
||||
}>()
|
||||
|
||||
const { pieces, loading, loadPieces } = usePieces()
|
||||
const { loading: globalLoading, loadPieces } = usePieces()
|
||||
|
||||
const pieceOptions = computed(() => {
|
||||
const baseOptions = Array.isArray(pieces.value) ? pieces.value : []
|
||||
if (!props.typePieceId) {
|
||||
return baseOptions
|
||||
const localPieces = ref<any[]>([])
|
||||
const localLoading = ref(false)
|
||||
const loading = computed(() => localLoading.value || globalLoading.value)
|
||||
|
||||
const pieceOptions = computed(() => localPieces.value)
|
||||
|
||||
const loadFilteredPieces = async () => {
|
||||
if (!props.typePieceId) return
|
||||
localLoading.value = true
|
||||
try {
|
||||
const result = await loadPieces({ typePieceId: props.typePieceId, itemsPerPage: 500, force: true })
|
||||
if (result.success && result.data?.items) {
|
||||
localPieces.value = result.data.items
|
||||
}
|
||||
}
|
||||
|
||||
const allowedTypeId = String(props.typePieceId)
|
||||
return baseOptions.filter((piece: any) => {
|
||||
const typeId =
|
||||
piece?.typePieceId ||
|
||||
piece?.typePiece?.id ||
|
||||
null
|
||||
return typeId ? String(typeId) === allowedTypeId : false
|
||||
})
|
||||
})
|
||||
catch (error: unknown) {
|
||||
console.error('Erreur lors du chargement des pièces:', error)
|
||||
}
|
||||
finally {
|
||||
localLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (pieceOptions.value.length === 0) {
|
||||
loadPieces({ itemsPerPage: 200 }).catch((error: unknown) => {
|
||||
console.error('Erreur lors du chargement des pièces:', error)
|
||||
})
|
||||
}
|
||||
loadFilteredPieces()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
if (typeof value === 'string' && value) {
|
||||
const exists = pieceOptions.value.some((piece: any) => piece.id === value)
|
||||
if (!exists && !loading.value) {
|
||||
loadPieces({ itemsPerPage: 200, force: true }).catch((error: unknown) => {
|
||||
console.error('Erreur lors du chargement des pièces:', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
() => props.typePieceId,
|
||||
() => {
|
||||
loadFilteredPieces()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -102,8 +98,12 @@ const updateValue = (value: string | number | null | undefined) => {
|
||||
|
||||
const formatDescription = (option: any) => {
|
||||
const parts: string[] = []
|
||||
const typeName = option?.typePiece?.name
|
||||
if (typeName) {
|
||||
parts.push(typeName)
|
||||
}
|
||||
if (option?.reference) {
|
||||
parts.push(option.reference)
|
||||
parts.push(`Ref. ${option.reference}`)
|
||||
}
|
||||
if (option?.prix !== undefined && option.prix !== null) {
|
||||
const price = Number(option.prix)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import SearchSelect from '~/components/common/SearchSelect.vue'
|
||||
import { useProducts } from '~/composables/useProducts'
|
||||
|
||||
@@ -52,43 +52,39 @@ const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | null): void
|
||||
}>()
|
||||
|
||||
const { products, loading, loadProducts } = useProducts()
|
||||
const { loading: globalLoading, loadProducts } = useProducts()
|
||||
|
||||
const productOptions = computed(() => {
|
||||
const baseOptions = Array.isArray(products.value) ? products.value : []
|
||||
if (!props.typeProductId) {
|
||||
return baseOptions
|
||||
const localProducts = ref<any[]>([])
|
||||
const localLoading = ref(false)
|
||||
const loading = computed(() => localLoading.value || globalLoading.value)
|
||||
|
||||
const productOptions = computed(() => localProducts.value)
|
||||
|
||||
const loadFilteredProducts = async () => {
|
||||
if (!props.typeProductId) return
|
||||
localLoading.value = true
|
||||
try {
|
||||
const result = await loadProducts({ typeProductId: props.typeProductId, itemsPerPage: 500, force: true })
|
||||
if (result.success && result.data?.items) {
|
||||
localProducts.value = result.data.items
|
||||
}
|
||||
}
|
||||
|
||||
const allowedTypeId = String(props.typeProductId)
|
||||
return baseOptions.filter((product) => {
|
||||
const typeId =
|
||||
product?.typeProductId ||
|
||||
product?.typeProduct?.id ||
|
||||
null
|
||||
return typeId ? String(typeId) === allowedTypeId : false
|
||||
})
|
||||
})
|
||||
catch (error: unknown) {
|
||||
console.error('Erreur lors du chargement des produits:', error)
|
||||
}
|
||||
finally {
|
||||
localLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (productOptions.value.length === 0) {
|
||||
loadProducts().catch((error) => {
|
||||
console.error('Erreur lors du chargement des produits:', error)
|
||||
})
|
||||
}
|
||||
loadFilteredProducts()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
if (typeof value === 'string' && value) {
|
||||
const exists = productOptions.value.some((product) => product.id === value)
|
||||
if (!exists && !loading.value) {
|
||||
loadProducts({ force: true }).catch((error) => {
|
||||
console.error('Erreur lors du chargement des produits:', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
() => props.typeProductId,
|
||||
() => {
|
||||
loadFilteredProducts()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -102,8 +98,12 @@ const updateValue = (value: string | number | null | undefined) => {
|
||||
|
||||
const formatDescription = (option: any) => {
|
||||
const parts: string[] = []
|
||||
const typeName = option?.typeProduct?.name
|
||||
if (typeName) {
|
||||
parts.push(typeName)
|
||||
}
|
||||
if (option?.reference) {
|
||||
parts.push(option.reference)
|
||||
parts.push(`Ref. ${option.reference}`)
|
||||
}
|
||||
if (option?.supplierPrice !== undefined && option.supplierPrice !== null) {
|
||||
const price = Number(option.supplierPrice)
|
||||
|
||||
@@ -258,18 +258,7 @@
|
||||
{{ piece.typePieceId ? `Sélection : ${getPieceTypeLabel(piece.typePieceId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label py-1"><span class="label-text text-xs">Quantité</span></label>
|
||||
<input
|
||||
v-model.number="piece.quantity"
|
||||
type="number"
|
||||
:min="1"
|
||||
step="1"
|
||||
placeholder="Qté"
|
||||
class="input input-bordered input-sm md:input-md w-20"
|
||||
@input="piece.quantity = Math.max(1, piece.quantity || 1)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Quantity is set per-component on the component edit page -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
{{ resolveLabel(option) }}
|
||||
</slot>
|
||||
</span>
|
||||
<span v-if="resolveDescription(option)" class="text-xs text-base-content/50">
|
||||
<span v-if="$slots['option-description'] || resolveDescription(option)" class="text-xs text-base-content/50">
|
||||
<slot name="option-description" :option="option">
|
||||
{{ resolveDescription(option) }}
|
||||
</slot>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useProductTypes } from '~/composables/useProductTypes'
|
||||
import { usePieces } from '~/composables/usePieces'
|
||||
import { useProducts } from '~/composables/useProducts'
|
||||
import { useCustomFields } from '~/composables/useCustomFields'
|
||||
import type { SelectionEntry } from '~/shared/utils/structureSelectionUtils'
|
||||
import { useApi } from '~/composables/useApi'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { extractRelationId } from '~/shared/apiRelations'
|
||||
@@ -58,9 +57,9 @@ export function useComponentEdit(componentId: string) {
|
||||
const { componentTypes, loadComponentTypes } = useComponentTypes()
|
||||
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
||||
const { productTypes, loadProductTypes } = useProductTypes()
|
||||
const { updateComposant, loadComposants, composants: componentCatalogRef } = useComposants()
|
||||
const { pieces, loadPieces } = usePieces()
|
||||
const { products, loadProducts } = useProducts()
|
||||
const { updateComposant, composants: componentCatalogRef } = useComposants()
|
||||
const { pieces } = usePieces()
|
||||
const { products } = useProducts()
|
||||
const { ensureConstructeurs } = useConstructeurs()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
|
||||
const toast = useToast()
|
||||
@@ -348,19 +347,17 @@ export function useComponentEdit(componentId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const saveSlotQuantity = async (entry: SelectionEntry) => {
|
||||
const slotId = entry.slotId
|
||||
const quantity = typeof entry._definition?.quantity === 'number'
|
||||
? Math.max(1, entry._definition.quantity)
|
||||
: null
|
||||
if (!slotId || quantity === null) return
|
||||
try {
|
||||
await patch(`/composant-piece-slots/${slotId}`, { quantity })
|
||||
const saveSlotQuantity = async (slotId: string, quantity: number) => {
|
||||
if (!slotId || quantity < 1) return
|
||||
const result = await patch(`/composant-piece-slots/${slotId}`, { quantity: Math.max(1, quantity) })
|
||||
if (result.success) {
|
||||
const structure = component.value?.structure
|
||||
if (structure?.pieces) {
|
||||
const slot = (structure.pieces as any[]).find((s: any) => s.slotId === slotId)
|
||||
if (slot) slot.quantity = quantity
|
||||
}
|
||||
toast.showSuccess('Quantité mise à jour')
|
||||
}
|
||||
catch (error: any) {
|
||||
toast.showError(error?.message || 'Erreur lors de la mise à jour de la quantité')
|
||||
}
|
||||
}
|
||||
|
||||
const submitEdition = async () => {
|
||||
@@ -406,7 +403,7 @@ export function useComponentEdit(componentId: string) {
|
||||
],
|
||||
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
|
||||
)
|
||||
await router.push('/component-catalog')
|
||||
toast.showSuccess('Composant mis à jour avec succès.')
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
@@ -500,13 +497,6 @@ export function useComponentEdit(componentId: string) {
|
||||
fetchComponent(),
|
||||
])
|
||||
loading.value = false
|
||||
|
||||
// Load catalogs for slot selectors (force: true to bypass cache from list pages that load fewer items)
|
||||
Promise.allSettled([
|
||||
loadPieces({ itemsPerPage: 200, force: true }),
|
||||
loadProducts({ itemsPerPage: 200, force: true }),
|
||||
loadComposants({ itemsPerPage: 200, force: true }),
|
||||
]).catch(() => {})
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -42,6 +42,7 @@ interface LoadComposantsOptions {
|
||||
orderBy?: string
|
||||
orderDir?: 'asc' | 'desc'
|
||||
typeName?: string
|
||||
typeComposantId?: string
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
@@ -109,17 +110,18 @@ export function useComposants() {
|
||||
orderBy = 'name',
|
||||
orderDir = 'asc',
|
||||
typeName,
|
||||
typeComposantId,
|
||||
force = false,
|
||||
} = options
|
||||
|
||||
if (!force && loaded.value && !search && !typeName && page === 1) {
|
||||
if (!force && loaded.value && !search && !typeName && !typeComposantId && page === 1) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: composants.value, total: total.value, page, itemsPerPage },
|
||||
}
|
||||
}
|
||||
|
||||
if (loading.value) {
|
||||
if (!typeComposantId && loading.value) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: composants.value, total: total.value, page, itemsPerPage },
|
||||
@@ -128,7 +130,6 @@ export function useComposants() {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
const params = new URLSearchParams()
|
||||
params.set('itemsPerPage', String(itemsPerPage))
|
||||
params.set('page', String(page))
|
||||
@@ -141,20 +142,29 @@ export function useComposants() {
|
||||
params.set('typeComposant.name', typeName.trim())
|
||||
}
|
||||
|
||||
if (typeComposantId) {
|
||||
params.set('typeComposant', typeComposantId)
|
||||
}
|
||||
|
||||
params.set(`order[${orderBy}]`, orderDir)
|
||||
|
||||
const result = await get(`/composants?${params.toString()}`)
|
||||
if (result.success) {
|
||||
const items = extractCollection(result.data)
|
||||
const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item)))
|
||||
composants.value = enrichedItems
|
||||
total.value = extractTotal(result.data, items.length)
|
||||
loaded.value = true
|
||||
const resultTotal = extractTotal(result.data, items.length)
|
||||
|
||||
if (!typeComposantId) {
|
||||
composants.value = enrichedItems
|
||||
total.value = resultTotal
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
items: enrichedItems,
|
||||
total: total.value,
|
||||
total: resultTotal,
|
||||
page,
|
||||
itemsPerPage,
|
||||
},
|
||||
|
||||
@@ -412,7 +412,7 @@ export function usePieceEdit(pieceId: string) {
|
||||
],
|
||||
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
|
||||
)
|
||||
await router.push('/pieces-catalog')
|
||||
toast.showSuccess('Pièce mise à jour avec succès.')
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
|
||||
@@ -158,6 +158,13 @@ const buildPayload = (
|
||||
orderIndex: index,
|
||||
}
|
||||
|
||||
if (field.id) {
|
||||
payload.id = field.id
|
||||
}
|
||||
if (field.customFieldId) {
|
||||
payload.customFieldId = field.customFieldId
|
||||
}
|
||||
|
||||
if (type === 'select') {
|
||||
const options = normalizeLineEndings(field.optionsText)
|
||||
.split('\n')
|
||||
|
||||
@@ -43,6 +43,7 @@ interface LoadPiecesOptions {
|
||||
orderBy?: string
|
||||
orderDir?: 'asc' | 'desc'
|
||||
typeName?: string
|
||||
typePieceId?: string
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
@@ -119,17 +120,20 @@ export function usePieces() {
|
||||
orderBy = 'name',
|
||||
orderDir = 'asc',
|
||||
typeName,
|
||||
typePieceId,
|
||||
force = false,
|
||||
} = options
|
||||
|
||||
if (!force && loaded.value && !search && !typeName && page === 1) {
|
||||
// Only use cache for unfiltered full-catalog loads
|
||||
if (!force && loaded.value && !search && !typeName && !typePieceId && page === 1) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: pieces.value, total: total.value, page, itemsPerPage },
|
||||
}
|
||||
}
|
||||
|
||||
if (loading.value) {
|
||||
// For filtered queries, don't block on global loading state
|
||||
if (!typePieceId && loading.value) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: pieces.value, total: total.value, page, itemsPerPage },
|
||||
@@ -138,7 +142,6 @@ export function usePieces() {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
const params = new URLSearchParams()
|
||||
params.set('itemsPerPage', String(itemsPerPage))
|
||||
params.set('page', String(page))
|
||||
@@ -151,20 +154,30 @@ export function usePieces() {
|
||||
params.set('typePiece.name', typeName.trim())
|
||||
}
|
||||
|
||||
if (typePieceId) {
|
||||
params.set('typePiece', typePieceId)
|
||||
}
|
||||
|
||||
params.set(`order[${orderBy}]`, orderDir)
|
||||
|
||||
const result = await get(`/pieces?${params.toString()}`)
|
||||
if (result.success) {
|
||||
const items = extractCollection(result.data)
|
||||
const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item)))
|
||||
pieces.value = enrichedItems
|
||||
total.value = extractTotal(result.data, items.length)
|
||||
loaded.value = true
|
||||
const resultTotal = extractTotal(result.data, items.length)
|
||||
|
||||
// Only update global cache for unfiltered queries
|
||||
if (!typePieceId) {
|
||||
pieces.value = enrichedItems
|
||||
total.value = resultTotal
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
items: enrichedItems,
|
||||
total: total.value,
|
||||
total: resultTotal,
|
||||
page,
|
||||
itemsPerPage,
|
||||
},
|
||||
|
||||
@@ -41,6 +41,7 @@ interface LoadProductsOptions {
|
||||
orderBy?: string
|
||||
orderDir?: 'asc' | 'desc'
|
||||
typeName?: string
|
||||
typeProductId?: string
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
@@ -118,17 +119,18 @@ export function useProducts() {
|
||||
orderBy = 'name',
|
||||
orderDir = 'asc',
|
||||
typeName,
|
||||
typeProductId,
|
||||
force = false,
|
||||
} = options
|
||||
|
||||
if (!force && loaded.value && !search && !typeName && page === 1) {
|
||||
if (!force && loaded.value && !search && !typeName && !typeProductId && page === 1) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: products.value, total: total.value, page, itemsPerPage },
|
||||
}
|
||||
}
|
||||
|
||||
if (loading.value) {
|
||||
if (!typeProductId && loading.value) {
|
||||
return {
|
||||
success: true,
|
||||
data: { items: products.value, total: total.value, page, itemsPerPage },
|
||||
@@ -150,20 +152,29 @@ export function useProducts() {
|
||||
params.set('typeProduct.name', typeName.trim())
|
||||
}
|
||||
|
||||
if (typeProductId) {
|
||||
params.set('typeProduct', typeProductId)
|
||||
}
|
||||
|
||||
params.set(`order[${orderBy}]`, orderDir)
|
||||
|
||||
const result = await get(`/products?${params.toString()}`)
|
||||
if (result.success) {
|
||||
const items = extractCollection(result.data)
|
||||
const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item)))
|
||||
products.value = enrichedItems
|
||||
total.value = extractTotal(result.data, items.length)
|
||||
loaded.value = true
|
||||
const resultTotal = extractTotal(result.data, items.length)
|
||||
|
||||
if (!typeProductId) {
|
||||
products.value = enrichedItems
|
||||
total.value = resultTotal
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
items: enrichedItems,
|
||||
total: total.value,
|
||||
total: resultTotal,
|
||||
page,
|
||||
itemsPerPage,
|
||||
},
|
||||
|
||||
@@ -158,7 +158,6 @@ const handleSubmit = async (payload: Parameters<typeof updateModelType>[1]) => {
|
||||
await syncExecute(id, { confirmDeletions: false, confirmTypeChanges: false })
|
||||
await loadComponentTypes({ force: true })
|
||||
showSuccess('Catégorie de composant mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
}
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
@@ -183,7 +182,6 @@ const handleSyncConfirm = async () => {
|
||||
})
|
||||
await loadComponentTypes({ force: true })
|
||||
showSuccess('Catégorie de composant mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
} finally {
|
||||
|
||||
@@ -45,20 +45,33 @@
|
||||
<label class="label">
|
||||
<span class="label-text">Catégorie de composant</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="selectedTypeId"
|
||||
class="select select-bordered select-sm md:select-md"
|
||||
disabled
|
||||
>
|
||||
<option value="">Sélectionner une catégorie</option>
|
||||
<option
|
||||
v-for="type in componentTypeList"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
<div class="flex items-center gap-2">
|
||||
<select
|
||||
v-model="selectedTypeId"
|
||||
class="select select-bordered select-sm md:select-md flex-1"
|
||||
disabled
|
||||
>
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</select>
|
||||
<option value="">Sélectionner une catégorie</option>
|
||||
<option
|
||||
v-for="type in componentTypeList"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
>
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</select>
|
||||
<NuxtLink
|
||||
v-if="selectedTypeId"
|
||||
:to="`/component-category/${selectedTypeId}/edit`"
|
||||
class="btn btn-ghost btn-sm"
|
||||
title="Voir la catégorie"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||
</svg>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<p class="text-xs text-base-content/60 mt-1">
|
||||
La catégorie d'origine ne peut pas être modifiée depuis cette page.
|
||||
</p>
|
||||
@@ -173,12 +186,27 @@
|
||||
<label class="label">
|
||||
<span class="label-text text-xs font-medium">{{ slot.label }}</span>
|
||||
</label>
|
||||
<PieceSelect
|
||||
:model-value="slot.selectedPieceId"
|
||||
:disabled="!canEdit || saving"
|
||||
:type-piece-id="slot.typePieceId"
|
||||
@update:model-value="(value) => savePieceSlotSelection(slot.slotId, value)"
|
||||
/>
|
||||
<div class="flex items-start gap-2">
|
||||
<div class="flex-1">
|
||||
<PieceSelect
|
||||
:model-value="slot.selectedPieceId"
|
||||
:disabled="!canEdit || saving"
|
||||
:type-piece-id="slot.typePieceId"
|
||||
@update:model-value="(value) => savePieceSlotSelection(slot.slotId, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-20 shrink-0">
|
||||
<input
|
||||
type="number"
|
||||
:value="slot.quantity"
|
||||
min="1"
|
||||
class="input input-bordered input-sm w-full text-center"
|
||||
:disabled="!canEdit || saving"
|
||||
title="Quantité"
|
||||
@change="(e) => saveSlotQuantity(slot.slotId, Number((e.target as HTMLInputElement).value))"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,7 +156,6 @@ const handleSubmit = async (payload: Parameters<typeof updateModelType>[1]) => {
|
||||
await syncExecute(id, { confirmDeletions: false, confirmTypeChanges: false })
|
||||
await loadPieceTypes({ force: true })
|
||||
showSuccess('Catégorie de pièce mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
}
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
@@ -181,7 +180,6 @@ const handleSyncConfirm = async () => {
|
||||
})
|
||||
await loadPieceTypes({ force: true })
|
||||
showSuccess('Catégorie de pièce mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
} finally {
|
||||
|
||||
@@ -156,7 +156,6 @@ const handleSubmit = async (payload: Parameters<typeof updateModelType>[1]) => {
|
||||
await syncExecute(id, { confirmDeletions: false, confirmTypeChanges: false })
|
||||
await loadProductTypes({ force: true })
|
||||
showSuccess('Catégorie de produit mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
}
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
@@ -181,7 +180,6 @@ const handleSyncConfirm = async () => {
|
||||
})
|
||||
await loadProductTypes({ force: true })
|
||||
showSuccess('Catégorie de produit mise à jour avec succès.')
|
||||
await navigateBackToList()
|
||||
} catch (error) {
|
||||
showError(normalizeError(error))
|
||||
} finally {
|
||||
|
||||
@@ -482,7 +482,6 @@ const submitEdition = async () => {
|
||||
return
|
||||
}
|
||||
toast.showSuccess('Produit mis à jour avec succès')
|
||||
await router.push('/product-catalog')
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.showError(humanizeError(error?.message) || 'Impossible de mettre à jour le produit')
|
||||
|
||||
@@ -125,6 +125,8 @@ const hydratePieceCustomFields = (fields: any[]): PieceModelStructureEditorField
|
||||
? field.options.join('\n')
|
||||
: '',
|
||||
orderIndex: typeof field?.orderIndex === 'number' ? field.orderIndex : index,
|
||||
...(field?.id ? { id: field.id } : {}),
|
||||
...(field?.customFieldId ? { customFieldId: field.customFieldId } : {}),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ export interface PieceModelCustomField {
|
||||
key?: string
|
||||
value?: unknown
|
||||
defaultValue?: string | null
|
||||
id?: string
|
||||
customFieldId?: string
|
||||
}
|
||||
|
||||
export interface PieceModelProduct {
|
||||
|
||||
Reference in New Issue
Block a user