Rename the equipment domain to machines; retype the models catalog (ADR-011)
The equipment plugin is now the machines plugin, ending the UI-vs-code vocabulary split while the contract is pre-1.0 and nothing external depends on the old names. - plugins/equipment -> plugins/machines: manifest, class, /api/machines, machines.* permissions, registry key (with an auto-migrating load shim for existing installs). - Tables: equipment -> machines (equipmentid -> machineid) and equipmenttypes -> machinetypes, renamed in the plugin's own migration chain (machines0002rename), idempotent for both upgrading and fresh installs. - The legacy core machinetypes lookup actually types the vendor MODELS catalog, so it is renamed losslessly to modeltypes (models.modeltypeid, /api/modeltypes, Model Types settings page) rather than collapsed, freeing the machinetypes name. Core migration 7d17_machines_rename also flips data in place: assettypes row equipment -> machine, auditlog entitytype, identifier_/search_ settings keys, permission rows, and renames alembic_version_equipment. - Frontend: machinesApi/modeltypesApi, item.machine response shape, assettype value compares 'equipment' -> 'machine' (map, search, custom fields, relationships), routes machines.js with plugin gating retagged, /print/machine-badge, Machine Types (subtypes) and Model Types (catalog) settings pages, machines-by-type report id. - Docs swept; ADRs left as history per the authoring rule. Upgrade: flask db upgrade then flask plugin upgrade-all. Verified: dev DB flipped live (262 machines, 35 modeltypes, 95 models retyped, zero equipment tables remain); fresh scratch-MySQL install produces the new names; 341 tests green; naming/style green; frontend builds; live E2E on machines list/detail, PC relationships, map, reports, and both settings pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
168
frontend/src/views/print/MachineBadge.vue
Normal file
168
frontend/src/views/print/MachineBadge.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div>
|
||||
<button class="print-btn" @click="print" v-if="!loading">Print Badge</button>
|
||||
<div v-if="loading" class="loading-msg">Loading...</div>
|
||||
<div v-else-if="machine" class="badge-container">
|
||||
<div class="model-name">{{ modelName }}</div>
|
||||
<img
|
||||
v-if="isInspection"
|
||||
class="machine-image"
|
||||
:src="geLogo"
|
||||
alt="GE Logo"
|
||||
/>
|
||||
<img
|
||||
v-else-if="imageUrl"
|
||||
class="machine-image"
|
||||
:src="imageUrl"
|
||||
:alt="modelName"
|
||||
/>
|
||||
<div class="barcode-container">
|
||||
<svg ref="barcodeEl"></svg>
|
||||
<div class="machine-number">*{{ machine.assetnumber }}*</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="error-msg">Machine not found</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { machinesApi } from '../../api'
|
||||
import { getBadgeLogo } from '@/utils/siteSettings'
|
||||
import JsBarcode from 'jsbarcode'
|
||||
|
||||
const route = useRoute()
|
||||
const loading = ref(true)
|
||||
const machine = ref(null)
|
||||
const barcodeEl = ref(null)
|
||||
|
||||
const geLogo = ref('/ge-aerospace-logo.svg')
|
||||
|
||||
const isInspection = computed(() => {
|
||||
if (!machine.value) return false
|
||||
return machine.value.assetnumber?.startsWith('06')
|
||||
})
|
||||
|
||||
const modelName = computed(() => {
|
||||
if (!machine.value) return ''
|
||||
if (isInspection.value) return 'Inspection'
|
||||
return machine.value.machine?.modelname || ''
|
||||
})
|
||||
|
||||
const imageUrl = computed(() => {
|
||||
if (!machine.value) return null
|
||||
const img = machine.value.machine?.imageurl
|
||||
if (img) return img.startsWith('/') ? img : `/images/models/machines/${img}`
|
||||
return null
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
getBadgeLogo().then(logo => { geLogo.value = logo })
|
||||
try {
|
||||
const response = await machinesApi.get(route.params.id)
|
||||
machine.value = response.data.data
|
||||
} catch (error) {
|
||||
console.error('Error loading machine:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
await nextTick()
|
||||
generateBarcode()
|
||||
}
|
||||
})
|
||||
|
||||
function generateBarcode() {
|
||||
if (!barcodeEl.value || !machine.value) return
|
||||
try {
|
||||
JsBarcode(barcodeEl.value, machine.value.assetnumber, {
|
||||
format: 'CODE39',
|
||||
displayValue: false,
|
||||
width: 2,
|
||||
height: 70,
|
||||
margin: 0
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Barcode generation error:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function print() {
|
||||
window.print()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@page { size: 2.13in 3.38in; margin: 0; }
|
||||
|
||||
body { font-family: Arial, sans-serif; }
|
||||
|
||||
.badge-container {
|
||||
width: 2.13in;
|
||||
height: 3.38in;
|
||||
background: white;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.15in;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.model-name {
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 0.1in;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.machine-image {
|
||||
max-width: 1.8in;
|
||||
max-height: 1.5in;
|
||||
object-fit: contain;
|
||||
margin-bottom: 0.1in;
|
||||
}
|
||||
|
||||
.barcode-container {
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.barcode-container svg {
|
||||
width: 1.8in;
|
||||
height: 0.9in;
|
||||
}
|
||||
|
||||
.machine-number {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
margin-top: -0.1in;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.print-btn {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
padding: 10px 30px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.loading-msg, .error-msg {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
font-size: 1.125rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.print-btn { display: none; }
|
||||
.badge-container { border: none; margin: 0; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user