Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6744542f84 | ||
| 3e0e9d5270 | |||
|
|
4e0efc11ba | ||
| 9fc88df3ff | |||
|
|
041a04f0e9 | ||
| d089cd4873 | |||
|
|
b304cf6684 | ||
| 0fe7f3131e | |||
| a6bbcaf6d1 | |||
| 9f2e1da6ec |
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '1.9.20'
|
||||
app.version: '1.9.24'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<section class="space-y-3">
|
||||
<section v-if="!hideProducts" class="space-y-3">
|
||||
<header>
|
||||
<h3 class="text-sm font-semibold">
|
||||
Produits inclus par défaut
|
||||
@@ -166,6 +166,7 @@ defineOptions({ name: 'PieceModelStructureEditor' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: PieceModelStructure | null
|
||||
hideProducts?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -57,16 +57,6 @@
|
||||
/>
|
||||
</label>
|
||||
|
||||
<button
|
||||
v-if="canEdit"
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
:disabled="loading"
|
||||
@click="openCreatePage"
|
||||
>
|
||||
<IconLucidePlus class="w-4 h-4" aria-hidden="true" />
|
||||
Créer
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template #cell-name="{ row }">
|
||||
@@ -78,19 +68,15 @@
|
||||
<span v-else class="text-base-content/50">—</span>
|
||||
</template>
|
||||
|
||||
<template #cell-createdAt="{ row }">
|
||||
<span class="whitespace-nowrap">{{ formatDate(row.createdAt) }}</span>
|
||||
</template>
|
||||
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" class="btn btn-ghost btn-xs" @click="openRelatedModal(row)">
|
||||
Liés
|
||||
</button>
|
||||
<button
|
||||
v-if="canEdit && showConvertButton"
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs text-warning"
|
||||
@click="openConversionModal(row)"
|
||||
>
|
||||
Convertir
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs" @click="openEditPage(row)">
|
||||
Éditer
|
||||
</button>
|
||||
@@ -101,13 +87,6 @@
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<ConversionModal
|
||||
:open="conversionModalOpen"
|
||||
:model-type="conversionTarget"
|
||||
@close="closeConversionModal"
|
||||
@converted="onConverted"
|
||||
/>
|
||||
|
||||
<RelatedItemsModal
|
||||
:open="relatedModalOpen"
|
||||
:model-type="relatedType"
|
||||
@@ -121,7 +100,6 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch, type Ref } from 'vue'
|
||||
import { useHead, useRouter } from '#imports'
|
||||
import DataTable from '~/components/common/DataTable.vue'
|
||||
import ConversionModal from '~/components/model-types/ConversionModal.vue'
|
||||
import { useUrlState } from '~/composables/useUrlState'
|
||||
import type { DataTableSort } from '~/shared/types/dataTable'
|
||||
import {
|
||||
@@ -135,7 +113,7 @@ import { useToast } from '~/composables/useToast'
|
||||
import { humanizeError } from '~/shared/utils/errorMessages'
|
||||
import { invalidateEntityTypeCache } from '~/composables/useEntityTypes'
|
||||
import IconLucideSearch from '~icons/lucide/search'
|
||||
import IconLucidePlus from '~icons/lucide/plus'
|
||||
import { formatFrenchDate } from '~/utils/date'
|
||||
|
||||
const DEFAULT_DESCRIPTION
|
||||
= 'Gérez les catégories utilisées pour structurer les catalogues de composants, de pièces et de produits. Ajoutez, modifiez ou supprimez des entrées avec tri, recherche et pagination.'
|
||||
@@ -199,12 +177,11 @@ useHead(() => ({ title: headingText.value }))
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Nom', sortable: true },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'createdAt', label: 'Date', sortable: true },
|
||||
{ key: 'actions', label: 'Actions', align: 'right' as const, width: 'w-48' },
|
||||
]
|
||||
|
||||
const showConvertButton = computed(() =>
|
||||
selectedCategory.value === 'PIECE' || selectedCategory.value === 'COMPONENT',
|
||||
)
|
||||
const formatDate = formatFrenchDate
|
||||
|
||||
const categories: Array<{ label: string, value: ModelCategory }> = [
|
||||
{ label: 'Composants', value: 'COMPONENT' },
|
||||
@@ -339,13 +316,6 @@ const resolveCategoryBasePath = (category: ModelCategory) => {
|
||||
return '/product-category'
|
||||
}
|
||||
|
||||
const openCreatePage = () => {
|
||||
const basePath = resolveCategoryBasePath(selectedCategory.value)
|
||||
router.push(`${basePath}/new`).catch(() => {
|
||||
showError('Navigation impossible vers la page de création.')
|
||||
})
|
||||
}
|
||||
|
||||
const openEditPage = (item: ModelType) => {
|
||||
const category = item.category ?? selectedCategory.value
|
||||
const basePath = resolveCategoryBasePath(category)
|
||||
@@ -400,26 +370,6 @@ const openRelatedEdit = (entry: { id: string }) => {
|
||||
})
|
||||
}
|
||||
|
||||
const conversionModalOpen = ref(false)
|
||||
const conversionTarget = ref<ModelType | null>(null)
|
||||
|
||||
const openConversionModal = (item: ModelType) => {
|
||||
conversionTarget.value = item
|
||||
conversionModalOpen.value = true
|
||||
}
|
||||
|
||||
const closeConversionModal = () => {
|
||||
conversionModalOpen.value = false
|
||||
}
|
||||
|
||||
const onConverted = () => {
|
||||
conversionModalOpen.value = false
|
||||
invalidateEntityTypeCache('PIECE')
|
||||
invalidateEntityTypeCache('COMPONENT')
|
||||
showSuccess('Catégorie convertie avec succès.')
|
||||
doRefresh()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => searchInput.value,
|
||||
(value) => {
|
||||
|
||||
@@ -99,11 +99,7 @@
|
||||
v-else
|
||||
class="space-y-3 rounded-lg border border-base-300 p-4"
|
||||
>
|
||||
<p class="text-sm text-base-content/70">
|
||||
Aperçu :
|
||||
<span class="font-medium text-base-content">{{ productStructurePreview }}</span>
|
||||
</p>
|
||||
<PieceModelStructureEditor v-model="productStructure" />
|
||||
<PieceModelStructureEditor v-model="productStructure" hide-products />
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
@@ -194,15 +190,16 @@ const form = reactive<ModelTypePayload & { referenceFormula?: string | null }>({
|
||||
})
|
||||
|
||||
const formulaBuilderCustomFields = computed(() => {
|
||||
let fields: any[] = []
|
||||
if (form.category === 'PIECE') {
|
||||
const fields = pieceStructure.value?.customFields
|
||||
return Array.isArray(fields) ? fields : []
|
||||
const raw = pieceStructure.value?.customFields
|
||||
fields = Array.isArray(raw) ? raw : []
|
||||
}
|
||||
if (form.category === 'COMPONENT') {
|
||||
const fields = componentStructure.value?.customFields
|
||||
return Array.isArray(fields) ? fields : []
|
||||
else if (form.category === 'COMPONENT') {
|
||||
const raw = componentStructure.value?.customFields
|
||||
fields = Array.isArray(raw) ? raw : []
|
||||
}
|
||||
return []
|
||||
return fields.filter((f: any) => !f.machineContextOnly)
|
||||
})
|
||||
|
||||
const extractFormulaFields = (formula: string | null | undefined): string[] => {
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
import {
|
||||
hasAssignments,
|
||||
initializeStructureAssignments,
|
||||
isAssignmentNodeComplete,
|
||||
serializeStructureAssignments,
|
||||
} from '~/shared/utils/structureAssignmentHelpers'
|
||||
import type { ComponentModelStructure } from '~/shared/types/inventory'
|
||||
@@ -152,24 +151,14 @@ export function useComponentCreate() {
|
||||
values: computed(() => []),
|
||||
entityType: 'composant',
|
||||
entityId: createdComponentId,
|
||||
context: 'standalone',
|
||||
})
|
||||
|
||||
const structureHasRequirements = computed(() =>
|
||||
hasAssignments(structureAssignments.value),
|
||||
)
|
||||
|
||||
const structureSelectionsComplete = computed(() => {
|
||||
if (!structureHasRequirements.value) {
|
||||
return true
|
||||
}
|
||||
if (structureDataLoading.value) {
|
||||
return false
|
||||
}
|
||||
if (!structureAssignments.value) {
|
||||
return false
|
||||
}
|
||||
return isAssignmentNodeComplete(structureAssignments.value, true)
|
||||
})
|
||||
const structureSelectionsComplete = computed(() => true)
|
||||
|
||||
const canSubmit = computed(() => Boolean(
|
||||
canEdit.value
|
||||
@@ -307,11 +296,6 @@ export function useComponentCreate() {
|
||||
payload.productId = rootProductSelection.selectedProductId.trim()
|
||||
}
|
||||
|
||||
if (structureHasRequirements.value && !structureSelectionsComplete.value) {
|
||||
toast.showError('Complétez la sélection des pièces, produits et sous-composants.')
|
||||
return
|
||||
}
|
||||
|
||||
const serializedStructure = structureHasRequirements.value
|
||||
? serializeStructureAssignments(structureAssignments.value)
|
||||
: null
|
||||
@@ -414,6 +398,7 @@ export function useComponentCreate() {
|
||||
structureSelectionsComplete,
|
||||
canEdit,
|
||||
canSubmit,
|
||||
requiredCustomFieldsFilled,
|
||||
|
||||
// Functions
|
||||
typeOptionLabel,
|
||||
|
||||
@@ -209,6 +209,7 @@ export function useComponentEdit(componentId: string) {
|
||||
values: computed(() => component.value?.customFieldValues ?? []),
|
||||
entityType: 'composant',
|
||||
entityId: computed(() => component.value?.id ?? null),
|
||||
context: 'standalone',
|
||||
onValueCreated: (newValue) => {
|
||||
if (component.value && Array.isArray(component.value.customFieldValues)) {
|
||||
component.value.customFieldValues.push(newValue)
|
||||
@@ -556,6 +557,7 @@ export function useComponentEdit(componentId: string) {
|
||||
originalConstructeurLinks,
|
||||
constructeurIdsFromForm,
|
||||
customFieldInputs,
|
||||
requiredCustomFieldsFilled,
|
||||
historyFieldLabels,
|
||||
|
||||
// Computed
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
buildProductRequirementDescriptions,
|
||||
buildProductRequirementEntries,
|
||||
resizeProductSelections,
|
||||
areProductSelectionsFilled,
|
||||
applyProductSelection,
|
||||
collectNormalizedProductIds,
|
||||
} from '~/shared/utils/pieceProductSelectionUtils'
|
||||
@@ -99,6 +98,7 @@ export function usePieceEdit(pieceId: string) {
|
||||
values: computed(() => piece.value?.customFieldValues ?? []),
|
||||
entityType: 'piece',
|
||||
entityId: computed(() => piece.value?.id ?? null),
|
||||
context: 'standalone',
|
||||
onValueCreated: (newValue) => {
|
||||
if (piece.value && Array.isArray(piece.value.customFieldValues)) {
|
||||
piece.value.customFieldValues.push(newValue)
|
||||
@@ -198,13 +198,7 @@ export function usePieceEdit(pieceId: string) {
|
||||
buildProductRequirementEntries(structureProducts.value, 'piece-product-requirement'),
|
||||
)
|
||||
|
||||
const productSelectionsFilled = computed(() =>
|
||||
areProductSelectionsFilled(
|
||||
requiresProductSelection.value,
|
||||
productRequirementEntries.value,
|
||||
productSelections.value,
|
||||
),
|
||||
)
|
||||
const productSelectionsFilled = computed(() => true)
|
||||
|
||||
const setProductSelection = (index: number, value: string | null) => {
|
||||
productSelections.value = applyProductSelection(productSelections.value, index, value)
|
||||
@@ -354,11 +348,6 @@ export function usePieceEdit(pieceId: string) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!productSelectionsFilled.value) {
|
||||
toast.showError('Sélectionnez un produit conforme au squelette.')
|
||||
return
|
||||
}
|
||||
|
||||
const rawPrice = typeof editionForm.prix === 'string'
|
||||
? editionForm.prix.trim()
|
||||
: editionForm.prix === null || editionForm.prix === undefined
|
||||
@@ -435,6 +424,7 @@ export function usePieceEdit(pieceId: string) {
|
||||
constructeurIdsFromForm,
|
||||
productSelections,
|
||||
customFieldInputs,
|
||||
requiredCustomFieldsFilled,
|
||||
canEdit,
|
||||
|
||||
// Computed
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<h1 class="text-3xl font-bold tracking-tight">Composants</h1>
|
||||
<p class="text-sm text-base-content/70">Catalogue et catégories de composants.</p>
|
||||
</div>
|
||||
<NuxtLink v-if="canEdit" to="/component/create" class="btn btn-primary btn-sm md:btn-md">
|
||||
Ajouter un composant
|
||||
<NuxtLink v-if="canEdit" :to="activeTab === 'categories' ? '/component-category/new' : '/component/create'" class="btn btn-primary btn-sm md:btn-md">
|
||||
{{ activeTab === 'categories' ? 'Ajouter une catégorie' : 'Ajouter un composant' }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<h1 class="text-3xl font-bold tracking-tight">Pièces</h1>
|
||||
<p class="text-sm text-base-content/70">Catalogue et catégories de pièces.</p>
|
||||
</div>
|
||||
<NuxtLink v-if="canEdit" to="/pieces/create" class="btn btn-primary btn-sm md:btn-md">
|
||||
Ajouter une pièce
|
||||
<NuxtLink v-if="canEdit" :to="activeTab === 'categories' ? '/piece-category/new' : '/pieces/create'" class="btn btn-primary btn-sm md:btn-md">
|
||||
{{ activeTab === 'categories' ? 'Ajouter une catégorie' : 'Ajouter une pièce' }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<h1 class="text-3xl font-bold tracking-tight">Produits</h1>
|
||||
<p class="text-sm text-base-content/70">Catalogue et catégories de produits.</p>
|
||||
</div>
|
||||
<NuxtLink v-if="canEdit" to="/product/create" class="btn btn-primary btn-sm md:btn-md">
|
||||
Ajouter un produit
|
||||
<NuxtLink v-if="canEdit" :to="activeTab === 'categories' ? '/product-category/new' : '/product/create'" class="btn btn-primary btn-sm md:btn-md">
|
||||
{{ activeTab === 'categories' ? 'Ajouter une catégorie' : 'Ajouter un produit' }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
:model-value="productSelections[entry.index] || null"
|
||||
:disabled="!canEdit || saving"
|
||||
:type-product-id="entry.typeProductId"
|
||||
helper-text="Un produit valide est requis pour cette pièce."
|
||||
helper-text="Sélectionnez un produit (optionnel)."
|
||||
@update:model-value="(value) => setProductSelection(entry.index, value)"
|
||||
/>
|
||||
</div>
|
||||
@@ -359,6 +359,9 @@
|
||||
</header>
|
||||
<template v-if="isEditMode">
|
||||
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || saving" />
|
||||
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
|
||||
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
@@ -420,6 +423,9 @@
|
||||
Enregistrer les modifications
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="isEditMode && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
Merci de renseigner tous les champs personnalisés obligatoires.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -460,6 +466,7 @@ const {
|
||||
constructeurLinks,
|
||||
productSelections,
|
||||
customFieldInputs,
|
||||
requiredCustomFieldsFilled,
|
||||
pieceTypeList,
|
||||
selectedType,
|
||||
resolvedStructure,
|
||||
@@ -481,6 +488,8 @@ const {
|
||||
formatPieceStructurePreview,
|
||||
} = usePieceEdit(String(route.params.id))
|
||||
|
||||
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
|
||||
|
||||
const entityTabs = computed(() => [
|
||||
{ key: 'general', label: 'Général' },
|
||||
{ key: 'products', label: 'Produits liés', count: structureProducts.value.length },
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
:model-value="productSelections[entry.index] || null"
|
||||
:disabled="!canEdit || submitting || !selectedType"
|
||||
:type-product-id="entry.typeProductId"
|
||||
helper-text="Un produit est requis pour cette pièce."
|
||||
helper-text="Sélectionnez un produit (optionnel)."
|
||||
@update:model-value="(value) => setProductSelection(entry.index, value)"
|
||||
/>
|
||||
</div>
|
||||
@@ -218,6 +218,9 @@
|
||||
</p>
|
||||
</header>
|
||||
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" />
|
||||
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
|
||||
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
|
||||
</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
v-else
|
||||
@@ -237,6 +240,9 @@
|
||||
Créer la pièce
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="selectedType && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
Merci de renseigner tous les champs personnalisés obligatoires avant de créer la pièce.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -267,7 +273,6 @@ import {
|
||||
buildProductRequirementDescriptions,
|
||||
buildProductRequirementEntries,
|
||||
resizeProductSelections,
|
||||
areProductSelectionsFilled,
|
||||
applyProductSelection,
|
||||
collectNormalizedProductIds,
|
||||
} from '~/shared/utils/pieceProductSelectionUtils'
|
||||
@@ -311,7 +316,9 @@ const { fields: customFieldInputs, requiredFilled: requiredCustomFieldsFilled, s
|
||||
values: [] as any[],
|
||||
entityType: 'piece' as CustomFieldEntityType,
|
||||
entityId: createdEntityId,
|
||||
context: 'standalone',
|
||||
})
|
||||
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
|
||||
const selectedDocuments = ref<File[]>([])
|
||||
const uploadingDocuments = ref(false)
|
||||
|
||||
@@ -371,13 +378,7 @@ const productRequirementEntries = computed(() =>
|
||||
buildProductRequirementEntries(structureProducts.value, 'piece-create-product-requirement'),
|
||||
)
|
||||
|
||||
const productSelectionsFilled = computed(() =>
|
||||
areProductSelectionsFilled(
|
||||
requiresProductSelection.value,
|
||||
productRequirementEntries.value,
|
||||
productSelections.value,
|
||||
),
|
||||
)
|
||||
const productSelectionsFilled = computed(() => true)
|
||||
|
||||
const setProductSelection = (index: number, value: string | null) => {
|
||||
productSelections.value = applyProductSelection(productSelections.value, index, value)
|
||||
@@ -436,11 +437,6 @@ const submitCreation = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!productSelectionsFilled.value) {
|
||||
toast.showError('Sélectionnez un produit conforme au squelette.')
|
||||
return
|
||||
}
|
||||
|
||||
const payload: Record<string, any> = {
|
||||
name: creationForm.name.trim(),
|
||||
typePieceId: selectedType.value.id,
|
||||
|
||||
@@ -274,6 +274,9 @@
|
||||
</header>
|
||||
<template v-if="isEditMode">
|
||||
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || saving" />
|
||||
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
|
||||
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
@@ -338,7 +341,7 @@
|
||||
Enregistrer les modifications
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="isEditMode && product && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
<p v-if="isEditMode && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
Merci de renseigner tous les champs personnalisés obligatoires.
|
||||
</p>
|
||||
</div>
|
||||
@@ -409,6 +412,7 @@ const {
|
||||
values: cfValues,
|
||||
entityType: 'product' as CustomFieldEntityType,
|
||||
entityId,
|
||||
context: 'standalone',
|
||||
})
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
@@ -446,7 +450,7 @@ const editionForm = reactive({
|
||||
supplierPrice: '' as string,
|
||||
})
|
||||
|
||||
// requiredCustomFieldsFilled comes from useCustomFieldInputs composable
|
||||
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
|
||||
|
||||
const canSubmit = computed(() =>
|
||||
Boolean(canEdit.value && product.value && editionForm.name.trim().length >= 2 && requiredCustomFieldsFilled.value && !saving.value),
|
||||
|
||||
@@ -158,6 +158,9 @@
|
||||
</p>
|
||||
</header>
|
||||
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" />
|
||||
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
|
||||
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
|
||||
</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
v-else
|
||||
@@ -177,7 +180,7 @@
|
||||
Créer le produit
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="selectedType && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
<p v-if="selectedType && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
|
||||
Merci de renseigner tous les champs personnalisés obligatoires.
|
||||
</p>
|
||||
</div>
|
||||
@@ -241,7 +244,9 @@ const { fields: customFieldInputs, requiredFilled: requiredCustomFieldsFilled, s
|
||||
values: [] as any[],
|
||||
entityType: 'product' as CustomFieldEntityType,
|
||||
entityId: createdEntityId,
|
||||
context: 'standalone',
|
||||
})
|
||||
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
|
||||
|
||||
const productTypeList = computed<ProductCatalogType[]>(() =>
|
||||
(productTypes.value || []) as ProductCatalogType[],
|
||||
|
||||
Reference in New Issue
Block a user