import { computed, ref, watch } from 'vue' import { useApi } from '~/composables/useApi' import { extractCollection } from '~/shared/utils/apiHelpers' import { componentOptionDescription, componentOptionLabel, describePieceRequirement as _describePieceRequirement, describeProductRequirement as _describeProductRequirement, pieceOptionDescription, pieceOptionLabel, productOptionDescription, productOptionLabel, } from '~/shared/utils/structureAssignmentLabels' import type { ComponentOption, PieceOption, ProductOption, StructureAssignmentNode, StructurePieceAssignment, StructureProductAssignment, } from '~/shared/utils/structureAssignmentLabels' export type { ComponentOption, PieceOption, ProductOption, StructureAssignmentNode, StructurePieceAssignment, StructureProductAssignment, } from '~/shared/utils/structureAssignmentLabels' export interface StructureAssignmentFetchDeps { assignment: StructureAssignmentNode pieces: PieceOption[] | null products: ProductOption[] | null components: ComponentOption[] | null isRoot: () => boolean pieceTypeLabelMap: Record productTypeLabelMap: Record componentTypeLabelMap: Record } export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps) { const { get } = useApi() const pieceOptionsByPath = ref>({}) const productOptionsByPath = ref>({}) const componentOptionsByPath = ref>({}) const pieceLoadingByPath = ref>({}) const productLoadingByPath = ref>({}) const componentLoadingByPath = ref>({}) const setLoading = (target: Record, key: string, value: boolean) => { target[key] = value } const typeIri = (id: string) => `/api/model_types/${id}` const primedPiecePaths = new Set() const primedProductPaths = new Set() const primedComponentPaths = new Set() // --- Component options --- const componentOptions = computed(() => { if (deps.isRoot()) { return [] } const cached = componentOptionsByPath.value[deps.assignment.path] if (cached) { return cached } const definition = deps.assignment.definition || {} const requiredTypeId = definition.typeComposantId || definition.modelId || null const requiredFamilyCode = definition.familyCode || null return (deps.components || []).filter((component) => { if (!component || typeof component !== 'object') { return false } if (requiredTypeId) { return component.typeComposantId === requiredTypeId } if (requiredFamilyCode) { return ( component.typeComposant?.code === requiredFamilyCode || component.typeComposantId === requiredFamilyCode ) } return true }) }) const fetchComponentOptions = async (term = '') => { if (deps.isRoot()) { return } const key = deps.assignment.path if (componentLoadingByPath.value[key]) { return } const definition = deps.assignment.definition || {} const requiredTypeId = definition.typeComposantId || definition.modelId || definition.typeComposant?.id || null const params = new URLSearchParams() params.set('itemsPerPage', '200') if (term.trim()) { params.set('search', term.trim()) } if (requiredTypeId) { params.set('typeComposant', typeIri(requiredTypeId)) } setLoading(componentLoadingByPath.value, key, true) try { const result = await get(`/composants?${params.toString()}`) if (result.success) { componentOptionsByPath.value[key] = extractCollection(result.data) } } finally { setLoading(componentLoadingByPath.value, key, false) } } // --- Piece options --- const getPieceOptions = (assignment: StructurePieceAssignment) => { const cached = pieceOptionsByPath.value[assignment.path] if (cached) { return cached } const definition = assignment.definition const requiredTypeId = definition.typePieceId || definition.typePiece?.id || definition.familyCode || null return (deps.pieces || []).filter((piece) => { if (!piece || typeof piece !== 'object') { return false } if (!requiredTypeId) { return true } if (definition.typePieceId || definition.typePiece?.id) { return ( piece.typePieceId === requiredTypeId || piece.typePiece?.id === requiredTypeId ) } if (definition.familyCode) { return ( piece.typePiece?.code === requiredTypeId || piece.typePieceId === requiredTypeId ) } return false }) } const fetchPieceOptions = async (assignment: StructurePieceAssignment, term = '') => { const key = assignment.path if (pieceLoadingByPath.value[key]) { return } const definition = assignment.definition || {} const requiredTypeId = definition.typePieceId || definition.typePiece?.id || null const params = new URLSearchParams() params.set('itemsPerPage', '200') if (term.trim()) { params.set('search', term.trim()) } if (requiredTypeId) { params.set('typePiece', typeIri(requiredTypeId)) } setLoading(pieceLoadingByPath.value, key, true) try { const result = await get(`/pieces?${params.toString()}`) if (result.success) { pieceOptionsByPath.value[key] = extractCollection(result.data) } } finally { setLoading(pieceLoadingByPath.value, key, false) } } const describePieceRequirement = (assignment: StructurePieceAssignment) => { const options = getPieceOptions(assignment) return _describePieceRequirement(assignment, options, deps.pieceTypeLabelMap) } // --- Product options --- const getProductOptions = (assignment: StructureProductAssignment) => { const cached = productOptionsByPath.value[assignment.path] if (cached) { return cached } const definition = assignment.definition const requiredTypeId = definition.typeProductId || definition.typeProduct?.id || definition.familyCode || null return (deps.products || []).filter((product) => { if (!product || typeof product !== 'object') { return false } if (!requiredTypeId) { return true } if (definition.typeProductId || definition.typeProduct?.id) { return ( product.typeProductId === requiredTypeId || product.typeProduct?.id === requiredTypeId ) } if (definition.familyCode) { return ( product.typeProduct?.code === requiredTypeId || product.typeProductId === requiredTypeId ) } return false }) } const fetchProductOptions = async (assignment: StructureProductAssignment, term = '') => { const key = assignment.path if (productLoadingByPath.value[key]) { return } const definition = assignment.definition || {} const requiredTypeId = definition.typeProductId || definition.typeProduct?.id || null const params = new URLSearchParams() params.set('itemsPerPage', '200') if (term.trim()) { params.set('search', term.trim()) } if (requiredTypeId) { params.set('typeProduct', typeIri(requiredTypeId)) } setLoading(productLoadingByPath.value, key, true) try { const result = await get(`/products?${params.toString()}`) if (result.success) { productOptionsByPath.value[key] = extractCollection(result.data) } } finally { setLoading(productLoadingByPath.value, key, false) } } const describeProductRequirement = (assignment: StructureProductAssignment) => { const options = getProductOptions(assignment) return _describeProductRequirement(assignment, options, deps.productTypeLabelMap) } // --- Watchers --- watch( componentOptions, (options) => { if (deps.isRoot()) { return } const hasMatch = options.some( (component) => component.id === deps.assignment.selectedComponentId, ) if (!hasMatch) { deps.assignment.selectedComponentId = '' } }, { immediate: true }, ) watch( () => [deps.pieces, deps.assignment.pieces], () => { for (const pieceAssignment of deps.assignment.pieces) { const options = getPieceOptions(pieceAssignment) if ( pieceAssignment.selectedPieceId && !options.some((piece) => piece.id === pieceAssignment.selectedPieceId) ) { pieceAssignment.selectedPieceId = '' } if (!primedPiecePaths.has(pieceAssignment.path) && !pieceOptionsByPath.value[pieceAssignment.path]) { primedPiecePaths.add(pieceAssignment.path) fetchPieceOptions(pieceAssignment).catch(() => {}) } } }, { deep: true, immediate: true }, ) watch( () => [deps.products, deps.assignment.products], () => { for (const productAssignment of deps.assignment.products) { const options = getProductOptions(productAssignment) if ( productAssignment.selectedProductId && !options.some((product) => product.id === productAssignment.selectedProductId) ) { productAssignment.selectedProductId = '' } if (!primedProductPaths.has(productAssignment.path) && !productOptionsByPath.value[productAssignment.path]) { primedProductPaths.add(productAssignment.path) fetchProductOptions(productAssignment).catch(() => {}) } } }, { deep: true, immediate: true }, ) watch( () => deps.assignment.definition, () => { if (deps.isRoot()) { return } const key = deps.assignment.path if (!primedComponentPaths.has(key) && !componentOptionsByPath.value[key]) { primedComponentPaths.add(key) fetchComponentOptions().catch(() => {}) } }, { immediate: true }, ) return { pieceLoadingByPath, productLoadingByPath, componentLoadingByPath, componentOptions, componentOptionLabel, componentOptionDescription, fetchComponentOptions, getPieceOptions, pieceOptionLabel, pieceOptionDescription, fetchPieceOptions, describePieceRequirement, getProductOptions, productOptionLabel, productOptionDescription, fetchProductOptions, describeProductRequirement, } }