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>
This commit is contained in:
cproudlock
2026-02-04 07:32:44 -05:00
parent fe7a00b260
commit 83caaa7b7c
89 changed files with 3951 additions and 1138 deletions

View File

@@ -27,14 +27,14 @@
:key="rel.relationshipid"
class="relationship-item"
>
<div class="rel-icon">{{ getAssetIcon(rel.target_asset?.assettype) }}</div>
<div class="rel-icon"><component :is="getAssetIcon(rel.targetasset?.assettype)" :size="16" /></div>
<div class="rel-content">
<router-link :to="getAssetRoute(rel.target_asset)" class="rel-name">
{{ rel.target_asset?.name || rel.target_asset?.assetnumber || 'Unknown' }}
<router-link :to="getAssetRoute(rel.targetasset)" class="rel-name">
{{ rel.targetasset?.name || rel.targetasset?.assetnumber || 'Unknown' }}
</router-link>
<div class="rel-meta">
<span class="badge badge-outline">{{ rel.relationship_type_name }}</span>
<span class="rel-type-badge">{{ rel.target_asset?.assettype }}</span>
<span class="badge badge-outline">{{ rel.relationshiptypename }}</span>
<span class="rel-type-badge">{{ rel.targetasset?.assettype }}</span>
</div>
<div v-if="rel.notes" class="rel-notes">{{ rel.notes }}</div>
</div>
@@ -59,14 +59,14 @@
:key="rel.relationshipid"
class="relationship-item"
>
<div class="rel-icon">{{ getAssetIcon(rel.source_asset?.assettype) }}</div>
<div class="rel-icon"><component :is="getAssetIcon(rel.sourceasset?.assettype)" :size="16" /></div>
<div class="rel-content">
<router-link :to="getAssetRoute(rel.source_asset)" class="rel-name">
{{ rel.source_asset?.name || rel.source_asset?.assetnumber || 'Unknown' }}
<router-link :to="getAssetRoute(rel.sourceasset)" class="rel-name">
{{ rel.sourceasset?.name || rel.sourceasset?.assetnumber || 'Unknown' }}
</router-link>
<div class="rel-meta">
<span class="badge badge-outline">{{ rel.relationship_type_name }}</span>
<span class="rel-type-badge">{{ rel.source_asset?.assettype }}</span>
<span class="badge badge-outline">{{ rel.relationshiptypename }}</span>
<span class="rel-type-badge">{{ rel.sourceasset?.assettype }}</span>
</div>
<div v-if="rel.notes" class="rel-notes">{{ rel.notes }}</div>
</div>
@@ -173,6 +173,7 @@
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { Cog, Monitor, Printer, Globe, Package } from 'lucide-vue-next'
import { assetsApi, relationshipTypesApi } from '../api'
import { useAuthStore } from '../stores/auth'
@@ -296,7 +297,7 @@ function searchAssets() {
searchTimeout = setTimeout(async () => {
try {
const response = await assetsApi.search(assetSearchQuery.value, { per_page: 10 })
const response = await assetsApi.search(assetSearchQuery.value, { perpage: 10 })
// Filter out the current asset
assetSearchResults.value = (response.data.data || []).filter(
a => a.assetid !== resolvedAssetId.value
@@ -329,11 +330,11 @@ async function saveRelationship() {
}
if (newRel.value.direction === 'outgoing') {
data.source_assetid = resolvedAssetId.value
data.target_assetid = newRel.value.targetAssetId
data.sourceassetid = resolvedAssetId.value
data.targetassetid = newRel.value.targetAssetId
} else {
data.source_assetid = newRel.value.targetAssetId
data.target_assetid = resolvedAssetId.value
data.sourceassetid = newRel.value.targetAssetId
data.targetassetid = resolvedAssetId.value
}
await assetsApi.createRelationship(data)
@@ -374,12 +375,12 @@ function closeModal() {
function getAssetIcon(assettype) {
const icons = {
'equipment': '⚙',
'computer': '💻',
'printer': '🖨',
'network_device': '🌐'
'equipment': Cog,
'computer': Monitor,
'printer': Printer,
'network_device': Globe
}
return icons[assettype] || '📦'
return icons[assettype] || Package
}
function getAssetRoute(asset) {