Skip to content

Commit aa8ec15

Browse files
committed
Loading from query params
1 parent 97af90f commit aa8ec15

File tree

6 files changed

+148
-51
lines changed

6 files changed

+148
-51
lines changed

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

Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import { useAuditStore } from "@/stores/AuditStore";
44
import { storeToRefs } from "pinia";
55
import Message, { MessageStatus } from "@/resources/Message";
66
import moment from "moment";
7-
import { useRoute } from "vue-router";
7+
import { useRoute, useRouter } from "vue-router";
88
import ResultsCount from "@/components/ResultsCount.vue";
9-
import { dotNetTimespanToMilliseconds, formatDotNetTimespan, formatTypeName } from "@/composables/formatUtils.ts";
9+
import { dotNetTimespanToMilliseconds, formatDotNetTimespan } from "@/composables/formatUtils.ts";
1010
import "@vuepic/vue-datepicker/dist/main.css";
1111
import FiltersPanel from "@/components/audit/FiltersPanel.vue";
1212
1313
const store = useAuditStore();
1414
const { messages, totalCount } = storeToRefs(store);
1515
const route = useRoute();
16+
const router = useRouter();
1617
1718
function statusToName(messageStatus: MessageStatus) {
1819
switch (messageStatus) {
@@ -67,6 +68,19 @@ function hasWarning(message: Message) {
6768
6869
return false;
6970
}
71+
72+
function navigateToMessage(message: Message) {
73+
const query = router.currentRoute.value.query;
74+
75+
if (message.status === MessageStatus.Successful) {
76+
router.push({
77+
path: routeLinks.messages.successMessage.link(message.message_id, message.id),
78+
query: { ...query, ...{ back: route.path } },
79+
});
80+
} else {
81+
router.push({ path: routeLinks.messages.failedMessage.link(message.id), query: { ...query, ...{ back: route.path } } });
82+
}
83+
}
7084
</script>
7185

7286
<template>
@@ -78,39 +92,23 @@ function hasWarning(message: Message) {
7892
<ResultsCount :displayed="messages.length" :total="totalCount" />
7993
</div>
8094
<div class="row results-table">
81-
<section role="table" aria-label="endpoint-instances">
82-
<!--Table rows-->
83-
<!--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)-->
84-
<div role="rowgroup" aria-label="messages">
85-
<div role="row" :aria-label="message.message_id" class="row grid-row" v-for="message in messages" :key="message.id">
86-
<div role="cell" aria-label="status" class="status" :title="statusToName(message.status)">
87-
<div class="status-container">
88-
<div class="status-icon" :class="statusToIcon(message.status)"></div>
89-
<div v-if="hasWarning(message)" class="warning"></div>
90-
</div>
91-
</div>
92-
<div role="cell" aria-label="message-id" class="col-3 message-id">
93-
<div class="box-header">
94-
<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 } }">
95-
{{ message.message_id }}
96-
</RouterLink>
97-
<RouterLink v-else aria-label="details-link" :to="{ path: routeLinks.messages.failedMessage.link(message.id), query: { back: route.path } }">
98-
{{ message.message_id }}
99-
</RouterLink>
100-
</div>
101-
</div>
102-
<div role="cell" aria-label="message-type" class="col-3 message-type">
103-
{{ formatTypeName(message.message_type) }}
104-
</div>
105-
<div role="cell" aria-label="time-sent" class="col-2 time-sent">
106-
{{ moment(message.time_sent).local().format("LLLL") }}
107-
</div>
108-
<div role="cell" aria-label="processing-time" class="col-2 processing-time">
109-
{{ formatDotNetTimespan(message.processing_time) }}
95+
<template v-for="message in messages" :key="message.id">
96+
<div class="item" @click="navigateToMessage(message)">
97+
<div class="status">
98+
<div class="status-container" v-tippy="{ content: statusToName(message.status) }">
99+
<div class="status-icon" :class="statusToIcon(message.status)"></div>
100+
<div v-if="hasWarning(message)" class="warning"></div>
110101
</div>
111102
</div>
103+
<div class="message-id">{{ message.message_id }}</div>
104+
<div class="message-type">{{ message.message_type }}</div>
105+
<div class="time-sent"><span class="label-name">Time Sent:</span>{{ moment(message.time_sent).local().format("LLLL") }}</div>
106+
<div class="critical-time"><span class="label-name">Critical Time:</span>{{ formatDotNetTimespan(message.critical_time) }}</div>
107+
<div class="processing-time"><span class="label-name">Processing Time:</span>{{ formatDotNetTimespan(message.processing_time) }}</div>
108+
<div class="delivery-time"><span class="label-name">Delivery Time:</span>{{ formatDotNetTimespan(message.delivery_time) }}</div>
112109
</div>
113-
</section>
110+
<div class="spacer"></div>
111+
</template>
114112
</div>
115113
</div>
116114
</template>
@@ -120,13 +118,56 @@ function hasWarning(message: Message) {
120118
.results-table {
121119
margin-top: 1rem;
122120
margin-bottom: 5rem;
121+
padding: 10px 0;
122+
background-color: #ffffff;
123+
}
124+
.spacer {
125+
border-bottom: 1px solid #b1afaf;
126+
margin-top: 0.1rem;
127+
margin-bottom: 0.1rem;
128+
}
129+
.item {
130+
padding: 3px;
131+
border: 1px solid #ffffff;
132+
display: grid;
133+
grid-template-columns: 25px 1fr 1fr 1fr 1fr;
134+
grid-template-rows: 1fr 1fr;
135+
gap: 6px;
136+
grid-template-areas:
137+
"status message-type message-type message-type time-sent"
138+
"status message-id processing-time critical-time delivery-time";
139+
}
140+
.item:hover {
141+
border: 1px solid #00a3c4;
142+
background-color: #edf6f7;
143+
cursor: pointer;
144+
}
145+
.label-name {
146+
margin-right: 4px;
147+
color: #777f7f;
123148
}
124-
125149
.status {
126-
width: 5em;
127-
text-align: center;
150+
grid-area: status;
151+
}
152+
.message-id {
153+
grid-area: message-id;
154+
}
155+
.time-sent {
156+
grid-area: time-sent;
157+
}
158+
.message-type {
159+
grid-area: message-type;
160+
font-weight: bold;
161+
}
162+
.processing-time {
163+
grid-area: processing-time;
164+
}
165+
.critical-time {
166+
grid-area: critical-time;
167+
}
168+
.delivery-time {
169+
grid-area: delivery-time;
128170
}
129-
130171
.status-container {
131172
color: white;
132173
width: 20px;
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
<script setup lang="ts">
22
import VueDatePicker from "@vuepic/vue-datepicker";
3-
import { ref, watch } from "vue";
3+
import { ref, useTemplateRef, watch } from "vue";
44
55
const model = defineModel<Date[]>({ required: true });
66
const internalModel = ref<Date[]>([]);
77
const displayDataRange = ref<string>("No dates");
8+
const datePicker = useTemplateRef<typeof VueDatePicker>("datePicker");
89
910
watch(internalModel, () => {
1011
model.value = internalModel.value;
1112
if (internalModel.value.length === 2) {
1213
const from = internalModel.value[0];
1314
const to = internalModel.value[1];
1415
displayDataRange.value = `${from.toLocaleDateString()} ${from.toLocaleTimeString()} - ${to.toLocaleDateString()} ${to.toLocaleTimeString()}`;
16+
} else {
17+
displayDataRange.value = "No dates";
1518
}
1619
});
20+
21+
function clearCurrentDate() {
22+
internalModel.value = [];
23+
datePicker.value?.closeMenu();
24+
}
1725
</script>
1826

1927
<template>
20-
<VueDatePicker v-model="internalModel" :range="{ partialRange: false }" :enable-seconds="true" :max-date="new Date()">
28+
<VueDatePicker ref="datePicker" v-model="internalModel" :range="{ partialRange: false }" :enable-seconds="true" :max-date="new Date()" :action-row="{ showNow: false, showCancel: false, showSelect: true }">
2129
<template #trigger>
2230
<button type="button" class="btn btn-dropdown dropdown-toggle">
2331
{{ displayDataRange }}
2432
</button>
2533
</template>
34+
<template #action-extra>
35+
<button v-if="internalModel.length === 2" class="dp__action_button dp__action_cancel" @click="clearCurrentDate()">Clear range</button>
36+
</template>
2637
</VueDatePicker>
2738
</template>

src/Frontend/src/components/audit/FiltersPanel.vue

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import FilterInput from "@/components/FilterInput.vue";
33
import { storeToRefs } from "pinia";
44
import { useAuditStore } from "@/stores/AuditStore.ts";
55
import ListFilterSelector from "@/components/audit/ListFilterSelector.vue";
6-
import { computed, ref, watch } from "vue";
6+
import { computed, onBeforeMount, ref, watch } from "vue";
77
import DatePickerRange from "@/components/audit/DatePickerRange.vue";
8+
import { useRouter } from "vue-router";
89
910
const store = useAuditStore();
1011
const { sortBy, messageFilterString, selectedEndpointName, endpoints, itemsPerPage, dateRange } = storeToRefs(store);
@@ -19,6 +20,9 @@ const sortByItemsMap = new Map([
1920
]);
2021
const numberOfItemsPerPage = ["50", "100", "250", "500"];
2122
const sortByItems = computed(() => [...sortByItemsMap.keys()]);
23+
const selectedSortByItem = ref(findKeyByValue(`${sortBy.value.property},${sortBy.value.isAscending ? "asc" : "desc"}`));
24+
const selectedItemsPerPage = ref(itemsPerPage.value.toString());
25+
const router = useRouter();
2226
2327
function findKeyByValue(searchValue: string) {
2428
for (const [key, value] of sortByItemsMap.entries()) {
@@ -29,14 +33,44 @@ function findKeyByValue(searchValue: string) {
2933
return "";
3034
}
3135
32-
const r = `${sortBy.value.property},${sortBy.value.isAscending ? "asc" : "desc"}`;
36+
onBeforeMount(() => {
37+
const query = router.currentRoute.value.query;
3338
34-
const selectedSortByItem = ref(findKeyByValue(r)); //computed(() => sortByItemsMap.get(`${sortBy.value.property},${sortBy.value.isAscending ? "asc" : "desc"}`) || "time_sent,desc");
35-
const selectedItemsPerPage = ref(itemsPerPage.value.toString());
39+
watchHandle.pause();
40+
41+
if (query.filter) {
42+
messageFilterString.value = query.filter as string;
43+
}
44+
if (query.sortBy && query.sortDir) {
45+
sortBy.value = { isAscending: query.sortDir === "asc", property: query.sortBy as string };
46+
}
47+
if (query.pageSize) {
48+
itemsPerPage.value = Number(query.pageSize as string);
49+
}
50+
if (query.from && query.to) {
51+
dateRange.value = [new Date(query.from as string), new Date(query.to as string)];
52+
}
53+
if (query.endpoint) {
54+
selectedEndpointName.value = query.endpoint as string;
55+
}
56+
57+
watchHandle.resume();
58+
});
59+
60+
const watchHandle = watch([sortBy, messageFilterString, selectedEndpointName, dateRange, itemsPerPage], () => {
61+
let from = "",
62+
to = "";
63+
if (dateRange.value.length === 2) {
64+
from = dateRange.value[0].toISOString();
65+
to = dateRange.value[1].toISOString();
66+
}
67+
router.push({ query: { sortBy: sortBy.value.property, sortDir: sortBy.value.isAscending ? "asc" : "desc", filter: messageFilterString.value, endpoint: selectedEndpointName.value, from, to, pageSize: itemsPerPage.value } });
68+
});
3669
3770
watch(selectedItemsPerPage, (newValue) => {
3871
itemsPerPage.value = Number(newValue);
3972
});
73+
4074
watch(selectedSortByItem, (newValue) => {
4175
const item = sortByItemsMap.get(newValue);
4276
if (item) {

src/Frontend/src/components/audit/ListFilterSelector.vue

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function onclick(item: string, isSelected: boolean) {
2727
<template>
2828
<div class="dropdown">
2929
<button type="button" aria-label="open dropdown menu" class="btn btn-dropdown dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
30-
{{ selected ? selected : defaultEmptyText }}
30+
<span class="wrap-text">{{ selected ? selected : defaultEmptyText }}</span>
3131
</button>
3232
<div class="dropdown-menu wrapper">
3333
<div class="instructions">{{ instructions }}</div>
@@ -54,6 +54,10 @@ function onclick(item: string, isSelected: boolean) {
5454
</template>
5555

5656
<style scoped>
57+
.wrap-text {
58+
max-width: 250px;
59+
word-wrap: break-word;
60+
}
5761
.wrapper {
5862
padding: 0.5em;
5963
min-width: 200px;
@@ -64,6 +68,7 @@ function onclick(item: string, isSelected: boolean) {
6468
}
6569
.items-container {
6670
max-height: 300px;
71+
max-width: 400px;
6772
overflow-y: auto;
6873
}
6974
.item {
@@ -72,10 +77,14 @@ function onclick(item: string, isSelected: boolean) {
7277
font-weight: 400;
7378
cursor: pointer;
7479
color: #262626;
80+
word-wrap: break-word;
7581
text-decoration: none;
82+
width: 100%;
7683
}
7784
.item-container {
7885
padding: 0.3em 0;
86+
display: flex;
87+
place-items: center;
7988
}
8089
8190
.item-container:hover {
@@ -92,14 +101,15 @@ function onclick(item: string, isSelected: boolean) {
92101
font-weight: 400;
93102
cursor: pointer;
94103
text-decoration: none;
104+
margin-left: 6px;
95105
}
96106
97107
.selected {
98108
margin-left: 6px;
99109
}
100110
101111
.fa-cross {
102-
width: 16px;
103-
height: 16px;
112+
width: 14px;
113+
height: 14px;
104114
}
105115
</style>

src/Frontend/src/composables/formatter.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import moment from "moment";
22

3-
const secondDuration = moment.duration(10 * 1000);
3+
const secondDuration = moment.duration(1000);
44
const minuteDuration = moment.duration(60 * 1000);
5-
const hourDuration = moment.duration(60 * 1000); //this ensures that we never use minute formatting
5+
const hourDuration = moment.duration(60 * 60 * 1000); //this ensures that we never use minute formatting
66
const dayDuration = moment.duration(24 * 60 * 60 * 1000);
77

88
export interface ValueWithUnit {
@@ -14,7 +14,6 @@ export function useFormatTime(value?: number): ValueWithUnit {
1414
const time = { value: "0", unit: "ms" };
1515
if (value) {
1616
const duration = moment.duration(value);
17-
1817
if (duration >= dayDuration) {
1918
time.value = formatTimeValue(duration.days()) + " d " + formatTimeValue(duration.hours()) + " hrs";
2019
} else if (duration >= hourDuration) {

src/Frontend/src/stores/AuditStore.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ export const useAuditStore = defineStore("AuditStore", () => {
4040
let from = "",
4141
to = "";
4242
if (dateRange.value.length === 2) {
43-
console.log(dateRange.value);
4443
from = dateRange.value[0].toISOString();
4544
to = dateRange.value[1].toISOString();
4645
}
47-
46+
console.log("retrieveing messages2");
4847
const [response, data] = await useTypedFetchFromServiceControl<Message[]>(
4948
`messages2/?endpoint_name=${selectedEndpointName.value}&from=${from}&to=${to}&q=${messageFilterString.value}&page_size=${itemsPerPage.value}&sort=${sortByInstances.value.property}&direction=${sortByInstances.value.isAscending ? "asc" : "desc"}`
5049
);
@@ -60,7 +59,10 @@ export const useAuditStore = defineStore("AuditStore", () => {
6059
);
6160

6261
const refresh = dataRetriever.executeAndResetTimer;
63-
watch([itemsPerPage, sortByInstances, messageFilterString, selectedEndpointName, dateRange], () => refresh());
62+
watch([itemsPerPage, sortByInstances, messageFilterString, selectedEndpointName, dateRange], async () => {
63+
console.log("watch triggered");
64+
await refresh();
65+
});
6466

6567
return {
6668
refresh,

0 commit comments

Comments
 (0)