nav resilience after pool restart, thinner marker rings, drop dead marker popup

This commit is contained in:
2026-07-31 10:17:28 -04:00
parent d84f7e3911
commit ff61a4c5e9
6 changed files with 163 additions and 100 deletions

View File

@@ -96,7 +96,7 @@ import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import { loadMapConfig, blueprintUrlFor, state as mapConfig } from '../composables/mapConfig'
import { assetTypeLabel, assetDetailRoute } from '../utils/assetTypes'
import { assetTypeLabel } from '../utils/assetTypes'
import { getSubtypeId, markerRingColor, UNSPECIFIED_COLOR } from '../utils/mapColors'
import api from '../api'
@@ -356,22 +356,6 @@ function clearPosition() {
emit('positionPicked', null)
}
// Get the detail page route based on machine category
// Extensible for future addon types (network, cameras, etc.)
function getDetailRoute(machine) {
const category = machine.category?.toLowerCase() || ''
const routeMap = {
'machine': '/machines',
'pc': '/pcs',
'printer': '/printers',
// Future addon routes can be added here:
// 'network': '/network',
// 'camera': '/cameras',
}
const basePath = routeMap[category] || '/machines'
return `${basePath}/${machine.machineid}`
}
// Fetch the declared map overlays (ADR-010) and each overlay's per-asset
// decoration data, keyed by assetid for a fast join during rendering.
async function loadOverlays() {
@@ -452,7 +436,7 @@ function renderMarkers() {
const leafletX = item.mapx
// Determine color based on mode
let color, typeName, displayName, detailRoute
let color, typeName, displayName
if (props.assetTypeMode) {
// Unified asset mode - use subtype colors when a type is selected
@@ -470,13 +454,11 @@ function renderMarkers() {
if (item.dualpathpartner) {
displayName = `${item.assetnumber} / ${item.dualpathpartner}`
}
detailRoute = getAssetDetailRoute(item)
} else {
// Legacy machine mode
typeName = item.machinetype || ''
color = getTypeColor(typeName)
displayName = item.alias || item.machinenumber || 'Unknown'
detailRoute = getDetailRoute(item)
}
// Use circleMarker instead of divIcon marker - renders on canvas
@@ -487,7 +469,7 @@ function renderMarkers() {
// Ring keyed on the theme's surface: dark on the white blueprint, light on
// the dark one. A fixed white ring vanished against the light blueprint.
color: markerRingColor(props.theme),
weight: 2,
weight: 1.25,
fillOpacity: 1,
renderer: canvasRenderer
})
@@ -542,40 +524,11 @@ function renderMarkers() {
className: 'marker-tooltip'
})
// Click popup (detailed info)
let popupContent
if (props.assetTypeMode) {
popupContent = `
<div class="marker-popup">
<strong>${displayName}</strong>
<div class="popup-details">
<div><span class="label">Asset #:</span> ${item.assetnumber || '-'}</div>
<div><span class="label">Type:</span> ${item.assettype || '-'}</div>
<div><span class="label">Status:</span> ${item.status || '-'}</div>
<div><span class="label">Location:</span> ${item.location || '-'}</div>
${item.primaryip ? `<div><span class="label">IP:</span> ${item.primaryip}</div>` : ''}
</div>
<a href="${detailRoute}" class="popup-link">View Details</a>
</div>
`
} else {
popupContent = `
<div class="marker-popup">
<strong>${displayName}</strong>
<div class="popup-details">
<div><span class="label">Number:</span> ${item.machinenumber || '-'}</div>
<div><span class="label">Type:</span> ${typeName || '-'}</div>
<div><span class="label">Category:</span> ${item.category || '-'}</div>
<div><span class="label">Status:</span> ${item.status || '-'}</div>
<div><span class="label">Vendor:</span> ${item.vendor || '-'}</div>
<div><span class="label">Model:</span> ${item.model || '-'}</div>
</div>
<a href="${detailRoute}" class="popup-link">View Details</a>
</div>
`
}
marker.bindPopup(popupContent)
// Hover is a glance; the click belongs to the consumer. No popup is bound:
// MapView routes to the detail page on click, so a popup opened and was
// discarded by the navigation in the same tick - it was never visible. In
// the editor and the picker forms it DID render, where a 'View Details'
// link only served to pull the user off an unsaved form.
marker.on('click', () => emit('markerClick', item))
marker.addTo(map)
@@ -596,11 +549,6 @@ function renderMarkers() {
applyFilters()
}
// Get detail route for unified asset format (shared util = single source).
function getAssetDetailRoute(asset) {
return assetDetailRoute(asset)
}
function applyFilters() {
const searchTerm = filters.value.search.toLowerCase()
@@ -748,7 +696,8 @@ onUnmounted(() => {
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid var(--bg-card);
/* Ring color is bound inline (legendDotStyle) to match the markers. */
border: 1.5px solid var(--bg-card);
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}
@@ -795,39 +744,6 @@ onUnmounted(() => {
font-size: 1.125rem;
}
:deep(.marker-popup) {
min-width: 320px;
}
:deep(.marker-popup strong) {
display: block;
margin-bottom: 0.625rem;
font-size: 1.5rem;
}
:deep(.popup-details) {
font-size: 1.125rem;
line-height: 1.9;
}
:deep(.popup-details .label) {
color: #666;
font-weight: 500;
}
:deep(.popup-link) {
display: inline-block;
margin-top: 1rem;
color: #1976d2;
text-decoration: none;
font-size: 1.125rem;
font-weight: 500;
}
:deep(.popup-link:hover) {
text-decoration: underline;
}
:deep(.machine-marker) {
background: transparent !important;
border: none !important;