Files
shopdb-flask/plugins/measuringtools/frontend/views/MeasuringToolDetail.vue
cproudlock ebca0b00b0 ADR-013 Phase 4: relocate the remaining 9 plugin frontends (all 13 done)
Relocate warranty, measuringtools, network, printers, usb, notifications,
computers, and slides into plugins/<name>/frontend/. Each plugin's views are
pulled from wherever they lived (own dir, plus the shared views/settings/,
views/reports/, views/print/ dirs, and top-level views) into the plugin's
frontend/views/, and its route file becomes the self-contained routes.js.

Handled the messy cases:
- computers: name mismatch (its views live in views/pcs/) - moved by following
  the route file's own imports, so the dir name did not matter. Its OS/access-
  protocol/PC-type settings views move with it (only computers.js routed them).
- network: NetworkHub's sibling sub-views (NetworkDevicesList, SubnetsBrowse,
  not directly routed) moved too so its `./` imports resolve.
- printers: the qrLogo helper is SHARED with core AssetLabel, so it stays in
  views/print/ and PrinterQR imports it via @/views/print/qrLogo.
- slides: route file is toplevel-only (TVDashboard); SlideManager stays core
  (core.js routes /settings/slides).

frontend/src/views/ now holds only core views; frontend/src/router/routes/ holds
only core.js. All 13 plugins are self-contained under plugins/<name>/frontend/.
Verified live: Network (hub + moved sub-views), Computers (name mismatch),
GE-Enforce (helper), printedparts all render from their staged frontends. Build +
58 vitest + naming green.
2026-07-18 23:56:07 -04:00

209 lines
8.9 KiB
Vue

<template>
<div class="detail-page">
<div class="page-header">
<h2>Measuring Tool Details</h2>
<div class="header-actions">
<router-link :to="`/print/asset-label/measuring_tool/${tool?.measuringtool?.measuringtoolid}`" class="btn btn-secondary" v-if="tool" target="_blank">
Print Label
</router-link>
<router-link :to="`/measuringtools/${tool?.measuringtool?.measuringtoolid}/edit`" class="btn btn-primary" v-if="tool">
Edit
</router-link>
<router-link to="/measuringtools" class="btn btn-secondary">Back to List</router-link>
</div>
</div>
<div v-if="loading" class="loading">Loading...</div>
<template v-else-if="tool">
<!-- Hero Section -->
<div class="hero-card">
<div class="hero-content">
<div class="hero-title">
<h1>{{ tool.assetnumber }}</h1>
<span v-if="tool.name" class="hero-alias">{{ tool.name }}</span>
</div>
<div class="hero-meta">
<span class="badge badge-lg badge-primary">
{{ tool.assettypename || 'Measuring Tool' }}
</span>
<span class="badge badge-lg" :style="colorStyle(tool.measuringtool?.calibrationstatuscolor)">
{{ statusLabel(tool.measuringtool?.calibrationstatus) }}
</span>
</div>
<div class="hero-details">
<div class="hero-detail" v-if="tool.measuringtool?.measuringtooltypename">
<span class="hero-detail-label">Type</span>
<span class="hero-detail-value">{{ tool.measuringtool.measuringtooltypename }}</span>
</div>
<div class="hero-detail" v-if="tool.measuringtool?.nextcalibrationdate">
<span class="hero-detail-label">Next Calibration</span>
<span class="hero-detail-value">{{ formatDate(tool.measuringtool.nextcalibrationdate) }}</span>
</div>
<div class="hero-detail" v-if="tool.locationname">
<span class="hero-detail-label">Location</span>
<span class="hero-detail-value">{{ tool.locationname }}</span>
</div>
</div>
</div>
</div>
<!-- Canonical card order: Identity -> type-specific -> status -> Location & Organization -> domain -> Custom Fields -> Warranty -> Relationships -> Notes -> audit footer -->
<!-- Main Content Grid -->
<div class="content-grid">
<!-- Left Column -->
<div class="content-column">
<!-- Identity Section -->
<div class="section-card">
<h3 class="section-title">Identity</h3>
<div class="info-list">
<div class="info-row">
<span class="info-label">Asset Number (Gage Tag)</span>
<span class="info-value">{{ tool.assetnumber }}</span>
</div>
<div class="info-row" v-if="tool.name">
<span class="info-label">Name</span>
<span class="info-value">{{ tool.name }}</span>
</div>
<div class="info-row" v-if="tool.gaugelabreference && isEnabled('gaugelabreference', 'measuring_tool')">
<span class="info-label">Gauge Lab Reference</span>
<span class="info-value mono">{{ tool.gaugelabreference }}</span>
</div>
<div class="info-row" v-if="tool.maintenancereference && isEnabled('maintenancereference', 'measuring_tool')">
<span class="info-label">Maintenance Reference</span>
<span class="info-value mono">{{ tool.maintenancereference }}</span>
</div>
<div class="info-row" v-if="tool.serialnumber">
<span class="info-label">Serial Number</span>
<span class="info-value mono">{{ tool.serialnumber }}</span>
</div>
<div class="info-row">
<span class="info-label">Type</span>
<span class="info-value">{{ tool.measuringtool?.measuringtooltypename || '-' }}</span>
</div>
</div>
</div>
<!-- Calibration Section -->
<div class="section-card">
<h3 class="section-title">Calibration</h3>
<div class="info-list">
<div class="info-row">
<span class="info-label">Status</span>
<span class="info-value">
<span class="badge" :style="colorStyle(tool.measuringtool?.calibrationstatuscolor)">
{{ statusLabel(tool.measuringtool?.calibrationstatus) }}
</span>
</span>
</div>
<div class="info-row">
<span class="info-label">Last Calibration</span>
<span class="info-value">{{ tool.measuringtool?.lastcalibrationdate ? formatDate(tool.measuringtool.lastcalibrationdate) : '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">Next Calibration</span>
<span class="info-value">{{ tool.measuringtool?.nextcalibrationdate ? formatDate(tool.measuringtool.nextcalibrationdate) : '-' }}</span>
</div>
<div class="info-row" v-if="tool.measuringtool?.calibrationintervaldays">
<span class="info-label">Interval</span>
<span class="info-value">{{ tool.measuringtool.calibrationintervaldays }} days</span>
</div>
<div class="info-row" v-if="tool.measuringtool?.calibrationprovider">
<span class="info-label">Provider</span>
<span class="info-value">{{ tool.measuringtool.calibrationprovider }}</span>
</div>
</div>
</div>
</div>
<!-- Right Column -->
<div class="content-column">
<!-- Location & Organization -->
<div class="section-card">
<h3 class="section-title">Location & Organization</h3>
<div class="info-list">
<div class="info-row">
<span class="info-label">Location</span>
<span class="info-value">{{ tool.locationname || '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">Business Unit</span>
<span class="info-value">{{ tool.businessunitname || '-' }}</span>
</div>
</div>
</div>
<!-- Custom Fields -->
<CustomFieldsSection :assetid="tool.assetid" />
<!-- Warranty -->
<PluginAssetPanels :assetid="tool.assetid" />
<!-- All relationships (partof, connectedto, ...) -->
<AssetRelationships v-if="tool.assetid" :assetid="tool.assetid" />
<!-- Notes -->
<div class="section-card" v-if="tool.notes">
<h3 class="section-title">Notes</h3>
<p class="notes-text">{{ tool.notes }}</p>
</div>
</div>
</div>
<!-- Audit Footer -->
<div class="audit-footer">
<span>Created {{ formatDateTime(tool.createddate) }}<template v-if="tool.createdby"> by {{ tool.createdby }}</template></span>
<span>Modified {{ formatDateTime(tool.modifieddate) }}<template v-if="tool.modifiedby"> by {{ tool.modifiedby }}</template></span>
</div>
</template>
<div v-else class="card">
<p style="text-align: center; color: var(--text-light);">Measuring tool not found</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { colorStyle } from '@/utils/colorStyle'
import { measuringtoolsApi } from '@/api'
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
import AssetRelationships from '@/components/AssetRelationships.vue'
import { useIdentifierFlags } from '@/composables/identifierSettings'
const route = useRoute()
const { isEnabled } = useIdentifierFlags()
const loading = ref(true)
const tool = ref(null)
const STATUS_LABELS = { overdue: 'Overdue', duesoon: 'Due Soon', current: 'Current', unknown: 'Unknown' }
function statusLabel(status) { return STATUS_LABELS[status] || 'Unknown' }
function formatDate(d) { if (!d) return '-'; return new Date(d + 'T00:00:00').toLocaleDateString() }
function formatDateTime(d) { if (!d) return '-'; return new Date(d).toLocaleString() }
onMounted(async () => {
try {
// by-asset route resolves the tool from a core asset id (search/cross-link
// rows carry assetid, not the extension id); detail route keys on the id.
const response = route.params.assetid
? await measuringtoolsApi.getByAsset(route.params.assetid)
: await measuringtoolsApi.get(route.params.id)
tool.value = response.data.data
} catch (err) {
console.error('Error loading measuring tool:', err)
} finally {
loading.value = false
}
})
</script>
<style scoped>
.mono {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
}
</style>