Skip to content

Commit 14f115c

Browse files
warwickschroederjasontaylordev
authored andcommitted
Update capability cards WIP
1 parent b6fabc5 commit 14f115c

File tree

11 files changed

+215
-109
lines changed

11 files changed

+215
-109
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const props = defineProps<{
88
icon: IconDefinition;
99
title: Capability;
1010
subtitle: string;
11-
helpUrl: string;
12-
dataUrl: string;
11+
helpButtonText: string;
12+
helpButtonUrl: string;
1313
description: string;
1414
indicators?: StatusIndicator[];
1515
isLoading?: boolean;
@@ -25,7 +25,7 @@ const props = defineProps<{
2525
'capability-unavailable': !props.isLoading && props.status === CapabilityStatus.Unavailable,
2626
'capability-partially-unavailable': !props.isLoading && props.status === CapabilityStatus.PartiallyUnavailable,
2727
'capability-loading': props.isLoading,
28-
'capability-notconfigured': !props.isLoading && props.status === CapabilityStatus.NotConfigured,
28+
'capability-notconfigured': !props.isLoading && (props.status === CapabilityStatus.EndpointsNotConfigured || props.status === CapabilityStatus.InstanceNotConfigured),
2929
}"
3030
>
3131
<div v-if="props.isLoading" class="loading-overlay">
@@ -40,7 +40,7 @@ const props = defineProps<{
4040
'text-success': props.status === CapabilityStatus.Available,
4141
'text-danger': props.status === CapabilityStatus.Unavailable,
4242
'text-warning': props.status === CapabilityStatus.PartiallyUnavailable,
43-
'text-info': props.status === CapabilityStatus.NotConfigured,
43+
'text-info': props.status === CapabilityStatus.EndpointsNotConfigured || props.status === CapabilityStatus.InstanceNotConfigured,
4444
}"
4545
/>
4646
<div class="capability-info">
@@ -53,7 +53,7 @@ const props = defineProps<{
5353
class="indicator-light"
5454
:class="{
5555
'light-success': indicator.status === CapabilityStatus.Available,
56-
'light-warning': indicator.status === CapabilityStatus.NotConfigured || indicator.status === CapabilityStatus.PartiallyUnavailable,
56+
'light-warning': indicator.status === CapabilityStatus.EndpointsNotConfigured || indicator.status === CapabilityStatus.PartiallyUnavailable,
5757
'light-danger': indicator.status === CapabilityStatus.Unavailable,
5858
}"
5959
/>
@@ -63,7 +63,7 @@ const props = defineProps<{
6363
</div>
6464
<div class="capability-subtitle">{{ props.subtitle }}</div>
6565
</div>
66-
<div v-if="props.status !== CapabilityStatus.NotConfigured" class="capability-status">
66+
<div v-if="props.status !== CapabilityStatus.EndpointsNotConfigured && props.status !== CapabilityStatus.InstanceNotConfigured" class="capability-status">
6767
<span
6868
class="status-badge"
6969
:class="{
@@ -80,8 +80,8 @@ const props = defineProps<{
8080
<div class="capability-description">
8181
{{ props.description }}
8282
</div>
83-
<a :href="props.status === CapabilityStatus.Available ? props.dataUrl : props.helpUrl" :target="props.status === CapabilityStatus.Available ? '_self' : '_blank'" class="btn-primary learn-more-btn">
84-
{{ props.status !== CapabilityStatus.Available ? "Learn More" : props.title === Capability.Auditing ? "View Messages" : "View Metrics" }}
83+
<a :href="props.helpButtonUrl" :target="props.status === CapabilityStatus.Available ? '_self' : '_blank'" class="btn-primary learn-more-btn">
84+
{{ props.helpButtonText }}
8585
</a>
8686
</div>
8787
</div>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ const monitoring = useMonitoringCapability();
2323
:description="auditing.description.value"
2424
:indicators="auditing.indicators.value"
2525
:isLoading="auditing.isLoading.value"
26-
help-url="https://docs.particular.net/nservicebus/operations/auditing"
27-
data-url="#/messages"
26+
:help-button-text="auditing.helpButtonText.value"
27+
:help-button-url="auditing.helpButtonUrl.value"
2828
></CapabilityCard>
2929
<CapabilityCard
3030
:title="Capability.Monitoring"
@@ -34,8 +34,8 @@ const monitoring = useMonitoringCapability();
3434
:description="monitoring.description.value"
3535
:indicators="monitoring.indicators.value"
3636
:isLoading="monitoring.isLoading.value"
37-
help-url="https://docs.particular.net/monitoring/metrics/install-plugin"
38-
data-url="#/monitoring"
37+
:help-button-text="monitoring.helpButtonText.value"
38+
:help-button-url="monitoring.helpButtonUrl.value"
3939
></CapabilityCard>
4040
</div>
4141
</div>

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

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1-
import { computed, watchEffect } from "vue";
1+
import { computed } from "vue";
22
import { CapabilityStatus, StatusIndicator } from "@/components/platformcapabilities/types";
33
import useIsAllMessagesSupported, { minimumSCVersionForAllMessages } from "@/components/audit/isAllMessagesSupported";
44
import { storeToRefs } from "pinia";
5-
import { useAuditStore } from "@/stores/AuditStore";
6-
import { MessageStatus } from "@/resources/Message";
7-
import { type CapabilityComposable, useCapabilityBase } from "./BaseCapability";
8-
import { useRemoteInstancesStore } from "@/stores/RemoteInstancesStore";
5+
import { type CapabilityComposable, type CapabilityStatusToStringMap, useCapabilityBase } from "./BaseCapability";
6+
import useRemoteInstancesAutoRefresh from "@/composables/useRemoteInstancesAutoRefresh";
7+
import useAuditStoreAutoRefresh from "@/composables/useAuditStoreAutoRefresh";
98
import { RemoteInstanceStatus, type RemoteInstance } from "@/resources/RemoteInstance";
109

11-
enum AuditingCardDescription {
12-
NotConfigured = "Auditing instance is connected but no successful messages have been processed yet or you don't have auditing enabled for any endpoints.",
13-
Unavailable = "All Auditing instances are configured but not responding.",
14-
PartiallyUnavailable = "Some Auditing instances are not responding. Check individual instance status below.",
15-
NotSupported = `Auditing instance is connected but the "All Messages" feature requires ServiceControl ${minimumSCVersionForAllMessages} or higher.`,
16-
Available = "Auditing is available and processing successful messages.",
17-
}
10+
const AuditingDescriptions: CapabilityStatusToStringMap = {
11+
[CapabilityStatus.EndpointsNotConfigured]:
12+
"A ServiceControl Auditing instance is connected but no successful messages have been processed yet or you don't have auditing enabled for any endpoints. Click 'Learn More' to find out how to set up auditing for your endpoints.",
13+
[CapabilityStatus.InstanceNotConfigured]: "A ServiceControl Auditing instance has not been configured. Click 'Get Started' to learn more about setting up auditing.",
14+
[CapabilityStatus.Unavailable]: "All ServiceControl Auditing instances are configured but not responding.",
15+
[CapabilityStatus.PartiallyUnavailable]: "Some ServiceControl Auditing instances are not responding.",
16+
[CapabilityStatus.Available]: "All ServiceControl Auditing instances are available and endpoints have been configured to send audit messages.",
17+
};
18+
19+
const AuditingHelpButtonText: CapabilityStatusToStringMap = {
20+
[CapabilityStatus.EndpointsNotConfigured]: "Learn More",
21+
[CapabilityStatus.InstanceNotConfigured]: "Get Started",
22+
[CapabilityStatus.Available]: "View Messages",
23+
};
24+
25+
const AuditingHelpButtonUrl: CapabilityStatusToStringMap = {
26+
[CapabilityStatus.EndpointsNotConfigured]: "https://docs.particular.net/nservicebus/operations/auditing",
27+
[CapabilityStatus.InstanceNotConfigured]: "https://docs.particular.net/servicecontrol/audit-instances/",
28+
[CapabilityStatus.Available]: "#/messages",
29+
};
1830

1931
enum AuditingIndicatorTooltip {
2032
InstanceAvailable = "Auditing instance is configured and available",
@@ -65,34 +77,26 @@ function hasPartiallyUnavailableAuditInstances(instances: RemoteInstance[] | nul
6577
}
6678

6779
export function useAuditingCapability(): CapabilityComposable {
68-
const { getIconForStatus, createIndicator } = useCapabilityBase();
80+
const { getIconForStatus, getDescriptionForStatus, getHelpButtonTextForStatus, getHelpButtonUrlForStatus, createIndicator } = useCapabilityBase();
6981

7082
// This gives us the list of remote instances configured in ServiceControl.
71-
const remoteInstancesStore = useRemoteInstancesStore();
83+
// Uses auto-refresh to periodically check status (every 5 seconds)
84+
const { store: remoteInstancesStore } = useRemoteInstancesAutoRefresh();
7285
const { remoteInstances } = storeToRefs(remoteInstancesStore);
7386

74-
// This gives us the messages array which includes all messages (successful and failed).
75-
const auditStore = useAuditStore();
76-
const { messages } = storeToRefs(auditStore);
77-
const successfulMessageCount = computed(() => {
78-
return messages.value.filter((msg) => msg.status === MessageStatus.Successful || msg.status === MessageStatus.ResolvedSuccessfully).length;
79-
});
87+
// This gives us the hasSuccessfulMessages flag which indicates if any successful messages exist.
88+
// Uses auto-refresh (minimal) to periodically check for at least 1 successful message (every 5 seconds)
89+
const { store: auditStore } = useAuditStoreAutoRefresh();
90+
const { hasSuccessfulMessages } = storeToRefs(auditStore);
8091

92+
// This tells us if the "All Messages" feature is supported by checking the SC version
8193
const isAllMessagesSupported = useIsAllMessagesSupported();
8294

83-
watchEffect(() => {
84-
// Trigger initial load of audit messages if audit is configured
85-
if (hasAvailableAuditInstances(remoteInstances.value)) {
86-
// TODO: This is not auto refreshed. User will need to manually refresh the page to get updated data. Ideally this would auto refresh periodically.
87-
auditStore.refresh();
88-
}
89-
});
90-
9195
// Determine overall auditing status
9296
const auditStatus = computed(() => {
9397
// 1. Check if there are any audit instances configured.
9498
if (!remoteInstances.value || remoteInstances.value.length === 0) {
95-
return CapabilityStatus.NotConfigured;
99+
return CapabilityStatus.InstanceNotConfigured;
96100
}
97101

98102
// 2. Check if all audit instances are unavailable
@@ -105,9 +109,9 @@ export function useAuditingCapability(): CapabilityComposable {
105109
return CapabilityStatus.PartiallyUnavailable;
106110
}
107111

108-
// 4. Check if all messages feature is supported and there are successful messages
109-
if (!isAllMessagesSupported.value || successfulMessageCount.value === 0) {
110-
return CapabilityStatus.NotConfigured;
112+
// 4. Check if the 'All Messages' feature is not supported OR there are no successful messages
113+
if (!isAllMessagesSupported.value || !hasSuccessfulMessages.value) {
114+
return CapabilityStatus.EndpointsNotConfigured;
111115
}
112116

113117
// 5. Audit instance is available and there are successful audit messages
@@ -118,22 +122,13 @@ export function useAuditingCapability(): CapabilityComposable {
118122
const auditIcon = computed(() => getIconForStatus(auditStatus.value));
119123

120124
// Determine description based on status
121-
const auditDescription = computed(() => {
122-
if (auditStatus.value === CapabilityStatus.NotConfigured) {
123-
return AuditingCardDescription.NotConfigured;
124-
}
125+
const auditDescription = computed(() => getDescriptionForStatus(auditStatus.value, AuditingDescriptions));
125126

126-
if (auditStatus.value === CapabilityStatus.Available) {
127-
return AuditingCardDescription.Available;
128-
}
127+
// Determine help button text based on status
128+
const auditHelpButtonText = computed(() => getHelpButtonTextForStatus(auditStatus.value, AuditingHelpButtonText));
129129

130-
if (auditStatus.value === CapabilityStatus.PartiallyUnavailable) {
131-
return AuditingCardDescription.PartiallyUnavailable;
132-
}
133-
134-
// Unavailable
135-
return AuditingCardDescription.Unavailable;
136-
});
130+
// Determine help button URL based on status
131+
const auditHelpButtonUrl = computed(() => getHelpButtonUrlForStatus(auditStatus.value, AuditingHelpButtonUrl));
137132

138133
// Determine indicators
139134
const auditIndicators = computed(() => {
@@ -152,7 +147,7 @@ export function useAuditingCapability(): CapabilityComposable {
152147

153148
// Messages available indicator - show if at least one instance is available
154149
if (hasAvailableAuditInstances(remoteInstances.value)) {
155-
const messagesAvailable = isAllMessagesSupported.value && successfulMessageCount.value > 0;
150+
const messagesAvailable = isAllMessagesSupported.value && hasSuccessfulMessages.value;
156151

157152
let messageTooltip = "";
158153
if (messagesAvailable) {
@@ -163,7 +158,7 @@ export function useAuditingCapability(): CapabilityComposable {
163158
messageTooltip = AuditingIndicatorTooltip.MessagesUnavailable;
164159
}
165160

166-
indicators.push(createIndicator("Messages", messagesAvailable ? CapabilityStatus.Available : CapabilityStatus.NotConfigured, messageTooltip));
161+
indicators.push(createIndicator("Messages", messagesAvailable ? CapabilityStatus.Available : CapabilityStatus.EndpointsNotConfigured, messageTooltip));
167162
}
168163

169164
return indicators;
@@ -178,5 +173,7 @@ export function useAuditingCapability(): CapabilityComposable {
178173
description: auditDescription,
179174
indicators: auditIndicators,
180175
isLoading,
176+
helpButtonText: auditHelpButtonText,
177+
helpButtonUrl: auditHelpButtonUrl,
181178
};
182179
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ export interface CapabilityComposable {
88
description: ComputedRef<string>;
99
indicators: ComputedRef<StatusIndicator[]>;
1010
isLoading: ComputedRef<boolean>;
11+
helpButtonText: ComputedRef<string>;
12+
helpButtonUrl: ComputedRef<string>;
1113
}
1214

15+
export type CapabilityStatusToStringMap = Partial<Record<CapabilityStatus, string>>;
16+
1317
export function useCapabilityBase() {
1418
const getIconForStatus = (status: CapabilityStatus): IconDefinition => {
1519
switch (status) {
1620
case CapabilityStatus.Available:
1721
return faCheck;
18-
case CapabilityStatus.NotConfigured:
22+
case CapabilityStatus.EndpointsNotConfigured:
23+
case CapabilityStatus.InstanceNotConfigured:
1924
return faInfoCircle;
2025
case CapabilityStatus.Unavailable:
2126
return faTimes;
@@ -24,6 +29,18 @@ export function useCapabilityBase() {
2429
}
2530
};
2631

32+
const getDescriptionForStatus = (status: CapabilityStatus, descriptions: CapabilityStatusToStringMap): string => {
33+
return descriptions[status] ?? "";
34+
};
35+
36+
const getHelpButtonTextForStatus = (status: CapabilityStatus, helpButtonTexts: CapabilityStatusToStringMap): string => {
37+
return helpButtonTexts[status] || "Learn More";
38+
};
39+
40+
const getHelpButtonUrlForStatus = (status: CapabilityStatus, helpButtonUrls: CapabilityStatusToStringMap): string => {
41+
return helpButtonUrls[status] || "#/dashboard";
42+
};
43+
2744
const createIndicator = (label: string, status: CapabilityStatus, tooltip: string, url?: string, version?: string): StatusIndicator => {
2845
let fullTooltip = tooltip;
2946
if (url) {
@@ -41,6 +58,9 @@ export function useCapabilityBase() {
4158

4259
return {
4360
getIconForStatus,
61+
getDescriptionForStatus,
62+
getHelpButtonTextForStatus,
63+
getHelpButtonUrlForStatus,
4464
createIndicator,
4565
};
4666
}

0 commit comments

Comments
 (0)