Skip to content

Commit e8be495

Browse files
committed
Adding filters
1 parent 2de1a9f commit e8be495

File tree

6 files changed

+167
-135
lines changed

6 files changed

+167
-135
lines changed

src/Frontend/src/components/FilterInput.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ watch(localInput, (newValue) => {
3434
3535
div.filter-input {
3636
position: relative;
37-
width: 280px;
3837
height: 36px;
3938
}
4039

src/Frontend/src/components/audit/AuditList.vue

Lines changed: 109 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,39 @@
11
<script setup lang="ts">
22
import routeLinks from "@/router/routeLinks";
3-
import { ColumnNames, useAuditStore } from "@/stores/AuditStore";
3+
import { useAuditStore } from "@/stores/AuditStore";
44
import { storeToRefs } from "pinia";
5-
import SortableColumn from "../SortableColumn.vue";
65
import { MessageStatus } from "@/resources/Message";
76
import moment from "moment";
8-
import { useFormatTime } from "@/composables/formatter";
9-
import RefreshConfig from "../RefreshConfig.vue";
10-
import ItemsPerPage from "../ItemsPerPage.vue";
11-
import PaginationStrip from "../PaginationStrip.vue";
127
import { useRoute } from "vue-router";
8+
import FilterInput from "@/components/FilterInput.vue";
9+
import ResultsCount from "@/components/ResultsCount.vue";
10+
import DropDown from "@/components/DropDown.vue";
11+
import { computed } from "vue";
12+
import { formatDotNetTimespan, formatTypeName } from "@/composables/formatUtils.ts";
1313
1414
const store = useAuditStore();
15-
const { messages, sortByInstances, itemsPerPage, selectedPage, totalCount } = storeToRefs(store);
15+
const { messages, sortBy, totalCount, messageFilterString, selectedEndpointName, endpoints, itemsPerPage } = storeToRefs(store);
1616
const route = useRoute();
1717
18+
const endpointNames = computed(() => {
19+
return endpoints.value.map((endpoint) => ({
20+
text: endpoint.name,
21+
value: endpoint.name,
22+
}));
23+
});
24+
const selectedEndpointItem = computed(() => ({ text: selectedEndpointName.value, value: selectedEndpointName.value }));
25+
const sortByItems = [
26+
{ text: "Latest sent", value: "time_sent,desc" },
27+
{ text: "Oldest sent", value: "time_sent,asc" },
28+
{ text: "Fastest processing", value: "processing_time,asc" },
29+
{ text: "Slowest processing", value: "processing_time,desc" },
30+
];
31+
const selectedSortByItem = computed(() => sortByItems.find((item) => item.value === `${sortBy.value.property},${sortBy.value.isAscending ? "asc" : "desc"}`));
32+
33+
function setSortBy(item: { text: string; value: string }) {
34+
const strings = item.value.split(",");
35+
sortBy.value = { isAscending: strings[1] === "asc", property: strings[0] };
36+
}
1837
function statusToName(messageStatus: MessageStatus) {
1938
switch (messageStatus) {
2039
case MessageStatus.Successful:
@@ -48,97 +67,106 @@ function statusToIcon(messageStatus: MessageStatus) {
4867
return "fa retry-issued";
4968
}
5069
}
51-
52-
function friendlyTypeName(messageType: string) {
53-
if (messageType == null) return null;
54-
55-
const typeClass = messageType.split(",")[0];
56-
const typeName = typeClass.split(".").reverse()[0];
57-
return typeName.replace(/\+/g, ".");
58-
}
59-
60-
function formatDotNetTimespan(timespan: string) {
61-
//assuming if we have days in the timespan then something is very, very wrong
62-
const [hh, mm, ss] = timespan.split(":");
63-
const time = useFormatTime(((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000);
64-
return `${time.value} ${time.unit}`;
65-
}
6670
</script>
6771

6872
<template>
69-
<section class="section-table" role="table" aria-label="endpoint-instances">
70-
<div class="header">
71-
<RefreshConfig id="auditListRefresh" @change="store.updateRefreshTimer" @manual-refresh="store.refresh" />
72-
<!--Table headings-->
73-
<div role="row" aria-label="column-headers" class="row table-head-row" :style="{ borderTop: 0 }">
74-
<div role="columnheader" :aria-label="ColumnNames.Status" class="status">
75-
<SortableColumn :sort-by="ColumnNames.Status" v-model="sortByInstances" :default-ascending="true">Status</SortableColumn>
76-
</div>
77-
<div role="columnheader" :aria-label="ColumnNames.MessageId" class="col-3">
78-
<SortableColumn :sort-by="ColumnNames.MessageId" v-model="sortByInstances" :default-ascending="true">Message Id</SortableColumn>
73+
<div>
74+
<div class="row">
75+
<div class="filters">
76+
<div class="text-search-container">
77+
<FilterInput v-model="messageFilterString" placeholder="Search messages..." aria-label="Search messages" />
7978
</div>
80-
<div role="columnheader" :aria-label="ColumnNames.MessageType" class="col-3">
81-
<SortableColumn :sort-by="ColumnNames.MessageType" v-model="sortByInstances" :default-ascending="true">Type</SortableColumn>
79+
<div>
80+
<DropDown
81+
label="Filter by endpoint"
82+
:callback="
83+
(item) => {
84+
selectedEndpointName = item.value;
85+
}
86+
"
87+
:select-item="selectedEndpointItem"
88+
:items="endpointNames"
89+
/>
8290
</div>
83-
<div role="columnheader" :aria-label="ColumnNames.TimeSent" class="col-2">
84-
<SortableColumn :sort-by="ColumnNames.TimeSent" v-model="sortByInstances">Time Sent</SortableColumn>
91+
<div>
92+
<DropDown
93+
label="Show"
94+
:callback="
95+
(item) => {
96+
itemsPerPage = parseInt(item.value, 10);
97+
}
98+
"
99+
:select-item="{ text: itemsPerPage.toString(), value: itemsPerPage.toString() }"
100+
:items="[
101+
{ text: '50', value: '50' },
102+
{ text: '100', value: '100' },
103+
{ text: '250', value: '250' },
104+
{ text: '500', value: '500' },
105+
]"
106+
/>
85107
</div>
86-
<div role="columnheader" :aria-label="ColumnNames.ProcessingTime" class="col-2">
87-
<SortableColumn :sort-by="ColumnNames.ProcessingTime" v-model="sortByInstances">Processing Time</SortableColumn>
108+
<div>
109+
<DropDown label="Sort by" :callback="setSortBy" :select-item="selectedSortByItem" :items="sortByItems" />
88110
</div>
89111
</div>
90112
</div>
91-
<!--Table rows-->
92-
<!--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)-->
93-
<div class="messages" role="rowgroup" aria-label="messages">
94-
<div role="row" :aria-label="message.message_id" class="row grid-row" v-for="message in messages" :key="message.id">
95-
<div role="cell" aria-label="status" class="status" :title="statusToName(message.status)">
96-
<div class="status-icon" :class="statusToIcon(message.status)"></div>
97-
</div>
98-
<div role="cell" aria-label="message-id" class="col-3 message-id">
99-
<div class="box-header">
100-
<tippy :aria-label="message.message_id" :delay="[700, 0]" class="no-side-padding lead righ-side-ellipsis endpoint-details-link">
101-
<template #content>
102-
<p :style="{ overflowWrap: 'break-word' }">{{ message.message_id }}</p>
103-
</template>
104-
<RouterLink
105-
v-if="message.status === MessageStatus.Successful"
106-
class="hackToPreventSafariFromShowingTooltip"
107-
aria-label="details-link"
108-
:to="{ path: routeLinks.messages.successMessage.link(message.message_id, message.id), query: { back: route.path } }"
109-
>
110-
{{ message.message_id }}
111-
</RouterLink>
112-
<RouterLink v-else class="hackToPreventSafariFromShowingTooltip" aria-label="details-link" :to="{ path: routeLinks.messages.failedMessage.link(message.id), query: { back: route.path } }">
113-
{{ message.message_id }}
114-
</RouterLink>
115-
</tippy>
113+
<div class="row">
114+
<ResultsCount :displayed="messages.length" :total="totalCount" />
115+
</div>
116+
<div class="row results-table">
117+
<section class="section-table" role="table" aria-label="endpoint-instances">
118+
<!--Table rows-->
119+
<!--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)-->
120+
<div class="messages" role="rowgroup" aria-label="messages">
121+
<div role="row" :aria-label="message.message_id" class="row grid-row" v-for="message in messages" :key="message.id">
122+
<div role="cell" aria-label="status" class="status" :title="statusToName(message.status)">
123+
<div class="status-icon" :class="statusToIcon(message.status)"></div>
124+
</div>
125+
<div role="cell" aria-label="message-id" class="col-3 message-id">
126+
<div class="box-header">
127+
<RouterLink v-if="message.status === MessageStatus.Successful" aria-label="details-link" :to="{ path: routeLinks.messages.successMessage.link(message.message_id, message.id), query: { back: route.path } }">
128+
{{ message.message_id }}
129+
</RouterLink>
130+
<RouterLink v-else aria-label="details-link" :to="{ path: routeLinks.messages.failedMessage.link(message.id), query: { back: route.path } }">
131+
{{ message.message_id }}
132+
</RouterLink>
133+
</div>
134+
</div>
135+
<div role="cell" aria-label="message-type" class="col-3 message-type">
136+
{{ formatTypeName(message.message_type) }}
137+
</div>
138+
<div role="cell" aria-label="time-sent" class="col-2 time-sent">
139+
{{ moment(message.time_sent).local().format("LLLL") }}
140+
</div>
141+
<div role="cell" aria-label="processing-time" class="col-2 processing-time">
142+
{{ formatDotNetTimespan(message.processing_time) }}
143+
</div>
116144
</div>
117145
</div>
118-
<div role="cell" aria-label="message-type" class="col-3 message-type">
119-
{{ friendlyTypeName(message.message_type) }}
120-
</div>
121-
<div role="cell" aria-label="time-sent" class="col-2 time-sent">
122-
{{ moment(message.time_sent).local().format("LLLL") }}
123-
</div>
124-
<div role="cell" aria-label="processing-time" class="col-2 processing-time">
125-
{{ formatDotNetTimespan(message.processing_time) }}
126-
</div>
127-
</div>
146+
</section>
128147
</div>
129-
<div class="row">
130-
<ItemsPerPage v-model="itemsPerPage" />
131-
<PaginationStrip v-model="selectedPage" :totalCount="totalCount" :itemsPerPage="itemsPerPage" />
132-
</div>
133-
</section>
148+
</div>
134149
</template>
135150

136151
<style scoped>
137152
@import "../list.css";
153+
.results-table {
154+
margin-top: 1rem;
155+
margin-bottom: 5rem;
156+
}
138157
139-
.hackToPreventSafariFromShowingTooltip::after {
140-
content: "";
141-
display: block;
158+
.text-search-container {
159+
width: 25rem;
160+
}
161+
162+
.filters {
163+
background-color: #f3f3f3;
164+
margin-top: 0.3125rem;
165+
border: #8c8c8c 1px solid;
166+
border-radius: 3px;
167+
padding: 0.3125rem;
168+
display: flex;
169+
gap: 1.1rem;
142170
}
143171
144172
.section-table {

src/Frontend/src/components/messages2/FlowDiagram/FlowDiagram.vue

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import EndpointDetails from "@/resources/EndpointDetails.ts";
1414
import { hexToCSSFilter } from "hex-to-css-filter";
1515
import TextEllipses from "@/components/TextEllipses.vue";
1616
import { useLayout } from "@/components/messages2/FlowDiagram/useLayout.ts";
17+
import { formatTypeName } from "@/composables/formatUtils.ts";
1718
1819
enum MessageType {
1920
Event = "Event message",
@@ -40,18 +41,10 @@ class SagaInvocation {
4041
const sagaIdHeader = getHeaderByKey(message, NServiceBusHeaders.SagaId);
4142
const originatedSagaIdHeader = getHeaderByKey(message, NServiceBusHeaders.OriginatingSagaId);
4243
this.id = saga.saga_id;
43-
this.sagaType = this.toName(saga.saga_type);
44+
this.sagaType = formatTypeName(saga.saga_type);
4445
this.isSagaCompleted = saga.change_status === "Completed";
4546
this.isSagaInitiated = sagaIdHeader === undefined && originatedSagaIdHeader !== undefined;
4647
}
47-
48-
private toName(type: string) {
49-
const clazz = type.split(",")[0];
50-
let objectName = clazz.split(".").pop() ?? "";
51-
objectName = objectName.replace("+", ".");
52-
53-
return objectName;
54-
}
5548
}
5649
5750
interface NodeData {

src/Frontend/src/components/messages2/HeadersView.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import CopyToClipboard from "@/components/CopyToClipboard.vue";
33
import { computed, ref } from "vue";
44
import { useMessageStore } from "@/stores/MessageStore";
55
import { storeToRefs } from "pinia";
6+
import FilterInput from "@/components/FilterInput.vue";
67
78
const { headers } = storeToRefs(useMessageStore());
89
@@ -26,7 +27,9 @@ const filteredHeaders = computed(() => {
2627
<div class="col">
2728
<div class="text-search-container">
2829
<div class="text-search">
29-
<input type="search" aria-label="Filter by name" v-model="searchTerm" class="form-control format-text" placeholder="Search for a header key or value..." />
30+
<div>
31+
<FilterInput v-model="searchTerm" :aria-label="`Search for a header key or value`" :placeholder="'Search for a header key or value...'" />
32+
</div>
3033
</div>
3134
</div>
3235
</div>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useFormatTime } from "@/composables/formatter.ts";
2+
3+
export function formatTypeName(type: string) {
4+
const clazz = type.split(",")[0];
5+
return clazz;
6+
}
7+
8+
export function formatDotNetTimespan(timespan: string) {
9+
//assuming if we have days in the timespan then something is very, very wrong
10+
const [hh, mm, ss] = timespan.split(":");
11+
const time = useFormatTime(((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000);
12+
return `${time.value} ${time.unit}`;
13+
}

0 commit comments

Comments
 (0)