diff --git a/index.html b/index.html index 4f3756faff9d827c7c65e9cb4bcc7333443d09e7..8e0a1634324b7233e9015ffb88418736749cf4ab 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="/favicon.png"> - <title>Coscine UIv2</title> + <title>Coscine</title> </head> <body class="dark:bg-gray-800"> <noscript> diff --git a/src/App.vue b/src/App.vue index 820222908baada51e7c0c115b714fb8f7b798eae..1ed65d809092bc71282c88d2067c420676d937f4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,17 +1,20 @@ <template> <div id="app"> - <Navbar /> - <LoadingIndicator /> - <main> - <div class="mr-0 d-flex"> - <SidebarMenu class="mr-2" /> - <div class="container-fluid px-5"> - <Pilot /> - <Maintenance /> - <BreadCrumbs /> - <RouterView /> - </div> - </div> + <header class="coscine-header"> + <Navbar /> + <LoadingIndicator /> + </header> + <SidebarMenu /> + <main + :class="sidebarMenuToggled ? 'sidebar-active' : 'sidebar-inactive'" + class="coscine-main" + > + <b-container fluid> + <Pilot /> + <Maintenance /> + <BreadCrumbs /> + <RouterView /> + </b-container> </main> <ExpiryToast /> <NotificationToast /> @@ -43,6 +46,10 @@ export default defineComponent({ localeChange(): string { return this.mainStore.coscine.locale; }, + + sidebarMenuToggled(): boolean { + return this.mainStore.sidebarActive; + }, }, watch: { @@ -76,6 +83,18 @@ export default defineComponent({ </script> <style> +.coscine-header { + position: fixed; + top: 0; + width: 100%; + z-index: 999; +} + +.coscine-main { + margin-top: var(--sidebar-offset-top); + padding-right: 1.5rem; +} + .h-divider { margin-top: 5px; margin-bottom: 10px; @@ -84,8 +103,31 @@ export default defineComponent({ border-top: 1px solid var(--light); } +a[target="_blank"]:after { + /* URL icon styling for a new tab */ + content: "\F1C0"; + font-size: 0.8em; + color: var(--secondary); + font-family: "bootstrap-icons"; + src: url("bootstrap-icons.woff2?856008caa5eb66df68595e734e59580d") + format("woff2"), + url("bootstrap-icons.woff?856008caa5eb66df68595e734e59580d") format("woff"); +} + #card-deck { /* Used to align the cards' and container's left edges */ margin: 0em -0.5em; } + +.sidebar-active { + padding-left: calc(var(--sidebar-width-active) + var(--sidebar-left-margin)); + transition: all 0.3s; +} + +.sidebar-inactive { + padding-left: calc( + var(--sidebar-width-collapsed) + var(--sidebar-left-margin) + ); + transition: all 0.3s; +} </style> diff --git a/src/assets/css/_custom.css b/src/assets/css/_custom.css index b4bb2390b0dbb343dc1e7a9b04ac96ac6d948f84..70ef8ab13c1812d3a628b3fdc59c9dd8f368003c 100644 --- a/src/assets/css/_custom.css +++ b/src/assets/css/_custom.css @@ -1,7 +1,24 @@ :root { + /* Background color for static elements (e.g. Sidebar, Breadcrumbs) */ + --static-element-bg: var(--light); + + /* Navbar and loading indictor definitions */ --navbar-height: 2.5rem; --loading-indicator-height: 3px; --loading-indicator-margin: 1rem; + --search-bar-width: calc(10rem + 20vw); + + /* Beadcrumb definitions */ + --breadcrumbs-height: 3rem; + --breadcrumbs-margin: 1rem; + + /* Sidebar definitions */ + --sidebar-width-collapsed: 3rem; + --sidebar-default-width: 25rem; + --sidebar-width-active: calc(9rem + 10%); + --sidebar-left-margin: 0.5rem; + + /* Calculated heights */ --sidebar-offset-top: calc(var(--navbar-height) + var(--loading-indicator-height) + var(--loading-indicator-margin)); --sidebar-height: calc(100vh - var(--navbar-height) - calc(var(--loading-indicator-height) + var(--loading-indicator-margin))) } \ No newline at end of file diff --git a/src/assets/images/404.jpg b/src/assets/images/404.jpg deleted file mode 100644 index 89c27f9f17617926bd799d9ddcadb2fc687d4c9c..0000000000000000000000000000000000000000 Binary files a/src/assets/images/404.jpg and /dev/null differ diff --git a/src/assets/images/Admin.png b/src/assets/images/Admin.png deleted file mode 100644 index 0b8ec8da9176c0ef839bcac5e5a2abc92ecf94d9..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Admin.png and /dev/null differ diff --git a/src/assets/images/CreateProject.png b/src/assets/images/CreateProject.png deleted file mode 100644 index ef7d28c9663121510b605d1b6cab0a18b9210cca..0000000000000000000000000000000000000000 Binary files a/src/assets/images/CreateProject.png and /dev/null differ diff --git a/src/assets/images/Home.png b/src/assets/images/Home.png deleted file mode 100644 index 099603cb00d67d4fe0c737b65d97b0d615b4e504..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Home.png and /dev/null differ diff --git a/src/assets/images/Login.png b/src/assets/images/Login.png deleted file mode 100644 index 13ce0221ff82b1a8d304d92f045dced2f94e3cb2..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Login.png and /dev/null differ diff --git a/src/assets/images/Project-Id-CreateResource.png b/src/assets/images/Project-Id-CreateResource.png deleted file mode 100644 index fb372e34eb29a2261746e35838d6d4d5d64e77ee..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-CreateResource.png and /dev/null differ diff --git a/src/assets/images/Project-Id-Members.png b/src/assets/images/Project-Id-Members.png deleted file mode 100644 index 5b66b37927cd0634d4f8f71bdb45ee9337a8a412..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-Members.png and /dev/null differ diff --git a/src/assets/images/Project-Id-Quota.png b/src/assets/images/Project-Id-Quota.png deleted file mode 100644 index 96476d9b946ced5699ecb0490b6fcbdc50a5ceb9..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-Quota.png and /dev/null differ diff --git a/src/assets/images/Project-Id-R-Guid-Settings.png b/src/assets/images/Project-Id-R-Guid-Settings.png deleted file mode 100644 index ca85f066924d76a2bfc2e2ded1bc0bbef18b3e22..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-R-Guid-Settings.png and /dev/null differ diff --git a/src/assets/images/Project-Id-R-Guid.png b/src/assets/images/Project-Id-R-Guid.png deleted file mode 100644 index a463c5ad84848a256bc7090994c89de782f510ef..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-R-Guid.png and /dev/null differ diff --git a/src/assets/images/Project-Id-Settings.png b/src/assets/images/Project-Id-Settings.png deleted file mode 100644 index 1b0e557fad83468774877cffe787141879d6cbe6..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Id-Settings.png and /dev/null differ diff --git a/src/assets/images/Project-Slug.png b/src/assets/images/Project-Slug.png deleted file mode 100644 index e21dca86e829008c44664f51779064e1619fab8b..0000000000000000000000000000000000000000 Binary files a/src/assets/images/Project-Slug.png and /dev/null differ diff --git a/src/assets/images/UserProfile.png b/src/assets/images/UserProfile.png deleted file mode 100644 index 295dc84530793159b3c608e2292717d7081227eb..0000000000000000000000000000000000000000 Binary files a/src/assets/images/UserProfile.png and /dev/null differ diff --git a/src/assets/scss/_custom.scss b/src/assets/scss/_custom.scss index 8292e94f912a178b6f3d0807bedaab9edbd6ce16..0e347619147ffa3f1876b2ffde82c2f9e21619f7 100644 --- a/src/assets/scss/_custom.scss +++ b/src/assets/scss/_custom.scss @@ -12,7 +12,7 @@ $white: #fff; $gray: #646567; $gray-dark: #333; $primary: #00549f; -$secondary: #e1e1e1; +// $secondary: #e1e1e1; $success: #57ab27; $info: #c7ddf2; $warning: #fc0; diff --git a/src/components.d.ts b/src/components.d.ts index 3e6c56844e222a62ff176b88666ae62f46b3062a..6f4dbc45ee21a6b7d1b92c50b9f05a3346ca3596 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -5,64 +5,7 @@ import "@vue/runtime-core"; declare module "@vue/runtime-core" { export interface GlobalComponents { - BAlert: typeof import("bootstrap-vue")["BAlert"]; - BBadge: typeof import("bootstrap-vue")["BBadge"]; - BBreadcrumb: typeof import("bootstrap-vue")["BBreadcrumb"]; - BBreadcrumbItem: typeof import("bootstrap-vue")["BBreadcrumbItem"]; - BButton: typeof import("bootstrap-vue")["BButton"]; - BButtonGroup: typeof import("bootstrap-vue")["BButtonGroup"]; - BCard: typeof import("bootstrap-vue")["BCard"]; - BCardBody: typeof import("bootstrap-vue")["BCardBody"]; - BCardGroup: typeof import("bootstrap-vue")["BCardGroup"]; - BCardText: typeof import("bootstrap-vue")["BCardText"]; - BCol: typeof import("bootstrap-vue")["BCol"]; - BCollapse: typeof import("bootstrap-vue")["BCollapse"]; - BContainer: typeof import("bootstrap-vue")["BContainer"]; - BDropdown: typeof import("bootstrap-vue")["BDropdown"]; - BDropdownDivider: typeof import("bootstrap-vue")["BDropdownDivider"]; - BDropdownItem: typeof import("bootstrap-vue")["BDropdownItem"]; - BForm: typeof import("bootstrap-vue")["BForm"]; - BFormCheckbox: typeof import("bootstrap-vue")["BFormCheckbox"]; - BFormDatepicker: typeof import("bootstrap-vue")["BFormDatepicker"]; - BFormFile: typeof import("bootstrap-vue")["BFormFile"]; - BFormGroup: typeof import("bootstrap-vue")["BFormGroup"]; - BFormInput: typeof import("bootstrap-vue")["BFormInput"]; - BFormRadioGroup: typeof import("bootstrap-vue")["BFormRadioGroup"]; - BFormSelect: typeof import("bootstrap-vue")["BFormSelect"]; - BFormSelectOption: typeof import("bootstrap-vue")["BFormSelectOption"]; - BFormTag: typeof import("bootstrap-vue")["BFormTag"]; - BFormTags: typeof import("bootstrap-vue")["BFormTags"]; - BFormText: typeof import("bootstrap-vue")["BFormText"]; - BFormTextarea: typeof import("bootstrap-vue")["BFormTextarea"]; - BIcon: typeof import("bootstrap-vue")["BIcon"]; - BIconLink45deg: typeof import("bootstrap-vue")["BIconLink45deg"]; - BInputGroup: typeof import("bootstrap-vue")["BInputGroup"]; - BInputGroupAppend: typeof import("bootstrap-vue")["BInputGroupAppend"]; - BLink: typeof import("bootstrap-vue")["BLink"]; - BModal: typeof import("bootstrap-vue")["BModal"]; - BNavbar: typeof import("bootstrap-vue")["BNavbar"]; - BNavbarBrand: typeof import("bootstrap-vue")["BNavbarBrand"]; - BNavbarNav: typeof import("bootstrap-vue")["BNavbarNav"]; - BNavbarToggle: typeof import("bootstrap-vue")["BNavbarToggle"]; - BNavForm: typeof import("bootstrap-vue")["BNavForm"]; - BNavItem: typeof import("bootstrap-vue")["BNavItem"]; - BNavItemDropdown: typeof import("bootstrap-vue")["BNavItemDropdown"]; - BOverlay: typeof import("bootstrap-vue")["BOverlay"]; - BPagination: typeof import("bootstrap-vue")["BPagination"]; - BPopover: typeof import("bootstrap-vue")["BPopover"]; - BProgress: typeof import("bootstrap-vue")["BProgress"]; - BProgressBar: typeof import("bootstrap-vue")["BProgressBar"]; BreadCrumbs: typeof import("./components/elements/BreadCrumbs.vue")["default"]; - BRow: typeof import("bootstrap-vue")["BRow"]; - BSkeleton: typeof import("bootstrap-vue")["BSkeleton"]; - BSkeletonTable: typeof import("bootstrap-vue")["BSkeletonTable"]; - BSkeletonWrapper: typeof import("bootstrap-vue")["BSkeletonWrapper"]; - BSpinner: typeof import("bootstrap-vue")["BSpinner"]; - BTab: typeof import("bootstrap-vue")["BTab"]; - BTable: typeof import("bootstrap-vue")["BTable"]; - BTabs: typeof import("bootstrap-vue")["BTabs"]; - BToast: typeof import("bootstrap-vue")["BToast"]; - BTooltip: typeof import("bootstrap-vue")["BTooltip"]; CoscineCard: typeof import("./components/coscine/CoscineCard.vue")["default"]; CoscineFormGroup: typeof import("./components/coscine/CoscineFormGroup.vue")["default"]; CoscineHeadline: typeof import("./components/coscine/CoscineHeadline.vue")["default"]; diff --git a/src/components/elements/BreadCrumbs.vue b/src/components/elements/BreadCrumbs.vue index 370f4d86a183b8960819fb94b54894e80732dea6..bd1373475a8bd5fd00d92e4dbbbda0ecbcc91769 100644 --- a/src/components/elements/BreadCrumbs.vue +++ b/src/components/elements/BreadCrumbs.vue @@ -1,5 +1,5 @@ <template> - <b-breadcrumb> + <b-breadcrumb class="breadcrumbs"> <b-breadcrumb-item :to="{ name: 'list-projects' }" :active="crumbs.length === 0" @@ -134,3 +134,11 @@ export default defineComponent({ }, }); </script> + +<style scoped> +.breadcrumbs { + background-color: var(--static-element-bg); + height: var(--breadcrumbs-height); + margin-bottom: var(--breadcrumbs-margin); +} +</style> diff --git a/src/components/elements/Navbar.vue b/src/components/elements/Navbar.vue index dbc49fac3dba560b3f54f501b38696e15ac70f00..00871cff922f5eb7d008f1f88932b4d5f4f6c41b 100644 --- a/src/components/elements/Navbar.vue +++ b/src/components/elements/Navbar.vue @@ -1,6 +1,6 @@ <template> - <header id="header"> - <b-navbar toggleable="md" type="dark" variant="dark"> + <div id="navbar"> + <b-navbar toggleable="md" type="dark" variant="dark" sticky> <!-- Coscine Logo --> <b-navbar-brand id="coscineLogo"> <RouterLink :to="{ name: 'list-projects' }"> @@ -12,14 +12,18 @@ </RouterLink> </b-navbar-brand> - <b-navbar-toggle target="nav-collapse" class="b-0"></b-navbar-toggle - ><b-collapse id="nav-collapse" is-nav> + <b-navbar-toggle target="nav-collapse" class="b-0" /> + <b-collapse id="nav-collapse" is-nav> <!-- Search Bar --> - <b-navbar-nav class="ml-auto"> + <b-navbar-nav class="ml-auto px-2"> <b-nav-form @submit.stop.prevent="triggerSearch"> - <b-input-group size="sm" style="width: 25rem"> + <b-input-group size="sm" class="searchBar"> <template #prepend> - <b-button :disabled="!searchTerm" @click="triggerSearch"> + <b-button + :disabled="!searchTerm" + variant="light" + @click="triggerSearch" + > <b-icon icon="search" /> </b-button> </template> @@ -45,55 +49,54 @@ :key="index" :disabled="$root.$i18n.locale === locale" @click="changeLocale(locale)" - >{{ $t("nav.lang" + locale.toUpperCase()) }}</b-dropdown-item > + {{ $t("nav.lang" + locale.toUpperCase()) }} + </b-dropdown-item> </b-nav-item-dropdown> <b-nav-item-dropdown text="?" right> <!-- Help --> - <b-dropdown-item :href="$t('nav.url.extrasHelp')" target="_blank">{{ - $t("nav.extrasHelp") - }}</b-dropdown-item> + <b-dropdown-item :href="$t('nav.url.extrasHelp')" target="_blank"> + {{ $t("nav.extrasHelp") }} + </b-dropdown-item> <!-- Disclaimer --> <b-dropdown-item :href="$t('nav.url.extrasDisclaimer')" target="_blank" - >{{ $t("nav.extrasDisclaimer") }}</b-dropdown-item > + {{ $t("nav.extrasDisclaimer") }} + </b-dropdown-item> <!-- Imprint --> <b-dropdown-item :href="$t('nav.url.extrasImprint')" target="_blank" - >{{ $t("nav.extrasImprint") }}</b-dropdown-item > + {{ $t("nav.extrasImprint") }} + </b-dropdown-item> </b-nav-item-dropdown> <b-nav-item-dropdown v-if="loggedIn" right> - <template v-if="user" #button-content - >{{ $t("nav.user", { displayName: user.displayName }) }} + <template v-if="user" #button-content> + {{ $t("nav.user", { displayName: user.displayName }) }} </template> - <template v-else #button-content - ><b-skeleton + <template v-else #button-content> + <b-skeleton animation="fade" height="1.5rem" width="10rem" style="display: inline-flex; margin-bottom: 0rem" - ></b-skeleton> + /> </template> <!-- UserProfile --> <b-dropdown-item :to="{ name: 'userprofile' }"> - <!-- :to from this component currently causes two warnings! - 1. [vue-router] <router-link>'s tag prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: - 2. [vue-router] <router-link>'s event prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: - --> {{ $t("nav.userProfile") }} </b-dropdown-item> <!-- Divider --> <b-dropdown-divider /> <!-- Log out --> - <b-dropdown-item @click="logUserOut">{{ - $t("nav.userLogOut") - }}</b-dropdown-item> + <b-dropdown-item @click="logUserOut"> + {{ $t("nav.userLogOut") }} + </b-dropdown-item> </b-nav-item-dropdown> <router-link @@ -102,14 +105,14 @@ :to="{ name: 'login', query: { redirect: $route.fullPath } }" custom > - <b-nav-item right :disabled="isExactActive" :href="href">{{ - $t("nav.userLogIn") - }}</b-nav-item> + <b-nav-item right :disabled="isExactActive" :href="href"> + {{ $t("nav.userLogIn") }} + </b-nav-item> </router-link> </b-navbar-nav> </b-collapse> </b-navbar> - </header> + </div> </template> <script lang="ts"> @@ -170,7 +173,7 @@ export default defineComponent({ </script> <style scoped> -#header .navbar { +#navbar .navbar { padding-top: 0; padding-bottom: 0; } @@ -183,5 +186,9 @@ export default defineComponent({ object-fit: cover; object-position: 0%; width: 60px; + min-width: 60px; +} +.searchBar { + min-width: var(--search-bar-width); } </style> diff --git a/src/components/elements/SidebarMenu.vue b/src/components/elements/SidebarMenu.vue index aa057be2e5b735aef501c3d2035c7e440df8342e..8fc80a1b50aa28ebfa72b81dc1e79f3a392eae4d 100644 --- a/src/components/elements/SidebarMenu.vue +++ b/src/components/elements/SidebarMenu.vue @@ -2,9 +2,11 @@ <sidebar-menu :menu="menu" :collapsed="collapsed" - :relative="true" + :relative="false" :show-one-child="true" :disable-hover="false" + :width="widthActive" + :width-collapsed="widthCollapsed" theme="white-theme" @toggle-collapse="collapse" > @@ -269,6 +271,22 @@ export default defineComponent({ } return []; }, + widthActive(): string { + if (this.sidebarActive) { + return getComputedStyle(document.documentElement).getPropertyValue( + "--sidebar-width-active" + ); + } else { + return getComputedStyle(document.documentElement).getPropertyValue( + "--sidebar-default-width" + ); + } + }, + widthCollapsed(): string { + return getComputedStyle(document.documentElement).getPropertyValue( + "--sidebar-width-collapsed" + ); + }, }, watch: { collapsed() { @@ -293,13 +311,14 @@ export default defineComponent({ @import "/src/assets/scss/_custom.scss"; .v-sidebar-menu { + top: calc(100vh - var(--sidebar-height)); height: var(--sidebar-height); border-radius: 0rem 0.25rem 0.25rem 0rem; - background: $light; + background: var(--static-element-bg); + z-index: 998; } .v-sidebar-menu ::v-deep .vsm--toggle-btn { border-radius: 0rem 0rem 0.25rem 0rem; - background-color: var(--secondary); } .v-sidebar-menu.vsm_expanded { overflow-y: auto; diff --git a/src/modules/error/pages/NotFound.vue b/src/modules/error/pages/NotFound.vue index 3a3d13dd786caca739a7d46fde98b2547ee4b2f6..6369e6592ac283c48be16dfb5f2179fbf64952e9 100644 --- a/src/modules/error/pages/NotFound.vue +++ b/src/modules/error/pages/NotFound.vue @@ -1,22 +1,9 @@ <template> - <div> - <section - class="container flex flex-col items-center px-5 py-12 mx-auto text-gray-600 body-font md:flex-row" - > - <div> - <CoscineHeadline :headline="$t('page.notFound.title')" /> - <p class="mb-8 leading-relaxed dark:text-white"> - {{ $t("page.notFound.description") }} - </p> - <img alt="Oopsie!" src="@/assets/images/404.jpg" /> - </div> - </section> - </div> + <div></div> </template> <script lang="ts"> import { defineComponent } from "vue-demi"; -import CoscineHeadline from "@/components/coscine/CoscineHeadline.vue"; // import the store for current module import useErrorStore from "../store"; @@ -24,9 +11,6 @@ import useErrorStore from "../store"; import useMainStore from "@/store/index"; export default defineComponent({ - components: { - CoscineHeadline, - }, setup() { const mainStore = useMainStore(); const errorStore = useErrorStore(); diff --git a/src/modules/pid/pages/Pid.vue b/src/modules/pid/pages/Pid.vue index a130096c844edb8cd607925a4c104d512ca3604a..335632fd1df522a40bddc00674ae705b94e5b4a1 100644 --- a/src/modules/pid/pages/Pid.vue +++ b/src/modules/pid/pages/Pid.vue @@ -1,21 +1,9 @@ <template> - <div> - <section - class="container flex flex-col items-center px-5 py-12 mx-auto text-gray-600 body-font md:flex-row" - > - <div> - <CoscineHeadline :headline="$t('page.pid.title')" /> - <p class="mb-8 leading-relaxed dark:text-white"> - {{ $t("page.pid.description") }} - </p> - </div> - </section> - </div> + <div></div> </template> <script lang="ts"> import { defineComponent } from "vue-demi"; -import CoscineHeadline from "@/components/coscine/CoscineHeadline.vue"; // import the store for current module import usePidStore from "../store"; @@ -23,9 +11,6 @@ import usePidStore from "../store"; import useMainStore from "@/store/index"; export default defineComponent({ - components: { - CoscineHeadline, - }, setup() { const mainStore = useMainStore(); const pidStore = usePidStore(); diff --git a/src/modules/project/ProjectModule.vue b/src/modules/project/ProjectModule.vue index ad174642c6e1f24a39fca5401c7c27c1e86cf6e0..4c37c6bb4a00dae935b0e2149e08d5fd125625bd 100644 --- a/src/modules/project/ProjectModule.vue +++ b/src/modules/project/ProjectModule.vue @@ -98,10 +98,6 @@ export default defineComponent({ if (this.projectStore.roles === null) { this.projectStore.retrieveRoles(); } - // Load Invitations for the project if not present - if (this.projectStore.currentInvitations === null) { - this.projectStore.retrieveInvitations(this.project); - } }, }, }); diff --git a/src/modules/project/pages/CreateProject.vue b/src/modules/project/pages/CreateProject.vue index a6180ce923d20b65257f8888e2a453672e1d3dbf..0747065ccc7aea8ae94c56f385aae812930b8206 100644 --- a/src/modules/project/pages/CreateProject.vue +++ b/src/modules/project/pages/CreateProject.vue @@ -132,26 +132,28 @@ export default defineComponent({ }, }, - async created() { + created() { this.isLoading = true; - - // Load Project Visibilities if not present - if (this.projectStore.visibilities === null) { - await this.projectStore.retrieveVisibilities(); - } - // Load Project Disciplines if not present - if (this.projectStore.disciplines === null) { - await this.projectStore.retrieveDisciplines(); - } - - this.isRWTHMember = await this.projectStore.getIsMemberOfOrganization( - "https://ror.org/04xfq0f34" // RWTH Aachen University RoR - ); - + this.fetchData(); this.isLoading = false; }, methods: { + async fetchData() { + // Load Project Visibilities if not present + if (this.projectStore.visibilities === null) { + await this.projectStore.retrieveVisibilities(); + } + // Load Project Disciplines if not present + if (this.projectStore.disciplines === null) { + await this.projectStore.retrieveDisciplines(); + } + + this.isRWTHMember = await this.projectStore.getIsMemberOfOrganization( + "https://ror.org/04xfq0f34" // RWTH Aachen University RoR + ); + }, + async clickSave() { // Validate form again this.formValidations.naming.$touch(); diff --git a/src/modules/project/pages/Members.vue b/src/modules/project/pages/Members.vue index c942998064a1dc2a96388ee21ddc7c756cfa52ea..ea239bab55975e866c5c4dda7da3f0039e06127c 100644 --- a/src/modules/project/pages/Members.vue +++ b/src/modules/project/pages/Members.vue @@ -8,6 +8,7 @@ :member-role="memberRole" :project="project" :roles="roles" + :is-owner="isOwner" @addUser="addUser" @prepareInvitation="prepareInvitation" @setFilter="setFilter" @@ -31,6 +32,7 @@ :remove-text="$t('buttons.remove')" :owner-count="ownerCount" :owner-role="ownerRole" + :is-owner="isOwner" @tableFilteredRows="onFilteredRows" @setRole="setRole" @selectedItem="prepareDeletion" @@ -38,7 +40,10 @@ </b-tab> <!-- Invited Users --> - <b-tab :title="$t('page.members.externalUsersTabTitle')"> + <b-tab + v-if="isOwner" + :title="$t('page.members.externalUsersTabTitle')" + > <MembersTable id="invitationTable" :headers="invitationHeaders" @@ -271,6 +276,19 @@ export default defineComponent({ }, ]; }, + isOwner(): boolean | undefined { + return this.projectStore.currentUserRoleIsOwner; + }, + }, + + created() { + // Load Invitations for the project if not present + if ( + this.projectStore.currentInvitations === null && + this.projectStore.currentUserRoleIsOwner + ) { + this.projectStore.retrieveInvitations(this.project); + } }, methods: { diff --git a/src/modules/project/pages/Settings.vue b/src/modules/project/pages/Settings.vue index 464322c6a11d751207dc17563a5982beec5464da..16910799951a610baf6b26265dcc8e711d2987ec 100644 --- a/src/modules/project/pages/Settings.vue +++ b/src/modules/project/pages/Settings.vue @@ -160,25 +160,25 @@ export default defineComponent({ }, }, - async created() { + created() { this.isLoading = true; - - // Load Project Visibilities if not present - if (this.projectStore.visibilities === null) { - await this.projectStore.retrieveVisibilities(); - } - // Load Project Disciplines if not present - if (this.projectStore.disciplines === null) { - await this.projectStore.retrieveDisciplines(); - } - + this.fetchData(); // Fill the project form this.onProjectLoaded(); - this.isLoading = false; }, methods: { + async fetchData() { + // Load Project Visibilities if not present + if (this.projectStore.visibilities === null) { + await this.projectStore.retrieveVisibilities(); + } + // Load Project Disciplines if not present + if (this.projectStore.disciplines === null) { + await this.projectStore.retrieveDisciplines(); + } + }, onProjectLoaded() { if (this.project) { this.isLoading = true; diff --git a/src/modules/project/pages/components/MembersTable.vue b/src/modules/project/pages/components/MembersTable.vue index ae5037576f12a055b9bcb9b6b781069862d9311e..1e3c85b2688e4e9ceaaf8831700f185860dda832 100644 --- a/src/modules/project/pages/components/MembersTable.vue +++ b/src/modules/project/pages/components/MembersTable.vue @@ -36,7 +36,10 @@ v-model="row.item.role.id" :options="roles" :disabled=" - ownerCount === 1 && ownerRole && row.item.role.id === ownerRole.id + (ownerCount === 1 && + ownerRole && + row.item.role.id === ownerRole.id) || + !isOwner " text-field="displayName" value-field="id" @@ -61,7 +64,10 @@ variant="danger" size="sm" :disabled=" - ownerCount === 1 && ownerRole && row.item.role.id === ownerRole.id + (ownerCount === 1 && + ownerRole && + row.item.role.id === ownerRole.id) || + !isOwner " @click="deleteItem(row.item)" >{{ removeText }}</b-button @@ -171,6 +177,10 @@ export default defineComponent({ default: "user.surname", type: String, }, + isOwner: { + default: false, + type: Boolean, + }, }, data() { return { diff --git a/src/modules/project/pages/components/UserSearchRow.vue b/src/modules/project/pages/components/UserSearchRow.vue index cef3bbf3183518e8eb51aea2ad0ec36076c2a0bb..ccfa856034774f23bd80fcff45987aab1860aa77 100644 --- a/src/modules/project/pages/components/UserSearchRow.vue +++ b/src/modules/project/pages/components/UserSearchRow.vue @@ -12,6 +12,7 @@ adaptSelect: true, 'no-results': queriedUsers.length === 0, }" + :disabled="!isOwner" :options="queriedUsers" :filter-by="filterMock" :selectable="(option) => !option.hasProjectRole" @@ -67,6 +68,7 @@ ? memberRole.id : null " + :disabled="!isOwner" text-field="displayName" value-field="id" @input="setNewRole" @@ -80,7 +82,7 @@ <b-button v-if="validInvitation && !validSelection" name="inviteUserToProject" - :disabled="!validInvitation" + :disabled="!validInvitation || !isOwner" @click="prepareInvitation(newUserRole)" >{{ $t("buttons.invite") }}</b-button > @@ -88,12 +90,13 @@ <b-button v-else name="addUserToProject" - :disabled="!validSelection" + :disabled="!validSelection || !isOwner" @click="addUser(newUserRole)" >{{ $t("buttons.addUser") }}</b-button > <b-button name="importUser" + :disabled="!isOwner" @click="$bvModal.show('importUserModal')" >{{ $t("buttons.import") }}</b-button > @@ -155,6 +158,10 @@ export default defineComponent({ default: null, type: [Array, null] as PropType<RoleObject[] | null>, }, + isOwner: { + default: false, + type: Boolean, + }, }, setup() { const userStore = useUserStore(); @@ -266,10 +273,8 @@ export default defineComponent({ async fetchUserOptions(search: string, loading: (value: boolean) => void) { if (search && this.project && this.project.id) { loading(true); - this.queriedUsers = await this.userStore.getUser( - search, - this.project.id - ); + this.queriedUsers = + (await this.userStore.getUser(search, this.project.id)) ?? []; loading(false); if (this.validEmail && this.queriedUsers.length == 0) { // Add unknown entry as an entry to the list (email address of user to invite) diff --git a/src/modules/resource/components/create-resource/General.vue b/src/modules/resource/components/create-resource/General.vue index 51d8a663e3a2931704faeaf879b6a82af023f9d6..bdd6fd2d9f8645b0746731c8caa7d45055b4d24e 100644 --- a/src/modules/resource/components/create-resource/General.vue +++ b/src/modules/resource/components/create-resource/General.vue @@ -416,7 +416,7 @@ export default defineComponent({ }, }, - created() { + mounted() { this.initTabContent(); }, @@ -429,6 +429,8 @@ export default defineComponent({ if (this.resourceForm.license && this.resourceForm.license.id) { this.selectedLicense = this.resourceForm.license.id; } + console.debug("VIS: ", this.resourceForm.visibility); + console.debug("VISz: ", this.visibilities); if (this.resourceForm.visibility && this.resourceForm.visibility.id) { this.selectedVisibility = this.resourceForm.visibility.id; } else { @@ -501,6 +503,7 @@ export default defineComponent({ this.selectedVisibility = visibility.id; this.resourceForm.visibility = visibility; } + console.debug("DEF VIS: ", this.visibilities); }, translateResourceNameToDisplayName() { diff --git a/src/modules/resource/pages/CreateResource.vue b/src/modules/resource/pages/CreateResource.vue index 4dac3194643864f536bea5c99dcaa297e9801d4c..88370f720742ed332b1ba102037cd8a894179e77 100644 --- a/src/modules/resource/pages/CreateResource.vue +++ b/src/modules/resource/pages/CreateResource.vue @@ -171,26 +171,28 @@ export default defineComponent({ }, }, - async created() { + created() { this.isLoading = true; this.getGroupedApplicationProfilesList(); - - // Load Project Visibilities if not present - if (this.projectStore.visibilities === null) { - await this.projectStore.retrieveVisibilities(); - } - // Load Project Disciplines if not present - if (this.projectStore.disciplines === null) { - await this.projectStore.retrieveDisciplines(); - } - // Load Project Licenses if not present - if (this.projectStore.licenses === null) { - await this.projectStore.retrieveLicenses(); - } + this.fetchData(); this.isLoading = false; }, methods: { + async fetchData() { + // Load Project Visibilities if not present + if (this.projectStore.visibilities === null) { + await this.projectStore.retrieveVisibilities(); + } + // Load Project Disciplines if not present + if (this.projectStore.disciplines === null) { + await this.projectStore.retrieveDisciplines(); + } + // Load Project Licenses if not present + if (this.projectStore.licenses === null) { + await this.projectStore.retrieveLicenses(); + } + }, async getGroupedApplicationProfilesList() { let list = await this.resourceStore.getApplicationProfilesList(); if (list) { diff --git a/src/modules/resource/pages/Settings.vue b/src/modules/resource/pages/Settings.vue index 4b096661d2511834392d6b058997b38ad93cb10c..53d289cdd43db8c57f53610148a8afa3b7c53469 100644 --- a/src/modules/resource/pages/Settings.vue +++ b/src/modules/resource/pages/Settings.vue @@ -195,26 +195,28 @@ export default defineComponent({ }, }, - async created() { + created() { this.isLoading = true; - - // Load Project Visibilities if not present - if (this.projectStore.visibilities === null) { - await this.projectStore.retrieveVisibilities(); - } - // Load Project Disciplines if not present - if (this.projectStore.disciplines === null) { - await this.projectStore.retrieveDisciplines(); - } - // Load Project Licenses if not present - if (this.projectStore.licenses === null) { - await this.projectStore.retrieveLicenses(); - } + this.fetchData(); this.onResourceLoaded(); this.isLoading = false; }, methods: { + async fetchData() { + // Load Project Visibilities if not present + if (this.projectStore.visibilities === null) { + await this.projectStore.retrieveVisibilities(); + } + // Load Project Disciplines if not present + if (this.projectStore.disciplines === null) { + await this.projectStore.retrieveDisciplines(); + } + // Load Project Licenses if not present + if (this.projectStore.licenses === null) { + await this.projectStore.retrieveLicenses(); + } + }, onResourceLoaded() { if (this.resource) { // Fill the form. Note that regular assignment makes this.resource react on this.resourceForm changes! diff --git a/src/modules/search/pages/Search.vue b/src/modules/search/pages/Search.vue index 444fa78ce18188b59478c17066f952f1f59638e2..5d2e7a4ca53d0c9a1c1b3417f840e66968ab616e 100644 --- a/src/modules/search/pages/Search.vue +++ b/src/modules/search/pages/Search.vue @@ -2,11 +2,11 @@ <div class="search"> <CoscineHeadline :headline="$t('page.search.title')" /> - <b-row id="mainRow"> + <b-row id="mainRow" class="m-0"> <!-- Sidebar --> <Sidebar /> - <b-col ref="rightCol" sm="10" align-self="end" style="height: 100%"> + <b-col ref="rightCol" sm="10" align-self="end" class="h-100"> <!-- Search Bar Fields --> <b-row id="searchBarContainer" align-content="center"> <b-col id="searchField" align-self="start" class="pl-0"> @@ -147,7 +147,7 @@ </b-row> <!-- Pagination --> - <b-row class="mt-1 mb-1 text-right" align-v="center"> + <b-row class="my-1 text-right" align-v="center" no-gutters> <b-col align-self="center" class="p-0" /> <b-col align-self="center" class="p-0"> <b-pagination @@ -294,12 +294,12 @@ export default defineComponent({ <style scoped> #mainRow { - /* this style stretches the page vertically to fit the screen:-moz-animation: + /* this style stretches the page vertically to fit the screen: - container-fluid <-> top = 59px - mainRow <-> container-fluid = 102px - mainRow <-> bottom = 62px */ - height: calc(100vh - 59px - 102px - 62px); + height: calc(100vh - var(--sidebar-offset-top) - 102px - 62px); } #pagination { margin-top: 0.5rem; diff --git a/src/modules/search/pages/components/Sidebar.vue b/src/modules/search/pages/components/Sidebar.vue index bc86048f58acb3163f0590eca3eb4bbadcd745ff..216a940e9f66f6936489a2757f2b414eb9ef81c2 100644 --- a/src/modules/search/pages/components/Sidebar.vue +++ b/src/modules/search/pages/components/Sidebar.vue @@ -1,5 +1,5 @@ <template> - <b-col> + <b-col class="pl-0"> <b-card id="sidebarContainer" class="progress-bar-striped text-center bg-light" diff --git a/src/plugins/bootstrap-vue.ts b/src/plugins/bootstrap-vue.ts index ba5ef15694d25d83ae919b2d1a122ac752f3a898..31fd3642950ca49d9b66db184ddc4afdac9fb4a4 100644 --- a/src/plugins/bootstrap-vue.ts +++ b/src/plugins/bootstrap-vue.ts @@ -1,7 +1,14 @@ import Vue from "vue"; -import { IconsPlugin } from "bootstrap-vue"; +import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue"; + +// From Bootstrap Vue resolver +// import { IconsPlugin } from "bootstrap-vue"; import "bootstrap/dist/css/bootstrap.min.css"; import "bootstrap-vue/dist/bootstrap-vue.min.css"; -Vue.use(IconsPlugin); +Vue.use(BootstrapVue); +Vue.use(BootstrapVueIcons); + +// From Bootstrap Vue resolver +// Vue.use(IconsPlugin); diff --git a/src/util/bootstrap-vue-resolver.ts b/src/util/bootstrap-vue-resolver.ts index afc4e1dd9702e1cd045e884a62760ce2fe335bf7..018c8d4e20cacee19943772924184e1d47f09080 100644 --- a/src/util/bootstrap-vue-resolver.ts +++ b/src/util/bootstrap-vue-resolver.ts @@ -13,3 +13,5 @@ export function BootstrapVueResolver(): ComponentResolver { }, }; } + +// From Bootstrap Vue resolver diff --git a/vite.config.js b/vite.config.js index 56ee32f7905e82fce15d97b7c68390f2010248fa..5e155eaec1cece84d6413a3ec1a0bdac129a874f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -12,7 +12,8 @@ import replace from '@rollup/plugin-replace'; import WindiCSS from "vite-plugin-windicss"; import Components from "unplugin-vue-components/vite"; -import { BootstrapVueResolver } from "./src/util/bootstrap-vue-resolver"; +// From Bootstrap Vue resolver +// import { BootstrapVueResolver } from "./src/util/bootstrap-vue-resolver"; const config = defineConfig({ resolve: { @@ -81,23 +82,23 @@ const config = defineConfig({ "Readable$3.call": "Readable$2.call", 'return new Readable$2(options);': `{ - Object.assign(this, EventEmitter.prototype); - this.push = Readable$1.prototype.push; - this.unshift = Readable$1.prototype.unshift; - this.isPaused = Readable$1.prototype.isPaused; - this.setEncoding = Readable$1.prototype.setEncoding; - this.read = Readable$1.prototype.read; - this.pipe = Readable$1.prototype.pipe; - this.unpipe = Readable$1.prototype.unpipe; - this.on = Readable$1.prototype.on; - this.addListener = Readable$1.prototype.addListener; - this.resume = Readable$1.prototype.resume; - this.pause = Readable$1.prototype.pause; - this.wrap = Readable$1.prototype.wrap; - this.push = Transform$2.prototype.push; - this._read = Transform$2.prototype._read; - this._destroy = Transform$2.prototype._destroy; - }` + Object.assign(this, EventEmitter.prototype); + this.push = Readable$1.prototype.push; + this.unshift = Readable$1.prototype.unshift; + this.isPaused = Readable$1.prototype.isPaused; + this.setEncoding = Readable$1.prototype.setEncoding; + this.read = Readable$1.prototype.read; + this.pipe = Readable$1.prototype.pipe; + this.unpipe = Readable$1.prototype.unpipe; + this.on = Readable$1.prototype.on; + this.addListener = Readable$1.prototype.addListener; + this.resume = Readable$1.prototype.resume; + this.pause = Readable$1.prototype.pause; + this.wrap = Readable$1.prototype.wrap; + this.push = Transform$2.prototype.push; + this._read = Transform$2.prototype._read; + this._destroy = Transform$2.prototype._destroy; + }` }, delimiters: ['', ''], preventAssignment: false, @@ -114,7 +115,8 @@ const config = defineConfig({ include: [/\.vue$/, /\.vue\?vue/], exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], resolvers: [ - BootstrapVueResolver() + // From Bootstrap Vue resolver + // BootstrapVueResolver() ], }), ],