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 (16)
Showing
with 648 additions and 49 deletions
......@@ -4,6 +4,7 @@
declare module 'vue' {
export interface GlobalComponents {
BreadCrumbs: typeof import('./src/components/BreadCrumbs.vue')['default']
CoscineCard: typeof import('./src/components/CoscineCard.vue')['default']
CoscineFormGroup: typeof import('./src/components/CoscineFormGroup.vue')['default']
CoscineHeadline: typeof import('./src/components/CoscineHeadline.vue')['default']
......@@ -12,6 +13,7 @@ declare module 'vue' {
LoadingIndicator: typeof import('./src/components/LoadingIndicator.vue')['default']
MultiSelect: typeof import('./src/components/MultiSelect.vue')['default']
Navbar: typeof import('./src/components/Navbar.vue')['default']
SidebarMenu: typeof import('./src/components/SidebarMenu.vue')['default']
}
}
......
{
"name": "ui",
"version": "1.2.0",
"version": "1.3.0",
"private": true,
"scripts": {
"dev": "vite",
......@@ -10,13 +10,15 @@
"lint:fix": "eslint './src/**/*.{js,ts,tsx,vue,md}' --fix"
},
"dependencies": {
"@coscine/api-client": "^1.4.0",
"@coscine/api-client": "^1.3.0",
"@vueuse/core": "^6.5.3",
"axios": "^0.25.0",
"bootstrap": "^4.6.1",
"bootstrap-icons": "^1.8.1",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.20.0",
"http-status-codes": "^2.2.0",
"jose": "^4.5.1",
"jquery": "^3.6.0",
"lint-staged": "^12.1.2",
"lodash": "^4.17.21",
......@@ -31,6 +33,7 @@
"vue-i18n": "^8.27.0",
"vue-multiselect": "^2.1.6",
"vue-router": "^3.5.3",
"vue-sidebar-menu": "^4.8.1",
"vuelidate": "^0.7.7"
},
"devDependencies": {
......@@ -58,7 +61,7 @@
"eslint-plugin-vue": "^8.3.0",
"prettier": "^2.5.1",
"typescript": "^4.5.4",
"unplugin-vue-components": "^0.17.9",
"unplugin-vue-components": "0.17.11",
"vite": "^2.8.0",
"vite-plugin-vue2": "^1.9.3",
"vite-plugin-windicss": "^1.7.0",
......
......@@ -2,8 +2,14 @@
<div id="app">
<Navbar />
<LoadingIndicator :show="loading" />
<main class="container">
<main>
<div class="mr-0 d-flex">
<SidebarMenu class="mr-2" />
<div class="container-fluid mx-5">
<BreadCrumbs />
<RouterView />
</div>
</div>
</main>
<!--DevFooter /-->
</div>
......@@ -11,29 +17,43 @@
<script lang="ts">
import { defineComponent } from "vue-demi";
import * as jose from "jose";
import moment from "moment";
// import the main store
import { useMainStore } from "@/store/index";
import { useProjectStore } from "@/modules/project/store";
import { useUserStore } from "@/modules/user/store";
import { useLoginStore } from "@/modules/login/store";
export default defineComponent({
setup() {
const mainStore = useMainStore();
const projectStore = useProjectStore();
const userStore = useUserStore();
return { mainStore, userStore };
return { mainStore, projectStore, userStore };
},
created() {
this.initialize();
},
data() {
return {
tokenExpiredInterval: 0,
};
},
computed: {
loading(): boolean {
return this.mainStore.coscine.loading.counter > 0;
},
loggedIn(): boolean {
return this.mainStore.loggedIn;
},
localeChange(): string {
return this.mainStore.coscine.locale;
},
......@@ -43,6 +63,7 @@ export default defineComponent({
loggedIn() {
this.userStore.retrieveUser(this.loggedIn);
},
localeChange() {
this.$root.$i18n.locale = this.localeChange;
},
......@@ -51,8 +72,80 @@ export default defineComponent({
methods: {
async initialize() {
await this.userStore.retrieveUser(this.loggedIn);
await this.projectStore.retrieveAllProjects();
this.userStore.setUserLanguagePreference();
if (this.userStore.user !== null) {
// Trigger token expiration check
this.tokenExpiredInterval = window.setInterval(
this.notifyTokenExpired,
this.checkTokenExpiration()
);
}
},
notifyTokenExpired() {
const h = this.$createElement;
// Create the toast's message
const message = h("p", { class: ["text-center"] }, [
this.$t("toast.session.message").toString(),
h("br"),
h("b-button", {
class: ["mt-2"],
attrs: {
size: "sm",
variant: "secondary",
},
// On button click, use router to redirect to login page
on: {
click: () => {
const loginStore = useLoginStore();
loginStore.redirectToLogin(this.$route, true);
},
},
domProps: {
innerHTML: this.$t("toast.session.link").toString(),
},
}),
]);
// Create the toast
this.$bvToast.toast([message], {
title: this.$t("toast.session.title").toString(),
toaster: "b-toaster-bottom-right",
variant: "danger",
solid: true,
noAutoHide: true,
noCloseButton: true,
appendToast: false,
});
// Cancel interval expiration check
clearInterval(this.tokenExpiredInterval);
},
checkTokenExpiration(): number {
const jwt = jose.decodeJwt(this.mainStore.coscine.authorization.bearer);
// Use UTC to avoid time conversion errors
const tokenExpiresAt = moment(jwt.exp! * 1000).utc();
const now = moment.utc(moment.now());
const untilTokenExpiration = moment.duration(tokenExpiresAt.diff(now));
console.debug(
"Access Token Expiration Overview:\n",
"\nNow (UTC): ",
now.format("LL HH:mm:ss"),
"\nIssued (UTC): ",
moment(jwt.iat! * 1000)
.utc()
.format("LL HH:mm:ss"),
"\nExpiry (UTC): ",
tokenExpiresAt.format("LL HH:mm:ss"),
"\nExpiration: ",
untilTokenExpiration.humanize(true)
);
// Return as milliseconds for setInterval() method
return untilTokenExpiration.asMilliseconds();
},
},
});
</script>
<style scoped></style>
src/assets/images/Search.png

74.5 KiB

......@@ -4,6 +4,7 @@
declare module "vue" {
export interface GlobalComponents {
BreadCrumbs: typeof import("./components/BreadCrumbs.vue")["default"];
CoscineCard: typeof import("./components/CoscineCard.vue")["default"];
CoscineFormGroup: typeof import("./components/CoscineFormGroup.vue")["default"];
CoscineHeadline: typeof import("./components/CoscineHeadline.vue")["default"];
......@@ -12,6 +13,7 @@ declare module "vue" {
LoadingIndicator: typeof import("./components/LoadingIndicator.vue")["default"];
MultiSelect: typeof import("./components/MultiSelect.vue")["default"];
Navbar: typeof import("./components/Navbar.vue")["default"];
SidebarMenu: typeof import("./components/SidebarMenu.vue")["default"];
}
}
......
<template>
<b-breadcrumb>
<b-breadcrumb-item
:to="{ name: 'list-projects' }"
: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`) }}
</b-breadcrumb-item>
<b-breadcrumb-item
v-for="(crumb, id) in crumbs"
:key="id"
:to="crumb.to"
:active="crumb.active"
>
{{ crumb.text }}
</b-breadcrumb-item>
</b-breadcrumb>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
// import the main store
import { useMainStore } from "@/store/index";
// import the project store
import { useProjectStore } from "@/modules/project/store";
// import the resource store
import { useResourceStore } from "@/modules/resource/store";
import type {
ProjectObject,
ResourceObject,
} from "@coscine/api-client/dist/types/Coscine.Api.Project";
import { RawLocation } from "vue-router";
interface RouteLink {
to: RawLocation;
text: string;
active?: boolean;
}
export default defineComponent({
setup() {
const mainStore = useMainStore();
const projectStore = useProjectStore();
const resourceStore = useResourceStore();
return { mainStore, projectStore, resourceStore };
},
computed: {
crumbs(): RouteLink[] {
let pathArray = this.$route.path.split("/");
pathArray = pathArray.filter(
(path) =>
path.trim() !== "" && path.trim() !== "p" && path.trim() !== "r"
);
let breadcrumbs = pathArray.reduce(
(breadcrumbArray: RouteLink[], path, idx) => {
console.log(this.$route.matched[idx]);
breadcrumbArray.push({
to: {
name: this.$route.matched[idx].name
? this.$route.matched[idx].name
: this.$route.matched[idx].meta.default,
},
text:
this.$route.matched[idx] &&
this.$route.matched[idx].meta &&
this.$route.matched[idx].meta.breadCrumb
? this.$t(
`breadcrumbs.${this.$route.matched[idx].meta.breadCrumb}`,
{
path: path,
projectName: this.project
? this.project.displayName
: path,
resourceName: this.resource
? `${this.resource.type?.displayName?.toUpperCase()}: ${
this.resource.displayName
}`
: path,
}
).toString()
: path,
});
return breadcrumbArray;
},
[]
);
if (this.project && this.parentProjects) {
const parentBreadCrumbs = this.parentProjects.map((parentProject) => {
return {
to: {
name: "project-home",
params: { slug: parentProject.slug },
},
text: this.$t(`breadcrumbs.project.home`, {
projectName: parentProject.displayName,
}).toString(),
} as RouteLink;
});
breadcrumbs.unshift(...parentBreadCrumbs);
}
if (breadcrumbs.length > 0) {
breadcrumbs[breadcrumbs.length - 1].active = true;
}
return breadcrumbs;
},
parentProjects(): ProjectObject[] | null {
return this.projectStore.currentParentProjects;
},
project(): ProjectObject | null {
return this.projectStore.currentProject;
},
resource(): ResourceObject | null {
return this.resourceStore.currentResource;
},
},
});
</script>
......@@ -101,7 +101,7 @@
<router-link
v-else
:to="{ path: '/login', query: { redirect: $route.fullPath } }"
:to="{ name: 'login', query: { redirect: $route.fullPath } }"
custom
v-slot="{ href, isExactActive }"
>
......@@ -151,7 +151,9 @@ export default defineComponent({
},
methods: {
changeLocale(locale: string): void {
this.mainStore.coscine.locale = locale;
this.mainStore.$patch((state) => {
state.coscine.locale = locale;
});
},
logUserOut() {
this.loginStore.logout();
......@@ -161,7 +163,7 @@ export default defineComponent({
this.$router.push({ name: "search", query: { q: this.searchTerm } });
},
toggleSidebar() {
console.log("Toggling sidebar!");
this.mainStore.sidebarActive = !this.mainStore.sidebarActive;
},
},
});
......
<template>
<sidebar-menu
:menu="menu"
:collapsed="collapsed"
@toggle-collapse="collapse"
:relative="true"
:showOneChild="true"
:disableHover="true"
theme="white-theme"
>
<template v-slot:toggle-icon>
<b-icon v-if="!collapsed" icon="arrow-bar-left" />
<b-icon v-else icon="arrow-bar-right" />
</template>
<template v-slot:dropdown-icon>
<b-icon icon="caret-right" />
</template>
</sidebar-menu>
</template>
<script lang="ts">
import Vue from "vue";
import { defineComponent } from "vue-demi";
import "bootstrap-icons/font/bootstrap-icons.css";
import "bootstrap-icons/font/fonts/bootstrap-icons.woff";
import "bootstrap-icons/font/fonts/bootstrap-icons.woff2";
// import the main store
import { useMainStore } from "@/store/index";
// import the project store
import { useProjectStore } from "@/modules/project/store";
// import the resource store
import { useResourceStore } from "@/modules/resource/store";
import type {
ProjectObject,
ResourceObject,
} from "@coscine/api-client/dist/types/Coscine.Api.Project";
import VueSidebarMenu from "vue-sidebar-menu";
import "vue-sidebar-menu/dist/vue-sidebar-menu.css";
import type {
SidebarItem,
SidebarComponentItem,
SidebarHeaderItem,
} from "vue-sidebar-menu";
import { RawLocation } from "vue-router";
Vue.use(VueSidebarMenu);
// TODO: Waiting for role implementation (is owner for showing e.g. settings)
export default defineComponent({
setup() {
const mainStore = useMainStore();
const projectStore = useProjectStore();
const resourceStore = useResourceStore();
return { mainStore, projectStore, resourceStore };
},
created() {
this.collapsed = !this.sidebarActive;
},
data() {
return {
collapsed: false,
};
},
computed: {
menu(): Array<SidebarItem | SidebarComponentItem | SidebarHeaderItem> {
return [
{
href: "/",
title: this.$t("sidebarmenu.home").toString(),
icon: "bi bi-house",
},
...this.projectMenu,
...this.projectsMenu,
...this.resourcesMenu,
...this.settingsMenu,
];
},
project(): ProjectObject | null {
return this.projectStore.currentProject;
},
projects(): ProjectObject[] | null {
return this.projectStore.allProjects;
},
projectMenu(): Array<
SidebarItem | SidebarComponentItem | SidebarHeaderItem
> {
if (this.project && this.project.slug) {
return [
{
href: {
name: "project-home",
params: { slug: this.project.slug },
},
title:
this.$t("sidebarmenu.project").toString() +
": " +
this.project.displayName,
icon: "bi bi-folder-symlink",
},
];
}
return [];
},
projectsMenu(): Array<
SidebarItem | SidebarComponentItem | SidebarHeaderItem
> {
if (this.projects && this.projects.length) {
return [
{
title: this.$t("sidebarmenu.projects").toString(),
href: {},
icon: "bi bi-folder2-open",
child: this.projects.map((project) => {
return {
title: project.displayName,
href: {
name: "project-home",
params: { slug: project.slug! },
},
icon: "bi bi-folder2-open",
};
}) as Array<SidebarItem | SidebarComponentItem | SidebarHeaderItem>,
},
];
}
return [];
},
resources(): ResourceObject[] | null {
return this.projectStore.currentResources;
},
resourcesMenu(): Array<
SidebarItem | SidebarComponentItem | SidebarHeaderItem
> {
if (this.resources && this.resources.length && this.project) {
return [
{
title: this.$t("sidebarmenu.resources").toString(),
href: {},
icon: "bi bi-archive",
child: this.resources.map((resource) => {
return {
title:
(resource.type?.displayName
? resource.type.displayName + ": "
: "") + resource.displayName,
href: {
name: "resource-home",
params: { slug: this.project!.slug!, guid: resource.id! },
},
icon: "bi bi-archive",
badge: resource.archived
? {
text: this.$t("default.archived").toString(),
class: "vsm--badge_default",
}
: undefined,
};
}) as Array<SidebarItem | SidebarComponentItem | SidebarHeaderItem>,
},
];
}
return [];
},
settingsMenu(): Array<
SidebarItem | SidebarComponentItem | SidebarHeaderItem
> {
if (this.project && this.project.slug) {
return [
{
title: this.$t("sidebarmenu.settings").toString(),
href: {},
icon: "bi bi-gear",
child: [
{
title: this.$t("sidebarmenu.editProject").toString(),
href: {
name: "project-settings",
params: { slug: this.project.slug },
},
icon: "bi bi-pencil",
},
{
title: this.$t("sidebarmenu.manageUsers").toString(),
href: {
name: "project-members",
params: { slug: this.project.slug },
},
icon: "bi bi-people",
},
{
title: this.$t("sidebarmenu.manageQuota").toString(),
href: {
name: "project-quota",
params: { slug: this.project.slug },
},
icon: "bi bi-sliders",
},
],
},
];
}
return [];
},
sidebarActive(): boolean {
return this.mainStore.sidebarActive;
},
},
watch: {
collapsed() {
this.mainStore.sidebarActive = !this.collapsed;
},
sidebarActive() {
this.collapsed = !this.sidebarActive;
},
},
methods: {
collapse(collapsed: boolean) {
this.collapsed = collapsed;
},
},
});
</script>
<style scoped>
.v-sidebar-menu.vsm_expanded {
overflow-y: auto;
overflow-x: hidden;
max-height: calc(100vh - 50px - 30px);
}
.v-sidebar-menu >>> .vsm--badge {
background-color: #ffc107;
border-radius: 10rem;
text-transform: capitalize;
font-weight: bold;
}
.v-sidebar-menu .vsm--title {
overflow: hidden;
text-overflow: ellipsis;
}
</style>
......@@ -26,19 +26,68 @@ export default {
extrasImprint:
"http://www.itc.rwth-aachen.de/cms/IT-Center/Footer/Service/~epvv/Impressum/",
},
},
} as VueI18n.LocaleMessageObject,
sidebarmenu: {
home: "Übersicht",
project: "Projekt",
projects: "Projekte",
resources: "Ressourcen",
settings: "Einstellungen",
archived: "Archiviert",
editProject: "Projekt bearbeiten",
manageUsers: "Mitglieder verwalten",
manageQuota: "Quota verwalten",
} as VueI18n.LocaleMessageObject,
buttons: {
cancel: "Abbrechen",
close: "Schließen",
connect: "Verbinden",
connected: "Verbunden",
leave: "Verlassen",
revoke: "Widerrufen",
save: "Speichern",
tokenCreate: "Zugriffstoken erstellen",
},
} as VueI18n.LocaleMessageObject,
default: {
archived: "Archiviert",
},
toast: {
session: {
title: "Ihre Sitzung ist abgelaufen",
message: "Um weiterzuarbeiten, loggen Sie sich erneut ein:",
link: "@:(nav.userLogIn)",
},
},
breadcrumbs: {
admin: "Admin",
home: "Home",
login: "Einloggen",
pid: "PID",
search: "Suche",
error: {
notFound: "Nicht Gefunden",
},
project: {
home: "{projectName}",
create: "Projekt erstellen",
settings: "Projekt bearbeiten",
quota: "Quota Management",
members: "Benutzerverwaltung",
},
resource: {
home: "{resourceName}",
create: "Ressource erstellen",
settings: "Ressource bearbeiten",
},
user: {
profile: "Nutzerprofil",
},
},
} as VueI18n.LocaleMessageObject;
......@@ -26,19 +26,66 @@ export default {
extrasImprint:
"http://www.itc.rwth-aachen.de/cms/IT-Center/Footer/Service/~epvv/Impressum/",
},
},
} as VueI18n.LocaleMessageObject,
sidebarmenu: {
home: "Home",
project: "Project",
projects: "Projects",
resources: "Resources",
settings: "Settings",
editProject: "Edit Project",
manageUsers: "Manage Users",
manageQuota: "Manage Quota",
} as VueI18n.LocaleMessageObject,
buttons: {
cancel: "Cancel",
close: "Close",
connect: "Connect",
connected: "Connected",
leave: "Leave",
revoke: "Revoke",
save: "Save",
tokenCreate: "Create Access Token",
},
} as VueI18n.LocaleMessageObject,
default: {
archived: "Archived",
},
toast: {
session: {
title: "Your session has expired",
message: "To continue working, log in again:",
link: "@:(nav.userLogIn)",
},
},
breadcrumbs: {
admin: "Admin",
home: "Home",
login: "Login",
pid: "PID",
search: "Search",
error: {
notFound: "Not Found",
},
project: {
home: "{projectName}",
create: "Create Project",
settings: "Edit Project",
quota: "Quota Management",
members: "User Management",
},
resource: {
home: "{resourceName}",
create: "Create Resource",
settings: "Edit Resource",
},
user: {
profile: "User Profile",
},
},
} as VueI18n.LocaleMessageObject;
......@@ -7,16 +7,17 @@ export const AdminRoutes: RouteConfig[] = [
{
path: "/admin",
component: AdminModule,
children: [
{
path: "/",
name: "admin",
component: Admin,
// only authenticated users can access admin
meta: {
breadCrumb: "admin",
requiresAdmin: true,
requiresAuth: true,
},
children: [
{
path: "/",
name: "admin",
component: Admin,
},
],
},
......
......@@ -14,6 +14,7 @@ export const ErrorRoutes: RouteConfig[] = [
name: "not-found",
component: NotFound,
meta: {
breadCrumb: "error.notFound",
requiresAuth: false,
},
},
......
......@@ -27,9 +27,6 @@ export default defineComponent({
},
computed: {
loggedIn(): boolean {
return this.mainStore.loggedIn;
},
moduleIsReady(): boolean {
return true;
},
......
......@@ -17,20 +17,9 @@ export default defineComponent({
return { mainStore, loginStore };
},
computed: {
loggedIn(): boolean {
return this.mainStore.loggedIn;
},
},
mounted() {
if (!this.loggedIn) {
window.location.href =
"/coscine/api/Coscine.Api.STS/?redirect=" + this.$route.query?.redirect;
} else {
this.$router.push({
path: "/",
});
}
this.loginStore.redirectToLogin(this.$router.currentRoute);
},
});
</script>
......@@ -14,6 +14,7 @@ export const LoginRoutes: RouteConfig[] = [
component: Login,
meta: {
requiresAuth: false,
breadCrumb: "login",
},
},
],
......
import { defineStore } from "pinia";
import { LoginState } from "./types";
import VueRouter, { RawLocation, Route } from "vue-router";
// import the main store
import { useMainStore } from "@/store/index";
......@@ -43,10 +44,31 @@ export const useLoginStore = defineStore({
const mainStore = useMainStore();
mainStore.$patch((state) => {
state.coscine.authorization.bearer = "";
// add further fields if necessary
});
window.location.href = "/coscine/api/Coscine.Api.STS/account/logout";
},
/**
* @param route Route to redirect back to, after logging in.
* @param force (Default: false) When TRUE, a new token will be generated for the user's session and the user will be redirected to the desired route. When FALSE, the user will be redirected back to the root. This flag is necessary, because a Bearer token stays valid for another 5 minutes after its expiration time.
*/
redirectToLogin(route: Route, force = false) {
const mainStore = useMainStore();
const location = {
path: "/coscine/api/Coscine.Api.STS/",
query: { redirect: route.fullPath },
} as RawLocation;
// Only execute if the user is not logged in
if (!mainStore.loggedIn || force) {
window.location.href = this.router.resolve(location).href;
} else {
this.router.push({
path: "/",
});
}
},
},
});
......
......@@ -14,6 +14,7 @@ export const PidRoutes: RouteConfig[] = [
component: Pid,
meta: {
requiresAuth: false,
breadCrumb: "pid",
},
},
],
......
......@@ -14,6 +14,7 @@ import { useProjectStore } from "./store";
import { useMainStore } from "@/store/index";
import type { ProjectObject } from "@coscine/api-client/dist/types/Coscine.Api.Project";
import type { Route } from "vue-router";
export default defineComponent({
setup() {
......@@ -45,11 +46,12 @@ export default defineComponent({
methods: {
async initialize() {
await this.apiFetch(this.$router.currentRoute);
},
async apiFetch(route: Route) {
// Project may be unset (e.g. when entering from a direct link)
await this.projectStore.handleUnsetProject(
this.project,
this.$router.currentRoute
);
await this.projectStore.handleUnsetProject(this.project, route);
// Load Resources for the project if not present
if (this.projectStore.currentResources === null) {
this.projectStore.retrieveResources(this.project);
......@@ -58,20 +60,15 @@ export default defineComponent({
if (this.projectStore.currentSubProjects === null) {
this.projectStore.retrieveSubProjects(this.project);
}
// Load Project Roles for the project if not present
if (this.projectStore.currentProjectRoles === null) {
this.projectStore.retrieveProjectRoles(this.project);
}
},
},
async beforeRouteUpdate(to, from, next) {
// Route from slug and currentSlug may differ!
await this.projectStore.handleUnsetProject(this.project, to);
// Load Resources for the project if not present
if (this.projectStore.currentResources === null) {
this.projectStore.retrieveResources(this.project);
}
// Load Sub-Projects for the project if not present
if (this.projectStore.currentSubProjects === null) {
this.projectStore.retrieveSubProjects(this.project);
}
await this.apiFetch(to);
next();
},
});
......
......@@ -37,6 +37,7 @@ export default defineComponent({
methods: {
async initialize() {
this.projectStore.currentSlug = null;
if (this.projectStore.topLevelProjects === null) {
this.projectStore.retrieveTopLevelProjects();
}
......
......@@ -7,34 +7,53 @@ export default {
--------------------------------------------------------------------------------------
*/
page: {
// ListProjects.vue
listProjects: {
title: "Projekt | Projekte",
addProject: "Projekt hinzufügen",
},
// CreateProject.vue
createProject: {
title: "Create-Project-Seite",
description:
"Das ist die @:page.createProject.title des Coscine UIv2 Apps",
},
// Members.vue
members: {
title: "Members-Seite",
description: "Das ist die @:page.members.title des Coscine UIv2 Apps",
},
// ProjectPage.vue
project: {
title: "Projektseite",
resource: "Ressource | Ressourcen",
subProject: "Sub-Projekt | Sub-Projekte",
member: "Mitglied | Mitglieder",
addResource: "Ressource hinzufügen",
members: {
toProjectMembers: "Mitglieder verwalten...",
modal: {
// Leave project modal
title: 'Projekt "{name}" verlassen',
body: 'Sie verlassen das Projekt "{name}". Damit verlieren Sie Zugriff auf das Projekt und die Ressourcen des Projekts. Möchten Sie das Projekt verlassen?',
},
tooltip: "Das Projekt verlassen",
tooltipDisabled:
"Sie sind der einzige Projekt Owner. Fügen Sie einen weiteren Projekt Owner hinzu, um das Projekt verlassen zu können.",
},
},
// Quota.vue
quota: {
title: "Quota-Seite",
description: "Das ist die @:page.quota.title des Coscine UIv2 Apps",
},
// Settíngs.vue
settings: {
title: "Settings-Seite",
description: "Das ist die @:page.settings.title des Coscine UIv2 Apps",
......