Five of the seven asset types had no way to take a record out of ShopDB from the interface. Network devices and 3D printed parts had a button; machines, PCs, printers and measuring tools did not, so a scrapped tool stayed in every list and report until somebody went to the API. The endpoints were already there and already correct. Every one of these DELETEs sets asset.isactive = False and nothing more, so the record leaves the lists while its calibration history, backups and relationships stay exactly where they were. Called "Retire" rather than "Delete" for that reason: the two existing buttons say Delete, which reads as destruction of a record that is in fact kept and can be restored by setting isactive back. The confirm says so too, because the question somebody needs answered before clicking is whether the history goes with it. The button is guarded on the record having loaded, on the two pages whose action row renders before the fetch resolves - measuring tools showed Retire beside a hidden Edit while the tool was still loading, which would have called delete with an undefined id. printersApi had no delete method at all; added. USB is deliberately left out. It retires through a status column in the external CMMC database rather than isactive, has its own /retire endpoint, and needs its own decision about what retiring means there.
233 lines
10 KiB
Vue
233 lines
10 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>
|
|
<button @click="confirmRetire" class="btn btn-danger" v-if="tool">Retire</button>
|
|
<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, useRouter } 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'
|
|
import { setRecordTitle } from '@/composables/pageTitle'
|
|
import { useToast } from '@/composables/toast'
|
|
import { apiError } from '@/utils/apiError'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const toast = useToast()
|
|
|
|
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
|
|
setRecordTitle(tool.value?.assetnumber || tool.value?.name)
|
|
} catch (err) {
|
|
console.error('Error loading measuring tool:', err)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
|
|
// Retire, not delete: the server deactivates the asset, so the measuring tool
|
|
// leaves every list and report while its history and relationships stay put.
|
|
// Reversible by an admin setting isactive back to true.
|
|
async function confirmRetire() {
|
|
const label = tool.value?.assetnumber || tool.value?.name || 'this measuring tool'
|
|
if (!confirm(`Retire ${label}?\n\nIt will no longer appear in ShopDB. `
|
|
+ `History and relationships are kept, and it can be restored.`)) return
|
|
try {
|
|
await measuringtoolsApi.remove(tool.value?.measuringtool?.measuringtoolid)
|
|
toast.success(`Retired ${label}`)
|
|
router.push('/measuringtools')
|
|
} catch (error) {
|
|
console.error('Error retiring measuring tool:', error)
|
|
toast.error(apiError(error, 'Failed to retire measuring tool'))
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.mono {
|
|
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
|
}
|
|
</style>
|