diff --git a/frontend/src/components/ShopFloorMap.vue b/frontend/src/components/ShopFloorMap.vue index 675ce4b..5ab6411 100644 --- a/frontend/src/components/ShopFloorMap.vue +++ b/frontend/src/components/ShopFloorMap.vue @@ -97,6 +97,7 @@ 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 { getSubtypeId } from '../utils/mapColors' import api from '../api' const props = defineProps({ @@ -239,19 +240,6 @@ const visibleAssetTypes = computed(() => { return result }) -// Get subtype ID from asset based on asset type -function getSubtypeId(asset) { - if (!asset.typedata) return null - // Normalize network_device -> network device so the subtype id resolves. - const typeLower = (asset.assettype || '').toLowerCase().replace(/_/g, ' ') - if (typeLower === 'machine') return asset.typedata.machinetypeid - if (typeLower === 'computer') return asset.typedata.computertypeid - if (typeLower === 'network device') return asset.typedata.networkdevicetypeid - if (typeLower === 'printer') return asset.typedata.printertypeid - if (typeLower === 'measuring tool') return asset.typedata.measuringtooltypeid - return null -} - // Get visible subtypes when a type is selected const visibleSubtypes = computed(() => { if (!props.selectedAssetType) return {} diff --git a/frontend/src/utils/mapColors.spec.js b/frontend/src/utils/mapColors.spec.js new file mode 100644 index 0000000..36b9233 --- /dev/null +++ b/frontend/src/utils/mapColors.spec.js @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest' +import { getSubtypeId, resolveMarkerColor } from './mapColors' + +// One case per asset type that carries a subtype. A missing branch here means +// the map subtype filter silently drops every asset of that type. +const cases = [ + ['Machine', { machinetypeid: 3 }], + ['Computer', { computertypeid: 3 }], + ['Network Device', { networkdevicetypeid: 3 }], + ['network_device', { networkdevicetypeid: 3 }], + ['Printer', { printertypeid: 3 }], + ['Measuring Tool', { measuringtooltypeid: 3 }], +] + +describe('getSubtypeId', () => { + it.each(cases)('resolves the subtype id for %s', (assettype, typedata) => { + expect(getSubtypeId({ assettype, typedata })).toBe(3) + }) + + it('returns null without typedata', () => { + expect(getSubtypeId({ assettype: 'Machine' })).toBeNull() + }) + + it('returns null for an asset type with no subtype table', () => { + expect(getSubtypeId({ assettype: 'Furniture', typedata: {} })).toBeNull() + }) +}) + +describe('resolveMarkerColor', () => { + it('colors a measuring tool by its subtype when a type is selected', () => { + const asset = { assettype: 'Measuring Tool', typedata: { measuringtooltypeid: 7 } } + const color = resolveMarkerColor(asset, { + selectedType: 'Measuring Tool', + subtypeColors: { 7: '#00BCD4' }, + }) + expect(color).toBe('#00BCD4') + }) +}) diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index 88e2e5f..913be7f 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -85,6 +85,7 @@ import { useAuthStore } from '../stores/auth' import { loadMapConfig, state as mapConfig } from '../composables/mapConfig' import { exportMapPdf } from '../utils/mapPdf' import { assetTypeLabel, assetDetailRoute } from '../utils/assetTypes' +import { getSubtypeId } from '../utils/mapColors' import { useToast } from '../composables/toast' const toast = useToast() @@ -133,7 +134,8 @@ const subtypeLabel = computed(() => { 'machine': 'All Machine Types', 'computer': 'All Computer Types', 'network device': 'All Device Types', - 'printer': 'All Printer Types' + 'printer': 'All Printer Types', + 'measuring tool': 'All Tool Types' } return labels[selectedType.value.toLowerCase().replace(/_/g, ' ')] || 'All Subtypes' }) @@ -175,24 +177,12 @@ const filteredAssets = computed(() => { result = result.filter(a => a.assettype && a.assettype.toLowerCase() === selectedLower) } - // Filter by subtype (normalize network_device -> network device) + // Filter by subtype. Uses the shared getSubtypeId helper so this stays in + // step with the marker coloring - a local copy of the per-type id lookup went + // stale and silently dropped every measuring tool. if (selectedSubtype.value) { const subtypeId = parseInt(selectedSubtype.value) - const typeLower = (selectedType.value || '').toLowerCase().replace(/_/g, ' ') - result = result.filter(a => { - if (!a.typedata) return false - // Check different ID fields based on asset type - if (typeLower === 'machine') { - return a.typedata.machinetypeid === subtypeId - } else if (typeLower === 'computer') { - return a.typedata.computertypeid === subtypeId - } else if (typeLower === 'network device') { - return a.typedata.networkdevicetypeid === subtypeId - } else if (typeLower === 'printer') { - return a.typedata.printertypeid === subtypeId - } - return false - }) + result = result.filter(a => getSubtypeId(a) === subtypeId) } // Filter by business unit