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

[WIP] Remote File Sources and Storage Locations redesign #19521

Open
wants to merge 22 commits into
base: dev
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6301991
Move breakpoints SCSS file and import paths
itisAliRH Jan 29, 2025
57efb8f
Adds HTML rendering and expand text options to TextSummary
itisAliRH Jan 29, 2025
278558b
Refactors ManageIndexHeader component
itisAliRH Jan 29, 2025
5f29f9b
Adds file source type details and valid filters
itisAliRH Jan 29, 2025
a121325
Adds FileSourceTemplateCard component
itisAliRH Jan 29, 2025
c54ac00
Enhances file source template selection UI
itisAliRH Jan 29, 2025
26cf3be
Adds navigation links and heading to CreateInstance component
itisAliRH Jan 29, 2025
7ecdf1d
Removes SelectTemplate component
itisAliRH Jan 31, 2025
bbc5bc9
Adds user preferences link with icon to CreateInstance component header
itisAliRH Jan 31, 2025
9e30e7c
Adds header prop and updates button ID generation in ConfigTemplate h…
itisAliRH Jan 31, 2025
6bb67fe
Adds headers to object store and file source routes
itisAliRH Jan 31, 2025
5bbe64c
Consolidates config template card component
itisAliRH Jan 31, 2025
57a369c
Updates template card and icons in CreateUserFileSource
itisAliRH Jan 31, 2025
67512be
Adds heading with navigation links to CreateInstance
itisAliRH Jan 31, 2025
88b80bf
Adds object store template types and valid filters
itisAliRH Jan 31, 2025
4052539
Updates ObjectStoreBadge component to use FontAwesome icons directly
itisAliRH Jan 31, 2025
3984106
Removes unused badge icons import
itisAliRH Jan 31, 2025
6c8d66d
Refactors ObjectStoreBadges component and updates styles
itisAliRH Jan 31, 2025
2948f8e
Enhances template selection UI and filtering
itisAliRH Jan 31, 2025
cf28418
Updates badge type import and usage
itisAliRH Jan 31, 2025
d21a19b
Removes unused SelectTemplate component
itisAliRH Jan 31, 2025
5000391
Removes SelectTemplate component
itisAliRH Jan 31, 2025
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
Prev Previous commit
Next Next commit
Enhances file source template selection UI
Adds search and filter functionality for file source templates
Use FileSourceTemplateCard for each available template
Improves error handling and loading indicators
Updates UI components for better user experience
  • Loading branch information
itisAliRH committed Feb 3, 2025
commit c54ac006890a8e681718946e3eca379873c6659b
175 changes: 148 additions & 27 deletions client/src/components/FileSources/Templates/CreateUserFileSource.vue
Original file line number Diff line number Diff line change
@@ -1,41 +1,81 @@
<script lang="ts" setup>
import { BAlert } from "bootstrap-vue";
import { computed, ref } from "vue";
<script setup lang="ts">
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BAlert, BButton, BFormInput } from "bootstrap-vue";
import { computed, onMounted, ref } from "vue";
import { RouterLink } from "vue-router";
import { useRouter } from "vue-router/composables";

import {
FileSourcesValidFilters,
type FileSourceTypes,
type FileSourceTypesDetail,
templateTypes,
} from "@/api/fileSources";
import { Toast } from "@/composables/toast";
import { useFileSourceTemplatesStore } from "@/stores/fileSourceTemplatesStore";
import Filtering from "@/utils/filtering";

import SelectTemplate from "./SelectTemplate.vue";
import CreateInstance from "@/components/ConfigTemplates/CreateInstance.vue";
import Heading from "@/components/Common/Heading.vue";
import FileSourceTemplateCard from "@/components/FileSources/Templates/FileSourceTemplateCard.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";

const loadingTemplatesInfoMessage = "Loading file source templates";
type AvailableTypes = { type: FileSourceTypes } & FileSourceTypesDetail[keyof FileSourceTypesDetail];

const router = useRouter();

const fileSourceTemplatesStore = useFileSourceTemplatesStore();
fileSourceTemplatesStore.ensureTemplates();

const filterText = ref("");
const templates = computed(() => fileSourceTemplatesStore.latestTemplates);
const loading = computed(() => fileSourceTemplatesStore.loading);

const router = useRouter();
const templatesFilter = computed(() => new Filtering(FileSourcesValidFilters, undefined));
const typesAvailable = computed(() => {
const availableTypes: AvailableTypes[] = [];

const errorMessage = ref("");
for (const [key, value] of Object.entries(templateTypes)) {
if (templates.value.some((t) => t.type === key)) {
availableTypes.push({ type: key as FileSourceTypes, icon: value.icon, message: value.message });
}
}

function chooseTemplate(selectTemplateId: string) {
router.push({
path: `/file_source_templates/${selectTemplateId}/new`,
return availableTypes;
});
const filterActivated = computed(() => (t?: FileSourceTypes | "*") => {
return (
(t === "*" && !templatesFilter.value.getFilterValue(filterText.value, "type")) ||
(t && templatesFilter.value.getFilterValue(filterText.value, "type") === t)
);
});
const filteredTemplates = computed(() => {
return templates.value.filter((tp) => {
return (
filterText.value === "" ||
tp.name?.toLowerCase().includes(filterText.value.toLowerCase()) ||
tp.type === templatesFilter.value.getFilterValue(filterText.value, "type")
);
});
}
});

function onTypeFilter(t?: FileSourceTypes | "*") {
if (t === "*") {
filterText.value = templatesFilter.value.setFilterValue(
filterText.value,
"type",
templatesFilter.value.getFilterValue(filterText.value, "type")
);
} else {
filterText.value = templatesFilter.value.setFilterValue(filterText.value, "type", t);
}
}
function handleOAuth2Redirect() {
if (router.currentRoute.query.error === "access_denied") {
errorMessage.value = "You must authorize Galaxy to access this resource. Please try again.";
} else if (router.currentRoute.query.error) {
const error = router.currentRoute.query.error;
const { error } = router.currentRoute.query;

if (Array.isArray(error)) {
errorMessage.value = error[0] || "There was an error creating the file source.";
if (error) {
if (error === "access_denied") {
Toast.error("You must authorize Galaxy to access this resource. Please try again.");
} else {
errorMessage.value = error;
const errorMessage = Array.isArray(error) ? error[0] : error;
Toast.error(errorMessage || "There was an error creating the file source.");
}
}
}
Expand All @@ -44,11 +84,92 @@ handleOAuth2Redirect();
</script>

<template>
<CreateInstance :loading-message="loadingTemplatesInfoMessage" :loading="loading" prefix="file-source">
<BAlert v-if="errorMessage" variant="danger" show dismissible>
{{ errorMessage }}
</BAlert>
<div class="file-source-templates">
<div class="file-source-templates-list-header">
<Heading h1 separator inline size="xl" class="flex-grow-1 mb-2">
<RouterLink to="/file_source_instances/index"> Remote File Sources</RouterLink>
/ Templates
</Heading>
</div>

<div class="file-source-templates-search">
<Heading h2 size="sm">
Select file source template to create new file sources with. These templates are configured by your
Galaxy administrator.
</Heading>

<BFormInput v-model="filterText" placeholder="search templates" />

<div class="file-source-templates-type-filter">
Filter by type:
<BButton
v-b-tooltip.hover.noninteractive
size="sm"
variant="outline-primary"
title="Show all templates"
:pressed="filterActivated('*')"
@click="onTypeFilter('*')">
All
</BButton>

<SelectTemplate :templates="templates" @onSubmit="chooseTemplate" />
</CreateInstance>
<BButton
v-for="availableType in typesAvailable"
:key="`file-source-templates-type-${availableType.type}`"
v-b-tooltip.hover.noninteractive
size="sm"
variant="outline-primary"
:title="`Filter by file sources of type ${availableType.type}`"
:pressed="filterActivated(availableType.type)"
@click="onTypeFilter(availableType.type)">
<FontAwesomeIcon :icon="availableType.icon" />
{{ availableType.type.charAt(0).toUpperCase() + availableType.type.slice(1) }}
</BButton>
</div>
</div>

<BAlert v-if="fileSourceTemplatesStore.loading" variant="info" show>
<LoadingSpan message="Loading templates" />
</BAlert>
<BAlert v-else-if="!filterText && filteredTemplates.length === 0" variant="info" show>
No templates found.
</BAlert>
<BAlert v-else-if="filterText && filteredTemplates.length === 0" variant="info" show>
No templates found matching <span class="font-weight-bold">{{ filterText }}</span>
</BAlert>
<div v-else class="file-source-templates-cards-list">
<FileSourceTemplateCard
v-for="tp in filteredTemplates"
:key="tp.id"
:file-source-template="tp"
:template-types="templateTypes"
@typeFilter="onTypeFilter" />
</div>
</div>
</template>

<style scoped lang="scss">
@import "theme/blue.scss";
@import "breakpoints.scss";

.file-source-templates {
.file-source-templates-search {
margin-bottom: 1rem;

.file-source-templates-type-filter {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
margin-top: 0.5rem;
}
}

.file-source-templates-cards-list {
container: templates-list / inline-size;

display: flex;
flex-wrap: wrap;
gap: 1rem;
}
}
</style>