Skip to content

Commit

Permalink
Merge pull request #16607 from itisAliRH/workflow-card
Browse files Browse the repository at this point in the history
New Workflow List and Card View
  • Loading branch information
mvdbeek committed Feb 16, 2024
2 parents 8f956cb + cf95c38 commit 061993a
Show file tree
Hide file tree
Showing 46 changed files with 2,369 additions and 1,536 deletions.
4 changes: 1 addition & 3 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import FlexPanel from "@/components/Panels/FlexPanel.vue";
import NotificationsPanel from "@/components/Panels/NotificationsPanel.vue";
import SettingsPanel from "@/components/Panels/SettingsPanel.vue";
import ToolPanel from "@/components/Panels/ToolPanel.vue";
import WorkflowPanel from "@/components/Panels/WorkflowPanel.vue";
const { config, isConfigLoaded } = useConfig();
Expand Down Expand Up @@ -151,7 +150,7 @@ function onToggleSidebar(toggle: string) {
:to="activity.to"
@click="onToggleSidebar()" />
<ActivityItem
v-else-if="['tools', 'workflows', 'visualizations'].includes(activity.id)"
v-else-if="['tools', 'visualizations'].includes(activity.id)"
:id="`activity-${activity.id}`"
:key="activity.id"
:icon="activity.icon"
Expand Down Expand Up @@ -193,7 +192,6 @@ function onToggleSidebar(toggle: string) {
</div>
<FlexPanel v-if="isSideBarOpen" side="left" :collapsible="false">
<ToolPanel v-if="isActiveSideBar('tools')" />
<WorkflowPanel v-else-if="isActiveSideBar('workflows')" />
<VisualizationPanel v-else-if="isActiveSideBar('visualizations')" />
<NotificationsPanel v-else-if="isActiveSideBar('notifications')" />
<SettingsPanel v-else-if="isActiveSideBar('settings')" />
Expand Down
61 changes: 29 additions & 32 deletions client/src/components/Common/AsyncButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,36 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { ref } from "vue";
const loading = ref(false);
interface Props {
icon: string | object;
title?: string;
disabled?: boolean;
loadingTitle?: string;
size?: "sm" | "md" | "lg";
action: () => Promise<void>;
variant?:
| "outline-primary"
| "primary"
| "secondary"
| "success"
| "danger"
| "warning"
| "info"
| "light"
| "dark"
| "link";
}
const props = defineProps({
icon: {
type: String,
required: true,
},
title: {
type: String,
required: false,
default: "",
},
action: {
type: Function,
required: true,
},
size: {
type: String,
required: false,
default: "md",
},
variant: {
type: String,
required: false,
default: "link",
},
disabled: {
type: Boolean,
required: false,
default: false,
},
const props = withDefaults(defineProps<Props>(), {
title: "",
size: "md",
variant: "link",
loadingTitle: "Loading...",
});
const loading = ref(false);
async function onClick() {
loading.value = true;
await props.action();
Expand All @@ -51,8 +48,8 @@ async function onClick() {
:variant="variant"
:disabled="loading || disabled"
@click="onClick">
<span v-if="loading" class="loading-icon fa fa-spinner fa-spin" title="loading"></span>
<FontAwesomeIcon v-else :icon="props.icon" @click="onClick" />
<span v-if="loading" class="loading-icon fa fa-spinner fa-spin" :title="loadingTitle" />
<FontAwesomeIcon v-else :icon="props.icon" fixed-width />
<slot></slot>
</BButton>
</template>
123 changes: 123 additions & 0 deletions client/src/components/Common/ListHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faAngleDown, faAngleUp, faBars, faGripVertical } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { computed, ref } from "vue";
import { useUserStore } from "@/stores/userStore";
library.add(faAngleDown, faAngleUp, faBars, faGripVertical);
type ListView = "grid" | "list";
type SortBy = "create_time" | "update_time" | "name";
interface Props {
showViewToggle?: boolean;
}
withDefaults(defineProps<Props>(), {
showViewToggle: false,
});
const userStore = useUserStore();
const sortDesc = ref(true);
const sortBy = ref<SortBy>("update_time");
const listViewMode = computed<ListView>(() => (userStore.preferredListViewMode as ListView) || "grid");
function onSort(newSortBy: SortBy) {
if (sortBy.value === newSortBy) {
sortDesc.value = !sortDesc.value;
} else {
sortBy.value = newSortBy;
}
}
function onToggleView(newView: ListView) {
userStore.setPreferredListViewMode(newView);
}
defineExpose({
sortBy,
sortDesc,
listViewMode,
});
</script>

<template>
<div class="list-header">
<div class="list-header-filters">
Sort by:
<BButtonGroup>
<BButton
id="sortby-name"
v-b-tooltip.hover
size="sm"
:title="sortDesc ? 'Sort by name ascending' : 'Sort by name descending'"
:pressed="sortBy === 'name'"
variant="outline-primary"
@click="onSort('name')">
<FontAwesomeIcon v-show="sortBy === 'name'" :icon="sortDesc ? faAngleDown : faAngleUp" />
Name
</BButton>

<BButton
id="sortby-update-time"
v-b-tooltip.hover
size="sm"
:title="sortDesc ? 'Sort by update time ascending' : 'Sort by update time descending'"
:pressed="sortBy === 'update_time'"
variant="outline-primary"
@click="onSort('update_time')">
<FontAwesomeIcon v-show="sortBy === 'update_time'" :icon="sortDesc ? faAngleDown : faAngleUp" />
Update time
</BButton>
</BButtonGroup>

<slot name="extra-filter" />
</div>

<div v-if="showViewToggle">
Display:
<BButtonGroup>
<BButton
id="view-grid"
v-b-tooltip
title="Grid view"
size="sm"
:pressed="listViewMode === 'grid'"
variant="outline-primary"
@click="onToggleView('grid')">
<FontAwesomeIcon :icon="faGripVertical" />
</BButton>

<BButton
id="view-list"
v-b-tooltip
title="List view"
size="sm"
:pressed="listViewMode === 'list'"
variant="outline-primary"
@click="onToggleView('list')">
<FontAwesomeIcon :icon="faBars" />
</BButton>
</BButtonGroup>
</div>
</div>
</template>

<style scoped lang="scss">
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
.list-header-filters {
display: flex;
gap: 0.25rem;
flex-wrap: wrap;
align-items: center;
}
}
</style>
20 changes: 20 additions & 0 deletions client/src/components/Common/LoginRequired.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { BPopover } from "bootstrap-vue";
import { useUserStore } from "@/stores/userStore";
import { withPrefix } from "@/utils/redirect";
defineProps<{
title: string;
target: string;
}>();
const userStore = useUserStore();
</script>

<template>
<BPopover v-if="userStore.isAnonymous" :target="target" triggers="hover focus" placement="bottom">
<template v-slot:title> {{ title }} </template>
Please <a :href="withPrefix('/login')">log in or register</a> to use this feature.
</BPopover>
</template>
64 changes: 20 additions & 44 deletions client/src/components/Common/TextSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,39 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { computed } from "vue";
import { computed, ref } from "vue";
const props = defineProps({
description: {
type: String,
required: true,
},
showDetails: {
type: Boolean,
default: false,
},
maxLength: {
type: Number,
default: 150,
},
});
library.add(faChevronUp, faChevronDown);
const emit = defineEmits<{
(e: "update:show-details", showDetails: boolean): void;
}>();
interface Props {
maxLength?: number;
description: string;
}
const propShowDetails = computed({
get: () => {
return props.showDetails;
},
set: (val) => {
emit("update:show-details", val);
},
const props = withDefaults(defineProps<Props>(), {
maxLength: 150,
});
library.add(faChevronUp, faChevronDown);
const collapsedEnableIcon = "fas fa-chevron-down";
const collapsedDisableIcon = "fas fa-chevron-up";
// summarized length
const x = Math.round(props.maxLength - props.maxLength / 2);
const showDetails = ref(false);
const summary = computed(() => props.description.length > props.maxLength);
const textTooLong = computed(() => props.description.length > props.maxLength);
const text = computed(() =>
props.description.length > props.maxLength ? props.description.slice(0, x) : props.description
textTooLong.value && !showDetails.value
? props.description.slice(0, Math.round(props.maxLength - props.maxLength / 2)) + "..."
: props.description
);
</script>

<template>
<div>
{{ text }}
<span v-if="summary">
<a
v-if="!propShowDetails"
class="text-summary-expand"
href="javascript:void(0)"
@click.stop="propShowDetails = true">
... <FontAwesomeIcon :icon="collapsedEnableIcon" />
</a>
<a v-else href="javascript:void(0)" @click.stop="propShowDetails = false">
... <FontAwesomeIcon :icon="collapsedDisableIcon" />
</a>
<span
v-if="textTooLong"
v-b-tooltip.hover
class="info-icon cursor-pointer"
:title="textTooLong ? 'Show more' : 'Show less'"
@click="showDetails = !showDetails">
<FontAwesomeIcon :icon="showDetails ? 'chevron-up' : 'chevron-down'" />
</span>
</div>
</template>
Loading

0 comments on commit 061993a

Please sign in to comment.