Files
shopdb-flask/frontend/src/views/reports/PCRelationshipsReport.vue
cproudlock 83caaa7b7c Add print badges, pagination, route splitting, JWT auth fixes, and list page alignment
- Fix equipment badge barcode not rendering (loading race condition)
- Fix printer QR code not rendering on initial load (same race condition)
- Add model image to equipment badge via imageurl from Model table
- Fix white-on-white machine number text on badge, tighten barcode spacing
- Add PaginationBar component used across all list pages
- Split monolithic router into per-plugin route modules
- Fix 25 GET API endpoints returning 401 (jwt_required -> optional=True)
- Align list page columns across Equipment, PCs, and Network pages
- Add print views: EquipmentBadge, PrinterQRSingle, PrinterQRBatch, USBLabelBatch
- Add PC Relationships report, migration docs, and CLAUDE.md project guide
- Various plugin model, API, and frontend refinements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 07:32:44 -05:00

145 lines
4.0 KiB
Vue

<template>
<div>
<div class="page-header">
<h2>PC-Machine Relationships</h2>
<div class="header-actions">
<button class="btn btn-primary" @click="copyTable">Copy Table</button>
<button class="btn btn-secondary" @click="copyCSV">Copy CSV</button>
<button class="btn btn-secondary" @click="copyJSON">Copy JSON</button>
<router-link to="/reports" class="btn btn-secondary">Back to Reports</router-link>
</div>
</div>
<p class="text-muted">PCs with relationships to shop floor machines</p>
<div v-if="copied" class="copy-toast">{{ copiedFormat }} copied to clipboard!</div>
<div class="card">
<div v-if="loading" class="loading">Loading...</div>
<template v-else>
<div class="table-container">
<table id="relationshipsTable">
<thead>
<tr>
<th>Machine #</th>
<th>Vendor</th>
<th>Model</th>
<th>PC Hostname</th>
<th>PC IP</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, idx) in data" :key="idx">
<td>{{ row.machine_number }}</td>
<td>{{ row.vendor }}</td>
<td>{{ row.model }}</td>
<td class="mono">{{ row.hostname }}</td>
<td class="mono">{{ row.ip }}</td>
</tr>
<tr v-if="data.length === 0">
<td colspan="5" style="text-align: center; color: var(--text-light);">
No relationships found
</td>
</tr>
</tbody>
</table>
</div>
<p class="record-count">{{ data.length }} records found</p>
</template>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { reportsApi } from '../../api'
const loading = ref(true)
const data = ref([])
const copied = ref(false)
const copiedFormat = ref('')
onMounted(async () => {
try {
const response = await reportsApi.pcRelationships()
data.value = response.data.data?.data || []
} catch (error) {
console.error('Error loading report:', error)
} finally {
loading.value = false
}
})
function showCopied(format) {
copiedFormat.value = format
copied.value = true
setTimeout(() => { copied.value = false }, 2000)
}
function copyTable() {
const headers = ['Machine #', 'Vendor', 'Model', 'PC Hostname', 'PC IP']
let text = headers.join('\t') + '\n'
for (const row of data.value) {
text += [row.machine_number, row.vendor, row.model, row.hostname, row.ip].join('\t') + '\n'
}
navigator.clipboard.writeText(text).then(() => showCopied('Table'))
}
function copyCSV() {
const headers = ['Machine #', 'Vendor', 'Model', 'PC Hostname', 'PC IP']
let csv = headers.map(h => `"${h}"`).join(',') + '\n'
for (const row of data.value) {
csv += [row.machine_number, row.vendor, row.model, row.hostname, row.ip]
.map(v => `"${v}"`)
.join(',') + '\n'
}
navigator.clipboard.writeText(csv).then(() => showCopied('CSV'))
}
function copyJSON() {
const jsonData = data.value.map(row => ({
Name: row.hostname,
IpAddress: row.ip,
Group: null
}))
navigator.clipboard.writeText(JSON.stringify(jsonData, null, 2)).then(() => showCopied('JSON'))
}
</script>
<style scoped>
.text-muted {
color: var(--text-light);
margin-bottom: 1rem;
}
.mono {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
}
.record-count {
color: var(--text-light);
margin-top: 1rem;
font-size: 0.875rem;
}
.copy-toast {
position: fixed;
top: 20px;
right: 20px;
background: #28a745;
color: white;
padding: 0.75rem 1.5rem;
border-radius: 6px;
font-weight: 500;
z-index: 9999;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>