Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results

Target

Select target project
  • coscine/frontend/apps/ui
1 result
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results
Show changes
Commits on Source (10)
Showing
with 255 additions and 167 deletions
{
"name": "ui",
"version": "1.16.3",
"version": "1.17.0",
"private": true,
"scripts": {
"dev": "vite",
......@@ -10,7 +10,7 @@
"lint:fix": "eslint './src/**/*.{js,ts,tsx,vue,md}' --fix"
},
"dependencies": {
"@coscine/api-client": "^2.5.0",
"@coscine/api-client": "^2.6.0",
"@coscine/form-generator": "^3.0.7",
"@rdfjs-elements/formats-pretty": "^0.4.3",
"@rdfjs/data-model": "^2.0.0",
......@@ -18,7 +18,7 @@
"@rdfjs/types": "^1.1.0",
"@vuelidate/core": "^2.0.0-alpha.41",
"@vuelidate/validators": "^2.0.0-alpha.29",
"@vueuse/core": "^6.5.3",
"@vueuse/core": "^9.3.0",
"axios": "^0.26.1",
"bootstrap": "4.6.1",
"bootstrap-icons": "^1.8.1",
......@@ -41,7 +41,7 @@
"sass-loader": "^12.6.0",
"semantic-release": "^19.0.2",
"stream-to-string": "^1.2.0",
"uuid": "^8.3.2",
"uuid": "^9.0.0",
"vite-aliases": "^0.9.1",
"vue": "^2.7.10",
"vue-demi": "^0.13.11",
......@@ -84,11 +84,11 @@
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-polyfill-node": "^0.10.2",
"typescript": "^4.5.4",
"typescript": "^4.8.3",
"unplugin-vue-components": "^0.22.4",
"vite": "^3.0.9",
"vite-plugin-windicss": "^1.8.3",
"vue-template-compiler": "^2.7.10"
},
"packageManager": "yarn@3.2.3"
"packageManager": "yarn@3.2.4"
}
......@@ -4,25 +4,36 @@
<Navbar />
<LoadingIndicator />
</header>
<SidebarMenu />
<div v-if="!navigationError">
<SidebarMenu v-if="isLoggedIn && areTosAccepted" />
<main
:class="sidebarMenuToggled ? 'sidebar-active' : 'sidebar-inactive'"
class="coscine-main"
:class="
sidebarMenuToggled && isLoggedIn && areTosAccepted
? 'sidebar-active'
: 'sidebar-inactive'
"
>
<b-container fluid>
<Pilot />
<Pilot v-if="isLoggedIn" />
<Maintenance />
<BreadCrumbs />
<BreadCrumbs v-if="isLoggedIn && areTosAccepted" />
<RouterView />
</b-container>
</main>
<ExpiryToast />
<ExpiryToast v-if="routeRequiresAuth" />
<NotificationToast />
</div>
<main v-else>
<!-- Router View for Error Pages -->
<RouterView />
</main>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the main store
import useMainStore from "@/store/index";
......@@ -41,8 +52,16 @@ export default defineComponent({
},
computed: {
loggedIn(): boolean {
return this.mainStore.loggedIn;
isLoggedIn(): boolean {
return this.loginStore.isLoggedIn;
},
routeRequiresAuth(): boolean {
return this.$route.meta?.requiresAuth ?? false;
},
areTosAccepted(): boolean | null {
return this.loginStore.areTosAccepted;
},
localeChange(): string {
......@@ -52,6 +71,12 @@ export default defineComponent({
sidebarMenuToggled(): boolean {
return this.mainStore.sidebarActive;
},
navigationError(): boolean {
if (this.$route.name == "not-found") {
return true;
} else return false;
},
},
watch: {
......@@ -60,8 +85,12 @@ export default defineComponent({
window.location.reload();
},
loggedIn() {
this.initialize();
async isLoggedIn() {
await this.initializeAfterLogin();
},
async areTosAccepted() {
await this.initializeUser();
},
localeChange() {
......@@ -69,19 +98,31 @@ export default defineComponent({
},
},
created() {
this.initialize();
async mounted() {
await this.initialize();
},
methods: {
async initialize() {
if (!this.loggedIn) {
this.loginStore.redirectToLogin(this.$route, true);
} else {
// Fetch Current TOS Version
if (!this.loginStore.currentTosVersion) {
await this.loginStore.retrieveCurrentTosVersion();
}
await this.initializeAfterLogin();
},
async initializeAfterLogin() {
if (this.isLoggedIn) {
await this.loginStore.retrieveTosAcceptanceStatus();
}
},
async initializeUser() {
if (this.areTosAccepted) {
if (!this.userStore.user) {
await this.userStore.retrieveUser();
await this.projectStore.retrieveAllProjects();
this.userStore.setUserLanguagePreference();
await this.mainStore.getMaintenance();
}
}
},
},
......@@ -96,9 +137,10 @@ export default defineComponent({
z-index: 999;
}
.coscine-main {
main {
margin-top: var(--sidebar-offset-top);
padding-right: 1.5rem;
padding-left: 0;
}
.h-divider {
......@@ -139,6 +181,7 @@ a[target="_blank"]:after {
/* 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;
......
......@@ -8,16 +8,18 @@
>
<i18n :path="messagePath" tag="p">
<template #link>
<a :href="maintenance.url" target="_blank">{{
$t("banner.maintenance.moreInformation")
}}</a>
<a
:href="maintenance.url ? maintenance.url : undefined"
target="_blank"
>{{ $t("banner.maintenance.moreInformation") }}</a
>
</template>
</i18n>
</b-alert>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the main store
import useMainStore from "@/store/index";
import type { MaintenanceReturnObject } from "@coscine/api-client/dist/types/Coscine.Api.Notices";
......
......@@ -2,13 +2,13 @@
<b-alert
v-if="pilotVisibility"
:show="pilotVisibility"
dismissible
:dismissible="dismissible"
variant="warning"
@dismissed="saveVisibility"
>
<i18n path="banner.pilot.pilotBannerText" tag="p">
<template #link>
<a :href="$t('email.serviceDeskMailTo')" target="_blank">{{
<a :href="$t('email.serviceDeskMailTo').toString()" target="_blank">{{
$t("email.serviceDeskName")
}}</a>
</template>
......@@ -17,9 +17,19 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
import useMainStore from "@/store/index";
export default defineComponent({
props: {
persistent: {
default: false,
type: Boolean,
},
dismissible: {
default: true,
type: Boolean,
},
},
setup() {
const mainStore = useMainStore();
return { mainStore };
......@@ -27,7 +37,9 @@ export default defineComponent({
computed: {
pilotVisibility(): boolean {
return this.mainStore.coscine.banner.pilotVisibility;
return this.persistent
? this.persistent
: this.mainStore.coscine.banner.pilotVisibility;
},
},
......
......@@ -52,7 +52,7 @@
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue-demi";
import { defineComponent, type PropType } from "vue";
import type { RawLocation } from "vue-router";
export default defineComponent({
......
......@@ -38,7 +38,8 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent, type PropType } from "vue";
import { TranslateResult } from "vue-i18n";
export default defineComponent({
name: "CoscineFormGroup",
......@@ -61,7 +62,7 @@ export default defineComponent({
},
label: {
default: null,
type: String,
type: [String, Object] as PropType<string | TranslateResult>,
},
type: {
default: "input",
......
......@@ -17,7 +17,7 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
export default defineComponent({
name: "CoscineModal",
......
<template>
<b-breadcrumb class="breadcrumbs">
<b-breadcrumb-item
:to="{ name: 'list-projects' }"
:active="crumbs.length === 0"
>
<b-breadcrumb-item :to="{ name: 'home' }" :active="crumbs.length === 0">
<b-icon v-if="crumbs.length === 0" icon="house-fill" aria-hidden="true" />
<b-icon v-else icon="house" aria-hidden="true" />
{{ $t(`breadcrumbs.home`) }}
......@@ -20,10 +17,8 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the main store
import useMainStore from "@/store/index";
// import the project store
import useProjectStore from "@/modules/project/store";
// import the resource store
......@@ -43,11 +38,10 @@ interface RouteLink {
export default defineComponent({
setup() {
const mainStore = useMainStore();
const projectStore = useProjectStore();
const resourceStore = useResourceStore();
return { mainStore, projectStore, resourceStore };
return { projectStore, resourceStore };
},
computed: {
......@@ -149,9 +143,23 @@ export default defineComponent({
""
);
}
document.title = this.$t("default.documentModifiedTitle", {
this.setDocumentTitle(title);
},
},
created() {
this.setDocumentTitle();
},
methods: {
setDocumentTitle(title = "") {
if (title.trim() !== "") {
document.title = this.$t("title.modified", {
title: title,
}).toString();
} else {
document.title = this.$t("title.default").toString();
}
},
},
});
......
......@@ -17,7 +17,7 @@ import useSearchStore from "@/modules/search/store";
import useUserStore from "@/modules/user/store";
import { loadingCounterEventHandler } from "@/plugins/loadingCounter";
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
export default defineComponent({
setup() {
......
......@@ -3,7 +3,7 @@
<b-navbar toggleable="md" type="dark" variant="dark" sticky>
<!-- Coscine Logo -->
<b-navbar-brand id="coscineLogo">
<RouterLink :to="{ name: 'list-projects' }">
<RouterLink :to="{ name: 'home' }">
<img
alt="Coscine Logo"
src="@/assets/svg/coscine_white.svg"
......@@ -56,28 +56,37 @@
<b-nav-item-dropdown text="?" right>
<!-- Help -->
<b-dropdown-item :href="$t('nav.url.extrasHelp')" target="_blank">
<b-dropdown-item
:href="$t('nav.url.extrasHelp').toString()"
target="_blank"
>
{{ $t("nav.extrasHelp") }}
</b-dropdown-item>
<!-- Disclaimer -->
<b-dropdown-item
:href="$t('nav.url.extrasDisclaimer')"
:href="$t('nav.url.extrasDisclaimer').toString()"
target="_blank"
>
{{ $t("nav.extrasDisclaimer") }}
</b-dropdown-item>
<!-- Imprint -->
<b-dropdown-item
:href="$t('nav.url.extrasImprint')"
:href="$t('nav.url.extrasImprint').toString()"
target="_blank"
>
{{ $t("nav.extrasImprint") }}
</b-dropdown-item>
</b-nav-item-dropdown>
<b-nav-item-dropdown v-if="loggedIn" right>
<b-nav-item-dropdown v-if="isLoggedIn" right>
<template v-if="user" #button-content>
{{ $t("nav.user", { displayName: user.displayName }) }}
{{
$t("nav.user", {
displayName: user
? user.displayName
: $t("default.none").toString(),
})
}}
</template>
<template v-else #button-content>
<b-skeleton
......@@ -101,11 +110,11 @@
<router-link
v-else
v-slot="{ href, isExactActive }"
:to="{ name: 'login', query: { redirect: $route.fullPath } }"
v-slot="{ href, isActive }"
:to="{ name: 'login' }"
custom
>
<b-nav-item right :disabled="isExactActive" :href="href">
<b-nav-item right :disabled="isActive" :href="href">
{{ $t("nav.userLogIn") }}
</b-nav-item>
</router-link>
......@@ -116,7 +125,7 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the main store
import useMainStore from "@/store/index";
// import the login store
......@@ -142,8 +151,8 @@ export default defineComponent({
locales(): Array<string> {
return this.$root.$i18n.availableLocales;
},
loggedIn(): boolean {
return this.mainStore.loggedIn;
isLoggedIn(): boolean {
return this.loginStore.isLoggedIn;
},
user(): null | UserObject {
return this.userStore.user;
......@@ -151,9 +160,7 @@ export default defineComponent({
},
methods: {
changeLocale(locale: string): void {
this.mainStore.$patch((state) => {
state.coscine.locale = locale;
});
this.mainStore.coscine.locale = locale;
},
logUserOut() {
this.loginStore.logout();
......
......@@ -23,7 +23,7 @@
<script lang="ts">
import Vue from "vue";
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
import "bootstrap-icons/font/bootstrap-icons.css";
import "bootstrap-icons/font/fonts/bootstrap-icons.woff";
import "bootstrap-icons/font/fonts/bootstrap-icons.woff2";
......@@ -317,54 +317,48 @@ export default defineComponent({
background: var(--static-element-bg);
z-index: 998;
}
.v-sidebar-menu ::v-deep .vsm--toggle-btn {
.v-sidebar-menu :deep(.vsm--toggle-btn) {
border-radius: 0rem 0rem 0.25rem 0rem;
}
.v-sidebar-menu.vsm_expanded {
overflow-y: auto;
overflow-x: hidden;
}
.v-sidebar-menu ::v-deep .vsm--badge {
.v-sidebar-menu :deep(.vsm--badge) {
background-color: #ffc107;
border-radius: 10rem;
text-transform: capitalize;
font-weight: bold;
}
.v-sidebar-menu ::v-deep .vsm--title {
.v-sidebar-menu :deep(.vsm--title) {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.v-sidebar-menu.vsm_expanded ::v-deep .vsm--item_open .vsm--link_level-1 {
.v-sidebar-menu.vsm_expanded :deep(.vsm--item_open .vsm--link_level-1) {
background: $primary;
}
.v-sidebar-menu.vsm_expanded
::v-deep
.vsm--item_open
.vsm--link_level-1
.vsm--icon {
:deep(.vsm--item_open .vsm--link_level-1 .vsm--icon) {
background: $primary;
}
.v-sidebar-menu.vsm_collapsed ::v-deep .vsm--mobile-item .vsm--item_open {
.v-sidebar-menu.vsm_collapsed :deep(.vsm--mobile-item .vsm--item_open) {
background-color: $primary;
}
.v-sidebar-menu.vsm_collapsed ::v-deep .vsm--mobile-item .vsm--mobile-bg {
.v-sidebar-menu.vsm_collapsed :deep(.vsm--mobile-item .vsm--mobile-bg) {
background-color: $primary;
}
.v-sidebar-menu.vsm_collapsed
::v-deep
.vsm--link_level-1.vsm--link_hover
.vsm--icon {
:deep(.vsm--link_level-1.vsm--link_hover .vsm--icon) {
background-color: $primary;
}
.v-sidebar-menu.vsm_white-theme
::v-deep
.vsm--link_level-1.vsm--link_exact-active,
.v-sidebar-menu ::v-deep .vsm--link_level-1.vsm--link_active {
:deep(.vsm--link_level-1.vsm--link_exact-active),
.v-sidebar-menu :deep(.vsm--link_level-1.vsm--link_active) {
box-shadow: 3px 0px 0px 0px $primary inset;
-webkit-box-shadow: 3px 0px 0px 0px $primary inset;
}
.v-sidebar-menu ::v-deep .vsm--link_level-2.vsm--link_active {
.v-sidebar-menu :deep(.vsm--link_level-2.vsm--link_active) {
// Use this to edit the second level element when active
background-color: var(--primary);
color: $white;
......
......@@ -4,7 +4,7 @@
<b-overlay
no-wrap
:fixed="true"
:show="loginStore.expiredSession"
:show="showOverlay"
:variant="'white'"
:opacity="0.9"
:z-index="1000"
......@@ -18,7 +18,7 @@
<!-- Expiry Toast -->
<b-toast
v-model="loginStore.expiredSession"
v-model="isSessionExpired"
toaster="b-toaster-bottom-right"
variant="danger"
solid
......@@ -32,6 +32,7 @@
<!-- Login Button -->
<div class="d-flex justify-content-center">
<b-button
v-if="showOverlay"
class="mt-2"
size="sm"
variant="primary"
......@@ -45,13 +46,11 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import * as jose from "jose";
import moment from "moment";
import { defineComponent } from "vue";
import useMainStore from "@/store/index";
import useLoginStore from "@/modules/login/store";
import type { RawLocation } from "vue-router";
export default defineComponent({
setup() {
......@@ -60,16 +59,30 @@ export default defineComponent({
return { mainStore, loginStore };
},
data() {
return {
tokenExpiredInterval: 0,
};
},
computed: {
token(): string {
return this.mainStore.coscine.authorization.bearer;
},
isLoggedIn(): boolean {
return this.loginStore.isLoggedIn;
},
isSessionExpired(): boolean | null {
return this.loginStore.expiredSession;
},
showOverlay(): boolean {
if (this.$route.name !== "login" && this.isSessionExpired) {
return true;
} else return false;
},
},
watch: {
token() {
this.initialize();
......@@ -78,48 +91,40 @@ export default defineComponent({
created() {
this.initialize();
},
methods: {
getCurrentTokenExpirationDuration(): number {
try {
const jwt = jose.decodeJwt(this.token);
const now = moment.utc(moment.now());
if (jwt.exp) {
// Use UTC to avoid time conversion errors
const tokenExpiresAt = moment(jwt.exp * 1000).utc();
const untilTokenExpiration = moment.duration(
tokenExpiresAt.diff(now)
);
// Return as milliseconds for setInterval() method
return untilTokenExpiration.asMilliseconds();
} else {
return 0;
}
} catch (error) {
// Provided token is not a valid JWT Token
return 0;
}
},
methods: {
initialize() {
this.loginStore.expiredSession = false;
if (this.tokenExpiredInterval) {
clearInterval(this.tokenExpiredInterval);
}
if (this.token) {
this.tokenExpiredInterval = this.setInterval(
this.notifyTokenExpired,
this.getCurrentTokenExpirationDuration()
this.mainStore.getCurrentTokenExpirationDuration()
);
}
},
notifyTokenExpired() {
this.loginStore.expiredSession = true;
// Cancel interval expiration check
clearInterval(this.tokenExpiredInterval);
},
redirectToLogin() {
this.loginStore.redirectToLogin(this.$route, true);
const location = {
name: "login",
query: this.$route
? { redirect: window.location.pathname + window.location.search }
: null,
} as RawLocation;
// Only execute if the user is not logged in and is not located on the login page
if (!this.isLoggedIn) {
this.$router.push(location);
}
},
setInterval(fn: () => void, delay: number): number {
const maxDelay = Math.pow(2, 31) - 1;
if (delay > maxDelay) {
......
......@@ -3,18 +3,23 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
import useNotificationStore from "@/store/notification";
import type { NotificationToast } from "@/store/types";
import useLoginStore from "@/modules/login/store";
export default defineComponent({
setup() {
const notificationStore = useNotificationStore();
const loginStore = useLoginStore();
return { notificationStore };
return { notificationStore, loginStore };
},
computed: {
isSessionExpired(): boolean | null {
return this.loginStore.expiredSession;
},
currentToast(): NotificationToast | undefined {
return this.notificationStore.notificationQueue
? (this.notificationStore.notificationQueue.at(0) as NotificationToast)
......@@ -26,8 +31,12 @@ export default defineComponent({
currentToast() {
const toast = this.currentToast;
if (toast) {
// Display the notification toast
// Display the notification toast.
// Prevent the creation of notification toasts when the session is expired.
// Such cases are handled by the ExpiryToast element.
if (this.isSessionExpired !== null && !this.isSessionExpired) {
this.makeToast(toast);
}
// Toast has been shown, remove it from the queue
this.notificationStore.deleteNotification(toast);
}
......
......@@ -67,8 +67,6 @@ export default {
} as VueI18n.LocaleMessageObject,
default: {
documentTitle: "Coscine",
documentModifiedTitle: "{title} | @:(default.documentTitle)",
archive: "Archivieren",
archived: "Archiviert",
deleted: "Gelöscht",
......@@ -78,6 +76,14 @@ export default {
none: "k. A.",
},
title: {
default: "Coscine",
modified: "{title} | @:(title.default)",
error: {
notFound: "404 - Not Found | @:(title.default)",
},
},
toast: {
contactChange: {
success: {
......@@ -92,7 +98,7 @@ export default {
},
session: {
title: "Ihre Sitzung ist abgelaufen",
message: "Um weiterzuarbeiten, loggen Sie sich erneut ein:",
message: "Um weiterzuarbeiten, loggen Sie sich erneut ein.",
link: "@:(nav.userLogIn)",
},
onSave: {
......@@ -132,6 +138,7 @@ export default {
breadcrumbs: {
admin: "Admin",
home: "Home",
tos: "Nutzungsbedingungen und Datenschutzerklärung",
login: "Einloggen",
pid: "PID",
search: "Suche",
......@@ -156,29 +163,30 @@ export default {
},
resourceTypes: {
resource: "Ressource",
rdsrwth: {
displayName: "RWTH-RDS-Web",
fullName: "Research Data Storage (RDS)",
description:
"Research Data Storage (RDS) ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Resourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
"Research Data Storage (RDS) ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Ressourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
},
rdsude: {
displayName: "UDE-RDS-Web",
fullName: "Research Data Storage (RDS)",
description:
"Research Data Storage (RDS) Universität Duisburg-Essen ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Resourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
"Research Data Storage (RDS) Universität Duisburg-Essen ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Ressourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
},
rdstudo: {
displayName: "TUDO-RDS-Web",
fullName: "Research Data Storage (RDS)",
description:
"Research Data Storage (RDS) Technische Universität Dortmund ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Resourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
"Research Data Storage (RDS) Technische Universität Dortmund ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Ressourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
},
rdsnrw: {
displayName: "NRW-RDS-Web",
fullName: "Research Data Storage (RDS)",
description:
"Research Data Storage (RDS) Nordrhein-Westfalen ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Resourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
"Research Data Storage (RDS) Nordrhein-Westfalen ist objektbasierter Speicher für Forschungsdaten. Sie können RDS-Ressourcen mit Speicherplatz für Ihre Forschungsdaten erstellen solange das Projekt ausreichend Speicherquota hat. Projektbesitzer können zusätzliche Speicherquota beantragen.",
},
s3: {
displayName: "S3 Bucket",
......
......@@ -65,8 +65,6 @@ export default {
} as VueI18n.LocaleMessageObject,
default: {
documentTitle: "Coscine",
documentModifiedTitle: "{title} | @:(default.documentTitle)",
archive: "Archive",
archived: "Archived",
deleted: "Deleted",
......@@ -76,6 +74,14 @@ export default {
none: "n/a",
},
title: {
default: "Coscine",
modified: "{title} | @:(title.default)",
error: {
notFound: "404 - Not Found | @:(title.default)",
},
},
toast: {
contactChange: {
success: {
......@@ -90,7 +96,7 @@ export default {
},
session: {
title: "Your session has expired",
message: "To continue working, log in again:",
message: "To continue working, log back in.",
link: "@:(nav.userLogIn)",
},
onSave: {
......@@ -130,6 +136,7 @@ export default {
breadcrumbs: {
admin: "Admin",
home: "Home",
tos: "Terms of Use and Privacy Policy",
login: "Login",
pid: "PID",
search: "Search",
......@@ -154,6 +161,7 @@ export default {
},
resourceTypes: {
resource: "Resource",
rdsrwth: {
displayName: "RWTH-RDS-Web",
fullName: "Research Data Storage (RDS)",
......
/* Vue */
import Vue from "vue";
import { createApp, h, markRaw } from "vue-demi";
import { h, markRaw } from "vue";
/* Vue i18n*/
import i18n from "@/plugins/vue-i18n";
......@@ -29,7 +29,7 @@ import "windi.css";
Vue.config.productionTip = false;
Vue.config.devtools = true;
const app = createApp({
const app = new Vue({
router,
pinia,
i18n,
......@@ -42,4 +42,4 @@ pinia.use(({ store }) => {
store.router = markRaw(router);
});
app.mount("#app");
app.$mount("#app");
<template>
<div>
<router-view v-if="true" />
<RouterView />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the store for current module
import useAdminStore from "./store";
// import the main store
import useMainStore from "@/store/index";
export default defineComponent({
setup() {
const mainStore = useMainStore();
const adminStore = useAdminStore();
return { mainStore, adminStore };
return { adminStore };
},
computed: {
......@@ -32,9 +29,8 @@ export default defineComponent({
methods: {
async initialize() {
// await Promise.all([
// //this.adminStore.retrieveProjectQuotas(this.adminStore.project.projectGuid),
// ])
// do initialization stuff (e.g. API calls, element loading, etc.)
// ...
},
},
});
......
......@@ -110,7 +110,7 @@
type="number"
aria-describedby="projectHelp"
:placeholder="$t('page.admin.newQuotaInputPlaceHolder')"
@keypress.enter.stop="saveNewQuota(project.id, row.item)"
@keypress.enter.stop="saveNewQuota(project, row.item)"
>
</b-form-input>
</template>
......@@ -123,7 +123,7 @@
:id="`action${row.index}`"
variant="primary"
:disabled="!isQuotaValueValid(row.item)"
@click.stop.prevent="saveNewQuota(project.id, row.item)"
@click.stop.prevent="saveNewQuota(project, row.item)"
>
{{ $t("buttons.save") }}
</b-button>
......@@ -155,7 +155,7 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the store for current module
import useAdminStore from "../store";
// import the main store
......@@ -302,10 +302,12 @@ export default defineComponent({
methods: {
async saveNewQuota(
projectId: string,
project: AdminProjectObject | null,
resourceQuota: ExtendedProjectQuotaObject
) {
if (
project &&
project.id &&
resourceQuota.resourceTypeId &&
this.isQuotaValueValid(resourceQuota)
) {
......@@ -315,7 +317,7 @@ export default defineComponent({
if (isNumber(updatedQuota.maximumGiB) && updatedQuota.maximumGiB >= 0) {
this.isWaitingForResponse = true;
const success = await this.adminStore.updateProjectQuota(
projectId,
project.id,
resourceQuota.resourceTypeId,
updatedQuota
);
......
......@@ -24,7 +24,7 @@
<!-- Project Query Status -->
<span class="d-block h6 my-3">
{{
project && project.guid
project && project.id
? $t("page.admin.projectFound")
: $t("page.admin.projectNotSelected")
}}
......@@ -65,27 +65,23 @@
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import CoscineHeadline from "@/components/coscine/CoscineHeadline.vue";
import { defineComponent } from "vue";
// import the store for current module
import useAdminStore from "../store";
// import the main store
import useMainStore from "@/store/index";
import type { AdminProjectObject } from "@coscine/api-client/dist/types/Coscine.Api.Admin";
import QuotaTable from "../components/QuotaTable.vue";
export default defineComponent({
components: {
CoscineHeadline,
QuotaTable,
},
setup() {
const mainStore = useMainStore();
const adminStore = useAdminStore();
return { mainStore, adminStore };
return { adminStore };
},
data() {
......
<template>
<div>
<router-view v-if="moduleIsReady" />
<RouterView v-if="moduleIsReady" />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
import { defineComponent } from "vue";
// import the store for current module
import useErrorStore from "./store";
// import the main store
import useMainStore from "@/store/index";
export default defineComponent({
setup() {
const mainStore = useMainStore();
const errorStore = useErrorStore();
return { mainStore, errorStore };
return { errorStore };
},
computed: {
......