Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change InvocationsList into a grid using GridList #18088

Merged
merged 8 commits into from
May 20, 2024
1 change: 1 addition & 0 deletions client/src/api/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { components, fetcher } from "@/api/schema";
export type StoredWorkflowDetailed = components["schemas"]["StoredWorkflowDetailed"];

export const workflowsFetcher = fetcher.path("/api/workflows").method("get").create();
export const workflowFetcher = fetcher.path("/api/workflows/{workflow_id}").method("get").create();

export const invocationCountsFetcher = fetcher.path("/api/workflows/{workflow_id}/counts").method("get").create();

Expand Down
34 changes: 34 additions & 0 deletions client/src/components/Grid/GridElements/GridExpand.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BLink } from "bootstrap-vue";

library.add(faChevronDown, faChevronUp);

interface Props {
detailsShowing?: boolean;
}
const props = defineProps<Props>();

const emit = defineEmits<{
(e: "show-details", show: boolean): void;
}>();

function showDetails() {
if (props.detailsShowing === undefined) {
emit("show-details", true);
} else {
emit("show-details", !props.detailsShowing);
}
}
</script>

<template>
<BLink data-description="toggle row details" @click.stop="showDetails">
<FontAwesomeIcon
:title="props.detailsShowing ? 'Hide details' : 'Show details'"
size="lg"
:icon="props.detailsShowing ? faChevronUp : faChevronDown" />
</BLink>
</template>
130 changes: 130 additions & 0 deletions client/src/components/Grid/GridInvocation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { computed } from "vue";

import invocationsGridConfig from "@/components/Grid/configs/invocations";
import invocationsHistoryGridConfig from "@/components/Grid/configs/invocationsHistory";
import invocationsWorkflowGridConfig from "@/components/Grid/configs/invocationsWorkflow";
import { useUserStore } from "@/stores/userStore";

import type { GridConfig } from "./configs/types";

import Heading from "../Common/Heading.vue";
import UtcDate from "../UtcDate.vue";
import WorkflowInvocationState from "../WorkflowInvocationState/WorkflowInvocationState.vue";
import GridList from "@/components/Grid/GridList.vue";

interface Props {
noInvocationsMessage?: string;
headerMessage?: string;
ownerGrid?: boolean;
filteredFor?: { type: "History" | "StoredWorkflow"; id: string; name: string };
}

const props = withDefaults(defineProps<Props>(), {
noInvocationsMessage: "No Workflow Invocations to display",
headerMessage: "",
ownerGrid: true,
filteredFor: undefined,
});

const { currentUser } = storeToRefs(useUserStore());

const forStoredWorkflow = computed(() => props.filteredFor?.type === "StoredWorkflow");
const forHistory = computed(() => props.filteredFor?.type === "History");

const effectiveNoInvocationsMessage = computed(() => {
let message = props.noInvocationsMessage;
if (props.filteredFor) {
message += ` for ${props.filteredFor?.name}`;
}
return message;
});

const effectiveTitle = computed(() => {
let retVal = `Workflow Invocations`;
const type = props.filteredFor?.type === "StoredWorkflow" ? "Workflow" : "History";
if (props.filteredFor && props.filteredFor?.name) {
retVal += ` for ${type} "${props.filteredFor.name}"`;
}
return retVal;
});

const extraProps = computed(() => {
const params: {
workflow_id?: string;
history_id?: string;
include_terminal?: boolean;
user_id?: string;
} = {};
if (forStoredWorkflow.value) {
params.workflow_id = props.filteredFor?.id;
}
if (forHistory.value) {
params.history_id = props.filteredFor?.id;
}
if (!props.ownerGrid) {
params.include_terminal = false;
} else if (currentUser.value && !currentUser.value?.isAnonymous) {
params.user_id = currentUser.value?.id;
}
return params;
});

let gridConfig: GridConfig;
if (forStoredWorkflow.value) {
gridConfig = invocationsWorkflowGridConfig;
} else if (forHistory.value) {
gridConfig = invocationsHistoryGridConfig;
} else {
gridConfig = invocationsGridConfig;
}

function refreshTable() {
// TODO: Implement
}
</script>

<template>
<div class="d-flex flex-column">
<div v-if="forStoredWorkflow || forHistory" class="d-flex">
<Heading h1 separator inline truncate size="xl" class="flex-grow-1 mb-2">{{ effectiveTitle }}</Heading>
</div>
<GridList
v-if="!currentUser?.isAnonymous && currentUser?.id"
class="grid-invocation"
:grid-config="gridConfig"
:grid-message="props.headerMessage"
:no-data-message="effectiveNoInvocationsMessage"
:extra-props="extraProps"
:embedded="forStoredWorkflow || forHistory">
<template v-slot:expanded="{ rowData }">
<span class="float-right position-absolute mr-4" style="right: 0" :data-invocation-id="rowData.id">
<small>
<b>Last updated: <UtcDate :date="rowData.update_time" mode="elapsed" />; Invocation ID:</b>
</small>
<code>{{ rowData.id }}</code>
</span>
<WorkflowInvocationState :invocation-id="rowData.id" @invocation-cancelled="refreshTable" />
</template>
</GridList>
</div>
</template>

<style scoped lang="scss">
.grid-invocation {
// prevent steps & zoom (within rowDetails) from overlapping header and footer
&:deep(.grid-header) {
z-index: 100;
}
&:deep(.grid-footer) {
z-index: 100;
}
&:deep(.flex-panel.right) {
z-index: 50;
}
&:deep(.zoom-control) {
z-index: 50;
}
}
</style>
Loading
Loading