refactor(frontend) : extract shared piece product selection utils
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
104
app/shared/utils/pieceProductSelectionUtils.ts
Normal file
104
app/shared/utils/pieceProductSelectionUtils.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { PieceModelProduct, PieceModelStructure } from '~/shared/types/inventory'
|
||||
|
||||
/**
|
||||
* Extract the products array from a piece model structure, defaulting to [].
|
||||
*/
|
||||
export const getStructureProducts = (structure: PieceModelStructure | null): PieceModelProduct[] =>
|
||||
Array.isArray(structure?.products) ? structure.products : []
|
||||
|
||||
/**
|
||||
* Build a human-readable label for a single product requirement.
|
||||
*/
|
||||
export const describeProductRequirement = (requirement: PieceModelProduct, index: number): string => {
|
||||
if (!requirement) {
|
||||
return `Produit ${index + 1}`
|
||||
}
|
||||
const parts: string[] = []
|
||||
if (requirement.role) {
|
||||
parts.push(requirement.role)
|
||||
}
|
||||
if (requirement.typeProductLabel) {
|
||||
parts.push(requirement.typeProductLabel)
|
||||
} else if (requirement.typeProductId) {
|
||||
parts.push(`Catégorie #${requirement.typeProductId}`)
|
||||
}
|
||||
if (requirement.familyCode) {
|
||||
parts.push(`Famille ${requirement.familyCode}`)
|
||||
}
|
||||
if (parts.length === 0) {
|
||||
parts.push(`Produit ${index + 1}`)
|
||||
}
|
||||
return parts.join(' • ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Build description strings for every product requirement in a structure.
|
||||
*/
|
||||
export const buildProductRequirementDescriptions = (
|
||||
products: PieceModelProduct[],
|
||||
): string[] =>
|
||||
products.map((requirement, index) => describeProductRequirement(requirement, index))
|
||||
|
||||
/**
|
||||
* Build the entry objects used to render product selection inputs.
|
||||
*/
|
||||
export const buildProductRequirementEntries = (
|
||||
products: PieceModelProduct[],
|
||||
keyPrefix: string,
|
||||
) =>
|
||||
products.map((requirement, index) => ({
|
||||
index,
|
||||
key: `${keyPrefix}-${index}-${requirement?.typeProductId || 'any'}`,
|
||||
label: describeProductRequirement(requirement, index),
|
||||
typeProductId: requirement?.typeProductId ? String(requirement.typeProductId) : null,
|
||||
}))
|
||||
|
||||
/**
|
||||
* Resize the selections array to match the expected count, preserving existing values.
|
||||
*/
|
||||
export const resizeProductSelections = (
|
||||
current: (string | null)[],
|
||||
count: number,
|
||||
): (string | null)[] =>
|
||||
Array.from({ length: count }, (_, index) => current[index] ?? null)
|
||||
|
||||
/**
|
||||
* Return true when all required product slots have a non-empty string value,
|
||||
* or when no product selection is required.
|
||||
*/
|
||||
export const areProductSelectionsFilled = (
|
||||
requiresSelection: boolean,
|
||||
entries: { index: number }[],
|
||||
selections: (string | null)[],
|
||||
): boolean =>
|
||||
!requiresSelection ||
|
||||
entries.every((entry) => {
|
||||
const value = selections[entry.index]
|
||||
return typeof value === 'string' && value.trim().length > 0
|
||||
})
|
||||
|
||||
/**
|
||||
* Set a single product selection by index, returning a new array.
|
||||
*/
|
||||
export const applyProductSelection = (
|
||||
current: (string | null)[],
|
||||
index: number,
|
||||
value: string | null,
|
||||
): (string | null)[] => {
|
||||
const normalized = typeof value === 'string' ? value : null
|
||||
const next = [...current]
|
||||
next[index] = normalized
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract normalized product IDs from the current selections based on requirement entries.
|
||||
*/
|
||||
export const collectNormalizedProductIds = (
|
||||
entries: { index: number }[],
|
||||
selections: (string | null)[],
|
||||
): string[] =>
|
||||
entries
|
||||
.map((entry) => selections[entry.index])
|
||||
.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)
|
||||
.map((value) => value.trim())
|
||||
Reference in New Issue
Block a user