67
Alinex GUI GUI for Alinex Server Alexander Schilling Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Alinex GUIGUI for Alinex Server

Alexander Schilling

Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 2: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Table of contents

51. General

51.1 Alinex GUI Client

111.1.1 Chapters

111.1.2 Support

121.2 Last Changes

121.2.1 Version 0.4.2 (08.11.2020)

121.2.2 Version 0.4.1 (14.10.2020)

121.2.3 Version 0.4.0 (12.10.2020)

121.2.4 Version 0.3.0 (31.07.2020)

121.2.5 Version 0.2.0 (14.06.2020)

131.2.6 Version 0.1.0 (05.05.2020)

141.3 Roadmap

141.3.1 Version 0.5.0

141.3.2 Version 1.0.0

151.4 Privacy statement

162. Usage

162.1 User Manual

172.2 Basic Layout

222.3 Authentication

232.4 Management of Access Control Settings

252.5 System Information

263. Technology

263.1 Used Technologies

273.2 Vue.js

273.2.1 Basics

283.3 Quasar Framework

283.3.1 Structure

283.3.2 Pages and Components

293.4 State Management

293.4.1 Store

303.4.2 State

303.4.3 Getters

313.4.4 Mutations

313.4.5 Actions

313.4.6 Modules

Table of contents

- 2/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 3: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

333.5 Feathers Integration

333.5.1 Authentication

333.5.2 Setup Services

333.5.3 Store

363.5.4 Model

383.5.5 Components

414. Framework

414.1 Core Framework

414.1.1 Features

414.1.2 3rd Party Packages

424.2 Setup

424.2.1 Configuration

424.2.2 Builds

444.3 Layout

444.3.1 Icons

444.3.2 Dark Mode

444.3.3 Sass Variables

514.4 Internationalization

514.4.1 File Structure

514.4.2 Use of translations

534.4.3 Management

544.5 Special Components

544.5.1 Global

544.5.2 Dialogs

554.5.3 Module Organization

574.6 Object Create, Read, Update and Delete

574.6.1 Routing

574.6.2 Store

574.6.3 Pages

574.6.4 Security

574.6.5 Translations

584.7 Store

595. Development

595.1 Development

605.2 Parent Project

615.2.1 Setup

645.3 Customization

645.3.1 Logo

Table of contents

- 3/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 4: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

645.3.2 Colors

655.4 Extending Modules

655.4.1 Routing

655.4.2 Pages

665.4.3 Menu

675.4.4 Dashboard

Table of contents

- 4/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 5: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1. General

1.1 Alinex GUI ClientThis is the client used for the Alinex Server. It's an interface to do access and work with the server

through a graphical interface. It should be a base for specialized tools for specific environments

which can be customized to it's needs.

It is included in the Alinex Server.

1. General

- 5/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 6: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.1 Alinex GUI Client

- 6/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 7: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.1 Alinex GUI Client

- 7/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 8: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.1 Alinex GUI Client

- 8/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 9: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.1 Alinex GUI Client

- 9/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 10: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

See more screenshots under usage.

Big Features:

modern design, responsive

different web technologies (SPA, PWA, SSR)

mobile apps (Android, IOS)

desktop application (Windows, Mac, Linux)

multi lingual with dark mode

1.1 Alinex GUI Client

- 10/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 11: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

currently about 85% of current browser share is supported by default:

Chrome for Android >= 81

Firefox for Android >= 68

Android >= 81

Chrome >= 73

Edge >= 79

Firefox >= 68

iOS >= 10.0-10.2

Opera >= 64

Safari >= 10.1

1.1.1 Chapters

Read all about the alinex-gui in the chapters (top navigation menu):

Usage - user manual on how to work with examples of screens and explanations

Framework - manual on how to extend functionality

Developer - detailed developer information

If you want to have an offline access to the documentation, feel free to download: alinex-gui.pdf or alinex-gui.epub.

1.1.2 Support

I don't give any paid support but you may create GitLab Issues:

Bug Reports or Feature: Please make sure to give as much information as possible. And explain how the system should behave in

your opinion.

Code Fixing or Extending: If you help to develop this package I am thankful. Develop in a fork and make a merge request after

done. I will have a look at it.

Should anybody be willing to join the core team feel free to ask, too.

Any other comment or discussion as far as it is on this package is also kindly accepted.

Please use for all of them the GitLab Issues which only requires you to register with a free account.

1.1.1 Chapters

- 11/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 12: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.2 Last Changesupdate documentation theme

1.2.1 Version 0.4.2 (08.11.2020)

fix socketio timeout to 5 Minutes

fix changing password / no password

update packages and doc structure

add autologin field to user

replace checks with checkup

1.2.2 Version 0.4.1 (14.10.2020)

fix include flags in build

1.2.3 Version 0.4.0 (12.10.2020)

role administration

sidebar open on access to subentry

fix ability check to also support conditions

roles edit in user view

abilities display for own user

extracted subject possibilities to data module

abilities in info dialog optimized

show users of role and role abilities in user

show disabled state visually

readonly mode if field is not updateable

1.2.4 Version 0.3.0 (31.07.2020)

restructured translation index

updated framework docs

support additional project routes

updated base framework and dropped ie11 support (per default)

faster development mode in parent project

check abilities in router

login placeholder only in demo

start page in sidebar not always highlighted

reset on logout for data cleanup

create/update/delete user now working

1.2.5 Version 0.2.0 (14.06.2020)

automated build

project support

1.2 Last Changes

- 12/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 13: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

authentication

logout

store state in session

check abilities in router

module layout with sidebar

info service

privacy like about dialog

check abilities for menu and icon view

1.2.6 Version 0.1.0 (05.05.2020)

initial setup

i18n

deep structure

language selection

dialog components

modularization

login form

building all aplications

1.2.6 Version 0.1.0 (05.05.2020)

- 13/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 14: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.3 RoadmapWhat's coming next?

That's a big question on a freelancer project. The roadmap may change any time or progress may be stuck. But hopefully I will come

further on in any way.

don't upgrade to @types/node@12 because of electron problem

don't upgrade to socket.io-client v3.0.0 - connection not longer possible

1.3.1 Version 0.5.0

login icon on dashboard

field ability checking

role view

abilities optimizer (on server update)

strict password validation

authentication management (lost-password, email verify)

password generator

1.3.2 Version 1.0.0

google auth or github auth

1.3 Roadmap

- 14/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 15: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

1.4 Privacy statementThis documentation is part of the alinex.gitlab.io site and as such please have a look on the site's privacy statement.

1.4 Privacy statement

- 15/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 16: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2. Usage

2.1 User ManualThis user manual only describes the Alinex core functionality. Because Alinex is a framework your individual application will have a

lot more, which is not included here.

The core contains:

Layout

Authentication

Access Control Management

System Information

2. Usage

- 16/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 17: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.2 Basic LayoutAt first the system will greet you with a simple dashboard, only containing the few elements available without authentication.

The layout is responsive and also is also possible on mobile phones or tablets.

2.2 Basic Layout

- 17/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 18: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.2 Basic Layout

- 18/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 19: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

To see more a login is necessary. Which will give the user more rights.

2.2 Basic Layout

- 19/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 20: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Now the dashboard on the home page shows some more links.

A dark mode is also possible, which will be set automatically if detectable.

2.2 Basic Layout

- 20/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 21: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.2 Basic Layout

- 21/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 22: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.3 AuthenticationIn the moment only a simple email/password authentication is possible. No cookies are needed here.

On logout the client will be reseted, after some seconds.

2.3 Authentication

- 22/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 23: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.4 Management of Access Control SettingsAs admin you can setup the user rights.

This allows to create and update user records and extend their rights by setting their roles.

To extend the roles with new ones:

2.4 Management of Access Control Settings

- 23/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 24: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

You should know the REST interface and data structure to define this. Maybe the swagger is enabled on the server which can help

giving structure information.

2.4 Management of Access Control Settings

- 24/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 25: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

2.5 System InformationThe system information is needed to help giving the developer more details about the current setup.

The following information is included:

client with container software and application

user with roles and abilities

server with system user and nodejs version

host hardware and operating system

store databases

2.5 System Information

- 25/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 26: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3. Technology

3.1 Used TechnologiesWe also don't reinvent the wheel, so we base this framework on some other libraries which we saw as most fitting at the moment we

started this project. It was no selection out of space but with some tryouts before.

The biggest technologies are:

VueJS - A progressive framework for building user interfaces for the web.

Quasar Framework - Composition of VueJS components with a framework to build different apps.

State Management - Vuex Store is used as state management holding the data.

Feathers - Integration of the Feathers Client into VueJS with state management.

All this is based on JavaScript as client language and for the build tool.

Other, smaller modules are also included which are not of such big concern.

3. Technology

- 26/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 27: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.2 Vue.jsVue.js is a web framework for building the view component of web applications.

3.2.1 Basics

Vue.js uses vue files as a base which are a combination of HTML + JavaScript + Style like:

Templates can use:

Binding: Use <a v-bind:href="url"> or <a :href="url"> to bind its value to the given JavaScript expression or variable.

Dynamic Attributes: <a v-bind:[attrName]='url'> or <a :[attrName]='url'> will set the attribute defined within attrName but

no expressions, as attribute names (use computed properties therefore).

Conditionals: <span v-if="flag">Flag is true</span> will show only if flag variable is true. Also the use of v-else or v-else-if

in the direct following element is possible.

Keys: The key attribute within elements tells vue to trade it as individuals.

Show: Using v-show will switch visibiliy, while it is always rendered.

Loops: <li v-for="todo in todos">{{ todo.text }}</li> will add the li tag multiple times.

User Input: <button v-on:click="fn"> or <button @click="fn"> will call function if clicked.

Modifiers: <form v-on:submit.prevent="onSubmit"> will call event.preventDeault()

2-Way-Binding: `

JavaScript

To put some of the complexity out of the template itself and make this simpler, you can add it to the script.

data is a map with values to be directly used.

computed contains an alternative map with functions which will be called at the time of use.

methods are a collection of functions to be called.

computed defines set ter and get ter methods per property which will be called if the property is read or changed.

watch defines functions per properties, which are called if the property has changed.

Computed properties will only update if their used reactive element changes.

Components

The components let a big application be build from smaller and reusable pieces which will be put together hierarchically. To make a

complex application more manageable it should be split into multiple, small units.

https://medium.com/quasar-framework/component-building-with-quasar-fc101b6730ae

<template> <div id="app"> {{ message }} </div></template>

<script>export default { name: 'App', data: { message: 'Hello Vue!' }}</script>

<style>/* possible css styles */</style>

3.2 Vue.js

- 27/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 28: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.3 Quasar FrameworkQuasar is a framework based on Vue which makes you write one application which can be build into a desktop website, a desktop app

or a mobile app, or all of them. It comes with a lot of ready to use components and a very powerful build making a small footprint

using tree shaking, code splitting, transpiling and lazy loading.

3.3.1 Structure

3.3.2 Pages and Components

Have a look at the Vue.js description.

As of now TypeScript is supported but makes your live a lot trickier with quasar. It is not as well supported to work fast with it.

Speaking of now, the most examples also are based on ES6 - making it easier for you to just use ES6.

Note

├── public/ # pure static assets (directly copied)├── src/│ ├── assets/ # dynamic assets (processed by webpack)│ ├── components/ # .vue components used in pages & layouts│ ├── css/ # CSS/Stylus/Sass/... files for your app| | ├── app.styl| │ └── quasar.variables.styl # Quasar Stylus variables for you to tweak│ ├── layouts/ # layout .vue files│ ├── pages/ # page .vue files│ ├── boot/ # boot files (app initialization code)│ ├── router/ # Vue Router| | ├── index.js # Vue Router definition| │ └── routes.js # App Routes definitions│ ├── store/ # Vuex Store| | ├── index.js # Vuex Store definition| │ ├── <folder> # Vuex Store Module...| │ └── <folder> # Vuex Store Module...│ ├── App.vue # root Vue component of your App│ └── index.template.html # Template for index.html├── src-ssr/ # SSR specific code (like production Node webserver)├── src-pwa/ # PWA specific code (like Service Worker)├── src-cordova/ # Cordova generated folder used to create Mobile Apps├── src-electron/ # Electron specific code (like "main" thread)├── dist/ # where production builds go│ ├── spa/ # example when building SPA│ ├── ssr/ # example when building SSR│ ├── electron/ # example when building Electron│ └── ....├── quasar.conf.js # Quasar App Config file├── babel.config.js # Babeljs config├── .editorconfig # editor config├── .eslintignore # ESlint ignore paths├── .eslintrc.js # ESlint config├── .postcssrc.js # PostCSS config├── .stylintrc # Stylus lint config├── .gitignore # GIT ignore paths├── package.json # npm scripts and dependencies└── README.md # readme for your website/App

3.3 Quasar Framework

- 28/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 29: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.4 State ManagementState management allows objects to have one or more states. As a state changes, the correct chain of events will be fired of and

appropriate properties will be modified. By defining and separating the concepts involved in state management and enforcing certain

rules, this gives the code more structure and maintainability.

The basic concept is described below, but you may also visit the Vuex Manual.

3.4.1 Store

The store is basically a container that holds the application state. It is reactive so a change of state will also trigger the change of

elements using this state. And the only way to change state is to commit mutations. This ensures every state change leaves a track-

able record, and enables tooling that helps us better understand the applications.

By providing the store option to the root instance, the store will be injected into all child components of the root and will be available

on them as this.$store :

computed: { count () { return this.$store.state.count }}

3.4 State Management

- 29/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 30: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.4.2 State

Vuex uses a single state tree including all application level state objects and serves as the "single source of truth". For debugging the

application a snapshot of the state tree may help. To modularize the application the single state tree is split into sub modules.

The mapState helper which generates computed getter functions for us, saving us some keystrokes:

We can also pass a string array to mapState when the name of a mapped computed property is the same as a state sub tree name.

Use the spread operator to mix the outer object with the state object:

3.4.3 Getters

Getters are methods defined in the store to retrieve derived states:

You can then call them using:

The mapGetters helper simply maps store getters to local computed properties:

// in full builds helpers are exposed as Vuex.mapStateimport { mapState } from 'vuex'

export default { // ... computed: mapState({ // arrow functions can make the code very succinct! count: state => state.count, // passing the string value 'count' is same as `state => state.count` countAlias: 'count', })}

computed: mapState([ // map this.count to store.state.count 'count'])

computed: { localComputed () { .... }, ...mapState({ // ... })}

const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } }})

computed: { doneTodosCount () { return this.$store.getters.doneTodosCount }}

import { mapGetters } from 'vuex'

export default { // ... computed: { // mix the getters into computed with object spread operator ...mapGetters([ 'doneTodosCount' ]) }}

3.4.2 State

- 30/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 31: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.4.4 Mutations

The only way to actually change state in a Vuex store is by committing a mutation. Vuex mutations are very similar to events: each

mutation has a string type and a handler. The handler function is where we perform actual state modifications, and it will receive the

state as the first argument:

The mutation is calles using a commit:

Keep in mind that all mutations have to be synchronous.

Also a helper is there to map mutations directly to component methods:

3.4.5 Actions

Actions are similar to mutations, in reality actions will trigger mutations. The real differences is that they may be asynchronous.

As before you may use mapActions here.

3.4.6 Modules

Due to using a single state tree, all state of our application is contained inside one big object. However, as our application grows in

scale, the store can get really bloated.

To help with that, Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters,

and even nested modules. They can be accessed using their complete path like some/nested/module/action .

But VueX also contains a handy method to make namespaced helpers:

const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { state.count++ } }})

store.commit('increment')

import { mapMutations } from 'vuex'

export default { // ... methods: { ...mapMutations([ 'increment', // map `this.increment()` to `this.$store.commit('increment')`

// `mapMutations` also supports payloads: 'incrementBy' // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // map `this.add()` to `this.$store.commit('increment')` }) }}

const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})

3.4.4 Mutations

- 31/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 32: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default { computed: { // look up in `some/nested/module` ...mapState({ a: a, b: b }) }, methods: { // look up in `some/nested/module` ...mapActions([ 'foo', 'bar' ]) }}

3.4.6 Modules

- 32/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 33: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.5 Feathers IntegrationThis implements VueX using a feathers based server. It is ready configured to work with the API Server using websockets.

3.5.1 Authentication

This is also ready configured.

3.5.2 Setup Services

Therefore the ServicePlugin is used.

This will be loaded automatically through webpack. Read more about defining the service plugin.

You may access it through this.$FeathersVuex.api.User({})

3.5.3 Store

Getter

list Array - an array of items. The array form of keyedById Read only.

store/services/users.jsimport feathersClient, { makeServicePlugin, BaseModel } from '../../feathers'

class User extends BaseModel { // Required for $FeathersVuex plugin to work after production transpile. static modelName = 'User'; // Define default properties here static instanceDefaults () { return { email: '', password: '' } }}

const servicePath = 'users'const servicePlugin = makeServicePlugin({ Model: User, service: feathersClient.service(servicePath), servicePath})

// Setup the client-side Feathers hooks.feathersClient.service(servicePath).hooks({ before: { all: [], find: [], get: [], create: [], update: [], patch: [], remove: [] }, after: { all: [], find: [], get: [], create: [], update: [], patch: [], remove: [] }, error: { all: [], find: [], get: [], create: [], update: [], patch: [], remove: [] }})

export default servicePlugin

3.5 Feathers Integration

- 33/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 34: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

find(params) Function - a helper function that allows you to use the Feathers Adapter Common API and Query API to pull data

from the store. This allows you to treat the store just like a local Feathers database adapter (but without hooks).

params Object - an object with a query object and an optional paginate boolean property. The query is in the FeathersJS

query format. You can set params.paginate to false to disable pagination for a single request.

get(id[, params]) Function - a function that allows you to query the store for a single item, by id. It works the same way as get

requests in Feathers database adapters.

id Number|String - the id of the data to be retrieved by id from the store.

params Object - an object containing a Feathers query object.

Mutations

addItem(state, item) - adds a single item to the keyedById map.

item Object - The item to be added to the store.

addItems(state, items) - adds an array of items to the keyedById map.

items Array - the items to be added to the store.

updateItem(state, item) - updates an item in the store to match the passed in item.

item Object - the item, including id, to replace the currently-stored item.

updateItems(state, items) - updates multiple items in the store to match the passed in array of items.

items Array - An array of items.

removeItem(state, item) - removes a single item. item can be

item Number|String|Object - the item or id of the item to be deleted.

removeTemps(state, tempIds) - removes temp records. Also cleans up tempsByNewId

tempIds Array - an array of ids or of objects with tempIds that will be removed from the data store

removeItems(state, items) - Removes the passed in items or ids from the store.

items Array - An array of ids or of objects with ids that will be removed from the data store.

clearAll(state) - clears all data from ids, keyedById, and currentId

Actions

An action is included for each of the Feathers service interface methods. These actions will affect changes in both the Feathers API

server and the Vuex store.

FIND(PARAMS)

Query an array of records from the server and add to the Vuex store.

params Object - an object containing a query object and an optional paginate boolean. You can set params.paginate to false to

disable pagination for a single request.

You would typically not call these directly, but instead with store.commit('removeItem', 'itemId') . Using vuex's mapMutations on a

Vue component can simplify that to this.removeItem('itemId') .

Note

Because Vuex only supports providing a single argument to actions, there is a slight change in syntax that works well. If you need

to pass multiple arguments to a service method, pass an array to the action with the order of the array elements matching the

order of the arguments.

Note

3.5.3 Store

- 34/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 35: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Pagination can be used through the query object:

GET(ID) OR GET([ID, PARAMS])

Query a single record from the server & add to Vuex store

id Number|String - the id of the record being requested from the API server.

params Object - an object containing a query object.

CREATE(DATA|PARAMARRAY)

Create one or multiple records. Note that the method is overloaded to accept two types of arguments. If you want a consistent

interface for creating single or multiple records, use the array syntax, described below. Creating multiple records requires using the

paramArray syntax.

data Object - if an object is provided, a single record will be created.

ParamArray Array - if an array is provided, it is assumed to have this structure. Array containing the two parameters that

Feathers' service.create method accepts.

data {Object|Array} - the data to create. Providing an object creates a single record. Providing an array of objects creates

multiple records.

params {Object} - optional - an object containing a query object. Can be useful in rare situations.

UPDATE(PARAMARRAY)

Update (overwrite) a record.

paramArray Array - array containing the three parameters update accepts.

id Number|String - the id of the existing record being requested from the API server.

data Object - the data that will overwrite the existing record

params Object - an object containing a query object.

Alternatively in a Vue component

const params = {query: {completed: true}}store.dispatch('todos/find', params)

const params = { query: { $limit: 25, $skip: 0 } }store.dispatch('todos/find', params)

store.dispatch('todos/get', 1)// Use an array to pass paramslet params = {}store.dispatch('todos/get', [1, params])

const newTodo = {description: 'write good tests'}store.dispatch('todos/create', newTodo)

let data = {id: 5, description: 'write your tests', completed: true}let params = {}// Overwrite item 1 with the above data (FYI: Most databases won't let you change the id.)store.dispatch('todos/update', [1, data, params])

import { mapActions } from 'vuex'export default { methods: { ...mapActions('todos', [ 'update' ]), addTodo () { let data = {id: 5, description: 'write your tests', completed: true} this.update([1, data, {}]) } }}

3.5.3 Store

- 35/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 36: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

PATCH(PARAMARRAY)

Patch (merge in changes) one or more records

paramArray Array - array containing the three parameters patch takes.

id Number|String - the id of the existing record being requested from the API server.

data Object - the data that will be merged into the existing record

params Object - an object containing a query object. If params.data is provided, it will be used as the patch data, providing a

simple way to patch with partial data.

REMOVE(ID)

Remove/delete the record with the given id.

id Number|String - the id of the existing record being requested from the API server.

Events

created events will add new record to the store.

patched events will add (if new) or update (if present) the record in the store.

updated events will add (if new) or update (if present) the record in the store.

removed events will remove the record from the store, if present.

3.5.4 Model

Feathers also supports a model which can be used for easy access and while do the interaction with the store. It was already shown

in the code above.

To get the model it should look like:

Static Methods

find(params) - a proxy to the find action.

findInStore(params) - a proxy to the find getter.

get(id, params) - a proxy to the get action. Notice that the signature is more Feathers-like, and doesn't require using an array

to passing both id and params.

getFromStore(id, params) - a proxy to the get getter.

let data = {description: 'write your tests', completed: true}let params = {}store.dispatch('todos/patch', [1, data, params])

store.dispatch('todos/remove', 1)

Within Vue component

Directly from Vue

created () { const { User } = this.$FeathersVuex.api // do something with it}

import Vue from 'vue'

const { Todo } = Vue.$FeathersVuex.api// do something with it

3.5.4 Model

- 36/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 37: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Model Events

Setting eventhandlers will be done using:

on - register event handlers to listen to events.

once - register an event handler that only occurs once.

off - remove an event handler.

All FeathersJS events are supported like:

.on('created')

.on('updated')

.on('patched')

.on('removed')

Here’s an example of how to use it in a component:

Instantiation

Calling new User({ firstName: 'Marshall' }) will create the instance with the firstName filled in, already. It will also include defaults

which are set in the model.

Instance Methods

.save() - a convenience wrapper for the create/patch methods. If the records has no _id , the .create() method will be used,

else .patch() .

.create() - calls the create action (service method) using the instance data.

.patch(params) - calls the patch action (service method) using the instance data. You can set params.data as the patch data.

.update() - calls the update action (service method) using the instance data.

.remove() - calls the remove action (service method) using the instance data.

Working with clone

This allows you to make changes to the clone and not update visible data until you commit or save the data.

There's another use case for using .clone() . Vuex has a strict mode that's really useful in development. It throws errors if any

changes occur in the Vuex store state outside of mutations. Clone really comes in handy here, because you can make changes to the

clone without having to write custom Vuex mutations. When you're finished making changes, call .commit() to update the store. This

gives you strict mode compliance with little effort!

.clone() - creates a deep copy of the record and stores it on Model.copiesById .

.commit() - update the data from the clone in the store.

.reset() - reset the clone record to match the one in the store.

export default { created() { this.$FeathersVuex.api.Todo.on(‘created’, this.handleTodoCreated) }, destroyed() { this.$FeathersVuex.api.Todo.off(‘created’, this.handleTodoCreated) }, methods: { handleTodoCreated(todo) { console.log(todo) } }}

const { User } = this.$FeathersVuex.apiconst user = new User({ id: 1, description: 'Do something!' })const userCopy = user.clone()

3.5.4 Model

- 37/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 38: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

3.5.5 Components

There are two new renderless data provider components: <FeathersVuexFind> and <FeathersVuexGet> . They simplify performing

queries against the store and/or the API server. They make the data available inside each component's default slot.

The <FeathersVuexFormWrapper> and <FeathersVuexInputWrapper> are renderless components which assist in connecting your

feathers-vuex data to a form.

FeathersVuexFind

The FeathersVuexFind component retrieves data from the API server, puts it in the Vuex store, then transparently retrieves the live,

reactive data from the store and displays it to the user.

By default, the following props are available in the scope data:

items Array - the resulting array of records for find operations.

isFindPending Boolean - when there's an active request to the API server, this will be true.

pagination Object - pagination data from the Vuex store, keyed by the qid attribute. By default, this will be specific to this

component instance.

FeathersVuexGet

The FeathersVuexGet component allows fetching data from directly inside a template. It makes the slot scope available to the child

components.

item Object - the resulting object for get operations

isGetPending Boolean - the same as the isFindPending, but for get requests.

FeathersVuexFormWrapper

The FeathersVuexFormWrapper component uses the "clone and commit" pattern to connect a single record to a child form within its

default slot.

userCopy.description = 'Do something else!'userCopy.commit() // --> Update the data in the store.

console.log(user.description) // --> 'Do something else!'console.log(userCopy.description) // --> 'Do something else!'

<template> <FeathersVuexFind service="users" :query="{}" watch="query"> <section slot-scope="{ items: users }"> {{users}} </section> </FeathersVuexFind></template>

<template> <FeathersVuexGet service="users" :id="id" :watch="id"> <template slot-scope="{ item: user }"> {{ user }} </template> </FeathersVuexGet></template>

<template> <FeathersVuexFormWrapper :item="currentItem" watch> <template v-slot="{ clone, save, reset, remove }"> <SomeEditor :item="clone" @save="save().then(handleSaveResponse)" @reset="reset" @remove="remove" ></SomeEditor> </template> </FeathersVuexFormWrapper></template>

<script>

3.5.5 Components

- 38/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 39: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Props:

item Object - a model instance from the Vuex store.

watch Boolean|Array - when enabled, if the original record is updated, the data will be re-cloned.

eager Boolean - while this is enabled, using the save method will first commit the result to the store then it will send a network

request. The UI display will update immediately, without waiting for any response from the API server. Default: true.

Slot:

clone Object - the cloned record. Each record in the store can have a single clone. The clones are stored on the service's model

class, by default.

save Function - when called, it commits the data and saves the record (with eager updating, by default. See the eager prop.)

reset Function - when called, the clone data will be reset back to the data that is currently found in the store for the same

record.

remove Function - when called, it removes the record from the API server and the Vuex store.

FeathersVuexInputWrapper

Building on the same ideas as the FeathersVuexFormWrapper, the FeathersVuexInputWrapper reduces boilerplate for working with

the clone and commit pattern on a single input. One use case for this component is implementing an "edit-in-place" workflow. The

following example shows how to use the FeathersVuexInputWrapper to automatically save a record upon blur on a text input:

import { FeathersVuexFormWrapper } from 'feathers-vuex'

export default { name: 'MyComponent', components: { FeathersVuexFormWrapper }, props: { currentItem: { type: Object, required: true } }, methods: { handleSaveReponse(savedItem) { console.log(savedItem) // The item returned from the API call } }}</script>

<template> <div class="p-3"> <FeathersVuexInputWrapper :item="user" prop="email"> <template #default="{ current, prop, createClone, handler }"> <input v-model="current[prop]" type="text" @focus="createClone" @blur="e => handler(e, save)" /> </template> </FeathersVuexInputWrapper>

<!-- Simple readout to show that it's working. --> <pre class="bg-black text-white text-xs mt-2 p-1">{{user}}</pre> </div></template>

<script>export default { name: 'InputWrapperExample', methods: { // Optionally make the event handler async. async save({ event, clone, prop, data }) { const user = clone.commit() return user.patch(data) } }}</script>

3.5.5 Components

- 39/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 40: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Props:

item - the original (non-cloned) model instance.

prop - the property name on the model instance to be edited.

Slot Scope:

Only the default slot is used. The following props are available in the slot scope:

current clone|instance - returns the clone if it exists, or the original record. current = clone || item

clone clone - the internal clone. This is exposed for debugging purposes.

prop String - the value of the prop prop. If you have the prop stored in a variable in the outer scope, this is redundant and not

needed. You could just use this from the outer scope. It mostly comes in handy when you are manually specifying the prop name

on the component.

createClone Function - sets up the internal clone. Meant to be used as an event handler.

handler Function - has the signature handler(event, callback). It prepared data before calling the callback function that must be

provided from the outer scope.

3.5.5 Components

- 40/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 41: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4. Framework

4.1 Core FrameworkAs the Alinex GUI is no ready to use frontend, you have to use it as framework and build your real application with it. This section will

describe the core GUI framework with it's components. To extend it best is to make a parent project like described under

development.

4.1.1 Features

Internationalization

Dynamic loading using import in router

4.1.2 3rd Party Packages

Additional to the short descriptions here the original documentation may be useful:

Vue

VueX for application state

VueX Feathers

Quasar Framework

Style

Icons: Material or MDI using mdi-xxx

This is not a complete manual, you have to know the basics of the fundamental technologies on which the GUI is build.

Attention

4. Framework

- 41/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 42: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.2 Setup

4.2.1 Configuration

The configuration in package.json will define which browsers are supported using more or less polyfills. In example you may add old

MS Internet Explorer 11 Support with:

4.2.2 Builds

A development build is possible using:

This enables you to change things and see the result in the browser in ashort time. But because the projects data has to be rejoined

with the core each time it is not as fast as in single projects. It automatically runs a local server which will reload and also refresh the

browser if changes are made.

If everything looks fine you may build the different apps using npm run build , which will call the bin/gui-setup .

To specify the server to use which will be build into the client in the download files use the environment setting API :

It will create distributable files within the dist folder which may be published to be used. But often you will call this from the Alinex

Server or the project specific module extending it.

Browser

To run the web versions a web server is needed, which can be simulated using:

or

But first you have to build it like described above.

SINGLE PAGE APPLICATION

A Single-Page Application (SPA) is a web application or web site that interacts with the user by dynamically rewriting the current

page rather than loading entire new pages from a server. This approach avoids interruption of the user experience between

successive pages, making the application behave more like a desktop application. But the initial loading will take some time.

In a SPA the appropriate resources are dynamically loaded and added to the page as necessary, usually in response to user actions.

The page does not reload at any point in the process, nor does control transfer to another page, although the location hash or the

HTML5 History API can be used to provide the perception and navigability of separate logical pages in the application.

This is build under dist folder.

PROGRESSIVE WEB APPLICATION

A Progressive Web App (PWA) is a web app that uses modern web capabilities to deliver an app-like experience to users.

As PWA the application will be mostly connectivity independent by using service workers to work offline or on low quality networks.

"browserslist": [ "ie 11", // <<-- add it ...]

npm run dev

bin/gui-setup

bin/gui-setup --api http://my-sever.de:3000

node_modules/.bin/quasar serve dist

PORT=8000 node_modules/.bin/quasar serve dist

4.2 Setup

- 42/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 43: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

This alternative browser version is not used and build at the moment.

Mobile Apps

Using Capacitor the web applications is transformed into a mobile app. It exposes native device APIs in the form of JavaScript

modules.

If this is needed some preparations has to be done.

Here you can build for two devices.

ANDROID APP

You need to install the Android SDK with cli tools and licenses agreed before gui-setup can successfully build it.

You will find the APK under dist/download folder.

IOS APP

This is only possible to be made on a MacOS host with Xcode installed.

Desktop Applications

To make also an desktop app the JavaScript is bundled into an electron app. Electron is the main GUI framework behind several

notable open-source projects including GitHub’s Atom and Microsoft’s Visual Studio Code source code editors.

You will find the linux, darwin and win32 applications under dist/download folder.

Dokumentation

The documentation (html+pdf+epub) will be build using:

But you can also always run a development server of the documentation (mkdocs needs to be installed):

This will start an auto reloading documentation on http://127.0.0.1:8000

npm run pdf

mkdocs serve

4.2.2 Builds

- 43/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 44: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.3 LayoutThe whole application has a general 'default' layout which is used throughout most pages. It defines the basic page layout with:

drop-down menu (account and settings)

title bar

side bar

content area

Because the whole application is a single page, the title bar of the browser normally never changes. This is solved by setting it from

meta information which will be gathered from route.meta.module through i18n transtaltion using *.title and appending the global

title. This can be overwritten by setting meta.title in the page.

As styling language we use Sass in the modern form of SCSS, but other style processors are also possible.

4.3.1 Icons

Within the UI a lot of icons are used for better visualization. We won't mix to much different styles here so as far as possible we use

one of:

Material Icons (no prefix needed)

Material Design Icons (prefix mdi- is needed)

4.3.2 Dark Mode

The application supports a light mode (default) as well as a default, which may be switched within the settings (top right).

To support this two modes use the following css classes:

.body--light or .body--dark is added automaticaly to the body tag. Use them in the stylesheets to setup a specific style using

light or dark colors.

Also if a element has the class .light or .dark attached it will only be displayed in the specified mode.

4.3.3 Sass Variables

$space-base : 16px !default$space-x-base : $space-base !default$space-y-base : $space-base !default

$space-none : (x: 0, y: 0) !default$space-xs : (x: ($space-x-base * .25), y: ($space-y-base * .25)) !default$space-sm : (x: ($space-x-base * .5), y: ($space-y-base * .5)) !default$space-md : (x: $space-x-base, y: $space-y-base) !default$space-lg : (x: ($space-x-base * 1.5), y: ($space-y-base * 1.5)) !default$space-xl : (x: ($space-x-base * 3), y: ($space-y-base * 3)) !default

// sorry for long line; we need .sass and it doesn't support multi-line list$spaces: ('none': $space-none, 'xs': $space-xs, 'sm': $space-sm, 'md': $space-md, 'lg': $space-lg, 'xl': $space-xl) !default

// Max width at which point// current size ends$breakpoint-xs: 599px !default$breakpoint-sm: 1023px !default$breakpoint-md: 1439px !default$breakpoint-lg: 1919px !default

$flex-cols : 12 !default$flex-gutter-xs : ($space-base * .25) !default$flex-gutter-sm : ($space-base * .5) !default$flex-gutter-md : $space-base !default$flex-gutter-lg : ($space-base * 1.5) !default$flex-gutter-xl : ($space-base * 3) !default

$body-font-size : 14px !default$body-line-height : 1.5 !default

// sorry for long line; we need .sass and it doesn't support multi-line list$flex-gutter: ('none': 0, 'xs': $flex-gutter-xs, 'sm': $flex-gutter-sm, 'md': $flex-gutter-md, 'lg': $flex-gutter-lg, 'xl': $flex-gutter-xl) !default

4.3 Layout

- 44/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 45: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

// sorry for long line; we need .sass and it doesn't support multi-line list$sizes: ('xs': 0, 'sm': ($breakpoint-xs + 1), 'md': ($breakpoint-sm + 1), 'lg': ($breakpoint-md + 1), 'xl': ($breakpoint-lg + 1)) !default

$breakpoint-xs-min: 0 !default$breakpoint-xs-max: $breakpoint-xs !default

$breakpoint-sm-min: map-get($sizes, "sm") !default$breakpoint-sm-max: $breakpoint-sm !default

$breakpoint-md-min: map-get($sizes, "md") !default$breakpoint-md-max: $breakpoint-md !default

$breakpoint-lg-min: map-get($sizes, "lg") !default$breakpoint-lg-max: $breakpoint-lg !default

$breakpoint-xl-min: map-get($sizes, "xl") !default$breakpoint-xl-max: 9999px !default

$h1: (size: 6rem, line-height: 6rem, letter-spacing: -.01562em, weight: 300) !default$h2: (size: 3.75rem, line-height: 3.75rem, letter-spacing: -.00833em, weight: 300) !default$h3: (size: 3rem, line-height: 3.125rem, letter-spacing: normal, weight: 400) !default$h4: (size: 2.125rem, line-height: 2.5rem, letter-spacing: .00735em, weight: 400) !default$h5: (size: 1.5rem, line-height: 2rem, letter-spacing: normal, weight: 400) !default$h6: (size: 1.25rem, line-height: 2rem, letter-spacing: .0125em, weight: 500) !default$subtitle1: (size: 1rem, line-height: 1.75rem, letter-spacing: .00937em, weight: 400) !default$subtitle2: (size: .875rem, line-height: 1.375rem, letter-spacing: .00714em, weight: 500) !default$body1: (size: 1rem, line-height: 1.5rem, letter-spacing: .03125em, weight: 400) !default$body2: (size: .875rem, line-height: 1.25rem, letter-spacing: .01786em, weight: 400) !default$overline: (size: .75rem, line-height: 2rem, letter-spacing: .16667em, weight: 500) !default$caption: (size: .75rem, line-height: 1.25rem, letter-spacing: .03333em, weight: 400) !default

// sorry for long line; we need .sass and it doesn't support multi-line list$headings: ('h1': $h1, 'h2': $h2, 'h3': $h3, 'h4': $h4, 'h5': $h5, 'h6': $h6, 'subtitle1': $subtitle1, 'subtitle2': $subtitle2, 'body1': $body1, 'body2': $body2, 'overline': $overline, 'caption': $caption) !default

// sorry for long line; we need .sass and it doesn't support multi-line list$h-tags: (h1: map-get($headings, "h1"), h2: map-get($headings, "h2"), h3: map-get($headings, "h3"), h4: map-get($headings, "h4"), h5: map-get($headings, "h5"), h6: map-get($headings, "h6")) !default

// sorry for long line; we need .sass and it doesn't support multi-line list$text-weights: (thin: 100, light: 300, regular: 400, medium: 500, bold: 700, bolder: 900) !default

$primary : #1976D2 !default$secondary : #26A69A !default$accent : #9C27B0 !default

// used by dark mode$dark-page : #121212 !default$dark : #1d1d1d !default

$positive : #21BA45 !default$negative : #C10015 !default$info : #31CCEC !default$warning : #F2C037 !default

$dimmed-background : rgba(0, 0, 0, .4) !default$light-dimmed-background : rgba(255, 255, 255, .6) !default

$separator-color : rgba(0, 0, 0, .12) !default$separator-dark-color : rgba(255, 255, 255, .28) !default

$red : #f44336 !default$red-1 : #ffebee !default$red-2 : #ffcdd2 !default$red-3 : #ef9a9a !default$red-4 : #e57373 !default$red-5 : #ef5350 !default$red-6 : #f44336 !default$red-7 : #e53935 !default$red-8 : #d32f2f !default$red-9 : #c62828 !default$red-10 : #b71c1c !default$red-11 : #ff8a80 !default$red-12 : #ff5252 !default$red-13 : #ff1744 !default$red-14 : #d50000 !default$pink : #e91e63 !default$pink-1 : #fce4ec !default$pink-2 : #f8bbd0 !default$pink-3 : #f48fb1 !default$pink-4 : #f06292 !default$pink-5 : #ec407a !default$pink-6 : #e91e63 !default$pink-7 : #d81b60 !default$pink-8 : #c2185b !default$pink-9 : #ad1457 !default$pink-10 : #880e4f !default$pink-11 : #ff80ab !default$pink-12 : #ff4081 !default$pink-13 : #f50057 !default$pink-14 : #c51162 !default$purple : #9c27b0 !default$purple-1 : #f3e5f5 !default

4.3.3 Sass Variables

- 45/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 46: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

$purple-2 : #e1bee7 !default$purple-3 : #ce93d8 !default$purple-4 : #ba68c8 !default$purple-5 : #ab47bc !default$purple-6 : #9c27b0 !default$purple-7 : #8e24aa !default$purple-8 : #7b1fa2 !default$purple-9 : #6a1b9a !default$purple-10 : #4a148c !default$purple-11 : #ea80fc !default$purple-12 : #e040fb !default$purple-13 : #d500f9 !default$purple-14 : #aa00ff !default$deep-purple : #673ab7 !default$deep-purple-1 : #ede7f6 !default$deep-purple-2 : #d1c4e9 !default$deep-purple-3 : #b39ddb !default$deep-purple-4 : #9575cd !default$deep-purple-5 : #7e57c2 !default$deep-purple-6 : #673ab7 !default$deep-purple-7 : #5e35b1 !default$deep-purple-8 : #512da8 !default$deep-purple-9 : #4527a0 !default$deep-purple-10 : #311b92 !default$deep-purple-11 : #b388ff !default$deep-purple-12 : #7c4dff !default$deep-purple-13 : #651fff !default$deep-purple-14 : #6200ea !default$indigo : #3f51b5 !default$indigo-1 : #e8eaf6 !default$indigo-2 : #c5cae9 !default$indigo-3 : #9fa8da !default$indigo-4 : #7986cb !default$indigo-5 : #5c6bc0 !default$indigo-6 : #3f51b5 !default$indigo-7 : #3949ab !default$indigo-8 : #303f9f !default$indigo-9 : #283593 !default$indigo-10 : #1a237e !default$indigo-11 : #8c9eff !default$indigo-12 : #536dfe !default$indigo-13 : #3d5afe !default$indigo-14 : #304ffe !default$blue : #2196f3 !default$blue-1 : #e3f2fd !default$blue-2 : #bbdefb !default$blue-3 : #90caf9 !default$blue-4 : #64b5f6 !default$blue-5 : #42a5f5 !default$blue-6 : #2196f3 !default$blue-7 : #1e88e5 !default$blue-8 : #1976d2 !default$blue-9 : #1565c0 !default$blue-10 : #0d47a1 !default$blue-11 : #82b1ff !default$blue-12 : #448aff !default$blue-13 : #2979ff !default$blue-14 : #2962ff !default$light-blue : #03a9f4 !default$light-blue-1 : #e1f5fe !default$light-blue-2 : #b3e5fc !default$light-blue-3 : #81d4fa !default$light-blue-4 : #4fc3f7 !default$light-blue-5 : #29b6f6 !default$light-blue-6 : #03a9f4 !default$light-blue-7 : #039be5 !default$light-blue-8 : #0288d1 !default$light-blue-9 : #0277bd !default$light-blue-10 : #01579b !default$light-blue-11 : #80d8ff !default$light-blue-12 : #40c4ff !default$light-blue-13 : #00b0ff !default$light-blue-14 : #0091ea !default$cyan : #00bcd4 !default$cyan-1 : #e0f7fa !default$cyan-2 : #b2ebf2 !default$cyan-3 : #80deea !default$cyan-4 : #4dd0e1 !default$cyan-5 : #26c6da !default$cyan-6 : #00bcd4 !default$cyan-7 : #00acc1 !default$cyan-8 : #0097a7 !default$cyan-9 : #00838f !default$cyan-10 : #006064 !default$cyan-11 : #84ffff !default$cyan-12 : #18ffff !default$cyan-13 : #00e5ff !default$cyan-14 : #00b8d4 !default$teal : #009688 !default$teal-1 : #e0f2f1 !default$teal-2 : #b2dfdb !default$teal-3 : #80cbc4 !default$teal-4 : #4db6ac !default$teal-5 : #26a69a !default

4.3.3 Sass Variables

- 46/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 47: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

$teal-6 : #009688 !default$teal-7 : #00897b !default$teal-8 : #00796b !default$teal-9 : #00695c !default$teal-10 : #004d40 !default$teal-11 : #a7ffeb !default$teal-12 : #64ffda !default$teal-13 : #1de9b6 !default$teal-14 : #00bfa5 !default$green : #4caf50 !default$green-1 : #e8f5e9 !default$green-2 : #c8e6c9 !default$green-3 : #a5d6a7 !default$green-4 : #81c784 !default$green-5 : #66bb6a !default$green-6 : #4caf50 !default$green-7 : #43a047 !default$green-8 : #388e3c !default$green-9 : #2e7d32 !default$green-10 : #1b5e20 !default$green-11 : #b9f6ca !default$green-12 : #69f0ae !default$green-13 : #00e676 !default$green-14 : #00c853 !default$light-green : #8bc34a !default$light-green-1 : #f1f8e9 !default$light-green-2 : #dcedc8 !default$light-green-3 : #c5e1a5 !default$light-green-4 : #aed581 !default$light-green-5 : #9ccc65 !default$light-green-6 : #8bc34a !default$light-green-7 : #7cb342 !default$light-green-8 : #689f38 !default$light-green-9 : #558b2f !default$light-green-10 : #33691e !default$light-green-11 : #ccff90 !default$light-green-12 : #b2ff59 !default$light-green-13 : #76ff03 !default$light-green-14 : #64dd17 !default$lime : #cddc39 !default$lime-1 : #f9fbe7 !default$lime-2 : #f0f4c3 !default$lime-3 : #e6ee9c !default$lime-4 : #dce775 !default$lime-5 : #d4e157 !default$lime-6 : #cddc39 !default$lime-7 : #c0ca33 !default$lime-8 : #afb42b !default$lime-9 : #9e9d24 !default$lime-10 : #827717 !default$lime-11 : #f4ff81 !default$lime-12 : #eeff41 !default$lime-13 : #c6ff00 !default$lime-14 : #aeea00 !default$yellow : #ffeb3b !default$yellow-1 : #fffde7 !default$yellow-2 : #fff9c4 !default$yellow-3 : #fff59d !default$yellow-4 : #fff176 !default$yellow-5 : #ffee58 !default$yellow-6 : #ffeb3b !default$yellow-7 : #fdd835 !default$yellow-8 : #fbc02d !default$yellow-9 : #f9a825 !default$yellow-10 : #f57f17 !default$yellow-11 : #ffff8d !default$yellow-12 : #ffff00 !default$yellow-13 : #ffea00 !default$yellow-14 : #ffd600 !default$amber : #ffc107 !default$amber-1 : #fff8e1 !default$amber-2 : #ffecb3 !default$amber-3 : #ffe082 !default$amber-4 : #ffd54f !default$amber-5 : #ffca28 !default$amber-6 : #ffc107 !default$amber-7 : #ffb300 !default$amber-8 : #ffa000 !default$amber-9 : #ff8f00 !default$amber-10 : #ff6f00 !default$amber-11 : #ffe57f !default$amber-12 : #ffd740 !default$amber-13 : #ffc400 !default$amber-14 : #ffab00 !default$orange : #ff9800 !default$orange-1 : #fff3e0 !default$orange-2 : #ffe0b2 !default$orange-3 : #ffcc80 !default$orange-4 : #ffb74d !default$orange-5 : #ffa726 !default$orange-6 : #ff9800 !default$orange-7 : #fb8c00 !default$orange-8 : #f57c00 !default$orange-9 : #ef6c00 !default

4.3.3 Sass Variables

- 47/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 48: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

$orange-10 : #e65100 !default$orange-11 : #ffd180 !default$orange-12 : #ffab40 !default$orange-13 : #ff9100 !default$orange-14 : #ff6d00 !default$deep-orange : #ff5722 !default$deep-orange-1 : #fbe9e7 !default$deep-orange-2 : #ffccbc !default$deep-orange-3 : #ffab91 !default$deep-orange-4 : #ff8a65 !default$deep-orange-5 : #ff7043 !default$deep-orange-6 : #ff5722 !default$deep-orange-7 : #f4511e !default$deep-orange-8 : #e64a19 !default$deep-orange-9 : #d84315 !default$deep-orange-10 : #bf360c !default$deep-orange-11 : #ff9e80 !default$deep-orange-12 : #ff6e40 !default$deep-orange-13 : #ff3d00 !default$deep-orange-14 : #dd2c00 !default$brown : #795548 !default$brown-1 : #efebe9 !default$brown-2 : #d7ccc8 !default$brown-3 : #bcaaa4 !default$brown-4 : #a1887f !default$brown-5 : #8d6e63 !default$brown-6 : #795548 !default$brown-7 : #6d4c41 !default$brown-8 : #5d4037 !default$brown-9 : #4e342e !default$brown-10 : #3e2723 !default$brown-11 : #d7ccc8 !default$brown-12 : #bcaaa4 !default$brown-13 : #8d6e63 !default$brown-14 : #5d4037 !default$grey : #9e9e9e !default$grey-1 : #fafafa !default$grey-2 : #f5f5f5 !default$grey-3 : #eeeeee !default$grey-4 : #e0e0e0 !default$grey-5 : #bdbdbd !default$grey-6 : #9e9e9e !default$grey-7 : #757575 !default$grey-8 : #616161 !default$grey-9 : #424242 !default$grey-10 : #212121 !default$grey-11 : #f5f5f5 !default$grey-12 : #eeeeee !default$grey-13 : #bdbdbd !default$grey-14 : #616161 !default$blue-grey : #607d8b !default$blue-grey-1 : #eceff1 !default$blue-grey-2 : #cfd8dc !default$blue-grey-3 : #b0bec5 !default$blue-grey-4 : #90a4ae !default$blue-grey-5 : #78909c !default$blue-grey-6 : #607d8b !default$blue-grey-7 : #546e7a !default$blue-grey-8 : #455a64 !default$blue-grey-9 : #37474f !default$blue-grey-10 : #263238 !default$blue-grey-11 : #cfd8dc !default$blue-grey-12 : #b0bec5 !default$blue-grey-13 : #78909c !default$blue-grey-14 : #455a64 !default

$ios-statusbar-height : 20px !default

$z-fab : 990 !default$z-side : 1000 !default$z-marginals : 2000 !default$z-fixed-drawer : 3000 !default$z-fullscreen : 6000 !default$z-menu : 6000 !default$z-top : 7000 !default$z-tooltip : 9000 !default$z-notify : 9500 !default$z-max : 9998 !default

$shadow-color : #000 !default$shadow-transition : box-shadow .28s cubic-bezier(.4, 0, .2, 1) !default$inset-shadow : 0 7px 9px -7px rgba($shadow-color, .7) inset !default

$elevation-umbra : rgba($shadow-color, .2) !default$elevation-penumbra : rgba($shadow-color, .14) !default$elevation-ambient : rgba($shadow-color, .12) !default

$shadow-0 : 0 0 0 $elevation-umbra, 0 0 0 $elevation-penumbra, 0 0 0 $elevation-ambient !default$shadow-1 : 0 1px 3px $elevation-umbra, 0 1px 1px $elevation-penumbra, 0 2px 1px -1px $elevation-ambient !default$shadow-2 : 0 1px 5px $elevation-umbra, 0 2px 2px $elevation-penumbra, 0 3px 1px -2px $elevation-ambient !default$shadow-3 : 0 1px 8px $elevation-umbra, 0 3px 4px $elevation-penumbra, 0 3px 3px -2px $elevation-ambient !default$shadow-4 : 0 2px 4px -1px $elevation-umbra, 0 4px 5px $elevation-penumbra, 0 1px 10px $elevation-ambient !default$shadow-5 : 0 3px 5px -1px $elevation-umbra, 0 5px 8px $elevation-penumbra, 0 1px 14px $elevation-ambient !default$shadow-6 : 0 3px 5px -1px $elevation-umbra, 0 6px 10px $elevation-penumbra, 0 1px 18px $elevation-ambient !default

4.3.3 Sass Variables

- 48/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 49: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

$shadow-7 : 0 4px 5px -2px $elevation-umbra, 0 7px 10px 1px $elevation-penumbra, 0 2px 16px 1px $elevation-ambient !default$shadow-8 : 0 5px 5px -3px $elevation-umbra, 0 8px 10px 1px $elevation-penumbra, 0 3px 14px 2px $elevation-ambient !default$shadow-9 : 0 5px 6px -3px $elevation-umbra, 0 9px 12px 1px $elevation-penumbra, 0 3px 16px 2px $elevation-ambient !default$shadow-10 : 0 6px 6px -3px $elevation-umbra, 0 10px 14px 1px $elevation-penumbra, 0 4px 18px 3px $elevation-ambient !default$shadow-11 : 0 6px 7px -4px $elevation-umbra, 0 11px 15px 1px $elevation-penumbra, 0 4px 20px 3px $elevation-ambient !default$shadow-12 : 0 7px 8px -4px $elevation-umbra, 0 12px 17px 2px $elevation-penumbra, 0 5px 22px 4px $elevation-ambient !default$shadow-13 : 0 7px 8px -4px $elevation-umbra, 0 13px 19px 2px $elevation-penumbra, 0 5px 24px 4px $elevation-ambient !default$shadow-14 : 0 7px 9px -4px $elevation-umbra, 0 14px 21px 2px $elevation-penumbra, 0 5px 26px 4px $elevation-ambient !default$shadow-15 : 0 8px 9px -5px $elevation-umbra, 0 15px 22px 2px $elevation-penumbra, 0 6px 28px 5px $elevation-ambient !default$shadow-16 : 0 8px 10px -5px $elevation-umbra, 0 16px 24px 2px $elevation-penumbra, 0 6px 30px 5px $elevation-ambient !default$shadow-17 : 0 8px 11px -5px $elevation-umbra, 0 17px 26px 2px $elevation-penumbra, 0 6px 32px 5px $elevation-ambient !default$shadow-18 : 0 9px 11px -5px $elevation-umbra, 0 18px 28px 2px $elevation-penumbra, 0 7px 34px 6px $elevation-ambient !default$shadow-19 : 0 9px 12px -6px $elevation-umbra, 0 19px 29px 2px $elevation-penumbra, 0 7px 36px 6px $elevation-ambient !default$shadow-20 : 0 10px 13px -6px $elevation-umbra, 0 20px 31px 3px $elevation-penumbra, 0 8px 38px 7px $elevation-ambient !default$shadow-21 : 0 10px 13px -6px $elevation-umbra, 0 21px 33px 3px $elevation-penumbra, 0 8px 40px 7px $elevation-ambient !default$shadow-22 : 0 10px 14px -6px $elevation-umbra, 0 22px 35px 3px $elevation-penumbra, 0 8px 42px 7px $elevation-ambient !default$shadow-23 : 0 11px 14px -7px $elevation-umbra, 0 23px 36px 3px $elevation-penumbra, 0 9px 44px 8px $elevation-ambient !default$shadow-24 : 0 11px 15px -7px $elevation-umbra, 0 24px 38px 3px $elevation-penumbra, 0 9px 46px 8px $elevation-ambient !default

// sorry for long line; we need .sass and it doesn't support multi-line list$shadows: ($shadow-1, $shadow-2, $shadow-3, $shadow-4, $shadow-5, $shadow-6, $shadow-7, $shadow-8, $shadow-9, $shadow-10, $shadow-11, $shadow-12, $shadow-13, $shadow-14, $shadow-15, $shadow-16, $shadow-17, $shadow-18, $shadow-19, $shadow-20, $shadow-21, $shadow-22, $shadow-23, $shadow-24)

$shadow-up-0 : 0 0 0 $elevation-umbra, 0 0 0 $elevation-penumbra, 0 0 0 $elevation-ambient !default$shadow-up-1 : 0 -1px 3px $elevation-umbra, 0 -1px 1px $elevation-penumbra, 0 -2px 1px -1px $elevation-ambient !default$shadow-up-2 : 0 -1px 5px $elevation-umbra, 0 -2px 2px $elevation-penumbra, 0 -3px 1px -2px $elevation-ambient !default$shadow-up-3 : 0 -1px 8px $elevation-umbra, 0 -3px 4px $elevation-penumbra, 0 -3px 3px -2px $elevation-ambient !default$shadow-up-4 : 0 -2px 4px -1px $elevation-umbra, 0 -4px 5px $elevation-penumbra, 0 -1px 10px $elevation-ambient !default$shadow-up-5 : 0 -3px 5px -1px $elevation-umbra, 0 -5px 8px $elevation-penumbra, 0 -1px 14px $elevation-ambient !default$shadow-up-6 : 0 -3px 5px -1px $elevation-umbra, 0 -6px 10px $elevation-penumbra, 0 -1px 18px $elevation-ambient !default$shadow-up-7 : 0 -4px 5px -2px $elevation-umbra, 0 -7px 10px 1px $elevation-penumbra, 0 -2px 16px 1px $elevation-ambient !default$shadow-up-8 : 0 -5px 5px -3px $elevation-umbra, 0 -8px 10px 1px $elevation-penumbra, 0 -3px 14px 2px $elevation-ambient !default$shadow-up-9 : 0 -5px 6px -3px $elevation-umbra, 0 -9px 12px 1px $elevation-penumbra, 0 -3px 16px 2px $elevation-ambient !default$shadow-up-10 : 0 -6px 6px -3px $elevation-umbra, 0 -10px 14px 1px $elevation-penumbra, 0 -4px 18px 3px $elevation-ambient !default$shadow-up-11 : 0 -6px 7px -4px $elevation-umbra, 0 -11px 15px 1px $elevation-penumbra, 0 -4px 20px 3px $elevation-ambient !default$shadow-up-12 : 0 -7px 8px -4px $elevation-umbra, 0 -12px 17px 2px $elevation-penumbra, 0 -5px 22px 4px $elevation-ambient !default$shadow-up-13 : 0 -7px 8px -4px $elevation-umbra, 0 -13px 19px 2px $elevation-penumbra, 0 -5px 24px 4px $elevation-ambient !default$shadow-up-14 : 0 -7px 9px -4px $elevation-umbra, 0 -14px 21px 2px $elevation-penumbra, 0 -5px 26px 4px $elevation-ambient !default$shadow-up-15 : 0 -8px 9px -5px $elevation-umbra, 0 -15px 22px 2px $elevation-penumbra, 0 -6px 28px 5px $elevation-ambient !default$shadow-up-16 : 0 -8px 10px -5px $elevation-umbra, 0 -16px 24px 2px $elevation-penumbra, 0 -6px 30px 5px $elevation-ambient !default$shadow-up-17 : 0 -8px 11px -5px $elevation-umbra, 0 -17px 26px 2px $elevation-penumbra, 0 -6px 32px 5px $elevation-ambient !default$shadow-up-18 : 0 -9px 11px -5px $elevation-umbra, 0 -18px 28px 2px $elevation-penumbra, 0 -7px 34px 6px $elevation-ambient !default$shadow-up-19 : 0 -9px 12px -6px $elevation-umbra, 0 -19px 29px 2px $elevation-penumbra, 0 -7px 36px 6px $elevation-ambient !default$shadow-up-20 : 0 -10px 13px -6px $elevation-umbra, 0 -20px 31px 3px $elevation-penumbra, 0 -8px 38px 7px $elevation-ambient !default$shadow-up-21 : 0 -10px 13px -6px $elevation-umbra, 0 -21px 33px 3px $elevation-penumbra, 0 -8px 40px 7px $elevation-ambient !default$shadow-up-22 : 0 -10px 14px -6px $elevation-umbra, 0 -22px 35px 3px $elevation-penumbra, 0 -8px 42px 7px $elevation-ambient !default$shadow-up-23 : 0 -11px 14px -7px $elevation-umbra, 0 -23px 36px 3px $elevation-penumbra, 0 -9px 44px 8px $elevation-ambient !default$shadow-up-24 : 0 -11px 15px -7px $elevation-umbra, 0 -24px 38px 3px $elevation-penumbra, 0 -9px 46px 8px $elevation-ambient !default

// sorry for long line; we need .sass and it doesn't support multi-line list$shadows-up: ($shadow-up-1, $shadow-up-2, $shadow-up-3, $shadow-up-4, $shadow-up-5, $shadow-up-6, $shadow-up-7, $shadow-up-8, $shadow-up-9, $shadow-up-10, $shadow-up-11, $shadow-up-12, $shadow-up-13, $shadow-up-14, $shadow-up-15, $shadow-up-16, $shadow-up-17, $shadow-up-18, $shadow-up-19, $shadow-up-20, $shadow-up-21, $shadow-up-22, $shadow-up-23, $shadow-up-24)

$generic-border-radius : 4px !default$generic-hover-transition : .3s cubic-bezier(.25, .8, .5, 1) !default$typography-font-family : 'Roboto', '-apple-system', 'Helvetica Neue', Helvetica, Arial, sans-serif !default$min-line-height : 1.12 !default

$button-border-radius : 3px !default$button-padding : 4px 16px !default$button-dense-padding : .285em !default$button-transition : $generic-hover-transition !default$button-font-size : 14px !default$button-line-height : 1.715em !default$button-font-weight : 500 !default$button-shadow : $shadow-2 !default$button-shadow-active : $shadow-5 !default$button-rounded-border-radius : 28px !default$button-push-border-radius : 7px !default

$chat-message-received-color : #000 !default$chat-message-received-bg : $green-4 !default$chat-message-sent-color : #000 !default$chat-message-sent-bg : $grey-4 !default$chat-message-avatar-size : 48px !default$chat-message-border-radius : $generic-border-radius !default$chat-message-distance : 8px !default$chat-message-text-padding : 8px !default

$item-base-color : $grey-5 !default

$editor-border-color : $separator-color !default$editor-border-dark-color : $separator-dark-color !default$editor-content-padding : 10px !default$editor-content-min-height : 10em !default$editor-toolbar-padding : 4px !default$editor-hr-color : $editor-border-color !default$editor-hr-dark-color : $editor-border-dark-color !default$editor-button-gutter : 4px !default

$table-transition : $generic-hover-transition !default$table-border-radius : $generic-border-radius !default$table-box-shadow : $shadow-2 !default

4.3.3 Sass Variables

- 49/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 50: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

$table-border-color : $separator-color !default$table-hover-background : rgba(0, 0, 0, .03) !default$table-selected-background : rgba(0, 0, 0, .06) !default

$table-dark-border-color : $separator-dark-color !default$table-dark-hover-background : rgba(255, 255, 255, .07) !default$table-dark-selected-background : rgba(255, 255, 255, .1) !default

$toolbar-min-height : 50px !default$toolbar-padding : 0 12px !default$toolbar-inset-size : 58px !default$toolbar-title-font-size : 21px !default$toolbar-title-font-weight : normal !default$toolbar-title-letter-spacing : .01em !default$toolbar-title-padding : 0 12px !default

$layout-border : 1px solid $separator-color !default$layout-shadow : 0 0 10px 2px rgba(0,0,0,0.2), 0 0px 10px rgba(0,0,0,0.24) !default

$menu-background : #fff !default$menu-box-shadow : $shadow-2 !default$menu-max-width : 95vw !default

$rating-grade-color : $yellow !default$rating-shadow : 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !default

$tooltip-color : #fafafa !default$tooltip-background : $grey-7 !default$tooltip-padding : 6px 10px !default$tooltip-border-radius : $generic-border-radius !default$tooltip-fontsize : 10px !default$tooltip-mobile-padding : 8px 16px !default$tooltip-mobile-fontsize : 14px !default

$option-focus-transition : .22s cubic-bezier(0,0,.2,1) !default

$input-font-size : 14px !default$input-text-color : rgba(0,0,0,.87) !default$input-label-color : rgba(0,0,0,.6) !default$input-autofill-color : inherit !default

$img-width : 100% !default$img-background-repeat : no-repeat !default$img-loading-font-size : 50px !default$img-content-position : absolute !default$img-content-padding : 16px !default$img-content-color : #fff !default$img-content-background : rgba(0, 0, 0, .47) !default

4.3.3 Sass Variables

- 50/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 51: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.4 InternationalizationTo make the application multi lingual it uses internationalization through the vue-i18n module.

For each language specific files exists containing the translated phrases based on structured keys. Quasar components already

include translations which will be used automatically and have not to be translated again.

4.4.1 File Structure

Under src/i18n each language is added with it's translations. The values are split into logical groups which are located in individual

files for better readability. They all are put together in the index.js files:

All the JavaScript files listed above will evaluate to a big translation object.

But it is also possible to put some local translations, which are only for the specific page or component, within the '.vue' file. But this

should not be done because it will later not be updated and forgotten for new languages.

4.4.2 Use of translations

The general $t (translate) function registered in Vue is used therefore. To specify the translation element we use the above defined

structure with it's readable short path names to access in templates:

mykey1 in HTML body

mykey2 in attribute

mykey3 programmatically

Variables

Variables may be used in different forms. As named parameters by using a data object:

Or with an data array:

src/└── i18n/ ├── index,js # loading all languages ├── en-US/ │ ├── index.js # combining english translations │ ├── layout.js # layout translations │ └── core.js # core group translation └── de/ ├── index.js # combining german translations ├── layout.js # layout translations └── core.js # core group translation

<template> <q-page> <q-btn :label="$t('mykey2')"> {{ $t('mykey1') }} <span v-html="content"></span> </q-page></template>

<script>export default { data() { return { content: this.$t('mykey3') } }}</script>

// Messageconst messages = { en: { hello: "{msg} world" } };// Template<p>{{ $t('hello', { msg: 'hello' }) }}</p>// Output<p>hello world</p>

4.4 Internationalization

- 51/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 52: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

HTML Content

This should only be used in limited form, because it can destroy layouts:

Pluralization

You can translate with pluralization. You must define the locale that have a pipe | separator, and define plurals in pipe separator.

Your template will need to use $tc() instead of $t() .

You don't need to explicitly give the number for pluralization. The number can be accessed within locale messages via pre-defined

named arguments {count} and/or {n} .

Date

// Messageconst messages = { en: { message: { hello: '{0} world' } } }// Template<p>{{ $t('message.hello', ['hello']) }}</p>// Output<p>hello world</p>

// Messageconst messages = { en: { message: { hello: 'hello <br> world' } } }// Template<p v-html="$t('message.hello')"></p>// Output<p>hello <br> world</p>

// Messageconst messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' }}// Template<p>{{ $tc('car', 1) }}</p><p>{{ $tc('car', 2) }}</p><p>{{ $tc('apple', 0) }}</p><p>{{ $tc('apple', 1) }}</p><p>{{ $tc('apple', 10, { count: 10 }) }}</p>// Output<p>car</p><p>cars</p><p>no apples</p><p>one apple</p><p>10 apples</p>

// Messageconst messages = { en: { apple: 'no apples | one apple | {count} apples', banana: 'no bananas | {n} banana | {n} bananas' }}// Template<p>{{ $tc('apple', 10, { count: 10 }) }}</p><p>{{ $tc('apple', 10) }}</p><p>{{ $tc('banana', 1, { n: 1 }) }}</p><p>{{ $tc('banana', 1) }}</p><p>{{ $tc('banana', 100, { n: 'too many' }) }}</p>// Output<p>10 apples</p><p>10 apples</p><p>1 banana</p><p>1 banana</p><p>too many bananas</p>

<!-- Template --><p>{{ $d(new Date(), 'short') }}</p><p>{{ $d(new Date(), 'long', 'ja-JP') }}</p><!-- Output --><p>Apr 19, 2017</p><p>2017年4月19日(水) 午前2:19</p>

4.4.2 Use of translations

- 52/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 53: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Number

Linked locale messages

If there's a translation key that will always have the same concrete text as another one you can just link to it. To link to another

translation key, all you have to do is to prefix its contents with an @: sign followed by the full name of the translation key including

the namespace you want to link to.

4.4.3 Management

With the help of the VSCode Plugin i18n-ally it is easy to work with translations. It lets you:

show translation values in code

show all translations as hover

calculate translation progress

show missing or unused translations

extract text into i18n files

An other possibility is to use BabelEdit which is a downloadable standalone application which can load the json files and translate

them within it's dialogs. This allows to give the translation files to external translators. Afterwards the translated files has to be

imported into the code.

The base configuration is done in the boot/i18n.js . Here you may also change the base language used, if the translated is not

available.

Add new language

To add a new language copy an existing language folder within src/i18n/... and change it's text values.

You also have to add it within src/i18n/index.ts to be exported like en-us or the others.

<!-- Template --><p>{{ $n(100, 'currency') }}</p><p>{{ $n(100, 'currency', 'ja-JP') }}</p><!-- Output --><p>$100.00</p><p>¥100</p>

const messages = { en: { message: { the_world: "the world", dio: "DIO:", linked: "@:message.dio @:message.the_world !!!!" } }};

4.4.3 Management

- 53/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 54: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.5 Special ComponentsThe following special Alinex components are contained within the core.

4.5.1 Global

Notify

To give some response messages to the user the quasar notify object is used. This can be done within a vue file:

Or outside of a vue file (as negative message):

The plugin is predefined to display positive messages on the bottom of the page, negative ones directly in the center of the screen. An

additional logging to console should always be made. For debugging this makes it possible to see what was done last.

4.5.2 Dialogs

Dialog (ax-dialog)

This is the base setup to make all the dialogs the same. It contains the card layout with a titlebar with title and icon

properties. The content can be set with the default slot and the model property is used for the visibility state.

About Dialog (ax-about)

An implementation of a dialog with the about section containing information from package.json and the policy from the

translation properties under group about.* .

Login Dialog (ax-login)

An implementation of a dialog with the login form for local authentication complete with the login procedure. This maybe

extended later to also support missing password or other authentication types.

// log the error to the browser console using log(), warn() or error()console.log(message)// show the visible elementthis.$q.notify({ type: 'positive', message})

// load the pluginimport { Notify } from 'quasar'

// log the error to the browser console using log(), warn() or error()console.warn(message)// show the visible elementNotify.create({ type: 'negative', // or positive message: this.$t('layout.failed'), caption: message // caption will add a second line for a sub message})

4.5 Special Components

- 54/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 55: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.5.3 Module Organization

The following components are used to include modules into the layout in sidebar and as dashboard icon.

Sidebar Extend (ax-sidebar-extend)

Placeholder used to be overwritten in parent project to add more menu entries using the following link component.

Sidebar Link (ax-sidebar-link)

Entry in the sidebar configured using properties: title , caption , icon , link , service .

Index Extend (ax-index-extend)

Placeholder used to be overwritten in parent project to add modules to dashboard using the following icon component. This

works identical to the sidebar extension.

Index Icon (ax-index-icon)

Entry in the sidebar configured using properties: title , caption , icon , link , service .

Example

<template> <ax-dialog :model.sync="localModel" :title="xxxxxxx" icon="info"> <q-card-section class="q-pt-none"> <p>Here comes the content...</p> </q-card-actions> <!-- more card sections are possible --> </ax-dialog></template>

<script>import axDialog from 'components/dialog'

export default { name: 'MyDialog',

props: { model: { type: Boolean, required: true } },

components: { axDialog },

data () { // locally used data },

computed: { // used to keep model in sync localModel: { get () { return this.model }, set (localModel) { this.$emit('update:model', localModel) } } }}</script>

4.5.3 Module Organization

- 55/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 56: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Example

<template> <div class="icons row"> <ax-index-icon v-for="link in links" :key="link.title" v-bind="link" /> </div></template>

<script>import axIndexIcon from 'components/index-icon'

const list = vue => { return [ { title: vue.$t('mymodule.fn1.title'), caption: vue.$t('mymodule.fn1.subtitle'), icon: 'mdi-movie-search', link: '/mymodule/fn1' }, { title: vue.$t('mymodule.fn2.title'), caption: vue.$t('mymodule.fn2.subtitle'), icon: 'mdi-table-large', link: '/mymodule/fn2' } ]}

export default { name: 'indexExtend',

components: { axIndexIcon },

computed: { links () { return list(this) } }}</script>

4.5.3 Module Organization

- 56/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 57: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.6 Object Create, Read, Update and DeleteThis shows on the example of the user object how to administrate it in the GUI.

To do this you need:

routing

store

pages

translations

See below how to do it.

4.6.1 Routing

4.6.2 Store

Feathers-vuex focuses on abstracting away the Vuex syntax in favor of using Model classes

4.6.3 Pages

4.6.4 Security

The authorization works based on abilities. They will be checked in multiple places if the abilities which a user has through his roles

matches the abilities needed to access:

Within the router the meta.ability setting contains the right needed to access this route ( [action, subject, field] ).

In the page itself using the v-if="$can('read', service) attribute

This will be checked through the boot/casl.js plugin in the router loading of abilities will be done with the login.

4.6.5 Translations

1.

2.

4.6 Object Create, Read, Update and Delete

- 57/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 58: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

4.7 Store

4.7 Store

- 58/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 59: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5. Development

5.1 DevelopmentThis section will further describe how to create your specific application with this framework.

5. Development

- 59/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 60: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.2 Parent ProjectThe following pages will explain how your specific own system is build with the Alinex GUI as a base.

5.2 Parent Project

- 60/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 61: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.2.1 Setup

To make your own project you need the following files:

5.2.1 Setup

- 61/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 62: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

package.json

.eslint.rc

{ "name": "my-gui", "title": "My GUI", "version": "0.1.0", "build": "SNAPSHOT", "description": "Human interface to my server with it's applications.", "productName": "My-GUI","cordovaId": "de.alinex.my-gui",

"capacitorId": "", "responsible": "Alexander Schilling", "foreignData": "", "private": true, "author": "Alexander Schilling", "scripts": { "dev": "bin/gui-dev", "lint": "eslint --ext .js,.vue src --fix --ignore-path .gitignore ./", "test": "echo \"No test specified\" && exit 0", "play": "quasar dev -m cordova -T android", "build": "rm -r dist/* && npm run build-spa && npm run build-pwa && npm run build-linux && npm run build-darwin && npm run build-win32 && npm run build-android", "build-spa": "quasar build && tar -C dist -czf dist/Alinex-GUI_online-spa.tgz spa", "build-pwa": "quasar build -m pwa && tar -C dist -czf dist/Alinex-GUI_online-pwa.tgz pwa", "build-ssr": "quasar build -m ssr", "build-linux": "quasar build -m electron -T linux && tar -C dist/electron -czf dist/Alinex-GUI_linux-x64.tgz Alinex-GUI-linux-x64 >/dev/null", "build-darwin": "quasar build -m electron -T darwin && tar -C dist/electron -czf dist/Alinex-GUI_darwin-x64.tgz Alinex-GUI-darwin-x64 >/dev/null", "build-win32": "quasar build -m electron -T win32 && cd dist/electron && zip -r ../Alinex-GUI_win32-x64.zip Alinex-GUI-win32-x64 >/dev/null && cd ../..", "build-android": "ANDROID_HOME=~/Android/Sdk quasar build -m capacitor -T android && cp dist/capacitor/android/apk/release/app-release-unsigned.apk dist/Alinex-GUI_android.apk", "build-ios": "test \"$OSTYPE\" = \"darwin\" && ( quasar build -m capacitor -T ios ) || echo 'ios build is only possible on MacOS with Xcode installed.'", "pdf": "./mkdocs-pdf.sh", "preversion": "npm test && npm run pdf && git add -A && git commit -m \"Update PDF documentation\"", "postpublish": "git push origin --all && git push origin --tags" }, "dependencies": { "@alinex/gui": "^0.1.0", "@feathersjs/authentication-client": "^4.5.2", "@feathersjs/feathers": "^4.5.2", "@feathersjs/rest-client": "^4.5.2", "@feathersjs/socketio-client": "^4.5.2", "@quasar/extras": "^1.0.0", "@vue/composition-api": "^0.5.0", "axios": "^0.18.1", "feathers-hooks-common": "^5.0.2", "feathers-vuex": "^3.9.1", "flag-icon-css": "^3.4.6", "quasar": "^1.0.0", "socket.io-client": "^2.3.0", "vue-i18n": "^8.0.0", "vuelidate": "^0.7.5" }, "devDependencies": { "@capacitor/ios": "^1.5.2", "@quasar/app": "^1.0.0", "@quasar/quasar-app-extension-icon-genie": "^1.1.3", "babel-eslint": "^10.0.1", "devtron": "^1.4.0", "electron": "^8.2.0", "electron-debug": "^3.0.1", "electron-devtools-installer": "^2.2.4", "electron-packager": "^14.2.1", "eslint": "^6.8.0", "eslint-config-airbnb-base": "^14.0.0", "eslint-config-standard": "^14.1.1", "eslint-loader": "^3.0.3", "eslint-plugin-import": "^2.20.1", "eslint-plugin-node": "^11.0.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-vue": "^6.1.2" }, "engines": { "node": ">= 10.18.1", "npm": ">= 6.13.4", "yarn": ">= 1.21.1" }, "browserslist": [ "last 10 Chrome versions", "last 10 Firefox versions", "last 4 Edge versions", "last 7 Safari versions", "last 8 Android versions", "last 8 ChromeAndroid versions", "last 8 FirefoxAndroid versions", "last 10 iOS versions", "last 5 Opera versions" ], "resolutions": { "@babel/parser": "7.7.5" }}

module.exports = { root: true,

parserOptions: { parser: "babel-eslint", sourceType: "module" },

env: {

5.2.1 Setup

- 62/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 63: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

Now you can setup the application:

Now you can use npm run dev or npm run build .

mkdir bincd binln -s ../node_modules/@alinex/gui/bin/gui-* .cd ..npm install

5.2.1 Setup

- 63/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 64: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.3 Customization

5.3.1 Logo

This can be changed by overwriting the default with an image under the following position: - src/assets/logo.png -

src/assets/logo-black.png

The -black variant is used in dark mode. A transparent picture therefore may be identical.

5.3.2 Colors

The file src/css/quasar.variables.scss contains the colors used for the default names. To change the theme to your colors simple

copy this file from the Alinex GUI and change the color codes. You may use the Quasar Theme Builder to select and see the most

colors together.

5.3 Customization

- 64/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 65: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.4 Extending Modules

5.4.1 Routing

Within the application the routing will decide which page to show. The core routes can be extended using:

5.4.2 Pages

The title will be set by the layout but can also be set here to something other:

src/router/extend.jsexport default [ { path: '/myapp', component: () => import('layouts/Default.vue'), children: [ { path: '', component: () => import('pages/myapp/index') }, { path: 'info', component: () => import('pages/myapp/info'), meta: { module: 'core.info' } } ] }]

meta () { return { title: this.$t(`${this.$route.meta.module}.title`) }}

5.4 Extending Modules

- 65/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 66: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.4.3 Menu

To add new modules they need to be appended to the sidebar and to the start page. Therefore the following changes have to be made.

We add two menus, both using separate files to keep it more clean.

src/assets/data/my-menu.js

src/components/sidebar-extend.vue

src/components/sidebar-menu1.vue

export default vue => { return [ { title: vue.$t('core.info.title'), caption: vue.$t('core.info.subtitle'), icon: 'info', link: '/info' }, { title: vue.$t('core.users.title'), caption: vue.$t('core.users.subtitle'), icon: 'mdi-account', link: '/users', service: 'users' }, { title: vue.$t('core.roles.title'), caption: vue.$t('core.roles.subtitle'), icon: 'verified_user', link: '/roles', service: 'roles' } ]}

<template> <ax-sidebar-menu-one /> <ax-sidebar-menu-two /></template>

<script>import axSidebarMenuOne from 'components/sidebar-mymenu-one'import axSidebarMenuTwo from 'components/sidebar-mymenu-two'

export default { name: 'sidebarExtend',

components: { axSidebarMenuOne, axSidebarMenuTwo }}</script>

<template> <q-expansion-item switch-toggle-side expand-separator :label="$t('mymenu.title')"> <ax-sidebar-link v-for="link in links" :key="link.title" v-bind="link" /> </q-expansion-item></template>

<script>import axSidebarLink from 'components/sidebar-link'import myMenu from 'assets/data/my-menu'

export default { name: 'sidebarMenu1',

components: { axSidebarLink },

computed: { links () { return myMenu(this) } }}</script>

5.4.3 Menu

- 66/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>

Page 67: Alinex GUI · check abilities in router module layout with sidebar info service privacy like about dialog check abilities for menu and icon view 1.2.6 Version 0.1.0 (05.05.2020) initial

5.4.4 Dashboard

And at last the additional pages can also be added to the dashboard as icons using:

src/components/index-extend.vue<template> <div class="icons row"> <ax-index-icon v-for="link in links" :key="link.title" v-bind="link" /> </div></template>

<script>import axIndexIcon from 'components/index-icon'import myMenu from 'assets/data/my-menu'

export default { name: 'indexExtend',

components: { axIndexIcon },

computed: { links () { return myMenu(this) } }}</script>

5.4.4 Dashboard

- 67/67 - Copyright © 2020 - 2021 <a href="https://alinex.de">Alexander Schilling</a>