Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions src/Frontend/src/components/messages2/SequenceDiagram.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,24 @@ import Routes from "./SequenceDiagram/RoutesComponent.vue";
import { useSequenceDiagramStore } from "@/stores/SequenceDiagramStore";
import { storeToRefs } from "pinia";
import useTooltips from "./SequenceDiagram/tooltipOverlay.ts";
import { onMounted } from "vue";
import { onMounted, ref } from "vue";

const store = useSequenceDiagramStore();
const { maxWidth, maxHeight } = storeToRefs(store);
const endpointYOffset = ref(0);

useTooltips();

onMounted(() => store.refreshConversation());
</script>

<template>
<div class="outer">
<div class="outer" @scroll="(ev) => (endpointYOffset = (ev.target as Element).scrollTop)">
<svg class="sequence-diagram" :width="`max(100%, ${isNaN(maxWidth) ? 0 : maxWidth}px)`" :height="maxHeight + 20">
<Endpoints />
<Timeline />
<Handlers />
<Routes />
<Endpoints :yOffset="endpointYOffset" />
</svg>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ interface EndpointSurround {
const Endpoint_Gap = 30;
const Endpoint_Image_Width = 20;

defineProps<{
yOffset: number;
}>();

const store = useSequenceDiagramStore();
const { startX, endpoints } = storeToRefs(store);

Expand Down Expand Up @@ -79,7 +83,7 @@ function setEndpointTextRef(el: SVGTextElement, index: number) {
</script>

<template>
<g v-for="(endpoint, i) in endpointItems" :key="endpoint.name" transform="translate(0,15)" :ref="(el) => (endpoint.uiRef = el as SVGElement)">
<g v-for="(endpoint, i) in endpointItems" :key="endpoint.name" :transform="`translate(0,${yOffset + 15})`" :ref="(el) => (endpoint.uiRef = el as SVGElement)">
<rect
v-if="endpoint.surround"
:x="endpoint.surround.x"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function formatTime(milliseconds: number) {
<label>Processing Time:</label>
<span>{{ formatTime(handler.processingTime ?? 0) }}</span>
<label>Processing Of:</label>
<span>{{ handler.name }}</span>
<span>{{ handler.friendlyName }}</span>
<label v-if="handler.partOfSaga">Sagas Invoked:</label>
<span v-if="handler.partOfSaga">{{ handler.partOfSaga }}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { HandlerState } from "@/resources/SequenceDiagram/Handler";
import { computed, ref } from "vue";
import { computed, onActivated, ref, watch } from "vue";
import { Direction } from "@/resources/SequenceDiagram/RoutedMessage";
import { useSequenceDiagramStore } from "@/stores/SequenceDiagramStore";
import { storeToRefs } from "pinia";
Expand All @@ -10,9 +10,17 @@ const Handler_Gap = 20;
const Handler_Width = 14;

const store = useSequenceDiagramStore();
const { handlers, endpointCentrePoints, highlightId } = storeToRefs(store);
const { handlers, endpointCentrePoints, highlightId, selectedId } = storeToRefs(store);

const messageTypeRefs = ref<SVGTextElement[]>([]);
const hasMadeVisible = ref(false);
const selectedElement = ref<SVGElement>();
//reset values to allow scroll to element on switching back to this tab
onActivated(() => {
hasMadeVisible.value = false;
if (selectedElement.value) scrollToIfSelected(selectedElement.value, selectedId.value);
});
watch(selectedId, () => (selectedElement.value = undefined));

const handlerItems = computed(() => {
let nextY = 0;
Expand All @@ -27,7 +35,8 @@ const handlerItems = computed(() => {
const fill = (() => {
if (handler.id === "First") return "black";
if (handler.state === HandlerState.Fail) return "var(--error)";
if (handler.route?.name === highlightId.value) return "var(--highlight-background)";
if (handler.route && handler.route.name === selectedId.value) return "var(--highlight)";
if (handler.route && handler.route.name === highlightId.value) return "var(--highlight-background)";
return "var(--gray60)";
})();
const icon = (() => {
Expand All @@ -51,6 +60,8 @@ const handlerItems = computed(() => {

return {
id: handler.id,
messageId: { id: handler.selectedMessage?.message_id, uniqueId: handler.selectedMessage?.id },
isError: handler.state === HandlerState.Fail,
endpointName: handler.endpoint.name,
incomingId: handler.route?.name,
left,
Expand All @@ -60,9 +71,10 @@ const handlerItems = computed(() => {
fill,
icon,
iconSize,
messageType: handler.name,
messageType: handler.friendlyName,
messageTypeOffset,
messageTypeHighlight: handler.route?.name === highlightId.value,
messageTypeSelected: handler.route?.name === selectedId.value,
setUIRef: (el: SVGElement) => (handler.uiRef = el),
};
});
Expand All @@ -76,23 +88,46 @@ const handlerItems = computed(() => {
function setMessageTypeRef(el: SVGTextElement, index: number) {
if (el) messageTypeRefs.value[index] = el;
}

function scrollToIfSelected(el: SVGElement, handlerId: string | undefined) {
if (!hasMadeVisible.value && el && handlerId === selectedId.value) {
hasMadeVisible.value = true;
selectedElement.value = el;
//can't be done immediately since the sequence diagram hasn't completed layout yet
setTimeout(() => selectedElement.value!.scrollIntoView(false), 30);
}
}
</script>

<template>
<g v-for="(handler, i) in handlerItems" :key="`${handler.id}###${handler.endpointName}`" :transform="`translate(${handler.left}, ${handler.y})`">
<g v-for="(handler, i) in handlerItems" :key="`${handler.id}###${handler.endpointName}`" :ref="(el) => scrollToIfSelected(el as SVGElement, handler.incomingId)" :transform="`translate(${handler.left}, ${handler.y})`">
<!--Handler Activation Box-->
<g :ref="(el) => handler.setUIRef(el as SVGElement)">
<rect :width="Handler_Width" :height="handler.height" :class="handler.incomingId && 'clickable'" :fill="handler.fill" @mouseover="() => store.setHighlightId(handler.incomingId)" @mouseleave="() => store.setHighlightId()" />
<g :ref="(el) => handler.setUIRef(el as SVGElement)" class="activation-box">
<rect
:width="Handler_Width"
:height="handler.height"
:class="{
clickable: handler.incomingId && !handler.messageTypeSelected,
}"
:fill="handler.fill"
@mouseover="() => store.setHighlightId(handler.incomingId)"
@mouseleave="() => store.setHighlightId()"
@click="handler.incomingId && !handler.messageTypeSelected && store.navigateTo(handler.messageId.uniqueId, handler.messageId.id, handler.isError)"
/>
<path v-if="handler.icon" :d="handler.icon" fill="white" :transform="`translate(${Handler_Width / 2 - handler.iconSize / 2}, ${handler.height / 2 - handler.iconSize / 2})`" />
</g>
<!--Message Type and Icon-->
<g
v-if="handler.messageType"
:transform="`translate(${handler.messageTypeOffset}, 4)`"
class="clickable"
:fill="handler.messageTypeHighlight ? 'var(--highlight)' : 'var(--gray40)'"
:class="{
clickable: !handler.messageTypeSelected,
'message-type': true,
highlight: handler.messageTypeHighlight || handler.messageTypeSelected,
}"
@mouseover="() => store.setHighlightId(handler.incomingId)"
@mouseleave="() => store.setHighlightId()"
@click="handler.incomingId && !handler.messageTypeSelected && store.navigateTo(handler.messageId.uniqueId, handler.messageId.id, handler.isError)"
>
<path d="M9,3L9,3 9,0 0,0 0,3 4,3 4,6 0,6 0,9 4,9 4,12 0,12 0,15 9,15 9,12 5,12 5,9 9,9 9,6 5,6 5,3z" />
<text x="14" y="10" alignment-baseline="middle" :ref="(el) => setMessageTypeRef(el as SVGTextElement, i)">{{ handler.messageType }}</text>
Expand All @@ -104,4 +139,16 @@ function setMessageTypeRef(el: SVGTextElement, index: number) {
.clickable {
cursor: pointer;
}

.activation-box:focus {
outline: none;
}

.message-type {
fill: var(--gray40);
}

.message-type.highlight {
fill: var(--highlight);
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { Direction, RoutedMessageType } from "@/resources/SequenceDiagram/Routed
import { computed, ref } from "vue";
import { useSequenceDiagramStore } from "@/stores/SequenceDiagramStore";
import { storeToRefs } from "pinia";
import { HandlerState } from "@/resources/SequenceDiagram/Handler";

const Arrow_Head_Width = 10;
const Message_Type_Margin = 4;

const store = useSequenceDiagramStore();
const { routes, handlerLocations, highlightId } = storeToRefs(store);
const { selectedId, routes, handlerLocations, highlightId } = storeToRefs(store);

const messageTypeRefs = ref<SVGTextElement[]>([]);

Expand Down Expand Up @@ -41,6 +42,9 @@ const arrows = computed(() =>

return {
id: route.name,
selected: route.name === selectedId.value,
messageId: { uniqueId: route.fromRoutedMessage.selectedMessage.id, id: route.fromRoutedMessage.selectedMessage.message_id },
isHandlerError: route.processingHandler?.state === HandlerState.Fail,
fromX,
y,
direction,
Expand Down Expand Up @@ -73,25 +77,30 @@ function setMessageTypeRef(el: SVGTextElement, index: number) {
<path v-if="arrow.direction === Direction.Left" :d="`M${arrow.toHandlerCentre + 1} ${arrow.y} l10,-7.5 0,15z`" fill="black" />
</g>
<!--Highlight Arrow-->
<g v-if="arrow.highlight" :transform="`translate(${arrow.toHandlerCentre},${arrow.y})`" stroke="var(--highlight)" fill="var(--highlight)">
<g v-if="arrow.highlight || arrow.selected" :transform="`translate(${arrow.toHandlerCentre},${arrow.y})`" stroke="var(--highlight)" fill="var(--highlight)">
<path :d="`M0 0 v${arrow.height - 6}`" stroke-width="2" />
<path :d="`M0 ${arrow.height} l-3,-6 6,0z`" />
</g>
<!--Message Type and Icon-->
<g
class="clickable message-type"
:class="{
clickable: !arrow.selected,
'message-type': true,
highlight: arrow.highlight,
selected: arrow.selected,
}"
:transform="`translate(${arrow.messageTypeOffset}, ${arrow.y - 7.5 - Message_Type_Margin})`"
:fill="arrow.highlight ? 'var(--highlight)' : 'black'"
@mouseover="() => store.setHighlightId(arrow.id)"
@mouseleave="() => store.setHighlightId()"
@click="!arrow.selected && store.navigateTo(arrow.messageId.uniqueId, arrow.messageId.id, arrow.isHandlerError)"
:ref="(el) => arrow.setUIRef(el as SVGElement)"
>
<!--19 is width of MessageType icon, plus a gap-->
<rect
v-if="arrow.highlight && arrow.messageTypeOffset"
:width="arrow.highlightTextWidth + 19 + Message_Type_Margin + Message_Type_Margin"
:height="arrow.highlightTextHeight + Message_Type_Margin + Message_Type_Margin"
fill="var(--highlight-background)"
v-if="(arrow.highlight || arrow.selected) && arrow.messageTypeOffset"
:width="(arrow.highlightTextWidth ?? 0) + 19 + Message_Type_Margin + Message_Type_Margin"
:height="(arrow.highlightTextHeight ?? 0) + Message_Type_Margin + Message_Type_Margin"
class="border"
/>
<svg :x="Message_Type_Margin" :y="Message_Type_Margin" width="15" height="15" viewBox="0 0 32 32">
<path
Expand All @@ -116,7 +125,32 @@ function setMessageTypeRef(el: SVGTextElement, index: number) {
cursor: pointer;
}

.message-type {
fill: black;
outline: none;
}

.message-type.selected {
fill: white;
}

.message-type .border {
fill: var(--highlight-background);
}

.message-type.selected .border {
fill: var(--highlight);
}

.message-type:not(.selected).highlight {
fill: var(--highlight);
}

.message-type text::selection {
fill: white;
}

.message-type.selected text::selection {
background-color: black;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useTippy } from "vue-tippy";
import EndpointTooltip from "./EndpointTooltip.vue";
import HandlerTooltip from "./HandlerTooltip.vue";
import RouteTooltip from "./RouteTooltip.vue";
import { HandlerState } from "@/resources/SequenceDiagram/Handler";

export default function useTooltips() {
const store = useSequenceDiagramStore();
Expand All @@ -30,7 +31,7 @@ export default function useTooltips() {
() => handlers.value.map((handler) => handler.uiRef),
() =>
handlers.value
.filter((handler) => handler.uiRef)
.filter((handler) => handler.uiRef && handler.state !== HandlerState.Unknown)
.forEach((handler) =>
useTippy(handler.uiRef, {
interactive: true,
Expand Down
7 changes: 5 additions & 2 deletions src/Frontend/src/resources/SequenceDiagram/Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { friendlyTypeName } from "./SequenceModel";
export interface Handler {
readonly id: string;
name?: string;
friendlyName?: string;
readonly endpoint: Endpoint;
readonly isPartOfSaga: boolean;
partOfSaga?: string;
Expand All @@ -27,6 +28,7 @@ export interface Handler {
export enum HandlerState {
Fail,
Success,
Unknown,
}

export const ConversationStartHandlerName = "First";
Expand Down Expand Up @@ -60,7 +62,8 @@ export function updateProcessingHandler(handler: Handler, message: Message) {
//TODO: extract logic since it's also currently used in AuditList
const [hh, mm, ss] = message.processing_time.split(":");
handler.processingTime = ((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000;
handler.name = friendlyTypeName(message.message_type);
handler.name = message.message_type;
handler.friendlyName = friendlyTypeName(message.message_type);

if ((message.invoked_sagas?.length ?? 0) > 0) {
handler.partOfSaga = message.invoked_sagas!.map((saga) => friendlyTypeName(saga.saga_type)).join(", ");
Expand All @@ -85,7 +88,7 @@ class HandlerItem implements Handler {
name?: string;
partOfSaga?: string;
inMessage?: RoutedMessage;
state: HandlerState = HandlerState.Fail;
state: HandlerState = HandlerState.Unknown;
processedAt?: Date;
processingTime?: number;
route?: MessageProcessingRoute;
Expand Down
4 changes: 2 additions & 2 deletions src/Frontend/src/resources/SequenceDiagram/SequenceModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ export class ModelCreator implements ConversationModel {
sendingHandler.addOutMessage(routedMessage);
}

const start = firstOrderHandlers.find((h) => h.id === ConversationStartHandlerName);
const start = firstOrderHandlers.filter((h) => h.id === ConversationStartHandlerName);
const orderByHandledAt = firstOrderHandlers.filter((h) => h.id !== ConversationStartHandlerName).sort((a, b) => (a.handledAt?.getTime() ?? 0) - (b.handledAt?.getTime() ?? 0));

this.#handlers = [start!, ...orderByHandledAt];
this.#handlers = [...start, ...orderByHandledAt];
}

get endpoints(): Endpoint[] {
Expand Down
Loading