Consolidate asset relationships onto the shared card
The curated Controlled Machines (PC detail) and Connected PC (machine detail) cards duplicated what the generic relationships card already shows with type badges and inline add/remove - and hid every non-controls relationship type. Removed the curated cards and their plumbing (~105 lines of orphaned CSS included); the MachineForm controlling-PC picker stays as the write path. Also theme-variable fixes on the shared card styling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1137,26 +1137,7 @@ td.actions {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connected Device */
|
/* Device icon (network detail) */
|
||||||
.connected-device {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-link {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-link:hover .device-name {
|
|
||||||
color: var(--info);
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-icon {
|
.device-icon {
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
@@ -1173,76 +1154,6 @@ td.actions {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-alias {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.connection-type {
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
font-size: 11px;
|
|
||||||
background: rgba(4, 185, 98, 0.2);
|
|
||||||
color: var(--success);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Equipment List */
|
|
||||||
.equipment-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0.75rem;
|
|
||||||
background: var(--bg);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
transition: border-color 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-item:hover {
|
|
||||||
border-color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-name {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-alias {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.connection-tag {
|
|
||||||
padding: 0.2rem 0.5rem;
|
|
||||||
font-size: 11px;
|
|
||||||
background: rgba(4, 185, 98, 0.2);
|
|
||||||
color: var(--success);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Network */
|
/* Network */
|
||||||
.network-list {
|
.network-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -183,30 +183,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Connected PC -->
|
|
||||||
<div class="section-card">
|
|
||||||
<h3 class="section-title">Connected PC</h3>
|
|
||||||
<div v-if="!controllingPc" class="empty-message">
|
|
||||||
No controlling PC assigned
|
|
||||||
</div>
|
|
||||||
<div v-else class="connected-device">
|
|
||||||
<router-link :to="`/pcs/${controllingPc.pluginid || controllingPc.assetid}`" class="device-link">
|
|
||||||
<div class="device-icon">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
|
||||||
<line x1="8" y1="21" x2="16" y2="21"></line>
|
|
||||||
<line x1="12" y1="17" x2="12" y2="21"></line>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="device-info">
|
|
||||||
<span class="device-name">{{ controllingPc.assetnumber }}</span>
|
|
||||||
<span class="device-alias" v-if="controllingPc.name">{{ controllingPc.name }}</span>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
<span class="connection-type">{{ controllingPc.relationshipType }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Custom Fields -->
|
<!-- Custom Fields -->
|
||||||
<CustomFieldsSection :assetid="machine.assetid" />
|
<CustomFieldsSection :assetid="machine.assetid" />
|
||||||
|
|
||||||
@@ -238,9 +214,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { machinesApi, assetsApi } from '../../api'
|
import { machinesApi } from '../../api'
|
||||||
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
||||||
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
||||||
import WarrantyPanel from '../../components/WarrantyPanel.vue'
|
import WarrantyPanel from '../../components/WarrantyPanel.vue'
|
||||||
@@ -254,54 +230,13 @@ const { isEnabled } = useIdentifierFlags()
|
|||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const machine = ref(null)
|
const machine = ref(null)
|
||||||
const { warranties, heroWarranty, warrantyDate } = useWarrantyBadge(() => machine.value?.assetid)
|
const { warranties, heroWarranty, warrantyDate } = useWarrantyBadge(() => machine.value?.assetid)
|
||||||
const relationships = ref({ incoming: [], outgoing: [] })
|
|
||||||
|
|
||||||
// type name is data, sites may seed 'controls' or 'Controls' - compare folded
|
// relationships render via the shared AssetRelationships card
|
||||||
function isControls(rel) {
|
|
||||||
return (rel.relationshiptypename || '').toLowerCase() === 'controls'
|
|
||||||
}
|
|
||||||
|
|
||||||
const controllingPc = computed(() => {
|
|
||||||
// For a machine, find a related computer in any "Controls" relationship
|
|
||||||
// Check both incoming (computer controls this) and outgoing (legacy data may have machine -> computer)
|
|
||||||
|
|
||||||
// First check incoming - computer as source controlling this machine
|
|
||||||
for (const rel of relationships.value.incoming || []) {
|
|
||||||
if (rel.sourceasset?.assettypename === 'computer' && isControls(rel)) {
|
|
||||||
return {
|
|
||||||
...rel.sourceasset,
|
|
||||||
relationshipType: rel.relationshiptypename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also check outgoing - legacy data may have machine -> computer Controls relationships
|
|
||||||
for (const rel of relationships.value.outgoing || []) {
|
|
||||||
if (rel.targetasset?.assettypename === 'computer' && isControls(rel)) {
|
|
||||||
return {
|
|
||||||
...rel.targetasset,
|
|
||||||
relationshipType: rel.relationshiptypename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await machinesApi.get(route.params.id)
|
const response = await machinesApi.get(route.params.id)
|
||||||
machine.value = response.data.data
|
machine.value = response.data.data
|
||||||
|
|
||||||
// Load relationships using asset ID
|
|
||||||
if (machine.value?.assetid) {
|
|
||||||
try {
|
|
||||||
const relResponse = await assetsApi.getRelationships(machine.value.assetid)
|
|
||||||
relationships.value = relResponse.data.data || { incoming: [], outgoing: [] }
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Relationships not available')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading machine:', error)
|
console.error('Error loading machine:', error)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -180,28 +180,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Controlled Machines -->
|
|
||||||
<div class="section-card" v-if="controlledEquipment.length > 0">
|
|
||||||
<h3 class="section-title">Controlled Machines</h3>
|
|
||||||
<div class="equipment-list">
|
|
||||||
<router-link
|
|
||||||
v-for="item in controlledEquipment"
|
|
||||||
:key="item.relationshipid"
|
|
||||||
:to="`/machines/${item.pluginid || item.assetid}`"
|
|
||||||
class="equipment-item"
|
|
||||||
>
|
|
||||||
<div class="equipment-info">
|
|
||||||
<span class="equipment-name">{{ item.assetnumber }}</span>
|
|
||||||
<span class="equipment-alias" v-if="item.name">{{ item.name }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="equipment-meta">
|
|
||||||
<span class="category-tag">{{ item.assettypename }}</span>
|
|
||||||
<span class="connection-tag">{{ item.relationshipType }}</span>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Installed Applications -->
|
<!-- Installed Applications -->
|
||||||
<div class="section-card" v-if="installedApps.length > 0">
|
<div class="section-card" v-if="installedApps.length > 0">
|
||||||
<h3 class="section-title">Installed Applications</h3>
|
<h3 class="section-title">Installed Applications</h3>
|
||||||
@@ -254,10 +232,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { colorStyle } from "@/utils/colorStyle"
|
import { colorStyle } from "@/utils/colorStyle"
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { computersApi, applicationsApi, assetsApi } from '../../api'
|
import { computersApi, applicationsApi } from '../../api'
|
||||||
import { useWarrantyBadge } from '../../composables/warrantyBadge'
|
import { useWarrantyBadge } from '../../composables/warrantyBadge'
|
||||||
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
||||||
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
||||||
@@ -270,61 +248,18 @@ const { isEnabled } = useIdentifierFlags()
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const computer = ref(null)
|
const computer = ref(null)
|
||||||
const relationships = ref({ incoming: [], outgoing: [] })
|
|
||||||
const installedApps = ref([])
|
const installedApps = ref([])
|
||||||
|
|
||||||
// Warranty fetch + hero badge (shared across asset detail pages).
|
// Warranty fetch + hero badge (shared across asset detail pages).
|
||||||
const { warranties, heroWarranty, warrantyDate: formatWarrantyDate } = useWarrantyBadge(() => computer.value?.assetid)
|
const { warranties, heroWarranty, warrantyDate: formatWarrantyDate } = useWarrantyBadge(() => computer.value?.assetid)
|
||||||
|
|
||||||
// type name is data, sites may seed 'controls' or 'Controls' - compare folded
|
// relationships render via the shared AssetRelationships card
|
||||||
function isControls(rel) {
|
|
||||||
return (rel.relationshiptypename || '').toLowerCase() === 'controls'
|
|
||||||
}
|
|
||||||
|
|
||||||
const controlledEquipment = computed(() => {
|
|
||||||
// For computers, find related equipment in any "Controls" relationship
|
|
||||||
const items = []
|
|
||||||
|
|
||||||
// Check outgoing - computer controls equipment
|
|
||||||
for (const rel of relationships.value.outgoing || []) {
|
|
||||||
if (rel.targetasset?.assettypename === 'machine' && isControls(rel)) {
|
|
||||||
items.push({
|
|
||||||
...rel.targetasset,
|
|
||||||
relationshipid: rel.relationshipid,
|
|
||||||
relationshipType: rel.relationshiptypename
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also check incoming - legacy data may have equipment -> computer Controls relationships
|
|
||||||
for (const rel of relationships.value.incoming || []) {
|
|
||||||
if (rel.sourceasset?.assettypename === 'machine' && isControls(rel)) {
|
|
||||||
items.push({
|
|
||||||
...rel.sourceasset,
|
|
||||||
relationshipid: rel.relationshipid,
|
|
||||||
relationshipType: rel.relationshiptypename
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await computersApi.get(route.params.id)
|
const response = await computersApi.get(route.params.id)
|
||||||
computer.value = response.data.data
|
computer.value = response.data.data
|
||||||
|
|
||||||
// Load relationships using asset ID
|
|
||||||
if (computer.value?.assetid) {
|
|
||||||
try {
|
|
||||||
const relResponse = await assetsApi.getRelationships(computer.value.assetid)
|
|
||||||
relationships.value = relResponse.data.data || { incoming: [], outgoing: [] }
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Relationships not available')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load installed applications
|
// Load installed applications
|
||||||
try {
|
try {
|
||||||
const appsResponse = await applicationsApi.getMachineApps(route.params.id)
|
const appsResponse = await applicationsApi.getMachineApps(route.params.id)
|
||||||
@@ -434,21 +369,6 @@ function formatDate(dateStr) {
|
|||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Equipment meta */
|
|
||||||
.equipment-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-tag {
|
|
||||||
padding: 0.3rem 0.625rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
background: var(--primary);
|
|
||||||
color: white;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Network details */
|
/* Network details */
|
||||||
.network-details {
|
.network-details {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user