Generated project
This commit is contained in:
10
frontend/views/admin/admin-view.ts
Normal file
10
frontend/views/admin/admin-view.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { View } from '../../views/view';
|
||||
|
||||
@customElement('admin-view')
|
||||
export class AdminView extends View {
|
||||
render() {
|
||||
return html`<div>Content placeholder</div>`;
|
||||
}
|
||||
}
|
||||
10
frontend/views/home/home-view.ts
Normal file
10
frontend/views/home/home-view.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { View } from '../../views/view';
|
||||
|
||||
@customElement('home-view')
|
||||
export class HomeView extends View {
|
||||
render() {
|
||||
return html`<div>Content placeholder</div>`;
|
||||
}
|
||||
}
|
||||
40
frontend/views/login/login-view.ts
Normal file
40
frontend/views/login/login-view.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import '@vaadin/vaadin-login';
|
||||
import { LoginI18n } from '@vaadin/vaadin-login';
|
||||
import { html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators';
|
||||
import { View } from '../../views/view';
|
||||
|
||||
const loginI18nDefault: LoginI18n = {
|
||||
form: {
|
||||
title: 'Log in',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
submit: 'Log in',
|
||||
forgotPassword: 'Forgot password',
|
||||
},
|
||||
errorMessage: {
|
||||
title: 'Incorrect username or password',
|
||||
message: 'Check that you have entered the correct username and password and try again.',
|
||||
},
|
||||
};
|
||||
@customElement('login-view')
|
||||
export class LoginView extends View {
|
||||
@state()
|
||||
private error = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<vaadin-login-overlay
|
||||
opened
|
||||
.error=${this.error}
|
||||
action="login"
|
||||
no-forgot-password
|
||||
.i18n=${Object.assign(
|
||||
{ header: { title: 'Fusion Management', description: 'Login using user/user or admin/admin' } },
|
||||
loginI18nDefault
|
||||
)}
|
||||
>
|
||||
</vaadin-login-overlay>
|
||||
`;
|
||||
}
|
||||
}
|
||||
83
frontend/views/main-layout.ts
Normal file
83
frontend/views/main-layout.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import '@vaadin/vaadin-app-layout';
|
||||
import { AppLayoutElement } from '@vaadin/vaadin-app-layout';
|
||||
import '@vaadin/vaadin-app-layout/vaadin-drawer-toggle';
|
||||
import '@vaadin/vaadin-avatar/vaadin-avatar';
|
||||
import '@vaadin/vaadin-context-menu';
|
||||
import '@vaadin/vaadin-tabs';
|
||||
import '@vaadin/vaadin-tabs/vaadin-tab';
|
||||
import '@vaadin/vaadin-template-renderer';
|
||||
import { html, render } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { logout } from '../auth';
|
||||
import { router } from '../index';
|
||||
import { hasAccess, views } from '../routes';
|
||||
import { appStore } from '../stores/app-store';
|
||||
import { Layout } from './view';
|
||||
|
||||
interface RouteInfo {
|
||||
path: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
@customElement('main-layout')
|
||||
export class MainLayout extends Layout {
|
||||
render() {
|
||||
return html`
|
||||
<vaadin-app-layout primary-section="drawer">
|
||||
<header slot="navbar" theme="dark" class="sidemenu-header">
|
||||
<vaadin-drawer-toggle></vaadin-drawer-toggle>
|
||||
<h1>${appStore.currentViewTitle}</h1>
|
||||
${appStore.user
|
||||
? html` <vaadin-context-menu class="ms-auto me-m" open-on="click" .renderer="${this.renderLogoutOptions}">
|
||||
<vaadin-avatar img="${appStore.user.profilePictureUrl}" name="${appStore.user.name}"></vaadin-avatar
|
||||
></vaadin-context-menu>`
|
||||
: html`<a class="ms-auto me-m" router-ignore href="login">Sign in</a>`}
|
||||
</header>
|
||||
|
||||
<div slot="drawer" class="sidemenu-menu">
|
||||
<div id="logo">
|
||||
<img src="images/logo.png" alt="${appStore.applicationName} logo" />
|
||||
<span>${appStore.applicationName}</span>
|
||||
</div>
|
||||
<hr />
|
||||
<vaadin-tabs orientation="vertical" theme="minimal" .selected=${this.getSelectedViewRoute()}>
|
||||
${this.getMenuRoutes().map(
|
||||
(viewRoute) => html`
|
||||
<vaadin-tab>
|
||||
<span class="${viewRoute.icon} pr-s"></span>
|
||||
<a href=${router.urlForPath(viewRoute.path)} tabindex="-1">${viewRoute.title}</a>
|
||||
</vaadin-tab>
|
||||
`
|
||||
)}
|
||||
</vaadin-tabs>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</vaadin-app-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.classList.add('block', 'h-full');
|
||||
this.reaction(
|
||||
() => appStore.location,
|
||||
() => {
|
||||
AppLayoutElement.dispatchCloseOverlayDrawerEvent();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private renderLogoutOptions(root: HTMLElement) {
|
||||
render(html`<vaadin-list-box><vaadin-item @click=${() => logout()}>Logout</vaadin-item></vaadin-list-box>`, root);
|
||||
}
|
||||
|
||||
private getMenuRoutes(): RouteInfo[] {
|
||||
return views.filter((route) => route.title).filter((route) => hasAccess(route)) as RouteInfo[];
|
||||
}
|
||||
|
||||
private getSelectedViewRoute(): number {
|
||||
const path = appStore.location;
|
||||
return this.getMenuRoutes().findIndex((viewRoute) => viewRoute.path == path);
|
||||
}
|
||||
}
|
||||
10
frontend/views/profile/profile-view.ts
Normal file
10
frontend/views/profile/profile-view.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { View } from '../../views/view';
|
||||
|
||||
@customElement('profile-view')
|
||||
export class ProfileView extends View {
|
||||
render() {
|
||||
return html`<div>Content placeholder</div>`;
|
||||
}
|
||||
}
|
||||
51
frontend/views/view.ts
Normal file
51
frontend/views/view.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { MobxLitElement } from '@adobe/lit-mobx';
|
||||
import { applyTheme } from 'Frontend/generated/theme';
|
||||
import { autorun, IAutorunOptions, IReactionDisposer, IReactionOptions, IReactionPublic, reaction } from 'mobx';
|
||||
|
||||
export class MobxElement extends MobxLitElement {
|
||||
private disposers: IReactionDisposer[] = [];
|
||||
|
||||
/**
|
||||
* Creates a MobX reaction using the given parameters and disposes it when this element is detached.
|
||||
*
|
||||
* This should be called from `connectedCallback` to ensure that the reaction is active also if the element is attached again later.
|
||||
*/
|
||||
protected reaction<T>(
|
||||
expression: (r: IReactionPublic) => T,
|
||||
effect: (arg: T, prev: T, r: IReactionPublic) => void,
|
||||
opts?: IReactionOptions
|
||||
): void {
|
||||
this.disposers.push(reaction(expression, effect, opts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MobX autorun using the given parameters and disposes it when this element is detached.
|
||||
*
|
||||
* This should be called from `connectedCallback` to ensure that the reaction is active also if the element is attached again later.
|
||||
*/
|
||||
protected autorun(view: (r: IReactionPublic) => any, opts?: IAutorunOptions): void {
|
||||
this.disposers.push(autorun(view, opts));
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.disposers.forEach((disposer) => {
|
||||
disposer();
|
||||
});
|
||||
this.disposers = [];
|
||||
}
|
||||
}
|
||||
|
||||
export class View extends MobxElement {
|
||||
createRenderRoot() {
|
||||
// Do not use a shadow root
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class Layout extends MobxElement {
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
applyTheme(this.shadowRoot!);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user