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>
This commit is contained in:
Matthieu
2026-02-04 09:15:22 +01:00
parent 1f2d6c78e8
commit 1fbd1d1b2e
4 changed files with 1331 additions and 1250 deletions

View File

@@ -0,0 +1,220 @@
/**
* 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(', ')
}