Skip to content

Commit

Permalink
move progress bars to header, shorten WorkflowRunSuccess
Browse files Browse the repository at this point in the history
- The progress bars are in `WorkflowInvocationHeader` now, giving more space to the step header being visible below the graph
- `WorkflowRunSuccess` is a one line header (refactored), and it uses `GridInvocation` to show batch invocations.
  • Loading branch information
ahmedhamidawan committed Sep 20, 2024
1 parent 0841ef9 commit 642b3a6
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 299 deletions.
16 changes: 13 additions & 3 deletions client/src/components/Grid/GridInvocation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import { storeToRefs } from "pinia";
import { computed } from "vue";
import type { WorkflowInvocation } from "@/api/invocations";
import invocationsGridConfig from "@/components/Grid/configs/invocations";
import invocationsBatchConfig from "@/components/Grid/configs/invocationsBatch";
import invocationsHistoryGridConfig from "@/components/Grid/configs/invocationsHistory";
import invocationsWorkflowGridConfig from "@/components/Grid/configs/invocationsWorkflow";
import { useUserStore } from "@/stores/userStore";
Expand All @@ -19,19 +21,22 @@ interface Props {
headerMessage?: string;
ownerGrid?: boolean;
filteredFor?: { type: "History" | "StoredWorkflow"; id: string; name: string };
invocationsList?: WorkflowInvocation[];
}
const props = withDefaults(defineProps<Props>(), {
noInvocationsMessage: "No Workflow Invocations to display",
headerMessage: "",
ownerGrid: true,
filteredFor: undefined,
invocationsList: undefined,
});
const { currentUser } = storeToRefs(useUserStore());
const forStoredWorkflow = computed(() => props.filteredFor?.type === "StoredWorkflow");
const forHistory = computed(() => props.filteredFor?.type === "History");
const forBatch = computed(() => !!props.invocationsList?.length);
const effectiveNoInvocationsMessage = computed(() => {
let message = props.noInvocationsMessage;
Expand All @@ -51,6 +56,9 @@ const effectiveTitle = computed(() => {
});
const extraProps = computed(() => {
if (forBatch.value) {
return Object.fromEntries(props.invocationsList.map((invocation) => [invocation.id, invocation]));
}
const params: {
workflow_id?: string;
history_id?: string;
Expand All @@ -72,7 +80,9 @@ const extraProps = computed(() => {
});
let gridConfig: GridConfig;
if (forStoredWorkflow.value) {
if (forBatch.value) {
gridConfig = invocationsBatchConfig;
} else if (forStoredWorkflow.value) {
gridConfig = invocationsWorkflowGridConfig;
} else if (forHistory.value) {
gridConfig = invocationsHistoryGridConfig;
Expand All @@ -97,9 +107,9 @@ function refreshTable() {
:grid-message="props.headerMessage"
:no-data-message="effectiveNoInvocationsMessage"
:extra-props="extraProps"
:embedded="forStoredWorkflow || forHistory">
:embedded="forStoredWorkflow || forHistory || forBatch">
<template v-slot:expanded="{ rowData }">
<span class="float-right position-absolute mr-4" style="right: 0" :data-invocation-id="rowData.id">
<span class="position-absolute ml-4" :data-invocation-id="rowData.id">
<small>
<b>Last updated: <UtcDate :date="rowData.update_time" mode="elapsed" />; Invocation ID:</b>
</small>
Expand Down
80 changes: 80 additions & 0 deletions client/src/components/Grid/configs/invocationsBatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { faEye } from "@fortawesome/free-solid-svg-icons";

import { type WorkflowInvocation } from "@/api/invocations";
import { getAppRoot } from "@/onload";

import { type FieldArray, type GridConfig } from "./types";

/**
* Request and return invocations for the given workflow (and current user) from server
*/
async function getData(
offset: number,
limit: number,
search: string,
sort_by: string,
sort_desc: boolean,
extraProps?: Record<string, unknown>
) {
// extra props will be Record<string, Invocation>; get array of invocations
const data = Object.values(extraProps ?? {}) as WorkflowInvocation[];
const totalMatches = data.length;
return [data, totalMatches];
}

/**
* Declare columns to be displayed
*/
const fields: FieldArray = [
{
key: "expand",
title: null,
type: "expand",
},
{
key: "view",
title: "View",
type: "button",
icon: faEye,
handler: (data) => {
const url = `${getAppRoot()}workflows/invocations/${(data as WorkflowInvocation).id}`;
window.open(url, "_blank");
},
converter: () => "",
},
{
key: "history_id",
title: "History",
type: "history",
},
{
key: "create_time",
title: "Invoked",
type: "date",
},
{
key: "state",
title: "State",
type: "helptext",
converter: (data) => {
const invocation = data as WorkflowInvocation;
return `galaxy.invocations.states.${invocation.state}`;
},
},
];

/**
* Grid configuration
*/
const gridConfig: GridConfig = {
id: "invocations-batch-grid",
fields: fields,
getData: getData,
plural: "Workflow Invocations",
sortBy: "create_time",
sortDesc: true,
sortKeys: [],
title: "Workflow Invocations in Batch",
};

export default gridConfig;
141 changes: 49 additions & 92 deletions client/src/components/Workflow/Run/WorkflowRunSuccess.vue
Original file line number Diff line number Diff line change
@@ -1,102 +1,59 @@
<script setup lang="ts">
import { onMounted } from "vue";
import type { WorkflowInvocation } from "@/api/invocations";
import { useHistoryStore } from "@/stores/historyStore";
import { refreshContentsWrapper } from "@/utils/data";
import Webhooks from "@/utils/webhooks";
import GridInvocation from "@/components/Grid/GridInvocation.vue";
import WorkflowInvocationState from "@/components/WorkflowInvocationState/WorkflowInvocationState.vue";
const props = defineProps<{
workflowName: string;
invocations: WorkflowInvocation[];
}>();
onMounted(() => {
new Webhooks.WebhookView({
type: "workflow",
toolId: null,
toolVersion: null,
});
refreshContentsWrapper();
});
const historyStore = useHistoryStore();
const targetHistories = props.invocations.reduce((histories, invocation) => {
if (invocation.history_id && !histories.includes(invocation.history_id)) {
histories.push(invocation.history_id);
}
return histories;
}, [] as string[]);
const wasNewHistoryTarget =
props.invocations.length > 0 &&
props.invocations[0]?.history_id &&
historyStore.currentHistoryId !== props.invocations[0].history_id;
</script>

<template>
<div>
<div class="donemessagelarge">
<p>
Successfully invoked workflow <b>{{ workflowName }}</b>
<em v-if="multipleInvocations"> - {{ timesExecuted }} times</em>.
</p>

<p v-if="multipleHistoryTargets">
Successfully invoked workflow <b>{{ props.workflowName }}</b>
<em v-if="props.invocations.length > 1"> - {{ props.invocations.length }} times</em>.
<span v-if="targetHistories.length > 1">
This workflow will generate results in multiple histories. You can observe progress in the
<router-link to="/histories/view_multiple">history multi-view</router-link>.
</p>
<p v-else-if="wasNewHistoryTarget">
This workflow will generate results in a new history.
<a class="workflow-new-history-target-link" href="javascript:void(0)" @click="switchHistory"
>Switch to that history now</a
>.
</p>
<p v-else>You can check the status of queued jobs and view the resulting data the History panel.</p>
<p>
View all of your workflow invocations in the
<router-link to="/workflows/invocations">Invocations List</router-link>.
</p>
</span>
</div>
<GridInvocation v-if="props.invocations.length > 1" :invocations-list="props.invocations" />
<WorkflowInvocationState
v-for="(invocation, index) in invocations"
:key="invocation.id"
:index="index"
:invocation-id="invocation.id"
full-page />
v-else-if="props.invocations.length === 1 && props.invocations[0]"
:invocation-id="props.invocations[0].id"
:new-history-target="wasNewHistoryTarget ? props.invocations[0].history_id : undefined"
is-full-page
success />
<div id="webhook-view"></div>
</div>
</template>

<script>
import WorkflowInvocationState from "components/WorkflowInvocationState/WorkflowInvocationState";
import { getAppRoot } from "onload/loadConfig";
import { mapActions, mapState } from "pinia";
import { refreshContentsWrapper } from "utils/data";
import Webhooks from "utils/webhooks";
import { useHistoryStore } from "@/stores/historyStore";
export default {
components: {
WorkflowInvocationState,
},
props: {
workflowName: {
type: String,
required: true,
},
invocations: {
type: Array,
required: true,
},
},
computed: {
...mapState(useHistoryStore, ["currentHistoryId"]),
timesExecuted() {
return this.invocations.length;
},
multipleInvocations() {
return this.timesExecuted > 1;
},
multipleHistoryTargets() {
return this.targetHistories.length > 1;
},
targetHistories() {
return this.invocations.reduce((histories, invocation) => {
if (invocation.history_id && !histories.includes(invocation.history_id)) {
histories.push(invocation.history_id);
}
return histories;
}, []);
},
multiHistoryView() {
return `${getAppRoot()}histories/view_multiple`;
},
wasNewHistoryTarget() {
if (this.invocations.length < 1) {
return false;
}
return this.invocations[0].history_id && this.currentHistoryId != this.invocations[0].history_id;
},
},
mounted() {
new Webhooks.WebhookView({
type: "workflow",
toolId: null,
toolVersion: null,
});
refreshContentsWrapper();
},
methods: {
...mapActions(useHistoryStore, ["setCurrentHistory"]),
async switchHistory() {
await this.setCurrentHistory(this.invocations[0].history_id);
},
},
};
</script>
Loading

0 comments on commit 642b3a6

Please sign in to comment.