Skip to content

Commit 8fe04ac

Browse files
authored
Merge pull request #20342 from dannon/tab-icons
[25.0] DatasetView and Card Polish
2 parents e067f0e + af028c9 commit 8fe04ac

File tree

15 files changed

+309
-55
lines changed

15 files changed

+309
-55
lines changed

client/src/api/datatypes.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
11
import type { components } from "@/api";
2+
import { GalaxyApi } from "@/api";
3+
import { rethrowSimple } from "@/utils/simple-error";
24

35
export type CompositeFileInfo = components["schemas"]["CompositeFileInfo"];
6+
export type DatatypeDetails = components["schemas"]["DatatypeDetails"];
7+
8+
/**
9+
* Get details about a specific datatype
10+
*/
11+
export async function fetchDatatypeDetails(extension: string): Promise<DatatypeDetails> {
12+
try {
13+
const { GET } = GalaxyApi();
14+
const { data } = await GET("/api/datatypes/{datatype}", {
15+
params: {
16+
path: { datatype: extension },
17+
},
18+
});
19+
if (!data) {
20+
throw new Error(`Failed to fetch datatype details for ${extension}`);
21+
}
22+
return data as DatatypeDetails;
23+
} catch (error) {
24+
rethrowSimple(error);
25+
}
26+
}

client/src/api/schema/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9301,6 +9301,11 @@ export interface components {
93019301
* @description The URL to a detailed description for this datatype
93029302
*/
93039303
description_url: string | null;
9304+
/**
9305+
* Display behavior
9306+
* @description How this datatype behaves when displayed with preview=True: 'inline' (can be displayed in browser) or 'download' (triggers download)
9307+
*/
9308+
display_behavior?: string | null;
93049309
/**
93059310
* Display in upload
93069311
* @description If True, the associated file extension will be displayed in the `File Format` select list in the `Upload File from your computer` tool in the `Get Data` tool section of the tool panel
Lines changed: 150 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<script setup lang="ts">
2-
import { BNav, BNavItem } from "bootstrap-vue";
2+
import { faBug, faChartBar, faEye, faFileAlt, faInfoCircle, faPen } from "@fortawesome/free-solid-svg-icons";
3+
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
4+
import { BLink, BNav, BNavItem } from "bootstrap-vue";
35
import { computed, ref, watch } from "vue";
46
57
import { usePersistentToggle } from "@/composables/persistentToggle";
68
import { useDatasetStore } from "@/stores/datasetStore";
7-
import { useDatatypeVisualizationsStore } from "@/stores/datatypeVisualizationsStore";
9+
import { useDatatypeStore } from "@/stores/datatypeStore";
10+
import { bytesToString } from "@/utils/utils";
811
912
import DatasetError from "../DatasetInformation/DatasetError.vue";
1013
import LoadingSpan from "../LoadingSpan.vue";
@@ -17,50 +20,53 @@ import VisualizationFrame from "@/components/Visualizations/VisualizationFrame.v
1720
import CenterFrame from "@/entry/analysis/modules/CenterFrame.vue";
1821
1922
const datasetStore = useDatasetStore();
20-
const datatypeVisualizationsStore = useDatatypeVisualizationsStore();
23+
const datatypeStore = useDatatypeStore();
2124
const { toggled: headerCollapsed, toggle: toggleHeaderCollapse } = usePersistentToggle("dataset-header-collapsed");
2225
2326
interface Props {
2427
datasetId: string;
25-
tab?: "details" | "edit" | "error" | "preview" | "visualize";
28+
tab?: "details" | "edit" | "error" | "preview" | "raw" | "visualize";
2629
}
2730
2831
const props = withDefaults(defineProps<Props>(), {
2932
tab: "preview",
3033
});
3134
3235
const iframeLoading = ref(true);
33-
const preferredVisualization = ref<string>();
3436
3537
const dataset = computed(() => datasetStore.getDataset(props.datasetId));
3638
const headerState = computed(() => (headerCollapsed.value ? "closed" : "open"));
37-
const isLoading = computed(() => datasetStore.isLoadingDataset(props.datasetId));
39+
40+
// Track datatype loading state
41+
const isDatatypeLoading = ref(false);
42+
43+
// Consolidated loading state
44+
const isLoading = computed(() => {
45+
return datasetStore.isLoadingDataset(props.datasetId) || isDatatypeLoading.value;
46+
});
47+
3848
const showError = computed(
3949
() => dataset.value && (dataset.value.state === "error" || dataset.value.state === "failed_metadata")
4050
);
51+
const isAutoDownloadType = computed(
52+
() => dataset.value && datatypeStore.isDatatypeAutoDownload(dataset.value.file_ext)
53+
);
54+
const preferredVisualization = computed(
55+
() => dataset.value && datatypeStore.getPreferredVisualization(dataset.value.file_ext)
56+
);
4157
42-
// Check if the dataset has a preferred visualization by datatype
43-
async function checkPreferredVisualization() {
44-
if (dataset.value && dataset.value.file_ext) {
45-
try {
46-
const mapping = await datatypeVisualizationsStore.getPreferredVisualizationForDatatype(
47-
dataset.value.file_ext
48-
);
49-
if (mapping) {
50-
preferredVisualization.value = mapping.visualization;
51-
} else {
52-
preferredVisualization.value = undefined;
53-
}
54-
} catch (error) {
55-
preferredVisualization.value = undefined;
58+
// Watch for changes to the dataset to fetch datatype info
59+
watch(
60+
() => dataset.value?.file_ext,
61+
async () => {
62+
if (dataset.value && dataset.value.file_ext) {
63+
isDatatypeLoading.value = true;
64+
await datatypeStore.fetchDatatypeDetails(dataset.value.file_ext);
65+
isDatatypeLoading.value = false;
5666
}
57-
} else {
58-
preferredVisualization.value = undefined;
59-
}
60-
}
61-
62-
// Watch for changes to the dataset to check for preferred visualizations
63-
watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: true });
67+
},
68+
{ immediate: true }
69+
);
6470
</script>
6571

6672
<template>
@@ -76,7 +82,8 @@ watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: t
7682
class="flex-grow-1"
7783
:collapse="headerState"
7884
@click="toggleHeaderCollapse">
79-
{{ dataset?.hid }}: <span class="font-weight-bold">{{ dataset?.name }}</span>
85+
<span class="dataset-hid">{{ dataset?.hid }}:</span>
86+
<span class="dataset-name font-weight-bold">{{ dataset?.name }}</span>
8087
<span class="dataset-state-header">
8188
<DatasetState :dataset-id="datasetId" />
8289
</span>
@@ -100,29 +107,52 @@ watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: t
100107
{{ dataset.genome_build }}
101108
</BLink>
102109
</span>
103-
<div v-if="dataset.misc_info" class="info">
104-
<span class="value">{{ dataset.misc_info }}</span>
105-
</div>
110+
<span v-if="dataset.file_size" class="filesize">
111+
<span v-localize class="prompt">size</span>
112+
<span class="value font-weight-bold" v-html="bytesToString(dataset.file_size, false)" />
113+
</span>
106114
</div>
107115
</transition>
108116
</header>
109117
<BNav pills class="my-2 p-2 bg-light border-bottom">
110-
<BNavItem title="Preview" :active="tab === 'preview'" :to="`/datasets/${datasetId}/preview`">
111-
Preview
118+
<BNavItem
119+
title="View a preview of the dataset contents"
120+
:active="tab === 'preview'"
121+
:to="`/datasets/${datasetId}/preview`">
122+
<FontAwesomeIcon :icon="faEye" class="mr-1" /> Preview
123+
</BNavItem>
124+
<BNavItem
125+
v-if="preferredVisualization"
126+
title="View raw dataset contents"
127+
:active="tab === 'raw'"
128+
:to="`/datasets/${datasetId}/raw`">
129+
<FontAwesomeIcon :icon="faFileAlt" class="mr-1" /> Raw
112130
</BNavItem>
113131
<BNavItem
114132
v-if="!showError"
115-
title="Visualize"
133+
title="Explore available visualizations for this dataset"
116134
:active="tab === 'visualize'"
117135
:to="`/datasets/${datasetId}/visualize`">
118-
Visualize
136+
<FontAwesomeIcon :icon="faChartBar" class="mr-1" /> Visualize
119137
</BNavItem>
120-
<BNavItem title="Details" :active="tab === 'details'" :to="`/datasets/${datasetId}/details`">
121-
Details
138+
<BNavItem
139+
title="View detailed information about this dataset"
140+
:active="tab === 'details'"
141+
:to="`/datasets/${datasetId}/details`">
142+
<FontAwesomeIcon :icon="faInfoCircle" class="mr-1" /> Details
143+
</BNavItem>
144+
<BNavItem
145+
title="Edit dataset attributes and metadata"
146+
:active="tab === 'edit'"
147+
:to="`/datasets/${datasetId}/edit`">
148+
<FontAwesomeIcon :icon="faPen" class="mr-1" /> Edit
122149
</BNavItem>
123-
<BNavItem title="Edit" :active="tab === 'edit'" :to="`/datasets/${datasetId}/edit`">Edit</BNavItem>
124-
<BNavItem v-if="showError" title="Error" :active="tab === 'error'" :to="`/datasets/${datasetId}/error`">
125-
Error
150+
<BNavItem
151+
v-if="showError"
152+
title="View error information for this dataset"
153+
:active="tab === 'error'"
154+
:to="`/datasets/${datasetId}/error`">
155+
<FontAwesomeIcon :icon="faBug" class="mr-1" /> Error
126156
</BNavItem>
127157
</BNav>
128158
<div v-if="tab === 'preview'" class="h-100">
@@ -131,22 +161,49 @@ watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: t
131161
:dataset-id="datasetId"
132162
:visualization="preferredVisualization"
133163
@load="iframeLoading = false" />
164+
<div v-else-if="isAutoDownloadType" class="auto-download-message p-4">
165+
<div class="alert alert-info">
166+
<h4>Download Required</h4>
167+
<p>This file type ({{ dataset.file_ext }}) will download automatically when accessed directly.</p>
168+
<p>File size: <strong v-html="bytesToString(dataset.file_size || 0, false)" /></p>
169+
<a :href="`/datasets/${datasetId}/display`" class="btn btn-primary mt-2" download>
170+
<FontAwesomeIcon :icon="faFileAlt" class="mr-1" /> Download File
171+
</a>
172+
</div>
173+
</div>
134174
<CenterFrame
135175
v-else
136176
:src="`/datasets/${datasetId}/display/?preview=True`"
137-
:is_preview="true"
177+
:is-preview="true"
138178
@load="iframeLoading = false" />
139179
</div>
140-
<div v-else-if="tab === 'visualize'" class="d-flex flex-column overflow-hidden overflow-y">
180+
<div v-else-if="tab === 'raw'" class="h-100">
181+
<div v-if="isAutoDownloadType" class="auto-download-message p-4">
182+
<div class="alert alert-info">
183+
<h4>Download Required</h4>
184+
<p>This file type ({{ dataset.file_ext }}) will download automatically when accessed directly.</p>
185+
<p>File size: <strong v-html="bytesToString(dataset.file_size || 0, false)" /></p>
186+
<a :href="`/datasets/${datasetId}/display`" class="btn btn-primary mt-2" download>
187+
<FontAwesomeIcon :icon="faFileAlt" class="mr-1" /> Download File
188+
</a>
189+
</div>
190+
</div>
191+
<CenterFrame
192+
v-else
193+
:src="`/datasets/${datasetId}/display/?preview=True`"
194+
:is-preview="true"
195+
@load="iframeLoading = false" />
196+
</div>
197+
<div v-else-if="tab === 'visualize'" class="tab-content-panel">
141198
<VisualizationsList :dataset-id="datasetId" />
142199
</div>
143-
<div v-else-if="tab === 'edit'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
200+
<div v-else-if="tab === 'edit'" class="tab-content-panel mt-2">
144201
<DatasetAttributes :dataset-id="datasetId" />
145202
</div>
146-
<div v-else-if="tab === 'details'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
203+
<div v-else-if="tab === 'details'" class="tab-content-panel mt-2">
147204
<DatasetDetails :dataset-id="datasetId" />
148205
</div>
149-
<div v-else-if="tab === 'error'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
206+
<div v-else-if="tab === 'error'" class="tab-content-panel mt-2">
150207
<DatasetError :dataset-id="datasetId" />
151208
</div>
152209
</div>
@@ -161,6 +218,21 @@ watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: t
161218
opacity: 1;
162219
transition: all 0.25s ease;
163220
overflow: hidden;
221+
222+
.datatype,
223+
.dbkey,
224+
.filesize {
225+
margin-right: 1rem;
226+
}
227+
228+
.prompt {
229+
color: $text-muted;
230+
margin-right: 0.25rem;
231+
}
232+
233+
.blurb {
234+
margin-bottom: 0.25rem;
235+
}
164236
}
165237
166238
.header-enter, /* change to header-enter-from with Vue 3 */
@@ -172,8 +244,41 @@ watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: t
172244
opacity: 0;
173245
}
174246
247+
.dataset-hid,
248+
.dataset-state-header {
249+
white-space: nowrap;
250+
}
251+
252+
.dataset-hid {
253+
margin-right: 0.25rem;
254+
}
255+
256+
.dataset-name {
257+
word-break: break-word;
258+
}
259+
175260
.dataset-state-header {
176261
font-size: $h5-font-size;
177262
vertical-align: middle;
263+
margin-left: 0.5rem;
264+
}
265+
266+
.tab-content-panel {
267+
display: flex;
268+
flex-direction: column;
269+
overflow: hidden;
270+
overflow-y: auto;
271+
}
272+
273+
.auto-download-message {
274+
display: flex;
275+
align-items: flex-start;
276+
justify-content: center;
277+
height: 100%;
278+
279+
.alert {
280+
max-width: 600px;
281+
width: 100%;
282+
}
178283
}
179284
</style>

client/src/components/History/Content/ContentOptions.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
2+
import { faStop } from "@fortawesome/free-solid-svg-icons";
23
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
34
import axios from "axios";
45
import { BButton, BDropdown } from "bootstrap-vue";
5-
import { faStop } from "font-awesome-6";
6+
//@ts-ignore deprecated package without types (vue 2, remove this comment on vue 3 migration)
7+
import { ScanEye } from "lucide-vue";
68
import { computed, type Ref, ref } from "vue";
79
810
import { getAppRoot } from "@/onload/loadConfig";
@@ -112,14 +114,14 @@ function onDisplay($event: MouseEvent) {
112114
<BButton
113115
v-if="isDataset"
114116
v-b-tooltip.hover
115-
title="Display"
117+
title="View"
116118
tabindex="0"
117119
class="display-btn px-1"
118120
size="sm"
119121
variant="link"
120122
:href="displayUrl"
121123
@click.prevent.stop="onDisplay($event)">
122-
<icon icon="eye" />
124+
<ScanEye absolute-stroke-width :size="16" />
123125
</BButton>
124126
<BButton
125127
v-if="writable && isHistoryItem"

0 commit comments

Comments
 (0)