Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b148921
include audit section displaying audit messages
PhilBastian Mar 6, 2025
105cc68
show status icons
PhilBastian Mar 10, 2025
337fdef
Merge branch 'master' into si_integration
PhilBastian Mar 11, 2025
dd2cd69
add feature flag
PhilBastian Mar 11, 2025
36d7815
format message type as per SI
PhilBastian Mar 11, 2025
b281cf3
change name from audit to all messages and include message view route
PhilBastian Mar 11, 2025
f706a7f
display time_sent and processing_time values
PhilBastian Mar 11, 2025
db87e15
fix the body of the popup message in retry dialog
soujay Mar 13, 2025
ef84c78
Revert "fix the body of the popup message in retry dialog"
soujay Mar 13, 2025
2616724
enable auto refresh to be paused and to be configured after creation
PhilBastian Mar 13, 2025
0065abb
manual/auto refresh control
PhilBastian Mar 13, 2025
6933c0f
Merge branch 'si_integration' of https://github.com/Particular/Servic…
PhilBastian Mar 13, 2025
10ae9af
stripe results grid
PhilBastian Mar 13, 2025
0a510d0
change to use emits
PhilBastian Mar 13, 2025
ba62324
remove commented code
PhilBastian Mar 14, 2025
60e4bfa
add paging
PhilBastian Mar 14, 2025
322f801
implement server side sorting
PhilBastian Mar 14, 2025
9cf0ac4
scroll on list instead of view, allowing the table headers to always …
PhilBastian Mar 14, 2025
338d370
Merge branch 'master' into si_integration
PhilBastian Mar 16, 2025
3b206eb
change to use messages routing
PhilBastian Mar 16, 2025
9e77b04
increase default page size
PhilBastian Mar 16, 2025
be36fd2
add failedmessage redirect and remove unused message components
PhilBastian Mar 17, 2025
2fd84ca
Update src/Frontend/src/stores/AuditStore.ts
PhilBastian Mar 18, 2025
7d3e2a7
Merge branch 'master' into si_integration
PhilBastian Mar 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Frontend/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare global {
service_control_url: string;
monitoring_urls: string[];
showPendingRetry: boolean;
showAllMessages: boolean;
};
}
}
2 changes: 2 additions & 0 deletions src/Frontend/src/components/PageHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import EventsMenuItem from "@/components/events/EventsMenuItem.vue";
import DashboardMenuItem from "@/components/dashboard/DashboardMenuItem.vue";
import FeedbackButton from "@/components/FeedbackButton.vue";
import ThroughputMenuItem from "@/views/throughputreport/ThroughputMenuItem.vue";
import AuditMenuItem from "./audit/AuditMenuItem.vue";

// prettier-ignore
const menuItems = computed(
() => [
DashboardMenuItem,
HeartbeatsMenuItem,
...(useIsMonitoringEnabled() ? [MonitoringMenuItem] : []),
...(window.defaultConfig.showAllMessages ? [AuditMenuItem] : []),
FailedMessagesMenuItem,
CustomChecksMenuItem,
EventsMenuItem,
Expand Down
86 changes: 86 additions & 0 deletions src/Frontend/src/components/RefreshConfig.vue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that it may also make sense to save in local storage the current setting, so it won't reset

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added as idea

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<script setup lang="ts">
import { ref } from "vue";
import OnOffSwitch from "./OnOffSwitch.vue";

const props = defineProps<{
id: string;
initialTimeout?: number;
onManualRefresh: () => void;
}>();

const emit = defineEmits<{ change: [newValue: number | null]; manualRefresh: [] }>();

const autoRefresh = ref(props.initialTimeout != null);
const refreshTimeout = ref(props.initialTimeout ?? 5);

function toggleRefresh() {
autoRefresh.value = !autoRefresh.value;
updateTimeout();
}

function updateTimeout() {
validateTimeout();
emit("change", autoRefresh.value ? refreshTimeout.value * 1000 : null);
}

function validateTimeout() {
refreshTimeout.value = Math.max(1, Math.min(600, refreshTimeout.value));
}
</script>

<template>
<div class="refresh-config">
<button class="fa" title="refresh" @click="() => emit('manualRefresh')">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<button class="fa" title="refresh" @click="() => emit('manualRefresh')">
<button class="btn btn-secondary btn-sm" title="refresh" @click="() => emit('manualRefresh')">

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? I don't want this to look like other buttons in the system

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should this button be different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the layout

<i class="fa fa-lg fa-refresh" />
</button>
<span>|</span>
<label>Auto-Refresh:</label>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have some kind of visual indication that we are fetching new data - https://fontawesome.com/icons/spinner?f=classic&s=solid

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added as idea

<div>
<OnOffSwitch :id="id" @toggle="toggleRefresh" :value="autoRefresh" />
</div>
<input type="number" v-model="refreshTimeout" min="1" max="600" v-on:change="updateTimeout" />
<span class="unit">s</span>
Comment on lines +38 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could a dropdown with pre-set refresh ratings including zero be better option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this works, and looks fine...

</div>
</template>

<style scoped>
.refresh-config {
display: flex;
align-items: center;
gap: 0.5em;
}

.refresh-config .unit {
margin-left: -0.45em;
}

.refresh-config label {
margin: 0;
}

.refresh-config input {
width: 3.5em;
}

.refresh-config button {
background: none;
border: none;
width: 2em;
}

.refresh-config button .fa {
transition: all 0.15s ease-in-out;
transition: rotate 0.05s ease-in-out;
transform-origin: center;
}

.refresh-config button:hover .fa {
color: #00a3c4;
transform: scale(1.1);
}

.refresh-config button:active .fa {
transform: rotate(25deg);
text-shadow: #929e9e 0.25px 0.25px;
}
</style>
224 changes: 224 additions & 0 deletions src/Frontend/src/components/audit/AuditList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
<script setup lang="ts">
import routeLinks from "@/router/routeLinks";
import { ColumnNames, useAuditStore } from "@/stores/AuditStore";
import { storeToRefs } from "pinia";
import { useRoute } from "vue-router";
import SortableColumn from "../SortableColumn.vue";
import { MessageStatus } from "@/resources/Message";
import moment from "moment";
import { useFormatTime } from "@/composables/formatter";
import RefreshConfig from "../RefreshConfig.vue";
import ItemsPerPage from "../ItemsPerPage.vue";
import PaginationStrip from "../PaginationStrip.vue";

const route = useRoute();
const store = useAuditStore();
const { messages, sortByInstances, itemsPerPage, selectedPage, totalCount } = storeToRefs(store);

function statusToName(messageStatus: MessageStatus) {
switch (messageStatus) {
case MessageStatus.Successful:
return "Successful";
case MessageStatus.ResolvedSuccessfully:
return "Successful after retries";
case MessageStatus.Failed:
return "Failed";
case MessageStatus.ArchivedFailure:
return "Failed message deleted";
case MessageStatus.RepeatedFailure:
return "Repeated Failures";
case MessageStatus.RetryIssued:
return "Retry requested";
}
}

function statusToIcon(messageStatus: MessageStatus) {
switch (messageStatus) {
case MessageStatus.Successful:
return "fa successful";
case MessageStatus.ResolvedSuccessfully:
return "fa resolved-successfully";
case MessageStatus.Failed:
return "fa failed";
case MessageStatus.ArchivedFailure:
return "fa archived";
case MessageStatus.RepeatedFailure:
return "fa repeated-failure";
case MessageStatus.RetryIssued:
return "fa retry-issued";
}
}

function friendlyTypeName(messageType: string) {
if (messageType == null) return null;

const typeClass = messageType.split(",")[0];
const typeName = typeClass.split(".").reverse()[0];
return typeName.replace(/\+/g, ".");
}
Comment on lines +52 to +58
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be problematic, it may be worth adding a card to the board.

Copy link
Member

@johnsimons johnsimons Mar 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other places, we show the whole type name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added card


function formatDotNetTimespan(timespan: string) {
//assuming if we have days in the timespan then something is very, very wrong
const [hh, mm, ss] = timespan.split(":");
const time = useFormatTime(((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000);
return `${time.value} ${time.unit}`;
}
</script>

<template>
<section class="section-table" role="table" aria-label="endpoint-instances">
<div class="header">
<RefreshConfig id="auditListRefresh" @change="store.updateRefreshTimer" @manual-refresh="store.refresh" />
<!--Table headings-->
<div role="row" aria-label="column-headers" class="row table-head-row" :style="{ borderTop: 0 }">
<div role="columnheader" :aria-label="ColumnNames.Status" class="status">
<SortableColumn :sort-by="ColumnNames.Status" v-model="sortByInstances" :default-ascending="true">Status</SortableColumn>
</div>
<div role="columnheader" :aria-label="ColumnNames.MessageId" class="col-3">
<SortableColumn :sort-by="ColumnNames.MessageId" v-model="sortByInstances" :default-ascending="true">Message Id</SortableColumn>
</div>
<div role="columnheader" :aria-label="ColumnNames.MessageType" class="col-3">
<SortableColumn :sort-by="ColumnNames.MessageType" v-model="sortByInstances" :default-ascending="true">Type</SortableColumn>
</div>
<div role="columnheader" :aria-label="ColumnNames.TimeSent" class="col-2">
<SortableColumn :sort-by="ColumnNames.TimeSent" v-model="sortByInstances">Time Sent</SortableColumn>
</div>
<div role="columnheader" :aria-label="ColumnNames.ProcessingTime" class="col-2">
<SortableColumn :sort-by="ColumnNames.ProcessingTime" v-model="sortByInstances">Processing Time</SortableColumn>
</div>
</div>
</div>
<!--Table rows-->
<!--NOTE: currently the DataView pages on the client only: we need to make it server data aware (i.e. the total will be the count from the server, not the length of the data we have locally)-->
<div class="messages" role="rowgroup" aria-label="messages">
<div role="row" :aria-label="message.message_id" class="row grid-row" v-for="message in messages" :key="message.id">
<div role="cell" aria-label="status" class="status" :title="statusToName(message.status)">
<div class="status-icon" :class="statusToIcon(message.status)"></div>
</div>
<div role="cell" aria-label="message-id" class="col-3 message-id">
<div class="box-header">
<tippy :aria-label="message.message_id" :delay="[700, 0]" class="no-side-padding lead righ-side-ellipsis endpoint-details-link">
<template #content>
<p :style="{ overflowWrap: 'break-word' }">{{ message.message_id }}</p>
</template>
<RouterLink class="hackToPreventSafariFromShowingTooltip" aria-label="details-link" :to="{ path: routeLinks.messages.message.link(message.id), query: { back: route.path } }">
{{ message.message_id }}
</RouterLink>
</tippy>
</div>
</div>
<div role="cell" aria-label="message-type" class="col-3 message-type">
{{ friendlyTypeName(message.message_type) }}
</div>
<div role="cell" aria-label="time-sent" class="col-2 time-sent">
{{ moment(message.time_sent).local().format("LLLL") }}
</div>
<div role="cell" aria-label="processing-time" class="col-2 processing-time">
{{ formatDotNetTimespan(message.processing_time) }}
</div>
</div>
</div>
<div class="row">
<ItemsPerPage v-model="itemsPerPage" />
<PaginationStrip v-model="selectedPage" :totalCount="totalCount" :itemsPerPage="itemsPerPage" />
</div>
</section>
</template>

<style scoped>
@import "../list.css";

.hackToPreventSafariFromShowingTooltip::after {
content: "";
display: block;
}

.section-table {
overflow: auto;
flex: 1;
display: flex;
flex-direction: column;
}

.messages {
flex: 1;
overflow: auto;
}

.status {
width: 5em;
text-align: center;
}

.status-icon {
color: white;
border-radius: 0.75em;
width: 1.2em;
height: 1.2em;
}

.status-icon::before {
vertical-align: middle;
font-size: 0.85em;
}

.successful {
background: #6cc63f;
}
.successful::before {
content: "\f00c";
}

.resolved-successfully {
background: #3f881b;
}
.resolved-successfully::before {
content: "\f01e";
}

.failed {
background: #c63f3f;
}
.failed::before {
content: "\f00d";
}

.archived {
background: #000000;
}
.archived::before {
content: "\f187";
font-size: 0.85em;
}

.repeated-failure {
background: #c63f3f;
}
.repeated-failure::before {
content: "\f00d\f00d";
font-size: 0.6em;
}

.retry-issued {
background: #cccccc;
color: #000000;
}
.retry-issued::before {
content: "\f01e";
}

.grid-row {
display: flex;
position: relative;
border-top: 1px solid #eee;
border-right: 1px solid #fff;
border-bottom: 1px solid #eee;
border-left: 1px solid #fff;
background-color: #fff;
margin: 0;
}

.grid-row:nth-child(even) {
background-color: #eee;
}
</style>
16 changes: 16 additions & 0 deletions src/Frontend/src/components/audit/AuditMenuItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { RouterLink } from "vue-router";
import routeLinks from "@/router/routeLinks";
</script>

<template>
<RouterLink :to="routeLinks.messages.root">
<i class="fa fa-envelope icon-white" title="All Messages"></i>
<span class="navbar-label">All Messages</span>
</RouterLink>
</template>

<style scoped>
@import "@/assets/navbar.css";
@import "@/assets/header-menu-item.css";
</style>

This file was deleted.

Loading