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

Rework Layout of Object store dialog UI #19695

Draft
wants to merge 8 commits into
base: release_24.2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions client/src/components/Dataset/DatasetStorage/RelocateLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const props = defineProps<RelocateLinkProps>();
const showModal = ref(false);

const store = useObjectStoreStore();
const { isLoaded, selectableObjectStores } = storeToRefs(store);
const { loading, selectableObjectStores } = storeToRefs(store);

const currentObjectStore = computed<ConcreteObjectStoreModel | null>(() => {
const isLoadedVal = isLoaded.value;
const isLoadedVal = !loading.value;
const objectStores = selectableObjectStores.value;
const currentObjectStoreId = props.datasetStorageDetails.object_store_id;

Expand All @@ -40,7 +40,7 @@ const currentObjectStore = computed<ConcreteObjectStoreModel | null>(() => {
});

const validTargets = computed<ConcreteObjectStoreModel[]>(() => {
const isLoadedVal = isLoaded.value;
const isLoadedVal = !loading.value;
const objectStores = selectableObjectStores.value;
const currentObjectStoreId = props.datasetStorageDetails.object_store_id;

Expand Down
61 changes: 2 additions & 59 deletions client/src/components/History/CurrentHistory/HistoryCounter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faDatabase, faEyeSlash, faHdd, faMapMarker, faSync, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton, BButtonGroup, BModal } from "bootstrap-vue";
import { BButton, BButtonGroup } from "bootstrap-vue";
import { formatDistanceToNowStrict } from "date-fns";
import { storeToRefs } from "pinia";
import prettyBytes from "pretty-bytes";
Expand All @@ -11,16 +11,9 @@ import { useRouter } from "vue-router/composables";

import { type HistorySummaryExtended, userOwnsHistory } from "@/api";
import { HistoryFilters } from "@/components/History/HistoryFilters.js";
import { useConfig } from "@/composables/config";
import { useHistoryContentStats } from "@/composables/historyContentStats";
import { useStorageLocationConfiguration } from "@/composables/storageLocation";
import { useUserStore } from "@/stores/userStore";

import PreferredStorePopover from "./PreferredStorePopover.vue";
import SelectPreferredStore from "./SelectPreferredStore.vue";

const { isOnlyPreference } = useStorageLocationConfiguration();

library.add(faDatabase, faEyeSlash, faHdd, faMapMarker, faSync, faTrash);

const props = withDefaults(
Expand All @@ -44,31 +37,20 @@ const props = withDefaults(
const emit = defineEmits(["update:filter-text", "reloadContents"]);

const router = useRouter();
const { config } = useConfig();
const { currentUser, isAnonymous } = storeToRefs(useUserStore());
const { currentUser } = storeToRefs(useUserStore());
const { historySize, numItemsActive, numItemsDeleted, numItemsHidden } = useHistoryContentStats(
toRef(props, "history")
);

const reloadButtonLoading = ref(false);
const reloadButtonTitle = ref("");
const reloadButtonVariant = ref("link");
const showPreferredObjectStoreModal = ref(false);
const historyPreferredObjectStoreId = ref(props.history.preferred_object_store_id);

const niceHistorySize = computed(() => prettyBytes(historySize.value));
const canManageStorage = computed(
() => userOwnsHistory(currentUser.value, props.history) && !currentUser.value?.isAnonymous
);

const storageLocationTitle = computed(() => {
if (isOnlyPreference.value) {
return "History Preferred Storage Location";
} else {
return "History Storage Location";
}
});

function onDashboard() {
router.push({ name: "HistoryOverviewInAnalysis", params: { historyId: props.history.id } });
}
Expand Down Expand Up @@ -117,14 +99,6 @@ async function reloadContents() {
}, 1000);
}

function onUpdatePreferredObjectStoreId(preferredObjectStoreId: string | null) {
showPreferredObjectStoreModal.value = false;
// ideally this would be pushed back to the history object somehow
// and tracked there... but for now this is only component using
// this information.
historyPreferredObjectStoreId.value = preferredObjectStoreId;
}

onMounted(() => {
updateTime();
// update every second
Expand All @@ -148,24 +122,6 @@ onMounted(() => {
</BButton>

<BButtonGroup v-if="currentUser">
<BButton
v-if="config && config.object_store_allows_id_selection && !isAnonymous"
:id="`history-storage-${history.id}`"
title="Manage Preferred History Storage"
variant="link"
size="sm"
class="rounded-0 text-decoration-none"
@click="showPreferredObjectStoreModal = true">
<FontAwesomeIcon :icon="faHdd" />
</BButton>

<PreferredStorePopover
v-if="config && config.object_store_allows_id_selection && !isAnonymous"
:history-id="history.id"
:history-preferred-object-store-id="historyPreferredObjectStoreId"
:user="currentUser">
</PreferredStorePopover>

<BButtonGroup>
<BButton
v-b-tooltip.hover
Expand Down Expand Up @@ -218,19 +174,6 @@ onMounted(() => {
<FontAwesomeIcon :icon="faSync" :spin="reloadButtonLoading" />
</BButton>
</BButtonGroup>

<BModal
v-model="showPreferredObjectStoreModal"
:title="storageLocationTitle"
modal-class="history-preferred-object-store-modal"
title-tag="h3"
size="sm"
hide-footer>
<SelectPreferredStore
:user-preferred-object-store-id="currentUser.preferred_object_store_id"
:history="history"
@updated="onUpdatePreferredObjectStoreId" />
</BModal>
</BButtonGroup>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script setup lang="ts">
import type { HistorySummary } from "@/api";
import { useConfig } from "@/composables/config";
import { useHistoryStore } from "@/stores/historyStore";

import type { DetailsLayoutSummarized } from "../Layout/types";

import HistoryIndicators from "../HistoryIndicators.vue";
import StorageLocationIndicator from "./StorageLocationIndicator.vue";
import TextSummary from "@/components/Common/TextSummary.vue";
import DetailsLayout from "@/components/History/Layout/DetailsLayout.vue";

Expand All @@ -25,6 +27,8 @@ function onSave(newDetails: HistorySummary) {
const id = props.history.id;
historyStore.updateHistory({ ...newDetails, id });
}

const { config } = useConfig();
</script>

<template>
Expand All @@ -51,5 +55,6 @@ function onSave(newDetails: HistorySummary) {
<template v-if="summarized" v-slot:update-time>
<HistoryIndicators :history="history" detailed-time />
</template>
<StorageLocationIndicator v-if="config && config.object_store_allows_id_selection" :history="history" />
</DetailsLayout>
</template>
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
<script setup lang="ts">
import axios from "axios";
import { computed, ref } from "vue";
import { BModal, type BvModalEvent } from "bootstrap-vue";
import { computed, type PropType, ref } from "vue";

import { getPermissions, isHistoryPrivate, makePrivate, type PermissionsResponse } from "@/components/History/services";
import { useStorageLocationConfiguration } from "@/composables/storageLocation";
import { prependPath } from "@/utils/redirect";
import { errorMessageAsString } from "@/utils/simple-error";

import SelectObjectStore from "@/components/ObjectStore/SelectObjectStore.vue";

const props = defineProps({
userPreferredObjectStoreId: {
type: String,
type: String as PropType<string | null>,
default: null,
},
preferredObjectStoreId: {
type: String as PropType<string | null>,
default: null,
},
history: {
type: Object,
required: true,
},
showSubSetting: {
type: Boolean,
default: false,
},
});

const error = ref<string | null>(null);
const selectedObjectStoreId = ref(props.history.preferred_object_store_id);

const newDatasetsDescription = "New dataset outputs from tools and workflows executed in this history";
const galaxySelectionDefaultTitle = "Use Galaxy Defaults";
Expand Down Expand Up @@ -64,6 +73,7 @@ async function handleSubmit(preferredObjectStoreId: string | null, isPrivate: bo
await makePrivate(props.history.id, permissionResponse);
} catch {
error.value = "Failed to update default permissions for history.";
throw new Error();
}
}
}
Expand All @@ -73,19 +83,75 @@ async function handleSubmit(preferredObjectStoreId: string | null, isPrivate: bo
const url = prependPath(`api/histories/${props.history.id}`);
try {
await axios.put(url, payload);
emit("updated", preferredObjectStoreId);
} catch (e) {
error.value = errorMessageAsString(e);
throw new Error();
}
}

const { isOnlyPreference } = useStorageLocationConfiguration();
const storageLocationTitle = computed(() => {
if (isOnlyPreference.value) {
return "History Preferred Storage Location";
} else {
return "History Storage Location";
}
selectedObjectStoreId.value = preferredObjectStoreId;
emit("updated", preferredObjectStoreId);
});

const modalShown = ref(false);

function showModal() {
modalShown.value = true;
}

const currentSelectedStoreId = ref<string | null>(props.preferredObjectStoreId);
const currentSelectedStorePrivate = ref(false);

function selectionChanged(preferredObjectStoreId: string | null, isPrivate: boolean) {
currentSelectedStoreId.value = preferredObjectStoreId;
currentSelectedStorePrivate.value = isPrivate;
}

async function modalOk(event: BvModalEvent) {
if (currentSelectedStoreId.value !== props.preferredObjectStoreId) {
event.preventDefault();

try {
await handleSubmit(currentSelectedStoreId.value, currentSelectedStorePrivate.value);
modalShown.value = false;
} catch (_e) {
// pass
}
}
}

function reset() {
currentSelectedStoreId.value = props.preferredObjectStoreId;
currentSelectedStorePrivate.value = false;
}

defineExpose({
showModal,
});
</script>

<template>
<SelectObjectStore
:parent-error="error || undefined"
:for-what="newDatasetsDescription"
:selected-object-store-id="selectedObjectStoreId"
:default-option-title="defaultOptionTitle"
:default-option-description="defaultOptionDescription"
@onSubmit="handleSubmit" />
<BModal
v-model="modalShown"
:title="storageLocationTitle"
title-tag="h2"
title-class="h-sm"
@ok="modalOk"
@cancel="reset"
@close="reset">
<SelectObjectStore
:show-sub-setting="props.showSubSetting"
:parent-error="error || undefined"
:for-what="newDatasetsDescription"
:selected-object-store-id="currentSelectedStoreId"
:default-option-title="defaultOptionTitle"
:default-option-description="defaultOptionDescription"
@onSubmit="selectionChanged" />
</BModal>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script setup lang="ts">
import { faHdd } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, ref } from "vue";

import type { HistorySummary } from "@/api";
import { useSync } from "@/composables/sync";
import { useObjectStoreStore } from "@/stores/objectStoreStore";
import { useUserStore } from "@/stores/userStore";

import SelectPreferredStore from "./SelectPreferredStore.vue";

const props = defineProps<{
history: HistorySummary;
}>();

const preferredObjectStoreId = ref<string | null>(null);
useSync(() => props.history.preferred_object_store_id, preferredObjectStoreId);

const objectStoreStore = useObjectStoreStore();
objectStoreStore.fetchObjectStores();

const { currentUser, isAnonymous } = storeToRefs(useUserStore());

const userPreferredObjectStoreId = computed(() => {
const user = currentUser.value;

if (user && "preferred_object_store_id" in user) {
return user.preferred_object_store_id ?? null;
} else {
return null;
}
});

function onUpdatePreferredObjectStoreId(id: string | null) {
preferredObjectStoreId.value = id;
}

const selectPreferredStore = ref<InstanceType<typeof SelectPreferredStore>>();

const storageLocationButtonTitle = computed(() => {
if (!isAnonymous.value) {
return "View and select storage location";
} else {
return "Log in to view and select storage location";
}
});
</script>

<template>
<div class="storage-location-indicator">
<BButton
class="ui-link"
:title="storageLocationButtonTitle"
:disabled="isAnonymous"
@click="selectPreferredStore?.showModal()">
<FontAwesomeIcon :icon="faHdd" />
{{ objectStoreStore.getObjectStoreNameById(preferredObjectStoreId) ?? "Default Storage" }}
</BButton>

<SelectPreferredStore
ref="selectPreferredStore"
show-sub-setting
:user-preferred-object-store-id="userPreferredObjectStoreId"
:preferred-object-store-id="preferredObjectStoreId"
:history="history"
@updated="onUpdatePreferredObjectStoreId" />
</div>
</template>

<style lang="scss" scoped>
.storage-location-indicator {
margin: 0.5rem 0;
}
</style>
2 changes: 2 additions & 0 deletions client/src/components/History/Layout/DetailsLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ function selectText() {
<span v-localize>Cancel</span>
</BButton>
</div>

<slot></slot>
</section>
</template>

Expand Down
Loading
Loading