Skip to content

Commit ea148cf

Browse files
Support remote servicecontrol instances. Now will cache remotes. Add ability to hide individual cards, or entire platform capability section.
1 parent 90f810d commit ea148cf

File tree

9 files changed

+435
-95
lines changed

9 files changed

+435
-95
lines changed

src/Frontend/src/components/platformcapabilities/CapabilityCard.vue

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<script setup lang="ts">
22
import { ref, computed } from "vue";
33
import FAIcon from "@/components/FAIcon.vue";
4-
import { IconDefinition, faCircle } from "@fortawesome/free-solid-svg-icons";
4+
import { IconDefinition, faCircle, faTimes } from "@fortawesome/free-solid-svg-icons";
55
import { StatusIndicator, WizardPage } from "@/components/platformcapabilities/types";
66
import { Capability, CapabilityStatus } from "@/components/platformcapabilities/constants";
77
import WizardDialog from "./WizardDialog.vue";
88
9+
const emit = defineEmits<{
10+
hide: [];
11+
}>();
12+
913
const props = defineProps<{
1014
status: CapabilityStatus;
1115
icon: IconDefinition;
@@ -50,6 +54,9 @@ function handleButtonClick() {
5054
<div class="loading-spinner"></div>
5155
<div class="loading-text">Loading {{ props.title }} capability status...</div>
5256
</div>
57+
<button class="hide-card-btn" @click="emit('hide')" v-tippy="'Hide this card'">
58+
<FAIcon :icon="faTimes" />
59+
</button>
5360
<div v-if="!props.isLoading" class="capability-header">
5461
<FAIcon
5562
:icon="props.icon"
@@ -119,6 +126,31 @@ function handleButtonClick() {
119126
min-height: 150px;
120127
}
121128
129+
.hide-card-btn {
130+
position: absolute;
131+
top: 8px;
132+
right: 8px;
133+
background: none;
134+
border: none;
135+
color: var(--text-secondary, #999);
136+
cursor: pointer;
137+
padding: 4px 6px;
138+
border-radius: 4px;
139+
opacity: 0;
140+
transition: all 0.2s ease;
141+
font-size: 12px;
142+
z-index: 5;
143+
}
144+
145+
.capability-card:hover .hide-card-btn {
146+
opacity: 1;
147+
}
148+
149+
.hide-card-btn:hover {
150+
background-color: var(--hover-bg, #f0f0f0);
151+
color: var(--text-primary, #333);
152+
}
153+
122154
.capability-available {
123155
border-left: 4px solid var(--success-color, #28a745);
124156
}

src/Frontend/src/components/platformcapabilities/PlatformCapabilitiesDashboardItem.vue

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,50 @@
11
<script setup lang="ts">
22
import { computed } from "vue";
3+
import { storeToRefs } from "pinia";
34
import CapabilityCard from "@/components/platformcapabilities/CapabilityCard.vue";
45
import { useAuditingCapability } from "@/components/platformcapabilities/capabilities/AuditingCapability";
56
import { useMonitoringCapability } from "@/components/platformcapabilities/capabilities/MonitoringCapability";
67
import { useErrorCapability } from "@/components/platformcapabilities/capabilities/ErrorCapability";
78
import { Capability } from "@/components/platformcapabilities/constants";
89
import { getAuditingWizardPages } from "@/components/platformcapabilities/wizards/AuditingWizardPages";
910
import { getMonitoringWizardPages } from "@/components/platformcapabilities/wizards/MonitoringWizardPages";
10-
import { getErrorWizardPages } from "@/components/platformcapabilities/wizards/ErrorWizardPages";
11+
import { usePlatformCapabilitiesStore } from "@/stores/PlatformCapabilitiesStore";
12+
import FAIcon from "@/components/FAIcon.vue";
13+
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
14+
15+
const platformCapabilitiesStore = usePlatformCapabilitiesStore();
16+
const { visibility } = storeToRefs(platformCapabilitiesStore);
1117
1218
const auditing = useAuditingCapability();
1319
const monitoring = useMonitoringCapability();
1420
const error = useErrorCapability();
1521
1622
const auditingWizardPages = computed(() => getAuditingWizardPages(auditing.status.value));
1723
const monitoringWizardPages = computed(() => getMonitoringWizardPages(monitoring.status.value));
18-
const errorWizardPages = computed(() => getErrorWizardPages(error.status.value));
24+
25+
// Check if any cards are hidden (to show restore option)
26+
const hasHiddenCards = computed(() => !visibility.value.showAuditingCard || !visibility.value.showMonitoringCard || !visibility.value.showErrorCard);
1927
</script>
2028

2129
<template>
22-
<div class="platform-capabilities">
30+
<div v-if="visibility.showSection" class="platform-capabilities">
2331
<div class="capabilities-header">
24-
<h6>Platform Capabilities</h6>
25-
<p class="capabilities-description">Discover and configure the capabilities of the Particular Service Platform</p>
32+
<div class="capabilities-title-row">
33+
<div>
34+
<h6>Platform Capabilities</h6>
35+
<p class="capabilities-description">Discover and configure the capabilities of the Particular Service Platform</p>
36+
</div>
37+
<div class="capabilities-actions">
38+
<button v-if="hasHiddenCards" class="btn-link restore-btn" @click="platformCapabilitiesStore.showAll()" v-tippy="'Show all hidden cards'">Show All</button>
39+
<button class="btn-icon hide-section-btn" @click="platformCapabilitiesStore.toggleSection()" v-tippy="'Hide this section'">
40+
<FAIcon :icon="faChevronUp" />
41+
</button>
42+
</div>
43+
</div>
2644
</div>
2745
<div class="capabilities-list">
2846
<CapabilityCard
47+
v-if="visibility.showAuditingCard"
2948
:title="Capability.Auditing"
3049
subtitle="Track and search all successful messages flowing through your system"
3150
:status="auditing.status.value"
@@ -36,8 +55,10 @@ const errorWizardPages = computed(() => getErrorWizardPages(error.status.value))
3655
:help-button-text="auditing.helpButtonText.value"
3756
:help-button-url="auditing.helpButtonUrl.value"
3857
:wizard-pages="auditingWizardPages"
58+
@hide="platformCapabilitiesStore.toggleAuditingCard()"
3959
></CapabilityCard>
4060
<CapabilityCard
61+
v-if="visibility.showMonitoringCard"
4162
:title="Capability.Monitoring"
4263
subtitle="Monitor endpoint performance and throughput"
4364
:status="monitoring.status.value"
@@ -48,8 +69,10 @@ const errorWizardPages = computed(() => getErrorWizardPages(error.status.value))
4869
:help-button-text="monitoring.helpButtonText.value"
4970
:help-button-url="monitoring.helpButtonUrl.value"
5071
:wizard-pages="monitoringWizardPages"
72+
@hide="platformCapabilitiesStore.toggleMonitoringCard()"
5173
></CapabilityCard>
5274
<CapabilityCard
75+
v-if="visibility.showErrorCard"
5376
:title="Capability.Error"
5477
subtitle="Manage and retry failed messages"
5578
:status="error.status.value"
@@ -59,26 +82,98 @@ const errorWizardPages = computed(() => getErrorWizardPages(error.status.value))
5982
:isLoading="error.isLoading.value"
6083
:help-button-text="error.helpButtonText.value"
6184
:help-button-url="error.helpButtonUrl.value"
62-
:wizard-pages="errorWizardPages"
85+
@hide="platformCapabilitiesStore.toggleErrorCard()"
6386
></CapabilityCard>
6487
</div>
6588
</div>
89+
<div v-else class="platform-capabilities-collapsed">
90+
<button class="btn-link expand-btn" @click="platformCapabilitiesStore.toggleSection()">
91+
<FAIcon :icon="faChevronDown" />
92+
<span>Show Platform Capabilities</span>
93+
</button>
94+
</div>
6695
</template>
6796

6897
<style scoped>
6998
.capabilities-header {
7099
margin-bottom: 10px;
71100
}
101+
102+
.capabilities-title-row {
103+
display: flex;
104+
justify-content: space-between;
105+
align-items: flex-start;
106+
}
107+
72108
.capabilities-description {
73109
font-size: 14px;
74110
color: var(--text-secondary, #666);
75111
margin: 0;
76112
}
113+
114+
.capabilities-actions {
115+
display: flex;
116+
align-items: center;
117+
gap: 8px;
118+
}
119+
120+
.btn-link {
121+
background: none;
122+
border: none;
123+
color: var(--primary-color, #007bff);
124+
cursor: pointer;
125+
font-size: 14px;
126+
padding: 4px 8px;
127+
}
128+
129+
.btn-link:hover {
130+
text-decoration: underline;
131+
}
132+
133+
.btn-icon {
134+
background: none;
135+
border: none;
136+
color: var(--text-secondary, #666);
137+
cursor: pointer;
138+
padding: 4px 8px;
139+
border-radius: 4px;
140+
transition: all 0.2s ease;
141+
}
142+
143+
.btn-icon:hover {
144+
background-color: var(--hover-bg, #f0f0f0);
145+
color: var(--text-primary, #333);
146+
}
147+
77148
.capabilities-list {
78149
display: flex;
79150
gap: 16px;
80151
}
152+
81153
.capabilities-list > * {
82154
flex: 1;
83155
}
156+
157+
.platform-capabilities-collapsed {
158+
padding: 8px 0;
159+
}
160+
161+
.expand-btn {
162+
display: flex;
163+
align-items: center;
164+
gap: 8px;
165+
background: none;
166+
border: none;
167+
color: var(--text-secondary, #666);
168+
cursor: pointer;
169+
font-size: 14px;
170+
padding: 8px 12px;
171+
border-radius: 4px;
172+
transition: all 0.2s ease;
173+
}
174+
175+
.expand-btn:hover {
176+
background-color: var(--hover-bg, #f0f0f0);
177+
color: var(--text-primary, #333);
178+
}
84179
</style>

src/Frontend/src/components/platformcapabilities/capabilities/AuditingCapability.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,24 @@ import { storeToRefs } from "pinia";
66
import { type CapabilityComposable, type CapabilityStatusToStringMap, useCapabilityBase } from "./BaseCapability";
77
import useRemoteInstancesAutoRefresh from "@/composables/useRemoteInstancesAutoRefresh";
88
import useAuditStoreAutoRefresh from "@/composables/useAuditStoreAutoRefresh";
9-
import { RemoteInstanceStatus, type RemoteInstance } from "@/resources/RemoteInstance";
9+
import { RemoteInstanceStatus, RemoteInstanceType, type RemoteInstance } from "@/resources/RemoteInstance";
10+
11+
/**
12+
* Checks if a remote instance is an audit instance using the cached instance type
13+
*/
14+
function isAuditInstance(instance: RemoteInstance): boolean {
15+
return instance.cachedInstanceType === RemoteInstanceType.Audit;
16+
}
17+
18+
/**
19+
* Filters remote instances to only include audit instances
20+
*/
21+
function filterAuditInstances(instances: RemoteInstance[] | null | undefined): RemoteInstance[] {
22+
if (!instances) {
23+
return [];
24+
}
25+
return instances.filter(isAuditInstance);
26+
}
1027

1128
const AuditingDescriptions: CapabilityStatusToStringMap = {
1229
[CapabilityStatus.EndpointsNotConfigured]:
@@ -87,6 +104,9 @@ export function useAuditingCapability(): CapabilityComposable {
87104
const { store: remoteInstancesStore } = useRemoteInstancesAutoRefresh();
88105
const { remoteInstances } = storeToRefs(remoteInstancesStore);
89106

107+
// Filter to only include audit instances (those with audit_retention_period in configuration)
108+
const auditInstances = computed(() => filterAuditInstances(remoteInstances.value));
109+
90110
// This gives us the hasSuccessfulMessages flag which indicates if any successful messages exist.
91111
// Uses auto-refresh (minimal) to periodically check for at least 1 successful message (every 5 seconds)
92112
const { store: auditStore } = useAuditStoreAutoRefresh();
@@ -98,17 +118,17 @@ export function useAuditingCapability(): CapabilityComposable {
98118
// Determine overall auditing status
99119
const auditStatus = computed(() => {
100120
// 1. Check if there are any audit instances configured.
101-
if (!remoteInstances.value || remoteInstances.value.length === 0) {
121+
if (auditInstances.value.length === 0) {
102122
return CapabilityStatus.InstanceNotConfigured;
103123
}
104124

105125
// 2. Check if all audit instances are unavailable
106-
if (allAuditInstancesUnavailable(remoteInstances.value)) {
126+
if (allAuditInstancesUnavailable(auditInstances.value)) {
107127
return CapabilityStatus.Unavailable;
108128
}
109129

110130
// 3. Check if some but not all audit instances are unavailable
111-
if (hasPartiallyUnavailableAuditInstances(remoteInstances.value)) {
131+
if (hasPartiallyUnavailableAuditInstances(auditInstances.value)) {
112132
return CapabilityStatus.PartiallyUnavailable;
113133
}
114134

@@ -138,18 +158,18 @@ export function useAuditingCapability(): CapabilityComposable {
138158
const indicators: StatusIndicator[] = [];
139159

140160
// Add an indicator for each remote audit instance
141-
if (remoteInstances.value && remoteInstances.value.length > 0) {
142-
remoteInstances.value.forEach((instance, index) => {
161+
if (auditInstances.value.length > 0) {
162+
auditInstances.value.forEach((instance, index) => {
143163
const isAvailable = instance.status === RemoteInstanceStatus.Online;
144-
const label = remoteInstances.value!.length > 1 ? `Instance ${index + 1}` : "Instance";
164+
const label = auditInstances.value.length > 1 ? `Instance ${index + 1}` : "Instance";
145165
const tooltip = isAvailable ? AuditingIndicatorTooltip.InstanceAvailable : AuditingIndicatorTooltip.InstanceUnavailable;
146166

147167
indicators.push(createIndicator(label, isAvailable ? CapabilityStatus.Available : CapabilityStatus.Unavailable, tooltip, instance.api_uri, instance.version));
148168
});
149169
}
150170

151171
// Messages available indicator - show if at least one instance is available
152-
if (hasAvailableAuditInstances(remoteInstances.value)) {
172+
if (hasAvailableAuditInstances(auditInstances.value)) {
153173
const messagesAvailable = isAllMessagesSupported.value && hasSuccessfulMessages.value;
154174

155175
let messageTooltip = "";

0 commit comments

Comments
 (0)