Files
Inventory_frontend/app/shared/utils/assignmentUtils.ts
Matthieu 1fbd1d1b2e refacto(F1.2) : extract modules from machines/new.vue (2313→1231 LOC)
Extract assignment normalization utils to shared/utils/assignmentUtils.ts.
Extract selection state management to composables/useMachineCreateSelections.ts.
Extract preview computation and validation to composables/useMachineCreatePreview.ts.
Wire machines/new.vue to use extracted modules (-47% LOC).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:15:22 +01:00

221 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Entity assignment normalization and display utilities.
*
* Extracted from pages/machines/new.vue these pure functions resolve
* machine / component / piece assignments from nested API payloads.
*/
type AnyRecord = Record<string, unknown>
// ---------------------------------------------------------------------------
// Primitive helpers
// ---------------------------------------------------------------------------
const isPlainObject = (value: unknown): value is AnyRecord =>
value !== null && typeof value === 'object' && !Array.isArray(value)
const toTrimmedString = (value: unknown): string | null => {
if (typeof value === 'string') {
const trimmed = value.trim()
return trimmed.length > 0 ? trimmed : null
}
if (typeof value === 'number' && Number.isFinite(value)) {
return String(value)
}
return null
}
// ---------------------------------------------------------------------------
// Dedup
// ---------------------------------------------------------------------------
export const dedupeAssignments = (
assignments: AnyRecord[],
): AnyRecord[] => {
const seen = new Set<string>()
return assignments.filter((assignment) => {
if (!assignment) return false
const id = assignment.id != null ? String(assignment.id) : ''
const name = assignment.name != null ? String(assignment.name) : ''
const key = `${id}::${name}`
if (!id && !name) return false
if (seen.has(key)) return false
seen.add(key)
return true
})
}
// ---------------------------------------------------------------------------
// Machine assignments
// ---------------------------------------------------------------------------
export const normalizeMachineAssignment = (input: unknown): AnyRecord | null => {
if (!input) return null
if (typeof input === 'string') {
const name = toTrimmedString(input)
return name ? { id: name, name } : null
}
if (typeof input === 'number' && Number.isFinite(input)) {
const value = String(input)
return { id: value, name: value }
}
const container = (input as AnyRecord).machine || (input as AnyRecord).machineData || input
if (!isPlainObject(container)) return null
const id =
container.id ?? (input as AnyRecord).machineId ?? (input as AnyRecord).id ?? null
const name =
container.name ||
(input as AnyRecord).machineName ||
container.label ||
container.title ||
(typeof id === 'string' ? id : null) ||
(typeof id === 'number' ? String(id) : null)
if (id == null && name == null) return null
return {
id: id != null ? id : null,
name: name != null ? name : null,
}
}
export const collectMachineAssignments = (source: unknown): AnyRecord[] => {
if (!isPlainObject(source)) return []
const candidates = [
source.machines,
source.machineLinks,
source.machineAssignments,
source.machinesAssignments,
source.linkedMachines,
]
const assignments: AnyRecord[] = []
candidates.forEach((list) => {
if (Array.isArray(list)) {
list.forEach((item) => {
const normalized = normalizeMachineAssignment(item)
if (normalized) assignments.push(normalized)
})
}
})
if (!assignments.length) {
const direct = normalizeMachineAssignment(source.machine)
if (direct) assignments.push(direct)
}
if (!assignments.length) {
const idCandidate = source.machineId ?? source.machineID ?? null
const nameCandidate = source.machineName ?? null
const normalized = normalizeMachineAssignment(nameCandidate || idCandidate)
if (normalized) assignments.push(normalized)
}
return dedupeAssignments(assignments)
}
// ---------------------------------------------------------------------------
// Component assignments
// ---------------------------------------------------------------------------
export const normalizeComponentAssignment = (input: unknown): AnyRecord | null => {
if (!input) return null
if (typeof input === 'string') {
const value = toTrimmedString(input)
return value ? { id: value, name: value } : null
}
if (typeof input === 'number' && Number.isFinite(input)) {
const value = String(input)
return { id: value, name: value }
}
const container =
(input as AnyRecord).component || (input as AnyRecord).composant || input
if (!isPlainObject(container)) return null
const id =
container.id ??
(input as AnyRecord).componentId ??
(input as AnyRecord).composantId ??
(input as AnyRecord).id ??
null
const name =
container.name ||
(input as AnyRecord).componentName ||
(input as AnyRecord).composantName ||
container.label ||
(typeof id === 'string' ? id : null) ||
(typeof id === 'number' ? String(id) : null)
if (id == null && name == null) return null
return {
id: id != null ? id : null,
name: name != null ? name : null,
}
}
export const collectComponentAssignments = (source: unknown): AnyRecord[] => {
if (!isPlainObject(source)) return []
const candidates = [
source.components,
source.composants,
source.componentLinks,
source.linkedComponents,
]
const assignments: AnyRecord[] = []
candidates.forEach((list) => {
if (Array.isArray(list)) {
list.forEach((item) => {
const normalized = normalizeComponentAssignment(item)
if (normalized) assignments.push(normalized)
})
}
})
if (!assignments.length) {
const direct = normalizeComponentAssignment(source.component || source.composant)
if (direct) assignments.push(direct)
}
if (!assignments.length) {
const idCandidate = source.componentId ?? source.composantId ?? null
const normalized = normalizeComponentAssignment(idCandidate)
if (normalized) assignments.push(normalized)
}
return dedupeAssignments(assignments)
}
// ---------------------------------------------------------------------------
// Convenience wrappers
// ---------------------------------------------------------------------------
export const getComponentMachineAssignments = (component: unknown): AnyRecord[] =>
collectMachineAssignments(component || {})
export const getPieceMachineAssignments = (piece: unknown): AnyRecord[] =>
collectMachineAssignments(piece || {})
export const getPieceComponentAssignments = (piece: unknown): AnyRecord[] =>
collectComponentAssignments(piece || {})
// ---------------------------------------------------------------------------
// Display
// ---------------------------------------------------------------------------
export const formatAssignmentList = (assignments: AnyRecord[]): string => {
if (!Array.isArray(assignments) || assignments.length === 0) return ''
return assignments
.map((assignment) => (assignment?.name || assignment?.id) as string)
.filter(Boolean)
.join(', ')
}