@@ -102,6 +98,7 @@
import { ref, computed, onMounted } from 'vue'
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
import { useToast } from '~/composables/useToast'
+import PageHero from '~/components/PageHero.vue'
const { machineTypes, loading, loadMachineTypes, deleteMachineType } = useMachineTypesApi()
@@ -144,4 +141,4 @@ const confirmDeleteType = async (type) => {
onMounted(async () => {
await loadMachineTypes()
})
-
\ No newline at end of file
+
diff --git a/app/utils/printTemplates/machineReport.js b/app/utils/printTemplates/machineReport.js
new file mode 100644
index 0000000..746d297
--- /dev/null
+++ b/app/utils/printTemplates/machineReport.js
@@ -0,0 +1,567 @@
+
+
+const formatSize = (size) => {
+ if (size === undefined || size === null) return '—'
+ if (size === 0) return '0 B'
+ const units = ['B', 'KB', 'MB', 'GB']
+ const index = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024)))
+ const formatted = size / Math.pow(1024, index)
+ return `${formatted.toFixed(1)} ${units[index]}`
+}
+
+const renderPrintField = (label, value, fallback = '—') => {
+ const display = value !== undefined && value !== null && value !== '' ? value : fallback
+ return `
${display}
`
+}
+
+const renderPrintCustomFields = (fields = [], title, sectionClass = 'print-section') => {
+ if (!fields.length) return ''
+ const items = fields
+ .map((field) => `
${field.value || '—'}
`)
+ .join('')
+ return `
+
+
${title}
+
+ ${items}
+
+
+ `
+}
+
+const renderPrintDocuments = (documents = [], title, sectionClass = 'print-section') => {
+ if (!documents.length) return ''
+ const rows = documents
+ .map((doc) => `
| ${doc.name} | ${doc.type} | ${doc.size} |
`)
+ .join('')
+ return `
+
+
${title}
+
+
+
+ | Nom |
+ Type |
+ Taille |
+
+
+ ${rows}
+
+
+ `
+}
+
+const renderPrintPieces = (
+ pieces = [],
+ title = 'Pièces indépendantes',
+ sectionClass = 'print-section print-section--pieces',
+) => {
+ if (!pieces.length) return ''
+
+ const cards = pieces
+ .map((piece, idx) => {
+ const indexLabel = piece.indexPath ? piece.indexPath.join('.') : `${idx + 1}`
+ const constructeurBadge = piece.constructeur?.name
+ ? `
Constructeur: ${piece.constructeur.name}`
+ : ''
+
+ const customFields = (piece.customFields || [])
+ .filter((field) => field.value && field.value !== '—' && field.value !== '')
+ .map(
+ (field) => `
+
+ ${field.label}
+ ${field.value}
+
+ `,
+ )
+ .join('')
+
+ const customFieldsBlock = customFields
+ ? `
`
+ : ''
+
+ const documentsBlock = (piece.documents || []).length
+ ? `
Documents
${piece.documents
+ .map((doc) => `- ${doc.name} (${doc.type} • ${doc.size})
`)
+ .join('')}
`
+ : ''
+
+ return `
+
+
+ ${piece.description ? `
${piece.description}
` : ''}
+
+ ${customFieldsBlock}
+ ${documentsBlock}
+
+ `
+ })
+ .join('')
+
+ return `
+
+
${title}
+
+ ${cards}
+
+
+ `
+}
+
+const renderPrintComponents = (components = [], depth = 0, indexPath = []) => {
+ if (!components.length) return ''
+ return components
+ .map((component, idx) => {
+ const badges = []
+ if (component.constructeur?.name) {
+ badges.push(`Constructeur: ${component.constructeur.name}`)
+ }
+ const sectionClass = `print-section print-section--component print-section-depth-${Math.min(depth, 3)}`
+ const currentIndex = [...indexPath, idx + 1]
+ const indexLabel = currentIndex.join('.')
+ return `
+
+
+ ${indexLabel}
+ Composant : ${component.name}
+
+ ${component.description ? `
${component.description}
` : ''}
+ ${badges.length ? `
${badges.map((badge) => `${badge}`).join('')}
` : ''}
+ ${renderPrintCustomFields(
+ component.customFields,
+ 'Champs personnalisés',
+ 'print-section print-subsection print-section--custom-fields',
+ )}
+ ${renderPrintPieces(
+ (component.pieces || []).map((piece, pieceIdx) => ({ ...piece, indexPath: [...currentIndex, pieceIdx + 1] })),
+ 'Pièces du composant',
+ 'print-section print-subsection print-section--pieces',
+ )}
+ ${renderPrintDocuments(
+ component.documents,
+ 'Documents du composant',
+ 'print-section print-subsection print-section--documents',
+ )}
+ ${renderPrintComponents(component.subComponents || [], depth + 1, currentIndex)}
+
+ `
+ })
+ .join('')
+}
+
+const normalizeDocuments = (docs = []) => {
+ return docs.map((doc) => ({
+ id: doc.id,
+ name: doc.name || doc.filename || 'Document',
+ type: doc.mimeType || doc.type || '—',
+ size: formatSize(doc.size),
+ }))
+}
+
+const normalizeCustomFields = (values = []) => {
+ return values.map((value) => ({
+ id: value.id,
+ label: value.customField?.name || 'Champ',
+ value: value.value || value.customField?.defaultValue || '—',
+ }))
+}
+
+const normalizeConstructeur = (constructeur) => {
+ if (!constructeur) return null
+ return {
+ name: constructeur.name || '—',
+ contact: [constructeur.email, constructeur.phone].filter(Boolean).join(' • ') || '—',
+ }
+}
+
+const normalizePiece = (piece) => ({
+ id: piece.id,
+ name: piece.name || 'Pièce sans nom',
+ description: piece.description || '',
+ reference: piece.reference || '',
+ customFields: normalizeCustomFields(piece.customFieldValues || []),
+ documents: normalizeDocuments(piece.documents || []),
+ constructeur: normalizeConstructeur(piece.constructeur),
+ indexPath: piece.indexPath || null,
+})
+
+const normalizeComponent = (component) => ({
+ id: component.id,
+ name: component.name || 'Composant sans nom',
+ description: component.description || '',
+ customFields: normalizeCustomFields(component.customFieldValues || []),
+ documents: normalizeDocuments(component.documents || []),
+ pieces: (component.pieces || []).map(normalizePiece),
+ subComponents: (component.sousComposants || component.subComponents || []).map(normalizeComponent),
+ constructeur: normalizeConstructeur(component.constructeur),
+})
+
+export const buildMachinePrintContext = ({
+ machine,
+ machineName,
+ machineReference,
+ machineEmplacement,
+ machinePieces = [],
+ components = [],
+ selection,
+}) => {
+ const selectionState = selection || {}
+ const machineSelection = selectionState.machine || {}
+ const componentSelection = selectionState.components || {}
+ const pieceSelection = selectionState.pieces || {}
+
+ const includeMachineInfo = machineSelection.info !== false
+ const includeMachineCustomFields = machineSelection.customFields !== false
+ const includeMachineDocuments = machineSelection.documents !== false
+
+ const isComponentSelected = (id) => {
+ if (!id) return true
+ if (Object.prototype.hasOwnProperty.call(componentSelection, id)) {
+ return componentSelection[id]
+ }
+ return true
+ }
+
+ const isPieceSelected = (id) => {
+ if (!id) return true
+ if (Object.prototype.hasOwnProperty.call(pieceSelection, id)) {
+ return pieceSelection[id]
+ }
+ return true
+ }
+
+ const machineBadges = []
+ if (machine?.typeMachine?.category) {
+ machineBadges.push(machine.typeMachine.category)
+ }
+ if (machine?.site?.name) {
+ machineBadges.push(`Site: ${machine.site.name}`)
+ }
+ if (machineReference) {
+ machineBadges.push(`Ref: ${machineReference}`)
+ }
+
+ const normalizedPieces = machinePieces
+ .map(normalizePiece)
+ .filter((piece) => isPieceSelected(piece.id))
+ .map((piece, idx) => ({
+ ...piece,
+ indexPath: [idx + 1],
+ }))
+
+ const normalizedComponents = components.map(normalizeComponent)
+
+ const filterComponentTree = (component) => {
+ const filteredPieces = (component.pieces || []).filter((piece) => isPieceSelected(piece.id))
+ const filteredSubComponents = (component.subComponents || [])
+ .map(filterComponentTree)
+ .filter(Boolean)
+
+ const includeSelf = isComponentSelected(component.id)
+ const shouldInclude = includeSelf || filteredPieces.length > 0 || filteredSubComponents.length > 0
+
+ if (!shouldInclude) {
+ return null
+ }
+
+ return {
+ ...component,
+ pieces: filteredPieces,
+ subComponents: filteredSubComponents,
+ }
+ }
+
+ const filteredComponents = normalizedComponents
+ .map(filterComponentTree)
+ .filter(Boolean)
+
+ return {
+ generatedAt: new Date().toLocaleString('fr-FR'),
+ machine: {
+ id: machine?.id || null,
+ name: machineName,
+ description: machine?.description || '',
+ typeDescription: machine?.typeMachine?.description || '',
+ reference: machineReference,
+ emplacement: machineEmplacement,
+ site: machine?.site?.name || '',
+ category: machine?.typeMachine?.category || '',
+ badges: machineBadges,
+ constructeur: normalizeConstructeur(machine?.constructeur),
+ includeInfo: includeMachineInfo,
+ customFields: includeMachineCustomFields
+ ? normalizeCustomFields(machine?.customFieldValues || [])
+ : [],
+ documents: includeMachineDocuments
+ ? normalizeDocuments(machine?.documents || [])
+ : [],
+ },
+ components: filteredComponents,
+ pieces: normalizedPieces,
+ }
+}
+
+export const buildMachinePrintHtml = (context, styles) => {
+ const title = context.machine.name ? `Impression - ${context.machine.name}` : 'Impression machine'
+ const badgesHtml = context.machine.badges
+ .map((badge) => `
${badge}`)
+ .join('')
+ const sections = []
+
+ sections.push(`
+
+ Généré le ${context.generatedAt}
+ Machine ID: ${context.machine.id || '—'}
+
+
+ `)
+
+ if (context.machine.includeInfo) {
+ sections.push(`
+
+
Informations générales
+
+ ${renderPrintField('Nom', context.machine.name)}
+ ${renderPrintField('Référence', context.machine.reference, 'Non définie')}
+ ${renderPrintField('Emplacement', context.machine.emplacement, 'Non défini')}
+ ${renderPrintField('Site', context.machine.site, 'Non défini')}
+ ${renderPrintField('Constructeur', context.machine.constructeur?.name, 'Non défini')}
+ ${renderPrintField('Contact Constructeur', context.machine.constructeur?.contact, 'Non défini')}
+
+
+ `)
+ }
+
+ const customFieldsSection = renderPrintCustomFields(
+ context.machine.customFields,
+ 'Champs personnalisés de la machine',
+ 'print-section print-section--custom-fields',
+ )
+ if (customFieldsSection) {
+ sections.push(customFieldsSection)
+ }
+
+ const documentsSection = renderPrintDocuments(
+ context.machine.documents,
+ 'Documents liés à la machine',
+ 'print-section print-section--documents',
+ )
+ if (documentsSection) {
+ sections.push(documentsSection)
+ }
+
+ const componentsSection = renderPrintComponents(context.components)
+ if (componentsSection) {
+ sections.push(componentsSection)
+ }
+
+ const piecesSection = renderPrintPieces(
+ context.pieces,
+ 'Pièces indépendantes',
+ 'print-section print-section--pieces',
+ )
+ if (piecesSection) {
+ sections.push(piecesSection)
+ }
+
+ sections.push(`
+
+ Rapport généré automatiquement par Inventaire Pro.
+
+ `)
+
+ const content = sections.join('\n')
+
+ return `
+
+
+
+
+
+
${title}
+ ${styles}
+
+
+
+
+ ${content}
+
+
+ `
+}
\ No newline at end of file