Loading client/src/components/Form/Elements/FormData/FormData.test.js +27 −24 Original line number Diff line number Diff line import "tests/jest/mockHelpPopovers"; import "@/composables/__mocks__/filter"; import { createTestingPinia } from "@pinia/testing"; import { mount } from "@vue/test-utils"; Loading @@ -11,6 +12,8 @@ import { useEventStore } from "@/stores/eventStore"; import MountTarget from "./FormData.vue"; jest.mock("@/composables/filter"); const localVue = getLocalVue(); localVue.use(PiniaVuePlugin); Loading Loading @@ -51,7 +54,7 @@ const defaultOptions = { }; const SELECT_OPTIONS = ".multiselect__element"; const SELECTED_VALUE = ".multiselect__option--selected span"; const SELECTED_VALUE = ".multiselect__option--selected"; describe("FormData", () => { it("regular data", async () => { Loading @@ -74,11 +77,11 @@ describe("FormData", () => { expect(options.at(0).classes()).toContain("active"); expect(options.at(0).attributes("title")).toBe("Single dataset"); expect(wrapper.emitted().input[0][0]).toEqual(value_0); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("dceName4 (as dataset)"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("dceName4 (as dataset)"); await wrapper.setProps({ value: value_0 }); expect(wrapper.emitted().input.length).toEqual(1); await wrapper.setProps({ value: { values: [{ id: "hda2", src: "hda" }] } }); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("2: hdaName2"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("2: hdaName2"); expect(wrapper.emitted().input.length).toEqual(1); const elements_0 = wrapper.findAll(SELECT_OPTIONS); expect(elements_0.length).toEqual(6); Loading @@ -86,7 +89,7 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(2); expect(wrapper.emitted().input[1][0]).toEqual(value_1); await wrapper.setProps({ value: value_1 }); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("4: hdaName4"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("4: hdaName4"); }); it("optional dataset", async () => { Loading Loading @@ -128,8 +131,8 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(1); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(2); expect(selectedValues.at(0).text()).toBe("3: hdaName3"); expect(selectedValues.at(1).text()).toBe("2: hdaName2"); expect(selectedValues.at(0).text()).toContain("3: hdaName3"); expect(selectedValues.at(1).text()).toContain("2: hdaName2"); const value_0 = { batch: false, product: false, Loading Loading @@ -176,9 +179,9 @@ describe("FormData", () => { const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(3); // the values in the multiselect are sorted by hid DESC expect(selectedValues.at(0).text()).toBe("3: hdaName3"); expect(selectedValues.at(1).text()).toBe("2: hdaName2"); expect(selectedValues.at(2).text()).toBe("1: hdaName1"); expect(selectedValues.at(0).text()).toContain("3: hdaName3"); expect(selectedValues.at(1).text()).toContain("2: hdaName2"); expect(selectedValues.at(2).text()).toContain("1: hdaName1"); await selectedValues.at(0).trigger("click"); const value_sorted = { batch: false, Loading Loading @@ -226,11 +229,11 @@ describe("FormData", () => { expect(selectedValues.length).toBe(5); // when dces are mixed in their values are shown first and are // ordered by id descending expect(selectedValues.at(0).text()).toBe("dceName4 (as dataset)"); expect(selectedValues.at(1).text()).toBe("dceName3 (as dataset)"); expect(selectedValues.at(2).text()).toBe("dceName2 (as dataset)"); expect(selectedValues.at(3).text()).toBe("2: hdaName2"); expect(selectedValues.at(4).text()).toBe("1: hdaName1"); expect(selectedValues.at(0).text()).toContain("dceName4 (as dataset)"); expect(selectedValues.at(1).text()).toContain("dceName3 (as dataset)"); expect(selectedValues.at(2).text()).toContain("dceName2 (as dataset)"); expect(selectedValues.at(3).text()).toContain("2: hdaName2"); expect(selectedValues.at(4).text()).toContain("1: hdaName1"); await selectedValues.at(0).trigger("click"); const value_sorted = { batch: false, Loading Loading @@ -261,7 +264,7 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(1); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName1 (as dataset)"); expect(selectedValues.at(0).text()).toContain("dceName1 (as dataset)"); }); it("dataset collection element as hdca without map_over_type", async () => { Loading @@ -274,7 +277,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName2 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName2 (as dataset collection)"); }); it("dataset collection element as hdca mapped to batch field", async () => { Loading @@ -291,7 +294,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName3 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName3 (as dataset collection)"); }); it("dataset collection element as hdca mapped to non-batch field", async () => { Loading @@ -309,7 +312,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName3 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName3 (as dataset collection)"); }); it("dataset collection mapped to non-batch field", async () => { Loading @@ -327,7 +330,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("5: hdcaName5"); expect(selectedValues.at(0).text()).toContain("5: hdcaName5"); }); it("multiple dataset collection elements (as hdas)", async () => { Loading Loading @@ -513,22 +516,22 @@ describe("FormData", () => { }); const select_0 = wrapper_0.findAll(SELECT_OPTIONS); expect(select_0.length).toBe(4); expect(select_0.at(2).text()).toBe("2: hdaName2"); expect(select_0.at(3).text()).toBe("1: hdaName1"); expect(select_0.at(2).text()).toContain("2: hdaName2"); expect(select_0.at(3).text()).toContain("1: hdaName1"); const wrapper_1 = createTarget({ tag: "tag2", options: defaultOptions, }); const select_1 = wrapper_1.findAll(SELECT_OPTIONS); expect(select_1.length).toBe(4); expect(select_1.at(2).text()).toBe("3: hdaName3"); expect(select_1.at(3).text()).toBe("2: hdaName2"); expect(select_1.at(2).text()).toContain("3: hdaName3"); expect(select_1.at(3).text()).toContain("2: hdaName2"); const wrapper_2 = createTarget({ tag: "tag3", options: defaultOptions, }); const select_2 = wrapper_2.findAll(SELECT_OPTIONS); expect(select_2.length).toBe(3); expect(select_2.at(2).text()).toBe("3: hdaName3"); expect(select_2.at(2).text()).toContain("3: hdaName3"); }); }); client/src/components/Form/Elements/FormSelect.vue +32 −6 Original line number Diff line number Diff line Loading @@ -2,17 +2,21 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faCheckSquare, faSquare } from "@fortawesome/free-regular-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { computed, type ComputedRef, onMounted, type PropType, watch } from "vue"; import { computed, type ComputedRef, onMounted, type PropType, ref, watch } from "vue"; import Multiselect from "vue-multiselect"; import { useFilterObjectArray } from "@/composables/filter"; import { useMultiselect } from "@/composables/useMultiselect"; import { uid } from "@/utils/utils"; import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue"; library.add(faCheckSquare, faSquare); const { ariaExpanded, onOpen, onClose } = useMultiselect(); type SelectValue = Record<string, unknown> | string | number | null; type ValueWithTags = SelectValue & { tags: string[] }; interface SelectOption { label: string; Loading Loading @@ -51,19 +55,22 @@ const emit = defineEmits<{ (e: "input", value: SelectValue | Array<SelectValue>): void; }>(); const filter = ref(""); const filteredOptions = useFilterObjectArray(() => props.options, filter, ["label", ["value", "tags"]]); /** * When there are more options than this, push selected options to the end */ const optionReorderThreshold = 8; const reorderedOptions = computed(() => { if (props.options.length <= optionReorderThreshold) { return props.options; if (filteredOptions.value.length <= optionReorderThreshold) { return filteredOptions.value; } else { const selectedOptions: SelectOption[] = []; const unselectedOptions: SelectOption[] = []; props.options.forEach((option) => { filteredOptions.value.forEach((option) => { if (selectedValues.value.includes(option.value)) { selectedOptions.push(option); } else { Loading Loading @@ -140,7 +147,9 @@ function setInitialValue(): void { */ watch( () => props.options, () => setInitialValue() () => { setInitialValue(); } ); /** Loading @@ -149,6 +158,14 @@ watch( onMounted(() => { setInitialValue(); }); function isValueWithTags(item: SelectValue): item is ValueWithTags { return item !== null && typeof item === "object" && (item as ValueWithTags).tags !== undefined; } function onSearchChange(search: string): void { filter.value = search; } </script> <template> Loading @@ -169,11 +186,20 @@ onMounted(() => { :selected-label="selectedLabel" :select-label="null" track-by="value" :internal-search="false" @search-change="onSearchChange" @open="onOpen" @close="onClose"> <template v-slot:option="{ option }"> <div class="d-flex align-items-center justify-content-between"> <div> <span>{{ option.label }}</span> <StatelessTags v-if="isValueWithTags(option.value)" class="tags mt-2" :value="option.value.tags" disabled /> </div> <FontAwesomeIcon v-if="selectedValues.includes(option.value)" :icon="faCheckSquare" /> <FontAwesomeIcon v-else :icon="faSquare" /> </div> Loading client/src/components/HistoryImport.vue +2 −1 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import { waitOnJob } from "components/JobStates/wait"; import LoadingSpan from "components/LoadingSpan"; import { getAppRoot } from "onload/loadConfig"; import { errorMessageAsString } from "utils/simple-error"; import { capitalizeFirstLetter } from "utils/strings"; import Vue, { ref, watch } from "vue"; import { fetchFileSources } from "@/api/remoteFiles"; Loading Loading @@ -165,7 +166,7 @@ export default { return this.invocationImport ? "invocation" : "history"; }, identifierTextCapitalized() { return this.identifierText.charAt(0).toUpperCase() + this.identifierText.slice(1); return capitalizeFirstLetter(this.identifierText); }, identifierTextPlural() { return this.invocationImport ? "invocations" : "histories"; Loading client/src/components/Notifications/Categories/SharedItemNotification.vue +1 −4 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import { computed } from "vue"; import type { SharedItemNotification } from "@/api/notifications"; import { useNotificationsStore } from "@/stores/notificationsStore"; import { absPath } from "@/utils/redirect"; import { capitalizeFirstLetter } from "@/utils/strings"; import NotificationActions from "@/components/Notifications/NotificationActions.vue"; Loading @@ -21,10 +22,6 @@ const props = defineProps<Props>(); const notificationsStore = useNotificationsStore(); function capitalizeFirstLetter(string: string) { return string.charAt(0).toUpperCase() + string.slice(1); } const content = computed(() => props.notification.content); const sharedItemType = computed(() => { Loading client/src/components/User/ExternalIdentities/ExternalIdentities.vue +7 −1 Original line number Diff line number Diff line Loading @@ -46,7 +46,8 @@ title="Disconnect External Identity" class="d-block mt-3" @click="onDisconnect(item)"> Disconnect {{ item.provider.charAt(0).toUpperCase() + item.provider.slice(1) }} - {{ item.email }} Disconnect {{ capitalizeAsTitle(item.provider_label) }} - {{ item.email }} </b-button> <b-modal Loading Loading @@ -98,6 +99,8 @@ import { sanitize } from "dompurify"; import { userLogout } from "utils/logout"; import Vue from "vue"; import { capitalizeFirstLetter } from "@/utils/strings"; import svc from "./service"; import ExternalLogin from "components/User/ExternalIdentities/ExternalLogin.vue"; Loading Loading @@ -156,6 +159,9 @@ export default { Toast.success(notificationMessage); }, methods: { capitalizeAsTitle(str) { return capitalizeFirstLetter(str); }, loadIdentities() { this.loading = true; svc.getIdentityProviders() Loading Loading
client/src/components/Form/Elements/FormData/FormData.test.js +27 −24 Original line number Diff line number Diff line import "tests/jest/mockHelpPopovers"; import "@/composables/__mocks__/filter"; import { createTestingPinia } from "@pinia/testing"; import { mount } from "@vue/test-utils"; Loading @@ -11,6 +12,8 @@ import { useEventStore } from "@/stores/eventStore"; import MountTarget from "./FormData.vue"; jest.mock("@/composables/filter"); const localVue = getLocalVue(); localVue.use(PiniaVuePlugin); Loading Loading @@ -51,7 +54,7 @@ const defaultOptions = { }; const SELECT_OPTIONS = ".multiselect__element"; const SELECTED_VALUE = ".multiselect__option--selected span"; const SELECTED_VALUE = ".multiselect__option--selected"; describe("FormData", () => { it("regular data", async () => { Loading @@ -74,11 +77,11 @@ describe("FormData", () => { expect(options.at(0).classes()).toContain("active"); expect(options.at(0).attributes("title")).toBe("Single dataset"); expect(wrapper.emitted().input[0][0]).toEqual(value_0); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("dceName4 (as dataset)"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("dceName4 (as dataset)"); await wrapper.setProps({ value: value_0 }); expect(wrapper.emitted().input.length).toEqual(1); await wrapper.setProps({ value: { values: [{ id: "hda2", src: "hda" }] } }); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("2: hdaName2"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("2: hdaName2"); expect(wrapper.emitted().input.length).toEqual(1); const elements_0 = wrapper.findAll(SELECT_OPTIONS); expect(elements_0.length).toEqual(6); Loading @@ -86,7 +89,7 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(2); expect(wrapper.emitted().input[1][0]).toEqual(value_1); await wrapper.setProps({ value: value_1 }); expect(wrapper.find(SELECTED_VALUE).text()).toEqual("4: hdaName4"); expect(wrapper.find(SELECTED_VALUE).text()).toContain("4: hdaName4"); }); it("optional dataset", async () => { Loading Loading @@ -128,8 +131,8 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(1); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(2); expect(selectedValues.at(0).text()).toBe("3: hdaName3"); expect(selectedValues.at(1).text()).toBe("2: hdaName2"); expect(selectedValues.at(0).text()).toContain("3: hdaName3"); expect(selectedValues.at(1).text()).toContain("2: hdaName2"); const value_0 = { batch: false, product: false, Loading Loading @@ -176,9 +179,9 @@ describe("FormData", () => { const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(3); // the values in the multiselect are sorted by hid DESC expect(selectedValues.at(0).text()).toBe("3: hdaName3"); expect(selectedValues.at(1).text()).toBe("2: hdaName2"); expect(selectedValues.at(2).text()).toBe("1: hdaName1"); expect(selectedValues.at(0).text()).toContain("3: hdaName3"); expect(selectedValues.at(1).text()).toContain("2: hdaName2"); expect(selectedValues.at(2).text()).toContain("1: hdaName1"); await selectedValues.at(0).trigger("click"); const value_sorted = { batch: false, Loading Loading @@ -226,11 +229,11 @@ describe("FormData", () => { expect(selectedValues.length).toBe(5); // when dces are mixed in their values are shown first and are // ordered by id descending expect(selectedValues.at(0).text()).toBe("dceName4 (as dataset)"); expect(selectedValues.at(1).text()).toBe("dceName3 (as dataset)"); expect(selectedValues.at(2).text()).toBe("dceName2 (as dataset)"); expect(selectedValues.at(3).text()).toBe("2: hdaName2"); expect(selectedValues.at(4).text()).toBe("1: hdaName1"); expect(selectedValues.at(0).text()).toContain("dceName4 (as dataset)"); expect(selectedValues.at(1).text()).toContain("dceName3 (as dataset)"); expect(selectedValues.at(2).text()).toContain("dceName2 (as dataset)"); expect(selectedValues.at(3).text()).toContain("2: hdaName2"); expect(selectedValues.at(4).text()).toContain("1: hdaName1"); await selectedValues.at(0).trigger("click"); const value_sorted = { batch: false, Loading Loading @@ -261,7 +264,7 @@ describe("FormData", () => { expect(wrapper.emitted().input.length).toEqual(1); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName1 (as dataset)"); expect(selectedValues.at(0).text()).toContain("dceName1 (as dataset)"); }); it("dataset collection element as hdca without map_over_type", async () => { Loading @@ -274,7 +277,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName2 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName2 (as dataset collection)"); }); it("dataset collection element as hdca mapped to batch field", async () => { Loading @@ -291,7 +294,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName3 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName3 (as dataset collection)"); }); it("dataset collection element as hdca mapped to non-batch field", async () => { Loading @@ -309,7 +312,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("dceName3 (as dataset collection)"); expect(selectedValues.at(0).text()).toContain("dceName3 (as dataset collection)"); }); it("dataset collection mapped to non-batch field", async () => { Loading @@ -327,7 +330,7 @@ describe("FormData", () => { await wrapper.vm.$nextTick(); const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("5: hdcaName5"); expect(selectedValues.at(0).text()).toContain("5: hdcaName5"); }); it("multiple dataset collection elements (as hdas)", async () => { Loading Loading @@ -513,22 +516,22 @@ describe("FormData", () => { }); const select_0 = wrapper_0.findAll(SELECT_OPTIONS); expect(select_0.length).toBe(4); expect(select_0.at(2).text()).toBe("2: hdaName2"); expect(select_0.at(3).text()).toBe("1: hdaName1"); expect(select_0.at(2).text()).toContain("2: hdaName2"); expect(select_0.at(3).text()).toContain("1: hdaName1"); const wrapper_1 = createTarget({ tag: "tag2", options: defaultOptions, }); const select_1 = wrapper_1.findAll(SELECT_OPTIONS); expect(select_1.length).toBe(4); expect(select_1.at(2).text()).toBe("3: hdaName3"); expect(select_1.at(3).text()).toBe("2: hdaName2"); expect(select_1.at(2).text()).toContain("3: hdaName3"); expect(select_1.at(3).text()).toContain("2: hdaName2"); const wrapper_2 = createTarget({ tag: "tag3", options: defaultOptions, }); const select_2 = wrapper_2.findAll(SELECT_OPTIONS); expect(select_2.length).toBe(3); expect(select_2.at(2).text()).toBe("3: hdaName3"); expect(select_2.at(2).text()).toContain("3: hdaName3"); }); });
client/src/components/Form/Elements/FormSelect.vue +32 −6 Original line number Diff line number Diff line Loading @@ -2,17 +2,21 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faCheckSquare, faSquare } from "@fortawesome/free-regular-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { computed, type ComputedRef, onMounted, type PropType, watch } from "vue"; import { computed, type ComputedRef, onMounted, type PropType, ref, watch } from "vue"; import Multiselect from "vue-multiselect"; import { useFilterObjectArray } from "@/composables/filter"; import { useMultiselect } from "@/composables/useMultiselect"; import { uid } from "@/utils/utils"; import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue"; library.add(faCheckSquare, faSquare); const { ariaExpanded, onOpen, onClose } = useMultiselect(); type SelectValue = Record<string, unknown> | string | number | null; type ValueWithTags = SelectValue & { tags: string[] }; interface SelectOption { label: string; Loading Loading @@ -51,19 +55,22 @@ const emit = defineEmits<{ (e: "input", value: SelectValue | Array<SelectValue>): void; }>(); const filter = ref(""); const filteredOptions = useFilterObjectArray(() => props.options, filter, ["label", ["value", "tags"]]); /** * When there are more options than this, push selected options to the end */ const optionReorderThreshold = 8; const reorderedOptions = computed(() => { if (props.options.length <= optionReorderThreshold) { return props.options; if (filteredOptions.value.length <= optionReorderThreshold) { return filteredOptions.value; } else { const selectedOptions: SelectOption[] = []; const unselectedOptions: SelectOption[] = []; props.options.forEach((option) => { filteredOptions.value.forEach((option) => { if (selectedValues.value.includes(option.value)) { selectedOptions.push(option); } else { Loading Loading @@ -140,7 +147,9 @@ function setInitialValue(): void { */ watch( () => props.options, () => setInitialValue() () => { setInitialValue(); } ); /** Loading @@ -149,6 +158,14 @@ watch( onMounted(() => { setInitialValue(); }); function isValueWithTags(item: SelectValue): item is ValueWithTags { return item !== null && typeof item === "object" && (item as ValueWithTags).tags !== undefined; } function onSearchChange(search: string): void { filter.value = search; } </script> <template> Loading @@ -169,11 +186,20 @@ onMounted(() => { :selected-label="selectedLabel" :select-label="null" track-by="value" :internal-search="false" @search-change="onSearchChange" @open="onOpen" @close="onClose"> <template v-slot:option="{ option }"> <div class="d-flex align-items-center justify-content-between"> <div> <span>{{ option.label }}</span> <StatelessTags v-if="isValueWithTags(option.value)" class="tags mt-2" :value="option.value.tags" disabled /> </div> <FontAwesomeIcon v-if="selectedValues.includes(option.value)" :icon="faCheckSquare" /> <FontAwesomeIcon v-else :icon="faSquare" /> </div> Loading
client/src/components/HistoryImport.vue +2 −1 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import { waitOnJob } from "components/JobStates/wait"; import LoadingSpan from "components/LoadingSpan"; import { getAppRoot } from "onload/loadConfig"; import { errorMessageAsString } from "utils/simple-error"; import { capitalizeFirstLetter } from "utils/strings"; import Vue, { ref, watch } from "vue"; import { fetchFileSources } from "@/api/remoteFiles"; Loading Loading @@ -165,7 +166,7 @@ export default { return this.invocationImport ? "invocation" : "history"; }, identifierTextCapitalized() { return this.identifierText.charAt(0).toUpperCase() + this.identifierText.slice(1); return capitalizeFirstLetter(this.identifierText); }, identifierTextPlural() { return this.invocationImport ? "invocations" : "histories"; Loading
client/src/components/Notifications/Categories/SharedItemNotification.vue +1 −4 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import { computed } from "vue"; import type { SharedItemNotification } from "@/api/notifications"; import { useNotificationsStore } from "@/stores/notificationsStore"; import { absPath } from "@/utils/redirect"; import { capitalizeFirstLetter } from "@/utils/strings"; import NotificationActions from "@/components/Notifications/NotificationActions.vue"; Loading @@ -21,10 +22,6 @@ const props = defineProps<Props>(); const notificationsStore = useNotificationsStore(); function capitalizeFirstLetter(string: string) { return string.charAt(0).toUpperCase() + string.slice(1); } const content = computed(() => props.notification.content); const sharedItemType = computed(() => { Loading
client/src/components/User/ExternalIdentities/ExternalIdentities.vue +7 −1 Original line number Diff line number Diff line Loading @@ -46,7 +46,8 @@ title="Disconnect External Identity" class="d-block mt-3" @click="onDisconnect(item)"> Disconnect {{ item.provider.charAt(0).toUpperCase() + item.provider.slice(1) }} - {{ item.email }} Disconnect {{ capitalizeAsTitle(item.provider_label) }} - {{ item.email }} </b-button> <b-modal Loading Loading @@ -98,6 +99,8 @@ import { sanitize } from "dompurify"; import { userLogout } from "utils/logout"; import Vue from "vue"; import { capitalizeFirstLetter } from "@/utils/strings"; import svc from "./service"; import ExternalLogin from "components/User/ExternalIdentities/ExternalLogin.vue"; Loading Loading @@ -156,6 +159,9 @@ export default { Toast.success(notificationMessage); }, methods: { capitalizeAsTitle(str) { return capitalizeFirstLetter(str); }, loadIdentities() { this.loading = true; svc.getIdentityProviders() Loading