Compare commits

..

5 Commits

Author SHA1 Message Date
gitea-actions 4e0efc11ba chore : bump version to v1.9.23
Auto Tag Develop / tag (push) Successful in 9s
Build & Push Docker Image / build (push) Successful in 38s
2026-04-06 15:18:20 +00:00
matthieu 9fc88df3ff fix(piece) : rendre les slots produit optionnels en création et édition
Auto Tag Develop / tag (push) Has been cancelled
Les sélections de produits liés ne bloquent plus la soumission du
formulaire de création ou d'édition de pièce. Les slots vides restent
visibles et peuvent être remplis ultérieurement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:18:10 +02:00
gitea-actions 041a04f0e9 chore : bump version to v1.9.22
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 36s
2026-04-06 15:15:37 +00:00
matthieu d089cd4873 fix(model-type) : masquer uniquement les produits, garder les champs perso
Auto Tag Develop / tag (push) Has been cancelled
Ajoute une prop hideProducts au PieceModelStructureEditor pour masquer
la section « Produits inclus par défaut » sans retirer les champs
personnalisés. Utilisé pour les catégories PRODUCT.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:15:26 +02:00
gitea-actions b304cf6684 chore : bump version to v1.9.21
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 36s
2026-04-06 15:12:40 +00:00
6 changed files with 22 additions and 30 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
parameters: parameters:
app.version: '1.9.20' app.version: '1.9.23'
@@ -1,6 +1,6 @@
<template> <template>
<div class="space-y-6"> <div class="space-y-6">
<section class="space-y-3"> <section v-if="!hideProducts" class="space-y-3">
<header> <header>
<h3 class="text-sm font-semibold"> <h3 class="text-sm font-semibold">
Produits inclus par défaut Produits inclus par défaut
@@ -166,6 +166,7 @@ defineOptions({ name: 'PieceModelStructureEditor' })
const props = defineProps<{ const props = defineProps<{
modelValue?: PieceModelStructure | null modelValue?: PieceModelStructure | null
hideProducts?: boolean
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -95,6 +95,12 @@
<PieceModelStructureEditor v-model="pieceStructure" /> <PieceModelStructureEditor v-model="pieceStructure" />
</div> </div>
<div
v-else
class="space-y-3 rounded-lg border border-base-300 p-4"
>
<PieceModelStructureEditor v-model="productStructure" hide-products />
</div>
</template> </template>
</section> </section>
+1 -13
View File
@@ -20,7 +20,6 @@ import {
buildProductRequirementDescriptions, buildProductRequirementDescriptions,
buildProductRequirementEntries, buildProductRequirementEntries,
resizeProductSelections, resizeProductSelections,
areProductSelectionsFilled,
applyProductSelection, applyProductSelection,
collectNormalizedProductIds, collectNormalizedProductIds,
} from '~/shared/utils/pieceProductSelectionUtils' } from '~/shared/utils/pieceProductSelectionUtils'
@@ -199,13 +198,7 @@ export function usePieceEdit(pieceId: string) {
buildProductRequirementEntries(structureProducts.value, 'piece-product-requirement'), buildProductRequirementEntries(structureProducts.value, 'piece-product-requirement'),
) )
const productSelectionsFilled = computed(() => const productSelectionsFilled = computed(() => true)
areProductSelectionsFilled(
requiresProductSelection.value,
productRequirementEntries.value,
productSelections.value,
),
)
const setProductSelection = (index: number, value: string | null) => { const setProductSelection = (index: number, value: string | null) => {
productSelections.value = applyProductSelection(productSelections.value, index, value) productSelections.value = applyProductSelection(productSelections.value, index, value)
@@ -355,11 +348,6 @@ export function usePieceEdit(pieceId: string) {
return return
} }
if (!productSelectionsFilled.value) {
toast.showError('Sélectionnez un produit conforme au squelette.')
return
}
const rawPrice = typeof editionForm.prix === 'string' const rawPrice = typeof editionForm.prix === 'string'
? editionForm.prix.trim() ? editionForm.prix.trim()
: editionForm.prix === null || editionForm.prix === undefined : editionForm.prix === null || editionForm.prix === undefined
+10 -1
View File
@@ -261,7 +261,7 @@
:model-value="productSelections[entry.index] || null" :model-value="productSelections[entry.index] || null"
:disabled="!canEdit || saving" :disabled="!canEdit || saving"
:type-product-id="entry.typeProductId" :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)" @update:model-value="(value) => setProductSelection(entry.index, value)"
/> />
</div> </div>
@@ -359,6 +359,9 @@
</header> </header>
<template v-if="isEditMode"> <template v-if="isEditMode">
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || saving" /> <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>
<template v-else> <template v-else>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
@@ -420,6 +423,9 @@
Enregistrer les modifications Enregistrer les modifications
</button> </button>
</div> </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> </div>
</section> </section>
</main> </main>
@@ -460,6 +466,7 @@ const {
constructeurLinks, constructeurLinks,
productSelections, productSelections,
customFieldInputs, customFieldInputs,
requiredCustomFieldsFilled,
pieceTypeList, pieceTypeList,
selectedType, selectedType,
resolvedStructure, resolvedStructure,
@@ -481,6 +488,8 @@ const {
formatPieceStructurePreview, formatPieceStructurePreview,
} = usePieceEdit(String(route.params.id)) } = usePieceEdit(String(route.params.id))
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
const entityTabs = computed(() => [ const entityTabs = computed(() => [
{ key: 'general', label: 'Général' }, { key: 'general', label: 'Général' },
{ key: 'products', label: 'Produits liés', count: structureProducts.value.length }, { key: 'products', label: 'Produits liés', count: structureProducts.value.length },
+2 -14
View File
@@ -168,7 +168,7 @@
:model-value="productSelections[entry.index] || null" :model-value="productSelections[entry.index] || null"
:disabled="!canEdit || submitting || !selectedType" :disabled="!canEdit || submitting || !selectedType"
:type-product-id="entry.typeProductId" :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)" @update:model-value="(value) => setProductSelection(entry.index, value)"
/> />
</div> </div>
@@ -273,7 +273,6 @@ import {
buildProductRequirementDescriptions, buildProductRequirementDescriptions,
buildProductRequirementEntries, buildProductRequirementEntries,
resizeProductSelections, resizeProductSelections,
areProductSelectionsFilled,
applyProductSelection, applyProductSelection,
collectNormalizedProductIds, collectNormalizedProductIds,
} from '~/shared/utils/pieceProductSelectionUtils' } from '~/shared/utils/pieceProductSelectionUtils'
@@ -379,13 +378,7 @@ const productRequirementEntries = computed(() =>
buildProductRequirementEntries(structureProducts.value, 'piece-create-product-requirement'), buildProductRequirementEntries(structureProducts.value, 'piece-create-product-requirement'),
) )
const productSelectionsFilled = computed(() => const productSelectionsFilled = computed(() => true)
areProductSelectionsFilled(
requiresProductSelection.value,
productRequirementEntries.value,
productSelections.value,
),
)
const setProductSelection = (index: number, value: string | null) => { const setProductSelection = (index: number, value: string | null) => {
productSelections.value = applyProductSelection(productSelections.value, index, value) productSelections.value = applyProductSelection(productSelections.value, index, value)
@@ -444,11 +437,6 @@ const submitCreation = async () => {
return return
} }
if (!productSelectionsFilled.value) {
toast.showError('Sélectionnez un produit conforme au squelette.')
return
}
const payload: Record<string, any> = { const payload: Record<string, any> = {
name: creationForm.name.trim(), name: creationForm.name.trim(),
typePieceId: selectedType.value.id, typePieceId: selectedType.value.id,