Skip to content

Commit

Permalink
Latest prototype of a minimal workflow UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Sep 16, 2024
1 parent 2f52e7a commit 6c3fa57
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 29 deletions.
16 changes: 13 additions & 3 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,21 @@ function onToggleSidebar(toggle: string = "", to: string | null = null) {
userStore.toggleSideBar(toggle);
}
const syncActivities = () => {
activityStore.sync();
if (config.value && ["workflow_centric", "workflow_runner"].indexOf(config.value.client_mode) >= 0) {
userStore.untoggleToolbarIfNeeded();
}
};
watch(
() => hashedUserId.value,
() => {
activityStore.sync();
}
syncActivities,
);
watch(
isConfigLoaded,
syncActivities,
);
</script>

Expand Down
7 changes: 5 additions & 2 deletions client/src/components/Workflow/List/WorkflowCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ interface Props {
workflow: any;
gridView?: boolean;
publishedView?: boolean;
allowWorkflowManagement?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
gridView: false,
publishedView: false,
allowWorkflowManagement: true,
});
const emit = defineEmits<{
Expand Down Expand Up @@ -134,6 +136,7 @@ async function onTagClick(tag: string) {
<WorkflowActions
:workflow="workflow"
:published="publishedView"
:allow-workflow-management="allowWorkflowManagement"
@refreshList="emit('refreshList', true)"
@toggleShowPreview="toggleShowPreview" />
</div>
Expand Down Expand Up @@ -185,7 +188,7 @@ async function onTagClick(tag: string) {

<div class="workflow-edit-run-buttons">
<BButton
v-if="!isAnonymous && !shared"
v-if="!isAnonymous && !shared && allowWorkflowManagement"
v-b-tooltip.hover.noninteractive
:disabled="workflow.deleted"
size="sm"
Expand All @@ -198,7 +201,7 @@ async function onTagClick(tag: string) {
</BButton>

<AsyncButton
v-else
v-else-if="allowWorkflowManagement"
v-b-tooltip.hover.noninteractive
size="sm"
:disabled="isAnonymous"
Expand Down
22 changes: 19 additions & 3 deletions client/src/components/Workflow/List/WorkflowList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ type WorkflowsList = Record<string, never>[];
interface Props {
activeList?: "my" | "shared_with_me" | "published";
clientMode?: "full" | "workflow_centric" | "workflow_runner";
initialFilterText?: string;
}
const props = withDefaults(defineProps<Props>(), {
activeList: "my",
clientMode: "full",
initialFilterText: "",
});
const router = useRouter();
Expand Down Expand Up @@ -85,6 +89,7 @@ const validFilters = computed(() => workflowFilters.value.getValidFilters(rawFil
const invalidFilters = computed(() => workflowFilters.value.getValidFilters(rawFilters.value, true).invalidFilters);
const isSurroundedByQuotes = computed(() => /^["'].*["']$/.test(filterText.value));
const hasInvalidFilters = computed(() => !isSurroundedByQuotes.value && Object.keys(invalidFilters.value).length > 0);
const allowWorkflowManagement = computed(() => props.clientMode == "full");
function updateFilterValue(filterKey: string, newValue: any) {
const currentFilterText = filterText.value;
Expand Down Expand Up @@ -194,6 +199,16 @@ watch([filterText, sortBy, sortDesc, showBookmarked], async () => {
await load(true);
});
watch(
props,
() => {
if(props.initialFilterText && filterText.value == "") {
filterText.value = props.initialFilterText;
}
},
{ immediate: true },
);
onMounted(() => {
if (router.currentRoute.query.owner) {
updateFilterValue("user", `'${router.currentRoute.query.owner}'`);
Expand All @@ -208,10 +223,10 @@ onMounted(() => {
<div class="d-flex flex-gapx-1">
<Heading h1 separator inline size="xl" class="flex-grow-1 mb-2">Workflows</Heading>

<WorkflowListActions />
<WorkflowListActions v-if="allowWorkflowManagement" />
</div>

<BNav pills justified class="mb-2">
<BNav v-if="allowWorkflowManagement" pills justified class="mb-2">
<BNavItem id="my" :active="activeList === 'my'" :disabled="userStore.isAnonymous" to="/workflows/list">
My workflows
<LoginRequired v-if="userStore.isAnonymous" target="my" title="Manage your workflows" />
Expand Down Expand Up @@ -247,7 +262,7 @@ onMounted(() => {
</template>
</FilterMenu>

<ListHeader ref="listHeader" show-view-toggle>
<ListHeader ref="listHeader" :show-view-toggle="allowWorkflowManagement">
<template v-slot:extra-filter>
<div v-if="activeList === 'my'">
Filter:
Expand Down Expand Up @@ -331,6 +346,7 @@ onMounted(() => {
:published-view="published"
:grid-view="view === 'grid'"
:class="view === 'grid' ? 'grid-view' : 'list-view'"
:allow-workflow-management="allowWorkflowManagement"
@refreshList="load"
@tagClick="(tag) => updateFilterValue('tag', `'${tag}'`)"
@update-filter="updateFilterValue" />
Expand Down
9 changes: 9 additions & 0 deletions client/src/components/Workflow/List/WorkflowListActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ const userStore = useUserStore();
const { isAnonymous } = storeToRefs(userStore);
interface Props {
advancedOptions: boolean;
}
withDefaults(defineProps<Props>(), {
advancedOptions: true,
});
const createButtonTitle = computed(() => {
if (isAnonymous.value) {
return "Log in to create workflow";
Expand Down Expand Up @@ -45,6 +53,7 @@ function navigateToOldCreate() {
<div id="workflow-list-actions" class="d-flex justify-content-between">
<div>
<BButton
v-if="advancedOptions"
id="workflow-create"
v-b-tooltip.hover.noninteractive
size="sm"
Expand Down
10 changes: 8 additions & 2 deletions client/src/entry/analysis/modules/Analysis.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script setup>
import { onMounted, onUnmounted, ref } from "vue";
import { computed, onMounted, onUnmounted, ref } from "vue";
import { useRouter } from "vue-router/composables";
import { useConfig } from "@/composables/config";
import { usePanels } from "@/composables/usePanels";
import CenterFrame from "./CenterFrame.vue";
Expand All @@ -13,6 +14,11 @@ import DragAndDropModal from "@/components/Upload/DragAndDropModal.vue";
const router = useRouter();
const showCenter = ref(false);
const { showPanels } = usePanels();
const { config } = useConfig();
const showHistoryPanel = computed(() => {
return showPanels.value && config.value && config.value.client_mode == "full";
});
// methods
function hideCenter() {
Expand Down Expand Up @@ -44,7 +50,7 @@ onUnmounted(() => {
<router-view :key="$route.fullPath" class="h-100" />
</div>
</div>
<FlexPanel v-if="showPanels" side="right">
<FlexPanel v-if="showHistoryPanel" side="right">
<HistoryIndex />
</FlexPanel>
<DragAndDropModal />
Expand Down
8 changes: 8 additions & 0 deletions client/src/entry/analysis/modules/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
<ToolForm v-if="isTool && !isUpload" v-bind="toolParams" />
<WorkflowRun v-else-if="isWorkflow" v-bind="workflowParams" />
<div v-else-if="isController" :src="controllerParams" />
<div v-else-if="isWorkflowCentric">
<WorkflowLanding :client-mode="config.client_mode" :initial-filter-text="config.simplified_workflow_landing_initial_filter_text" />
</div>
<CenterFrame v-else src="/welcome" />
</div>
</template>

<script>
import ToolForm from "components/Tool/ToolForm";
import WorkflowRun from "components/Workflow/Run/WorkflowRun";
import WorkflowLanding from "./WorkflowLanding";
import decodeUriComponent from "decode-uri-component";
import CenterFrame from "entry/analysis/modules/CenterFrame";
Expand All @@ -20,6 +24,7 @@ export default {
CenterFrame,
ToolForm,
WorkflowRun,
WorkflowLanding,
},
props: {
config: {
Expand All @@ -32,6 +37,9 @@ export default {
},
},
computed: {
isWorkflowCentric() {
return ["workflow_centric", "workflow_runner"].indexOf(this.config.client_mode) >= 0;
},
isController() {
return this.query.m_c && this.query.m_a;
},
Expand Down
16 changes: 16 additions & 0 deletions client/src/entry/analysis/modules/WorkflowLanding.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import WorkflowList from "@/components/Workflow/List/WorkflowList.vue";
interface Props {
initialFilterText: string;
clientMode: string;
}
defineProps<Props>()
</script>

<template>
<div>
<WorkflowList active-list="published" :client-mode="clientMode" :initial-filter-text="initialFilterText" />
</div>
</template>
69 changes: 54 additions & 15 deletions client/src/stores/activitySetup.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,68 @@
/**
* List of built-in activities
*/
import { type Activity } from "@/stores/activityStore";
import { type Activity, type ClientMode, type RawActivity } from "@/stores/activityStore";
import { type EventData } from "@/stores/eventStore";

export const Activities = [
function isWorkflowCentric(clientMode: ClientMode) : boolean {
return ["workflow_centric", "workflow_runner"].indexOf(clientMode) >= 0;
}

function unlessWorkflowCentric(clientMode: ClientMode): boolean {
if (isWorkflowCentric(clientMode)) {
return false;
} else {
return true;
}
}

function ifWorkflowCentric(clientMode: ClientMode): boolean {
if (isWorkflowCentric(clientMode)) {
return true;
} else {
return false;
}
}

export const ActivitiesRaw: RawActivity[] = [
{
anonymous: false,
description: "Displays currently running interactive tools (ITs), if these are enabled by the administrator.",
icon: "fa-laptop",
id: "interactivetools",
mutable: false,
optional: false,
optional: ifWorkflowCentric,
panel: false,
title: "Interactive Tools",
tooltip: "Show active interactive tools",
to: "/interactivetool_entry_points/list",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: true,
description: "Opens a data dialog, allowing uploads from URL, pasted content or disk.",
icon: "upload",
id: "upload",
mutable: false,
optional: false,
optional: ifWorkflowCentric,
panel: false,
title: "Upload",
to: null,
tooltip: "Download from URL or upload files from disk",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: true,
description: "Displays the tool panel to search and access all available tools.",
icon: "wrench",
id: "tools",
mutable: false,
optional: false,
optional: ifWorkflowCentric,
panel: true,
title: "Tools",
to: null,
to: "/tools",
tooltip: "Search and run tools",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: true,
Expand Down Expand Up @@ -81,7 +101,7 @@ export const Activities = [
title: "Visualization",
to: null,
tooltip: "Visualize datasets",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: true,
Expand All @@ -94,7 +114,7 @@ export const Activities = [
title: "Histories",
tooltip: "Show all histories",
to: "/histories/list",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: false,
Expand All @@ -107,7 +127,7 @@ export const Activities = [
title: "History Multiview",
tooltip: "Select histories to show in History Multiview",
to: "/histories/view_multiple",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: false,
Expand All @@ -120,7 +140,7 @@ export const Activities = [
title: "Datasets",
tooltip: "Show all datasets",
to: "/datasets/list",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: true,
Expand All @@ -133,7 +153,7 @@ export const Activities = [
title: "Pages",
tooltip: "Show all pages",
to: "/pages/list",
visible: true,
visible: unlessWorkflowCentric,
},
{
anonymous: false,
Expand All @@ -146,10 +166,29 @@ export const Activities = [
title: "Libraries",
tooltip: "Access data libraries",
to: "/libraries",
visible: true,
visible: unlessWorkflowCentric,
},
];

function resolveActivity(activity: RawActivity, clientMode: ClientMode) : Activity {
let optional = activity.optional;
let visible = activity.visible;
if (typeof optional === 'function') {
optional = optional(clientMode);
}
if (typeof visible === 'function') {
visible = visible(clientMode);
}
return { ...activity, optional, visible};
}

export function getActivities(clientMode: ClientMode) {
const resolve = (activity: RawActivity) => {
return resolveActivity(activity, clientMode);
}
return ActivitiesRaw.map(resolve);
}

export function convertDropData(data: EventData): Activity | null {
if (data.history_content_type === "dataset") {
return {
Expand Down
Loading

0 comments on commit 6c3fa57

Please sign in to comment.