fix(composant) : preserve skeleton selections on form validation error

Shared module-level loading ref in useComposants caused structureDataLoading
to toggle during submission, unmounting the skeleton assignment UI. On remount,
watchers cleared selections not found in the limited local catalog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-26 10:36:07 +01:00
parent 452de8b069
commit d197d30eb0
2 changed files with 27 additions and 13 deletions

View File

@@ -106,7 +106,7 @@ export function useComponentCreate() {
const availableProducts = computed(() => productCatalogRef.value ?? []) const availableProducts = computed(() => productCatalogRef.value ?? [])
const availableComponents = computed(() => componentCatalogRef.value ?? []) const availableComponents = computed(() => componentCatalogRef.value ?? [])
const structureDataLoading = computed( const structureDataLoading = computed(
() => piecesLoading.value || componentsLoading.value || productsLoading.value, () => !submitting.value && (piecesLoading.value || componentsLoading.value || productsLoading.value),
) )
const fetchedPieceTypeMap = ref<Record<string, string>>({}) const fetchedPieceTypeMap = ref<Record<string, string>>({})

View File

@@ -279,6 +279,11 @@ export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps)
if (deps.isRoot()) { if (deps.isRoot()) {
return return
} }
// Only clear if we have loaded options (cache or catalog); skip when options are empty
// because the fetch may not have completed yet.
if (!options.length) {
return
}
const hasMatch = options.some( const hasMatch = options.some(
(component) => component.id === deps.assignment.selectedComponentId, (component) => component.id === deps.assignment.selectedComponentId,
) )
@@ -293,6 +298,11 @@ export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps)
() => [deps.pieces, deps.assignment.pieces], () => [deps.pieces, deps.assignment.pieces],
() => { () => {
for (const pieceAssignment of deps.assignment.pieces) { for (const pieceAssignment of deps.assignment.pieces) {
const hasCachedOptions = !!pieceOptionsByPath.value[pieceAssignment.path]
// Only clear selections when we have loaded options (cached or from catalog).
// When no cache exists, a fetch is about to fire — clearing now would lose
// user input before the real option list arrives.
if (hasCachedOptions) {
const options = getPieceOptions(pieceAssignment) const options = getPieceOptions(pieceAssignment)
if ( if (
pieceAssignment.selectedPieceId pieceAssignment.selectedPieceId
@@ -300,6 +310,7 @@ export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps)
) { ) {
pieceAssignment.selectedPieceId = '' pieceAssignment.selectedPieceId = ''
} }
}
if (!primedPiecePaths.has(pieceAssignment.path) && !pieceOptionsByPath.value[pieceAssignment.path]) { if (!primedPiecePaths.has(pieceAssignment.path) && !pieceOptionsByPath.value[pieceAssignment.path]) {
primedPiecePaths.add(pieceAssignment.path) primedPiecePaths.add(pieceAssignment.path)
fetchPieceOptions(pieceAssignment).catch(() => {}) fetchPieceOptions(pieceAssignment).catch(() => {})
@@ -313,6 +324,8 @@ export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps)
() => [deps.products, deps.assignment.products], () => [deps.products, deps.assignment.products],
() => { () => {
for (const productAssignment of deps.assignment.products) { for (const productAssignment of deps.assignment.products) {
const hasCachedOptions = !!productOptionsByPath.value[productAssignment.path]
if (hasCachedOptions) {
const options = getProductOptions(productAssignment) const options = getProductOptions(productAssignment)
if ( if (
productAssignment.selectedProductId productAssignment.selectedProductId
@@ -320,6 +333,7 @@ export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps)
) { ) {
productAssignment.selectedProductId = '' productAssignment.selectedProductId = ''
} }
}
if (!primedProductPaths.has(productAssignment.path) && !productOptionsByPath.value[productAssignment.path]) { if (!primedProductPaths.has(productAssignment.path) && !productOptionsByPath.value[productAssignment.path]) {
primedProductPaths.add(productAssignment.path) primedProductPaths.add(productAssignment.path)
fetchProductOptions(productAssignment).catch(() => {}) fetchProductOptions(productAssignment).catch(() => {})