Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
08f491e
Refactor Lint component to typescript
mvdbeek Feb 3, 2025
31f2968
Switch template and script tags
mvdbeek Feb 3, 2025
ac61022
Use useLintData and refactor `Lint.vue` to composition + ts
mvdbeek Feb 4, 2025
e3c9dcd
Hack Best Practices into special workflow thing
mvdbeek Feb 5, 2025
65ed12a
convert `LintSection` to composition + ts
ahmedhamidawan Jan 21, 2026
e04df19
use `confirmDialog` composable in lint
ahmedhamidawan Jan 21, 2026
5c7241b
remove unused `highlight-id` prop from `WorkflowGraph`
ahmedhamidawan Jan 21, 2026
9dc1cd7
convert lint items to cards
ahmedhamidawan Jan 21, 2026
d46a70a
highlight to-lint steps/inputs/outputs on the graph
ahmedhamidawan Jan 21, 2026
fe62616
add duplicate labels lint section, add `hasInputSteps` to step store
ahmedhamidawan Jan 22, 2026
a711988
fix and improve attribute best practices highlighting
ahmedhamidawan Jan 22, 2026
7864097
compute resolved and total issues in `useLinting`
ahmedhamidawan Jan 23, 2026
df68de7
ensure all lint sections have `data-description`
ahmedhamidawan Jan 23, 2026
9837dbc
show remaining best practices to be resolved on best practices activity
ahmedhamidawan Jan 23, 2026
2c63a6e
update best practices as soon as datatypes mapper is fetched
ahmedhamidawan Jan 23, 2026
f91e34b
ensure workflow saves update step indices
ahmedhamidawan Jan 23, 2026
b0463d5
disable disconnected input step fix links until user saves
ahmedhamidawan Jan 23, 2026
3a04203
improve `LintData` type and use it as prop
ahmedhamidawan Jan 23, 2026
318d3a9
fix `fromSimple` params
ahmedhamidawan Jan 23, 2026
8febada
remove `stepIndicesConsistent`
ahmedhamidawan Jan 23, 2026
b1d4633
show a new `LoadingOverlay` component (uses `GalaxyLoader`) on wf save
ahmedhamidawan Jan 24, 2026
bf7e8ae
add a success 🎉 reaction when all best practices are met
ahmedhamidawan Jan 24, 2026
fb5795b
use `LintSection` instead of a `GCard` for no outputs issue
ahmedhamidawan Jan 26, 2026
92ca958
move best practices activity above other fixed activities
ahmedhamidawan Jan 26, 2026
cd06ac0
adjust workflow graph transform when a save fetches updates positions
ahmedhamidawan Jan 27, 2026
e698250
hide workflow editor if read me is active, instead of not rendering i…
ahmedhamidawan Jan 27, 2026
b41f05c
shorten completed practices, render 1 annotation practice if both are…
ahmedhamidawan Jan 27, 2026
a455789
separate and move high priority best practices to top
ahmedhamidawan Jan 27, 2026
1dd6192
separate count for critical and attribute practices; render count vs …
ahmedhamidawan Jan 28, 2026
7fee8f3
fix/improve existing client tests related to best practices recent ch…
ahmedhamidawan Jan 29, 2026
3468922
add extensive tests for workflow best practices
ahmedhamidawan Jan 30, 2026
d439265
ensure inputs created in workflow refactors do not overlap
ahmedhamidawan Feb 1, 2026
f4638ee
add integration test that ensures new refactored inputs do not overlap
ahmedhamidawan Feb 1, 2026
7b3249c
if a step is active before save (form is open), reopen it on save
ahmedhamidawan Feb 1, 2026
71a5967
add viewport transform retention to the refactor method as well
ahmedhamidawan Feb 3, 2026
8950ed5
add tests for coordinate shift adjustment in useWorkflowBoundingBox
claude Feb 3, 2026
e5f647a
if no steps in a workflow, prevent infinity adjusted transform values
ahmedhamidawan Feb 3, 2026
7d65ea5
convert workflow editor version selector to a nice new multiselect
ahmedhamidawan Feb 3, 2026
b91d908
add a switch to latest version option in workflow editor
ahmedhamidawan Feb 3, 2026
3ecc184
convert `RefactorConfirmationModal` to composition API and TS
ahmedhamidawan Feb 3, 2026
c7cd395
implement extensive typing for workflow refactoring methods
ahmedhamidawan Feb 3, 2026
44ec598
add a version param to refactor workflow payload
ahmedhamidawan Feb 4, 2026
15fc412
add integration test for refactoring specific workflow versions
claude Feb 4, 2026
bec1512
add a confirm dialog to let user opt out of refactoring a prior version
ahmedhamidawan Feb 4, 2026
aaeda4a
add tests for confirm dialog when refactoring non-latest workflow ver…
claude Feb 4, 2026
af7ad51
prevent error checking for latest when version can be null
ahmedhamidawan Feb 4, 2026
0bd851b
make sure version is defined when versions exist
ahmedhamidawan Feb 11, 2026
995bd52
move `WorkflowTransform` type to `/modules/geometry`; use in more places
ahmedhamidawan Feb 16, 2026
004bb7a
use `WORKFLOW_SIMPLE_CAT_TWICE` in refactor API test
ahmedhamidawan Feb 16, 2026
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
2 changes: 2 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20557,6 +20557,8 @@ export interface components {
* @default export
*/
style: string;
/** Version */
version?: number | null;
};
/** RefactorResponse */
RefactorResponse: {
Expand Down
35 changes: 35 additions & 0 deletions client/src/api/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { rethrowSimple } from "@/utils/simple-error";
import { GalaxyApi } from "./client";

export type Creator = components["schemas"]["Person"] | components["schemas"]["CreatorOrganization"];
export type RefactorRequestAction = components["schemas"]["RefactorRequest"]["actions"][number];
export type RefactorResponse = components["schemas"]["RefactorResponse"];
export type RefactorResponseActionExecution = RefactorResponse["action_executions"][number];
export type StoredWorkflowDetailed = components["schemas"]["StoredWorkflowDetailed"];
export type WorkflowStepTyped = StoredWorkflowDetailed["steps"][number];

Expand Down Expand Up @@ -36,6 +39,13 @@ export type WorkflowSummary = {
show_in_tool_panel: boolean;
};

// TODO: Use schema type once `/api/workflows/{workflow_id}/versions` is typed.
export interface WorkflowVersion {
steps: number;
update_time: string;
version: number;
}

export type AnyWorkflow = WorkflowSummary | StoredWorkflowDetailed;

type SortBy = "create_time" | "update_time" | "name";
Expand Down Expand Up @@ -107,6 +117,31 @@ export async function getWorkflowInfo(workflowId: string, version?: number, inst
return data;
}

export async function refactor(
id: string,
actions: RefactorRequestAction[],
style: string,
dryRun = false,
version?: number,
) {
const { data, error } = await GalaxyApi().PUT("/api/workflows/{workflow_id}/refactor", {
params: {
path: { workflow_id: id },
},
body: {
actions: actions,
style: style,
dry_run: dryRun,
version: version,
},
});
if (error) {
rethrowSimple(error);
}

return data as RefactorResponse;
}

export async function undeleteWorkflow(id: string): Promise<WorkflowSummary> {
const { data, error } = await GalaxyApi().POST("/api/workflows/{workflow_id}/undelete", {
params: {
Expand Down
71 changes: 45 additions & 26 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const props = withDefaults(
defaultActivities?: Activity[];
activityBarId?: string;
specialActivities?: Activity[];
exitActivity?: Activity;
showAdmin?: boolean;
optionsTitle?: string;
optionsTooltip?: string;
Expand All @@ -50,6 +51,7 @@ const props = withDefaults(
defaultActivities: undefined,
activityBarId: "default",
specialActivities: () => [],
exitActivity: undefined,
showAdmin: true,
optionsTitle: "More",
optionsHeading: "Additional Activities",
Expand Down Expand Up @@ -293,6 +295,8 @@ defineExpose({
v-else
:id="`${activity.id}`"
:key="activity.id"
:indicator="activity.indicator"
:indicator-variant="activity.indicatorVariant"
:activity-bar-id="props.activityBarId"
:icon="activity.icon"
:is-active="isActiveRoute(activity.to)"
Expand All @@ -306,39 +310,15 @@ defineExpose({
</draggable>
</b-nav>
<b-nav v-if="!isAnonymous" vertical class="activity-footer flex-nowrap p-1">
<NotificationItem
v-if="isConfigLoaded && config.enable_notification_system"
id="notifications"
:activity-bar-id="props.activityBarId"
:icon="faBell"
:is-active="isActiveSideBar('notifications') || isActiveRoute('/user/notifications')"
title="Notifications"
@click="toggleSidebar('notifications')" />
<ActivityItem
id="settings"
:activity-bar-id="props.activityBarId"
:icon="props.optionsIcon"
:is-active="isActiveSideBar('settings')"
:title="props.optionsTitle"
:tooltip="props.optionsTooltip"
@click="toggleSidebar('settings')" />
<ActivityItem
v-if="isAdmin && showAdmin"
id="admin"
:activity-bar-id="props.activityBarId"
:icon="faUserCog"
:is-active="isActiveSideBar('admin')"
title="Admin"
tooltip="Administer this Galaxy"
variant="danger"
@click="toggleSidebar('admin')" />
<template v-for="activity in props.specialActivities">
<ActivityItem
v-if="activity.panel"
:id="`${activity.id}`"
:key="activity.id"
:activity-bar-id="props.activityBarId"
:icon="activity.icon"
:indicator="activity.indicator"
:indicator-variant="activity.indicatorVariant"
:is-active="panelActivityIsActive(activity)"
:title="activity.title"
:tooltip="activity.tooltip"
Expand All @@ -351,13 +331,52 @@ defineExpose({
:key="activity.id"
:activity-bar-id="props.activityBarId"
:icon="activity.icon"
:indicator="activity.indicator"
:indicator-variant="activity.indicatorVariant"
:is-active="isActiveRoute(activity.to)"
:title="activity.title"
:tooltip="activity.tooltip"
:to="activity.to ?? undefined"
:variant="activity.variant"
@click="onActivityClicked(activity)" />
</template>
<NotificationItem
v-if="isConfigLoaded && config.enable_notification_system"
id="notifications"
:activity-bar-id="props.activityBarId"
:icon="faBell"
:is-active="isActiveSideBar('notifications') || isActiveRoute('/user/notifications')"
title="Notifications"
@click="toggleSidebar('notifications')" />
<ActivityItem
id="settings"
:activity-bar-id="props.activityBarId"
:icon="props.optionsIcon"
:is-active="isActiveSideBar('settings')"
:title="props.optionsTitle"
:tooltip="props.optionsTooltip"
@click="toggleSidebar('settings')" />
<ActivityItem
v-if="isAdmin && showAdmin"
id="admin"
:activity-bar-id="props.activityBarId"
:icon="faUserCog"
:is-active="isActiveSideBar('admin')"
title="Admin"
tooltip="Administer this Galaxy"
variant="danger"
@click="toggleSidebar('admin')" />
<ActivityItem
v-if="props.exitActivity"
:id="`${props.exitActivity.id}`"
:activity-bar-id="props.activityBarId"
:icon="props.exitActivity.icon"
:indicator="props.exitActivity.indicator"
:indicator-variant="props.exitActivity.indicatorVariant"
:title="props.exitActivity.title"
:tooltip="props.exitActivity.tooltip"
:variant="props.exitActivity.variant"
@click="onActivityClicked(props.exitActivity)" />
</b-nav>
</div>
<FlexPanel
Expand Down
27 changes: 24 additions & 3 deletions client/src/components/ActivityBar/ActivityItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export interface Props {
activityBarId: string;
title?: string;
icon?: IconDefinition;
indicator?: number;
indicator?: number | IconDefinition;
indicatorVariant?: ActivityVariant;
isActive?: boolean;
tooltip?: string;
tooltipPlacement?: Placement;
Expand All @@ -41,6 +42,7 @@ const props = withDefaults(defineProps<Props>(), {
title: undefined,
icon: () => faQuestion,
indicator: 0,
indicatorVariant: "danger",
isActive: false,
options: undefined,
progressPercentage: 0,
Expand Down Expand Up @@ -89,9 +91,20 @@ const meta = computed(() => store.metaForId(props.id));
}" />
</span>
<div class="nav-icon">
<span v-if="indicator > 0" class="nav-indicator" data-description="activity indicator">
<span
v-if="typeof indicator === 'number' && indicator > 0"
class="nav-indicator"
:class="`${indicatorVariant}-indicator`"
data-description="activity indicator">
{{ Math.min(indicator, 99) }}
</span>
<span
v-else-if="typeof indicator !== 'number'"
class="nav-indicator"
:class="`${indicatorVariant}-indicator`"
data-description="activity indicator">
<FontAwesomeIcon :icon="indicator" />
</span>
<FontAwesomeIcon :icon="icon" />
</div>
<TextShort v-if="title" :text="localize(title)" class="nav-title" />
Expand Down Expand Up @@ -137,8 +150,16 @@ const meta = computed(() => store.metaForId(props.id));
}

.nav-indicator {
&.danger-indicator {
background: $brand-danger;
}
&.primary-indicator {
background: $brand-primary;
}
&.disabled-indicator {
background: $brand-secondary;
}
align-items: center;
background: $brand-danger;
border-radius: 50%;
color: $brand-light;
display: flex;
Expand Down
54 changes: 54 additions & 0 deletions client/src/components/Common/LoadingOverlay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
/**
* A loading overlay with a Galaxy Logo spinner.
*
* TODO: Should probably be improved and standardized to become a `GOverlay` component.
*/

import GalaxyLoader from "../GalaxyLoader.vue";

interface Props {
/** Opacity of the overlay background, between 0 and 1.
* @default 0.5
*/
opacity?: number;
/** Variant of the Galaxy loader.
* @default "light"
*/
variant?: "light" | "dark" | "eu";
}

const props = withDefaults(defineProps<Props>(), {
opacity: 0.5,
variant: "light",
});
</script>

<template>
<div class="galaxy-loading-shade" :style="{ '--opacity': props.opacity ?? 0.5 }">
<div class="galaxy-loading-spinner">
<GalaxyLoader :variant="props.variant" />
</div>
</div>
</template>

<style scoped>
.galaxy-loading-shade {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, var(--opacity, 0.5));
z-index: 10;
border-radius: 0.2rem;
}

.galaxy-loading-spinner {
position: sticky;
top: 50%;
display: flex;
justify-content: center;
transform: translateY(-50%);
}
</style>
31 changes: 28 additions & 3 deletions client/src/components/GalaxyLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,39 @@ export default {
border-radius: 50px;
position: relative;
overflow: hidden;
animation: galaxy-loader_pulse 2s ease-in-out infinite;
}

.galaxy-loader_light {
background-color: transparent;
border: 2px solid #58585a;
border: 1px solid #58585a;
box-sizing: border-box;
box-shadow:
0 8px 32px rgba(88, 88, 90, 0.1),
0 4px 16px rgba(88, 88, 90, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
backdrop-filter: blur(1px);
}

.galaxy-loader_dark {
background-color: #2c3143;
border: 2px solid #2c3143;
border: 1px solid #2c3143;
box-sizing: border-box;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 4px 16px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.galaxy-loader_eu {
background-color: transparent;
border: 2px solid #063591;
border: 1px solid #063591;
box-sizing: border-box;
box-shadow:
0 8px 32px rgba(6, 53, 145, 0.15),
0 4px 16px rgba(6, 53, 145, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
backdrop-filter: blur(1px);
}

.galaxy-loader > div {
Expand Down Expand Up @@ -160,4 +175,14 @@ export default {
-webkit-transform: scaleX(1.2);
}
}

@keyframes galaxy-loader_pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
</style>
Loading
Loading