* Add note to public page

* Add project chooser on public view

* Optimize loading order

So much nicer to use now

Closes #298

* Fix the private project switcher too

* Add transition to public view

* Prevent project switching if youre already on that project

* [WIP] Add reset password form

* Add request password reset page

* Add jwt-payload util

* Install base-64

* Fix test typing

* Add new errors to translations

* Finish reset password flow

* Fix foreground color on v-notice

* Fix tests

* Allow code in translateError + render project error translated on login

* Remove wrong reference to error component

* Render project key if name is unknown

* Fix date-fns version

* Fix tests
This commit is contained in:
Rijk van Zanten
2020-04-09 19:06:15 -04:00
committed by GitHub
parent 153eaec78a
commit 7485a97a3b
39 changed files with 710 additions and 244 deletions

View File

@@ -73,6 +73,7 @@ export const withCustomLogo = () =>
php_api: 'fpm-fcgi',
},
},
authenticated: true,
},
];
projectsStore.state.currentProjectKey = 'my-project';

View File

@@ -1,7 +1,7 @@
<template>
<div class="project-chooser">
<button class="toggle" :disabled="projects.length === 1" @click="active = !active">
{{ currentProject.api.project_name }}
{{ currentProject.name }}
</button>
<transition-expand>
<div v-if="active" class="options-wrapper">
@@ -17,7 +17,7 @@
<v-radio
:inputValue="currentProjectKey"
:value="project.key"
:label="(project.api && project.api.project_name) || project.key"
:label="project.name || project.key"
/>
</router-link>
</div>

View File

@@ -1,4 +0,0 @@
import PublicViewLogo from './logo.vue';
export { PublicViewLogo };
export default PublicViewLogo;

View File

@@ -1,22 +0,0 @@
<template functional>
<a href="https://directus.io" rel="noopener noreferrer" target="_blank" class="logo">
<img
v-tooltip.right.inverted="`Directus v${props.version}`"
alt="Directus Logo"
src="./logo-dark.svg"
/>
</a>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
props: {
version: {
type: String,
required: true,
},
},
});
</script>

View File

@@ -1,36 +0,0 @@
# Public View Logo
Renders the Directus logo and shows the current version of Directus on hover.
## Usage
```html
<template>
<public-view-logo version="9.0.0" />
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import PublicViewLogo from '@/views/public/components/logo';
export default {
components: {
PublicViewLogo
}
}
</script>
```
## Props
| Prop | Description | Default |
|-----------|---------------------------------|---------|
| `version` | The version to display on hover | -- |
## Events
n/a
## Slots
n/a
## CSS Variables
n/a

View File

@@ -0,0 +1,4 @@
import ProjectChooser from './project-chooser.vue';
export { ProjectChooser };
export default ProjectChooser;

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,6 +1,6 @@
import markdown from './readme.md';
import { defineComponent } from '@vue/composition-api';
import PublicViewLogo from './logo.vue';
import ProjectChooser from './project-chooser.vue';
import withPadding from '../../../../../.storybook/decorators/with-padding';
export default {
@@ -13,8 +13,8 @@ export default {
export const basic = () =>
defineComponent({
components: { PublicViewLogo },
components: { ProjectChooser },
template: `
<public-view-logo version="9.0.0" />
`,
<project-chooser />
`,
});

View File

@@ -0,0 +1,85 @@
<template>
<v-menu show-arrow placement="bottom" close-on-content-click>
<template #activator="{ toggle }">
<div class="project-chooser" @click="toggle">
<div class="public-view-logo" v-if="project && project.logo">
<img :src="project.logo" :alt="project.name || project.key" />
</div>
<img v-else class="default-logo" src="./logo-dark.svg" alt="Directus" />
<h1 class="title type-title">{{ project && (project.name || project.key) }}</h1>
<v-icon name="expand_more" />
</div>
</template>
<v-list dense>
<v-list-item
v-for="availableProject in projects"
:key="availableProject.key"
:active="availableProject.key === project.key"
:disabled="availableProject.key === project.key"
@click="toProject(availableProject.key)"
>
<v-list-item-icon><v-icon name="launch" /></v-list-item-icon>
<v-list-item-content>
{{ availableProject.name || availableProject.key }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import useProjectsStore from '@/stores/projects';
import router from '@/router';
export default defineComponent({
setup() {
const projectsStore = useProjectsStore();
return {
project: projectsStore.currentProject,
projects: projectsStore.formatted,
toProject,
};
function toProject(key: string) {
router.push(`/${key}/login`);
}
},
});
</script>
<style lang="scss" scoped>
.project-chooser {
display: flex;
align-items: center;
width: max-content;
height: 64px;
cursor: pointer;
}
.public-view-logo {
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
background-color: var(--brand);
border-radius: var(--border-radius);
}
.default-logo {
width: 64px;
}
.title {
margin-left: 12px;
}
.v-icon {
--v-icon-color: var(--foreground-subdued);
margin-left: 4px;
}
</style>

View File

@@ -0,0 +1,35 @@
# Project Chooser
Renders the project's logo, and allows you to switch to other projects. If there's only one project
available, it renders just the logo.
## Usage
```html
<template>
<project-chooser />
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import ProjectChooser from '@/views/public/components/project-chooser';
export default {
components: {
ProjectChooser
}
}
</script>
```
## Props
n/a
## Events
n/a
## Slots
n/a
## CSS Variables
n/a

View File

@@ -4,12 +4,19 @@ import { mount, createLocalVue, Wrapper } from '@vue/test-utils';
import VIcon from '@/components/v-icon/';
import { useProjectsStore } from '@/stores/projects/';
import { ProjectWithKey } from '@/stores/projects/types';
import Tooltip from '@/directives/tooltip/tooltip';
import ClickOutside from '@/directives/click-outside';
import VMenu from '@/components/v-menu';
import VList, { VListItem, VListItemIcon, VListItemContent } from '@/components/v-list';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-icon', VIcon);
localVue.directive('tooltip', Tooltip);
localVue.component('v-list', VList);
localVue.component('v-list-item', VListItem);
localVue.component('v-list-item-icon', VListItemIcon);
localVue.component('v-list-item-content', VListItemContent);
localVue.component('v-menu', VMenu);
localVue.directive('click-outside', ClickOutside);
import PublicView from './public-view.vue';
@@ -48,6 +55,7 @@ const mockProject: ProjectWithKey = {
php_api: 'fpm-fcgi',
},
},
authenticated: true,
};
describe('Views / Public', () => {
@@ -98,6 +106,7 @@ describe('Views / Public', () => {
code: 250,
message: 'Test error',
},
authenticated: false,
},
];
store.state.currentProjectKey = 'my-project';

View File

@@ -1,7 +1,7 @@
<template>
<div class="public-view">
<div class="container">
<public-view-logo :version="version" />
<project-chooser />
<div class="content" :class="{ wide }">
<slot />
</div>
@@ -9,19 +9,30 @@
<slot name="notice" />
</div>
</div>
<div class="art" :style="artStyles"></div>
<div class="art" :style="artStyles">
<transition name="scale">
<img
class="foreground"
v-if="project && project.foregroundImage"
:src="project.foregroundImage"
:alt="project.name"
/>
</transition>
<div class="note" v-if="project && project.note" v-html="marked(project.note)" />
</div>
</div>
</template>
<script lang="ts">
import { version } from '../../../package.json';
import { defineComponent, computed } from '@vue/composition-api';
import PublicViewLogo from './components/logo/';
import ProjectChooser from './components/project-chooser/';
import { useProjectsStore } from '@/stores/projects/';
import marked from 'marked';
export default defineComponent({
components: {
PublicViewLogo,
ProjectChooser,
},
props: {
wide: {
@@ -35,23 +46,19 @@ export default defineComponent({
const backgroundStyles = computed<string>(() => {
const defaultColor = '#263238';
let currentProject = projectsStore.currentProject.value;
if (currentProject === null) {
if (projectsStore.currentProject.value === null) {
return defaultColor;
}
if (currentProject.error !== undefined) {
if (projectsStore.currentProject.value.error) {
return defaultColor;
}
currentProject = currentProject;
if (currentProject.api?.project_background?.full_url) {
return `url(${currentProject.api?.project_background.full_url})`;
if (projectsStore.currentProject.value.backgroundImage) {
return `url(${projectsStore.currentProject.value.backgroundImage})`;
}
return currentProject.api?.project_color || defaultColor;
return projectsStore.currentProject.value.color || defaultColor;
});
const artStyles = computed(() => ({
@@ -60,7 +67,7 @@ export default defineComponent({
backgroundPosition: 'center center',
}));
return { version, artStyles };
return { version, artStyles, marked, project: projectsStore.currentProject };
},
});
</script>
@@ -97,14 +104,35 @@ export default defineComponent({
}
.art {
position: relative;
display: none;
flex-grow: 1;
align-items: center;
justify-content: center;
height: 100%;
background-position: center center;
background-size: cover;
.foreground {
max-width: 400px;
}
.note {
position: absolute;
right: 0;
bottom: 40px;
left: 0;
max-width: 340px;
margin: 0 auto;
padding: 8px 12px;
color: var(--white);
background-color: #2632383f;
border-radius: var(--border-radius);
backdrop-filter: blur(10px);
}
@include breakpoint(small) {
display: block;
display: flex;
}
}
@@ -120,4 +148,16 @@ export default defineComponent({
}
}
}
.scale-enter-active,
.scale-leave-active {
transition: all 600ms var(--transition);
}
.scale-enter,
.scale-leave-to {
position: absolute;
transform: scale(0.95);
opacity: 0;
}
</style>