Skip to content

Commit

Permalink
feat: introduce pageLayout route meta prop
Browse files Browse the repository at this point in the history
  • Loading branch information
kulmann committed Oct 28, 2022
1 parent 25e9cd3 commit 8621d05
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 300 deletions.
4 changes: 4 additions & 0 deletions packages/web-pkg/src/composables/router/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ export type LocationParams = Dictionary<ParamValue>
export const authContextValues = ['anonymous', 'user', 'publicLink', 'hybrid'] as const
export type AuthContext = typeof authContextValues[number]

export const pageLayoutValue = ['plain', 'loading', 'application'] as const
export type PageLayout = typeof pageLayoutValue[number]

export interface WebRouteMeta extends RouteMeta {
title?: string
authContext?: AuthContext
pageLayout?: PageLayout
patchCleanPath?: boolean
contextQueryItems?: string[]
}
16 changes: 11 additions & 5 deletions packages/web-runtime/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,24 @@ export default defineComponent({
...mapGetters(['configuration', 'capabilities', 'getSettingsValue']),
...mapGetters('runtime/auth', ['isUserContextReady', 'isPublicLinkContextReady']),
layout() {
const pageLayouts = {
plain: LayoutPlain,
loading: LayoutLoading,
application: LayoutApplication
}
const pageLayoutKey = this.$route.meta.pageLayout
if (!this.$route.name || isAnonymousContext(this.$router, this.$route)) {
return LayoutPlain
return pageLayouts[pageLayoutKey || 'plain']
}
const pageLayout = pageLayouts[pageLayoutKey || 'application']
if (isPublicLinkContext(this.$router, this.$route)) {
return this.isPublicLinkContextReady ? LayoutApplication : LayoutLoading
return this.isPublicLinkContextReady ? pageLayout : LayoutLoading
}
if (isUserContext(this.$router, this.$route)) {
return this.isUserContextReady ? LayoutApplication : LayoutLoading
return this.isUserContextReady ? pageLayout : LayoutLoading
}
return LayoutApplication
return pageLayout
},
favicon() {
return this.configuration.currentTheme.logo.favicon
Expand Down
28 changes: 25 additions & 3 deletions packages/web-runtime/src/layouts/Plain.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
<template>
<div>
<div
class="oc-login oc-height-viewport"
:style="{ backgroundImage: 'url(' + backgroundImg + ')' }"
>
<h1 class="oc-invisible-sr" v-text="pageTitle" />
<router-view />
</div>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api'
import { computed, defineComponent, unref } from '@vue/composition-api'
import { useRouteMeta, useStore, useTranslations } from 'web-pkg'
export default defineComponent({
name: 'PlainLayout'
name: 'PlainLayout',
setup() {
const store = useStore()
const { $gettext } = useTranslations()
const title = useRouteMeta('title')
const pageTitle = computed(() => {
return $gettext(unref(title))
})
const backgroundImg = computed(() => {
return store.getters.configuration?.currentTheme?.loginPage?.backgroundImg
})
return {
pageTitle,
backgroundImg
}
}
})
</script>
98 changes: 49 additions & 49 deletions packages/web-runtime/src/pages/accessDenied.vue
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
<template>
<div
class="oc-login oc-height-viewport"
:style="{ backgroundImage: 'url(' + backgroundImg + ')' }"
>
<h1 class="oc-invisible-sr" v-text="pageTitle" />
<div class="oc-login-card-wrapper oc-height-viewport oc-flex oc-flex-center oc-flex-middle">
<div class="oc-login-card">
<img class="oc-login-logo" :src="logoImg" alt="" :aria-hidden="true" />
<div class="oc-login-card-body oc-width-medium">
<h2 class="oc-login-card-title" v-text="cardTitle" />
<p v-text="cardHint" />
</div>
<div class="oc-height-viewport oc-flex oc-flex-column oc-flex-center oc-flex-middle">
<div class="oc-login-card">
<img class="oc-login-logo" :src="logoImg" alt="" :aria-hidden="true" />
<div class="oc-login-card-body oc-width-medium">
<h2 class="oc-login-card-title" v-text="cardTitle" />
<p v-text="cardHint" />
</div>
<div class="oc-login-card-footer oc-pt-rm">
<p>
{{ footerSlogan }}
</p>
</div>
<oc-button
id="exitAnchor"
type="router-link"
class="oc-mt-m oc-width-medium"
:to="{ name: 'login' }"
size="large"
appearance="filled"
variation="primary"
v-text="navigateToLoginText"
/>
</div>
<oc-button
id="exitAnchor"
type="router-link"
class="oc-mt-m oc-width-medium"
:to="{ name: 'login' }"
size="large"
appearance="filled"
variation="primary"
v-text="navigateToLoginText"
/>
</div>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api'
import { mapGetters } from 'vuex'
import { computed, defineComponent } from '@vue/composition-api'
import { useStore, useTranslations } from 'web-pkg'
export default defineComponent({
name: 'AccessDeniedPage',
computed: {
...mapGetters(['configuration']),
pageTitle() {
return this.$gettext(this.$route.meta.title)
},
cardTitle() {
return this.$gettext('Logged out')
},
cardHint() {
return this.$gettext('You were automatically logged out for security reasons.')
},
navigateToLoginText() {
return this.$gettext('Log in again')
},
logoImg() {
return this.configuration.currentTheme.logo.login
},
backgroundImg() {
return this.configuration.currentTheme.loginPage.backgroundImg
setup() {
const store = useStore()
const { $gettext } = useTranslations()
const logoImg = computed(() => {
return store.getters.configuration.currentTheme.logo.login
})
const cardTitle = computed(() => {
return $gettext('Logged out')
})
const cardHint = computed(() => {
return $gettext('You were automatically logged out for security reasons.')
})
const footerSlogan = computed(() => {
return store.getters.configuration.currentTheme.general.slogan
})
const navigateToLoginText = computed(() => {
return $gettext('Log in again')
})
return {
logoImg,
cardTitle,
cardHint,
footerSlogan,
navigateToLoginText
}
}
})
</script>

<style lang="scss" scoped>
.oc-login-card-wrapper {
flex-flow: column;
}
</style>
129 changes: 57 additions & 72 deletions packages/web-runtime/src/pages/login.vue
Original file line number Diff line number Diff line change
@@ -1,91 +1,76 @@
<template>
<div
v-if="initialized"
class="oc-login oc-height-viewport"
:style="{ backgroundImage: 'url(' + backgroundImg + ')' }"
>
<h1 class="oc-invisible-sr">{{ pageTitle }}</h1>
<div class="oc-login-card oc-position-center">
<img class="oc-login-logo" :src="logoImg" alt="" :aria-hidden="true" />
<div class="oc-login-card-body">
<h2 class="oc-login-card-title">
<translate :translate-params="{ productName: $_productName }"
>Welcome to %{productName}</translate
>
</h2>
<p v-translate>
Please click the button below to authenticate and get access to your data.
</p>
<oc-button
id="authenticate"
size="large"
variation="primary"
appearance="filled"
class="oc-login-authorize-button"
@click="performLogin"
>
<translate>Login</translate>
</oc-button>
</div>
<div class="oc-login-card-footer">
<p>
{{ configuration.currentTheme.general.slogan }}
</p>
<div class="oc-width-1-1 oc-height-1-1">
<app-loading-spinner v-if="autoRedirect" />
<div v-else class="oc-height-viewport oc-flex oc-flex-column oc-flex-center oc-flex-middle">
<div class="oc-login-card">
<img class="oc-login-logo" :src="logoImg" alt="" :aria-hidden="true" />
<div class="oc-login-card-body oc-width-medium">
<h2 class="oc-login-card-title">
<translate :translate-params="{ productName }">Welcome to %{productName}</translate>
</h2>
<p v-translate>
Please click the button below to authenticate and get access to your data.
</p>
</div>
<div class="oc-login-card-footer oc-pt-rm">
<p>{{ footerSlogan }}</p>
</div>
</div>
<oc-button
id="authenticate"
size="large"
variation="primary"
appearance="filled"
class="oc-mt-m oc-width-medium oc-login-authorize-button"
@click="performLogin"
>
<translate>Login</translate>
</oc-button>
</div>
</div>
</template>

<script lang="ts">
import { mapGetters } from 'vuex'
import { authService } from '../services/auth'
import { queryItemAsString, useRouteQuery } from 'web-pkg/src/composables'
import { defineComponent } from '@vue/composition-api'
import { queryItemAsString, useRouteQuery, useStore } from 'web-pkg/src/composables'
import { computed, defineComponent, unref } from '@vue/composition-api'
import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue'
export default defineComponent({
name: 'LoginPage',
setup() {
return {
redirectUrl: useRouteQuery('redirectUrl')
}
components: {
AppLoadingSpinner
},
data() {
return {
loading: false,
initialized: false
}
},
computed: {
...mapGetters(['configuration']),
pageTitle() {
return this.$gettext(this.$route.meta.title)
},
$_productName() {
return this.configuration.currentTheme.general.name
},
logoImg() {
return this.configuration.currentTheme.logo.login
},
setup() {
const store = useStore()
backgroundImg() {
return this.configuration.currentTheme.loginPage.backgroundImg
const redirectUrl = useRouteQuery('redirectUrl')
const performLogin = () => {
authService.loginUser(queryItemAsString(unref(redirectUrl)))
}
},
created() {
if (this.configuration.currentTheme.loginPage.autoRedirect) {
this.performLogin()
} else {
this.initialized = true
const autoRedirect = computed(() => {
return store.getters.configuration.currentTheme.loginPage.autoRedirect
})
if (unref(autoRedirect)) {
performLogin()
}
},
methods: {
performLogin() {
authService.loginUser(queryItemAsString(this.redirectUrl))
const productName = computed(() => {
return store.getters.configuration.currentTheme.general.name
})
const logoImg = computed(() => {
return store.getters.configuration.currentTheme.logo.login
})
const footerSlogan = computed(() => {
return store.getters.configuration.currentTheme.general.slogan
})
return {
autoRedirect,
productName,
logoImg,
footerSlogan,
performLogin
}
}
})
Expand Down
Loading

0 comments on commit 8621d05

Please sign in to comment.