Selaa lähdekoodia

feat: api docs editor (wip) + deps update

NGPixel 5 vuotta sitten
vanhempi
sitoutus
5d7509acdf

+ 3 - 0
client/components/admin.vue

@@ -29,6 +29,9 @@
               v-list-item-action(style='min-width:auto;')
               v-list-item-action(style='min-width:auto;')
                 v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-5`')
                 v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-5`')
                   .caption.grey--text {{ info.pagesTotal }}
                   .caption.grey--text {{ info.pagesTotal }}
+            v-list-item(to='/tags', v-if='hasPermission([`manage:system`])', disabled)
+              v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-tag-multiple
+              v-list-item-title {{ $t('admin:tags.title') }}
             v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
             v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
               v-list-item-avatar(size='24'): v-icon mdi-palette-outline
               v-list-item-avatar(size='24'): v-icon mdi-palette-outline
               v-list-item-title {{ $t('admin:theme.title') }}
               v-list-item-title {{ $t('admin:theme.title') }}

+ 43 - 41
client/components/admin/admin-theme.vue

@@ -52,21 +52,49 @@
                     :hint='$t(`admin:theme.darkModeHint`)'
                     :hint='$t(`admin:theme.darkModeHint`)'
                     )
                     )
 
 
-              //- v-card.mt-3.animated.fadeInUp.wait-p1s
-              //-   v-toolbar(color='primary', dark, dense, flat)
-              //-     v-toolbar-title.subtitle-1 {{$t(`admin:theme.options`)}}
-              //-     v-spacer
-              //-     v-chip(label, color='white', small).primary--text coming soon
-              //-   v-card-text
-              //-     v-select(
-              //-       :items='iconsets'
-              //-       outlined
-              //-       prepend-icon='mdi-border-vertical'
-              //-       v-model='config.iconset'
-              //-       label='Table of Contents Position'
-              //-       persistent-hint
-              //-       hint='Select whether the table of contents is shown on the left, right or not at all.'
-              //-       )
+              v-card.mt-3.animated.fadeInUp.wait-p1s
+                v-toolbar(color='primary', dark, dense, flat)
+                  v-toolbar-title.subtitle-1 {{$t(`admin:theme.options`)}}
+                  v-spacer
+                  v-chip(label, color='white', small).primary--text coming soon
+                v-card-text
+                  v-select(
+                    :items='[]'
+                    outlined
+                    prepend-icon='mdi-border-vertical'
+                    v-model='config.iconset'
+                    label='Table of Contents Position'
+                    persistent-hint
+                    hint='Select whether the table of contents is shown on the left, right or not at all.'
+                    disabled
+                    )
+
+            v-flex(lg6 xs12)
+              v-card.animated.fadeInUp.wait-p2s
+                v-toolbar(color='teal', dark, dense, flat)
+                  v-toolbar-title.subtitle-1 {{$t('admin:theme.downloadThemes')}}
+                  v-spacer
+                  v-chip(label, color='white', small).teal--text coming soon
+                v-data-table(
+                  :headers='headers',
+                  :items='themes',
+                  hide-default-footer,
+                  item-key='value',
+                  :items-per-page='1000'
+                )
+                  template(v-slot:item='thm')
+                    td
+                      strong {{thm.item.text}}
+                    td
+                      span {{ thm.item.author }}
+                    td.text-xs-center
+                      v-progress-circular(v-if='thm.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
+                      v-btn(v-else-if='thm.item.isInstalled && thm.item.installDate < thm.item.updatedAt', icon)
+                        v-icon.blue--text mdi-cached
+                      v-btn(v-else-if='thm.item.isInstalled', icon)
+                        v-icon.green--text mdi-check-bold
+                      v-btn(v-else, icon)
+                        v-icon.grey--text mdi-cloud-download
 
 
               v-card.mt-3.animated.fadeInUp.wait-p2s
               v-card.mt-3.animated.fadeInUp.wait-p2s
                 v-toolbar(color='primary', dark, dense, flat)
                 v-toolbar(color='primary', dark, dense, flat)
@@ -102,32 +130,6 @@
                     :hint='$t(`admin:theme.bodyHtmlInjectionHint`)'
                     :hint='$t(`admin:theme.bodyHtmlInjectionHint`)'
                     auto-grow
                     auto-grow
                     )
                     )
-            v-flex(lg6 xs12)
-              v-card.animated.fadeInUp.wait-p2s
-                v-toolbar(color='teal', dark, dense, flat)
-                  v-toolbar-title.subtitle-1 {{$t('admin:theme.downloadThemes')}}
-                  v-spacer
-                  v-chip(label, color='white', small).teal--text coming soon
-                v-data-table(
-                  :headers='headers',
-                  :items='themes',
-                  hide-default-footer,
-                  item-key='value',
-                  :items-per-page='1000'
-                )
-                  template(v-slot:item='thm')
-                    td
-                      strong {{thm.item.text}}
-                    td
-                      span {{ thm.item.author }}
-                    td.text-xs-center
-                      v-progress-circular(v-if='thm.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
-                      v-btn(v-else-if='thm.item.isInstalled && thm.item.installDate < thm.item.updatedAt', icon)
-                        v-icon.blue--text mdi-cached
-                      v-btn(v-else-if='thm.item.isInstalled', icon)
-                        v-icon.green--text mdi-check-bold
-                      v-btn(v-else, icon)
-                        v-icon.grey--text mdi-cloud-download
 </template>
 </template>
 
 
 <script>
 <script>

+ 1 - 0
client/components/admin/admin-utilities-importv1.vue

@@ -304,6 +304,7 @@ export default {
       gitUserName: '',
       gitUserName: '',
       gitRepoPath: './data/repo',
       gitRepoPath: './data/repo',
       progress: 0,
       progress: 0,
+      successGroups: 0,
       successUsers: 0,
       successUsers: 0,
       successPages: 0,
       successPages: 0,
       showFailedUsers: false,
       showFailedUsers: false,

+ 4 - 0
client/components/editor.vue

@@ -62,6 +62,7 @@ export default {
   i18nOptions: { namespaces: 'editor' },
   i18nOptions: { namespaces: 'editor' },
   components: {
   components: {
     AtomSpinner,
     AtomSpinner,
+    editorApi: () => import(/* webpackChunkName: "editor-api", webpackMode: "lazy" */ './editor/editor-api.vue'),
     editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
     editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
     editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
     editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
     editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
     editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
@@ -171,6 +172,9 @@ export default {
         return undefined
         return undefined
       }
       }
     }
     }
+
+    // this.$store.set('editor/mode', 'edit')
+    // this.currentEditor = `editorApi`
   },
   },
   methods: {
   methods: {
     openPropsModal(name) {
     openPropsModal(name) {

+ 9 - 0
client/components/editor/api/server-selector.vue

@@ -0,0 +1,9 @@
+<template lang="pug">
+
+</template>
+
+<script>
+export default {
+
+}
+</script>

+ 433 - 0
client/components/editor/editor-api.vue

@@ -0,0 +1,433 @@
+<template lang='pug'>
+  .editor-api
+    .editor-api-main
+      v-list.editor-api-sidebar.radius-0(nav, :class='$vuetify.theme.dark ? `grey darken-4` : `primary`', dark)
+        v-list-item-group(v-model='tab')
+          v-list-item.animated.fadeInLeft(value='info')
+            v-list-item-icon: v-icon mdi-book-information-variant
+            v-list-item-title Info
+          v-list-item.mt-3.animated.fadeInLeft.wait-p2s(value='servers')
+            v-list-item-icon: v-icon mdi-server
+            v-list-item-title Servers
+          v-list-item.mt-3.animated.fadeInLeft.wait-p3s(value='endpoints')
+            v-list-item-icon: v-icon mdi-code-braces
+            v-list-item-title Endpoints
+          v-list-item.mt-3.animated.fadeInLeft.wait-p4s(value='models')
+            v-list-item-icon: v-icon mdi-buffer
+            v-list-item-title Models
+          v-list-item.mt-3.animated.fadeInLeft.wait-p5s(value='auth')
+            v-list-item-icon: v-icon mdi-lock
+            v-list-item-title Authentication
+      .editor-api-editor
+        template(v-if='tab === `info`')
+          v-container.px-2.pt-1(fluid)
+            v-row(dense)
+              v-col(cols='12')
+                .pa-3
+                  .subtitle-2 API General Information
+                  .caption.grey--text.text--darken-1 Global metadata about the API
+              v-col(cols='12', lg='6')
+                v-card.pt-2
+                  v-card-text
+                    v-text-field(
+                      label='Title'
+                      outlined
+                      hint='Required - Title of the API'
+                      persistent-hint
+                      v-model='info.title'
+                    )
+                    v-divider.mt-2.mb-4
+                    v-text-field(
+                      label='Version'
+                      outlined
+                      hint='Required - Semantic versioning like 1.0.0 or an arbitrary string like 0.99-beta.'
+                      persistent-hint
+                      v-model='info.version'
+                    )
+                    v-divider.mt-2.mb-4
+                    v-textarea(
+                      label='Description'
+                      outlined
+                      hint='Optional - Markdown formatting is supported.'
+                      persistent-hint
+                      v-model='info.description'
+                    )
+              v-col(cols='12', lg='6')
+                v-card.pt-2
+                  v-card-text
+                    v-list(nav, two-line)
+                      v-list-item-group(v-model='kind', mandatory, color='primary')
+                        v-list-item(value='rest')
+                          v-list-item-avatar
+                            img(src='/svg/icon-transaction-list.svg', alt='REST')
+                          v-list-item-content
+                            v-list-item-title REST API
+                            v-list-item-subtitle Classic REST Endpoints
+                          v-list-item-avatar
+                            v-icon(:color='kind === `rest` ? `primary` : `grey lighten-3`') mdi-check-circle
+                        v-list-item(value='graphql', disabled)
+                          v-list-item-avatar
+                            img(src='/svg/icon-graphql.svg', alt='GraphQL')
+                          v-list-item-content
+                            v-list-item-title GraphQL
+                            v-list-item-subtitle.grey--text.text--lighten-1 Schema-based API
+                          v-list-item-action
+                            //- v-icon(:color='kind === `graphql` ? `primary` : `grey lighten-3`') mdi-check-circle
+                            v-chip(label, small) Coming soon
+        template(v-else-if='tab === `servers`')
+          v-container.px-2.pt-1(fluid)
+            v-row(dense)
+              v-col(cols='12')
+                .pa-3
+                  .d-flex.align-center.justify-space-between
+                    div
+                      .subtitle-2 List of servers / load balancers where this API reside
+                      .caption.grey--text.text--darken-1 Enter all environments, e.g. Integration, QA, Pre-production, Production, etc.
+                    v-btn(color='primary', large, @click='addServer')
+                      v-icon(left) mdi-plus
+                      span Add Server
+              v-col(cols='12', lg='6', v-for='srv of servers', :key='srv.id')
+                v-card.pt-1
+                  v-card-text
+                    .d-flex
+                      .d-flex.flex-column.justify-space-between
+                        v-menu(offset-y, min-width='200')
+                          template(v-slot:activator='{ on }')
+                            v-btn(text, x-large, style='min-width: 0;', v-on='on')
+                              v-icon(large, :color='iconColor(srv.icon)') {{iconKey(srv.icon)}}
+                          v-list(nav, dense)
+                            v-list-item-group(v-model='srv.icon', mandatory)
+                              v-list-item(:value='srvKey', v-for='(srv, srvKey) in serverTypes', :key='srvKey')
+                                v-list-item-icon: v-icon(large, :color='srv.color', v-text='srv.icon')
+                                v-list-item-content: v-list-item-title(v-text='srv.title')
+                        v-btn.mb-2(depressed, small, @click='removeServer(srv.id)')
+                          v-icon(left) mdi-close
+                          span Delete
+                      v-divider.ml-5(vertical)
+                      .pl-5(style='flex: 1 1 100%;')
+                        v-text-field(
+                          label='Environment / Server Name'
+                          outlined
+                          hint='Required - Name of the environment (e.g. QA, Production)'
+                          persistent-hint
+                          v-model='srv.name'
+                        )
+                        v-text-field.mt-4(
+                          label='URL'
+                          outlined
+                          hint='Required - URL of the environment (e.g. https://api.example.com/v1)'
+                          persistent-hint
+                          v-model='srv.url'
+                        )
+
+        template(v-else-if='tab === `endpoints`')
+          v-container.px-2.pt-1(fluid)
+            v-row(dense)
+              v-col(cols='12')
+                .pa-3
+                  .d-flex.align-center.justify-space-between
+                    div
+                      .subtitle-2 List of endpoints
+                      .caption.grey--text.text--darken-1 Groups of REST endpoints (GET, POST, PUT, DELETE).
+                    v-btn(color='primary', large, @click='addGroup')
+                      v-icon(left) mdi-plus
+                      span Add Group
+              v-col(cols='12', v-for='grp of endpointGroups', :key='grp.id')
+                v-card(color='grey darken-2')
+                  v-card-text
+                    v-toolbar(color='grey darken-2', flat, height='86')
+                      v-text-field.mr-1(
+                        flat
+                        dark
+                        label='Group Name'
+                        solo
+                        hint='Group Name'
+                        persistent-hint
+                        v-model='grp.name'
+                      )
+                      v-text-field.mx-1(
+                        flat
+                        dark
+                        label='Group Description'
+                        solo
+                        hint='Group Description'
+                        persistent-hint
+                        v-model='grp.description'
+                      )
+                      v-divider.mx-3(vertical, dark)
+                      v-btn.mx-1.align-self-start(color='grey lighten-2', @click='addEndpoint(grp)', dark, text, height='48')
+                        v-icon(left) mdi-trash-can
+                        span Delete
+                      v-divider.mx-3(vertical, dark)
+                      v-btn.ml-1.align-self-start(color='pink', @click='addEndpoint(grp)', dark, depressed, height='48')
+                        v-icon(left) mdi-plus
+                        span Add Endpoint
+                    v-container.pa-0.mt-2(fluid)
+                      v-row(dense)
+                        v-col(cols='12', v-for='ept of grp.endpoints', :key='ept.id')
+                          v-card.pt-1
+                            v-card-text
+                              .d-flex
+                                .d-flex.flex-column
+                                  v-menu(offset-y, min-width='140')
+                                    template(v-slot:activator='{ on }')
+                                      v-btn.subtitle-1(depressed, large, dark, style='min-width: 140px;', height='48', v-on='on', :color='methodColor(ept.method)')
+                                        strong {{ept.method}}
+                                    v-list(nav, dense)
+                                      v-list-item-group(v-model='ept.method', mandatory)
+                                        v-list-item(:value='mtd.key', v-for='mtd of endpointMethods', :key='mtd.key')
+                                          v-list-item-content
+                                            v-chip.text-center(label, :color='mtd.color', dark) {{mtd.key}}
+                                  v-btn.mt-2(v-if='!ept.expanded', small, @click='ept.expanded = true', color='pink', outlined)
+                                    v-icon(left) mdi-arrow-down-box
+                                    span Expand
+                                  v-btn.mt-2(v-else, small, @click='ept.expanded = false', color='pink', outlined)
+                                    v-icon(left) mdi-arrow-up-box
+                                    span Collapse
+                                  template(v-if='ept.expanded')
+                                    v-spacer
+                                    v-btn.my-2(depressed, small, @click='removeEndpoint(grp, ept.id)')
+                                      v-icon(left) mdi-close
+                                      span Delete
+                                v-divider.ml-5(vertical)
+                                .pl-5(style='flex: 1 1 100%;')
+                                  .d-flex
+                                    v-text-field.mr-2(
+                                      label='Path'
+                                      outlined
+                                      hint='Required - Path to the endpoint (e.g. /planets/{planetId})'
+                                      persistent-hint
+                                      v-model='ept.path'
+                                    )
+                                    v-text-field.ml-2(
+                                      label='Summary'
+                                      outlined
+                                      hint='Required - A short summary of the endpoint (a few words).'
+                                      persistent-hint
+                                      v-model='ept.summary'
+                                    )
+                                  template(v-if='ept.expanded')
+                                    v-text-field.mt-3(
+                                      label='Description'
+                                      outlined
+                                      v-model='ept.description'
+                                    )
+
+    v-system-bar.editor-api-sysbar(dark, status, color='grey darken-3')
+      .caption.editor-api-sysbar-locale {{locale.toUpperCase()}}
+      .caption.px-3 /{{path}}
+      template(v-if='$vuetify.breakpoint.mdAndUp')
+        v-spacer
+        .caption API Docs
+        v-spacer
+        .caption OpenAPI 3.0
+</template>
+
+<script>
+import _ from 'lodash'
+import uuid from 'uuid/v4'
+import { get, sync } from 'vuex-pathify'
+
+export default {
+  data() {
+    return {
+      tab: `endpoints`,
+      kind: 'rest',
+      kinds: [
+        { text: 'REST', value: 'rest' },
+        { text: 'GraphQL', value: 'graphql' }
+      ],
+      info: {
+        title: '',
+        version: '1.0.0',
+        description: ''
+      },
+      servers: [
+        { name: 'Production', url: 'https://api.example.com/v1', icon: 'server', id: '123456' }
+      ],
+      serverTypes: {
+        aws: {
+          color: 'orange',
+          icon: 'mdi-aws',
+          title: 'AWS'
+        },
+        azure: {
+          color: 'blue darken-2',
+          icon: 'mdi-azure',
+          title: 'Azure'
+        },
+        digitalocean: {
+          color: 'blue',
+          icon: 'mdi-digital-ocean',
+          title: 'DigitalOcean'
+        },
+        docker: {
+          color: 'blue',
+          icon: 'mdi-docker',
+          title: 'Docker'
+        },
+        google: {
+          color: 'red',
+          icon: 'mdi-google',
+          title: 'Google'
+        },
+        kubernetes: {
+          color: 'blue darken-2',
+          icon: 'mdi-kubernetes',
+          title: 'Kubernetes'
+        },
+        linux: {
+          color: 'grey darken-3',
+          icon: 'mdi-linux',
+          title: 'Linux'
+        },
+        mac: {
+          color: 'grey darken-2',
+          icon: 'mdi-apple',
+          title: 'Mac'
+        },
+        server: {
+          color: 'grey',
+          icon: 'mdi-server',
+          title: 'Server'
+        },
+        windows: {
+          color: 'blue darken-2',
+          icon: 'mdi-windows',
+          title: 'Windows'
+        }
+      },
+      endpointGroups: [
+        {
+          id: '345678',
+          name: '',
+          description: '',
+          endpoints: [
+            { method: 'GET', path: '/pet', expanded: false, id: '234567' }
+          ]
+        }
+      ],
+      endpointMethods: [
+        { key: 'GET', color: 'blue' },
+        { key: 'POST', color: 'green' },
+        { key: 'PUT', color: 'orange' },
+        { key: 'PATCH', color: 'cyan' },
+        { key: 'DELETE', color: 'red' },
+        { key: 'HEAD', color: 'deep-purple' },
+        { key: 'OPTIONS', color: 'blue-grey' }
+      ]
+    }
+  },
+  computed: {
+    isMobile() {
+      return this.$vuetify.breakpoint.smAndDown
+    },
+    locale: get('page/locale'),
+    path: get('page/path'),
+    mode: get('editor/mode'),
+    activeModal: sync('editor/activeModal')
+  },
+  methods: {
+    iconColor (val) {
+      return _.get(this.serverTypes, `${val}.color`, 'white')
+    },
+    iconKey (val) {
+      return _.get(this.serverTypes, `${val}.icon`, 'mdi-server')
+    },
+    methodColor (val) {
+      return _.get(_.find(this.endpointMethods, ['key', val]), 'color', 'grey')
+    },
+    addServer () {
+      this.servers.push({
+        id: uuid(),
+        name: 'Production',
+        url: 'https://api.example.com/v1',
+        icon: 'server'
+      })
+    },
+    removeServer (id) {
+      this.servers = _.reject(this.servers, ['id', id])
+    },
+    addGroup () {
+      this.endpointGroups.push({
+        id: uuid(),
+        name: '',
+        description: '',
+        endpoints: []
+      })
+    },
+    addEndpoint (grp) {
+      grp.endpoints.push({
+        id: uuid(),
+        method: 'GET',
+        path: '/pet',
+        expanded: false
+      })
+    },
+    removeEndpoint (grp, eptId) {
+      grp.endpoints = _.reject(grp.endpoints, ['id', eptId])
+    },
+    toggleModal(key) {
+      this.activeModal = (this.activeModal === key) ? '' : key
+      this.helpShown = false
+    },
+    closeAllModal() {
+      this.activeModal = ''
+      this.helpShown = false
+    }
+  },
+  mounted() {
+    this.$store.set('editor/editorKey', 'api')
+
+    if (this.mode === 'create') {
+      this.$store.set('editor/content', '<h1>Title</h1>\n\n<p>Some text here</p>')
+    }
+  },
+  beforeDestroy() {
+    this.$root.$off('editorInsert')
+  }
+}
+</script>
+
+<style lang='scss'>
+$editor-height: calc(100vh - 64px - 24px);
+$editor-height-mobile: calc(100vh - 56px - 16px);
+
+.editor-api {
+  &-main {
+    display: flex;
+    width: 100%;
+  }
+
+  &-editor {
+    background-color: darken(mc('grey', '100'), 4.5%);
+    flex: 1 1 50%;
+    display: block;
+    height: $editor-height;
+    position: relative;
+
+    @at-root .theme--dark & {
+      background-color: darken(mc('grey', '900'), 4.5%);
+    }
+  }
+
+  &-sidebar {
+    width: 200px;
+  }
+
+  &-sysbar {
+    padding-left: 0 !important;
+
+    &-locale {
+      background-color: rgba(255,255,255,.25);
+      display:inline-flex;
+      padding: 0 12px;
+      height: 24px;
+      width: 63px;
+      justify-content: center;
+      align-items: center;
+    }
+  }
+
+}
+</style>

+ 6 - 14
client/components/editor/editor-modal-editorselect.vue

@@ -1,6 +1,6 @@
 <template lang='pug'>
 <template lang='pug'>
   v-dialog(v-model='isShown', persistent, max-width='700', no-click-animation)
   v-dialog(v-model='isShown', persistent, max-width='700', no-click-animation)
-    v-btn(fab, fixed, bottom, right, color='grey darken-3', dark, @click='goBack'): v-icon mdi-undo-variant
+    v-btn(fab, fixed, bottom, right, color='grey darken-3', dark, @click='goBack', style='width: 50px;'): v-icon mdi-undo-variant
     v-card.radius-7(color='blue darken-3', dark)
     v-card.radius-7(color='blue darken-3', dark)
       v-card-text.text-center.py-4
       v-card-text.text-center.py-4
         .subtitle-1.white--text {{$t('editor:select.title')}}
         .subtitle-1.white--text {{$t('editor:select.title')}}
@@ -9,23 +9,15 @@
             v-flex(xs4)
             v-flex(xs4)
               v-hover
               v-hover
                 template(v-slot:default='{ hover }')
                 template(v-slot:default='{ hover }')
-                  v-card.radius-7.primary.animated.fadeInUp(
+                  v-card.radius-7.animated.fadeInUp(
                     hover
                     hover
                     light
                     light
                     ripple
                     ripple
                     )
                     )
-                    v-card-text.text-center(@click='')
-                      img(src='/svg/editor-icon-api.svg', alt='API', style='width: 36px; opacity: .5;')
-                      .body-2.mt-2.blue--text.text--lighten-2 API Docs
-                      .caption.blue--text.text--lighten-1 REST / GraphQL
-                    v-fade-transition
-                      v-overlay(
-                        v-if='hover'
-                        absolute
-                        color='primary'
-                        opacity='.8'
-                        )
-                        .body-2.mt-7 Coming Soon
+                    v-card-text.text-center(@click='selectEditor(`api`)')
+                      img(src='/svg/editor-icon-api.svg', alt='API', style='width: 36px;')
+                      .body-2.primary--text.mt-2 API Docs
+                      .caption.grey--text REST / GraphQL
             v-flex(xs4)
             v-flex(xs4)
               v-hover
               v-hover
                 template(v-slot:default='{ hover }')
                 template(v-slot:default='{ hover }')

+ 1 - 0
client/static/svg/icon-graphql.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#ff4081" d="M24.5,45.161L7,34.82V14.18L24.5,3.839L42,14.18V34.82L24.5,45.161z M9,33.68l15.5,9.159L40,33.68 V15.32L24.5,6.161L9,15.32V33.68z"/><circle cx="24.5" cy="5.5" r="3.5" fill="#ff4081"/><circle cx="24.5" cy="43.5" r="3.5" fill="#ff4081"/><circle cx="8.5" cy="33.5" r="3.5" fill="#ff4081"/><circle cx="40.5" cy="33.5" r="3.5" fill="#ff4081"/><circle cx="8.5" cy="15.5" r="3.5" fill="#ff4081"/><circle cx="40.5" cy="15.5" r="3.5" fill="#ff4081"/><path fill="#ff4081" d="M42.72,35H6.28L24.5,2.978L42.72,35z M9.72,33H39.28L24.5,7.022L9.72,33z"/></svg>

+ 1 - 0
client/static/svg/icon-transaction-list.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128px" height="128px"><path fill="#fff" d="M14,108V25c0-2.761,2.239-5,5-5h90c2.761,0,5,2.239,5,5v83"/><path fill="none" stroke="#9eb9d3" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6" d="M28 61L35 61M47 61L76 61"/><path fill="#9eb9d3" d="M88 58A3 3 0 1 0 88 64 3 3 0 1 0 88 58zM100 58A3 3 0 1 0 100 64 3 3 0 1 0 100 58z"/><path fill="none" stroke="#9eb9d3" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6" d="M28 77L35 77M47 77L76 77"/><path fill="#9eb9d3" d="M100 74A3 3 0 1 0 100 80A3 3 0 1 0 100 74Z"/><path fill="none" stroke="#9eb9d3" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6" d="M28 93L35 93M47 93L76 93"/><path fill="#9eb9d3" d="M88 90A3 3 0 1 0 88 96 3 3 0 1 0 88 90zM100 90A3 3 0 1 0 100 96 3 3 0 1 0 100 90zM114 43H14V25c0-2.761 2.239-5 5-5h90c2.761 0 5 2.239 5 5V43z"/><path fill="#fff" d="M104,30H69c-1.657,0-3,1.343-3,3c0,1.657,1.343,3,3,3h35c1.657,0,3-1.343,3-3S105.657,30,104,30z"/><path fill="none" stroke="#444b54" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6" d="M14,108V25 c0-2.761,2.239-5,5-5h90c2.761,0,5,2.239,5,5v83"/><path fill="#fff" d="M24 30A3 3 0 1 0 24 36 3 3 0 1 0 24 30zM34 30A3 3 0 1 0 34 36 3 3 0 1 0 34 30zM44 30A3 3 0 1 0 44 36 3 3 0 1 0 44 30z"/></svg>

+ 19 - 11
client/themes/default/components/nav-sidebar.vue

@@ -1,15 +1,23 @@
 <template lang="pug">
 <template lang="pug">
-  v-list.py-2(dense, :class='color', :dark='dark')
-    template(v-for='item of items')
-      v-list-item(
-        v-if='item.kind === `link`'
-        :href='item.target'
-        )
-        v-list-item-avatar(size='24')
-          v-icon {{ item.icon }}
-        v-list-item-title {{ item.label }}
-      v-divider.my-2(v-else-if='item.kind === `divider`')
-      v-subheader.pl-4(v-else-if='item.kind === `header`') {{ item.label }}
+  div
+    .blue.darken-3.pa-3.d-flex
+      v-btn(depressed, color='blue darken-2', style='min-width:0;', href='/')
+        v-icon(size='20') mdi-home
+      v-btn.ml-3(depressed, color='blue darken-2', style='flex: 1 1 100%;')
+        v-icon(left) mdi-file-tree
+        .body-2.text-none Browse
+    v-divider
+    v-list.py-2(dense, :class='color', :dark='dark')
+      template(v-for='item of items')
+        v-list-item(
+          v-if='item.kind === `link`'
+          :href='item.target'
+          )
+          v-list-item-avatar(size='24')
+            v-icon {{ item.icon }}
+          v-list-item-title {{ item.label }}
+        v-divider.my-2(v-else-if='item.kind === `divider`')
+        v-subheader.pl-4(v-else-if='item.kind === `header`') {{ item.label }}
 </template>
 </template>
 
 
 <script>
 <script>

+ 44 - 0
client/themes/default/theme.yml

@@ -5,3 +5,47 @@ version: 1.0.0
 requirements:
 requirements:
   minimum: '>= 2.0.0'
   minimum: '>= 2.0.0'
   maximum: '< 3.0.0'
   maximum: '< 3.0.0'
+props:
+  sdPosition:
+    type: String
+    default: 'left'
+    title: Table of Contents Position
+    hint: Should the content sidebar be shown on the left or right.
+    enum:
+      - 'hidden'
+      - 'left'
+      - 'right'
+    order: 1
+    icon: mdi-border-vertical
+  showTOC:
+    type: Boolean
+    default: true
+    title: Display the Table of Contents
+    order: 2
+  showTags:
+    type: Boolean
+    default: true
+    title: Display the Page Tags
+    order: 3
+  showTags:
+    type: Boolean
+    default: true
+    title: Display the Page Author and Date
+    order: 4
+  showTags:
+    type: Boolean
+    default: true
+    title: Display the Page Rating
+    order: 5
+  showSocialBar:
+    type: Boolean
+    default: true
+    title: Display the Social Links Bar
+    order: 6
+  showEditSpeedDial:
+    type: Boolean
+    default: true
+    title: Display the Edit Speed Dial
+    hint: Shown in the lower right corner of the page.
+    order: 7
+

+ 66 - 67
package.json

@@ -39,21 +39,21 @@
     "@exlinc/keycloak-passport": "1.0.2",
     "@exlinc/keycloak-passport": "1.0.2",
     "algoliasearch": "3.35.1",
     "algoliasearch": "3.35.1",
     "apollo-fetch": "0.7.0",
     "apollo-fetch": "0.7.0",
-    "apollo-server": "2.9.7",
-    "apollo-server-express": "2.9.7",
+    "apollo-server": "2.9.12",
+    "apollo-server-express": "2.9.12",
     "auto-load": "3.0.4",
     "auto-load": "3.0.4",
-    "aws-sdk": "2.556.0",
+    "aws-sdk": "2.580.0",
     "azure-search-client": "3.1.5",
     "azure-search-client": "3.1.5",
     "bcryptjs-then": "1.0.1",
     "bcryptjs-then": "1.0.1",
-    "bluebird": "3.7.1",
+    "bluebird": "3.7.2",
     "body-parser": "1.19.0",
     "body-parser": "1.19.0",
     "brute-knex": "4.0.0",
     "brute-knex": "4.0.0",
-    "chalk": "2.4.2",
+    "chalk": "3.0.0",
     "cheerio": "1.0.0-rc.3",
     "cheerio": "1.0.0-rc.3",
-    "chokidar": "3.2.2",
+    "chokidar": "3.3.0",
     "clean-css": "4.2.1",
     "clean-css": "4.2.1",
     "compression": "1.7.4",
     "compression": "1.7.4",
-    "connect-session-knex": "1.4.0",
+    "connect-session-knex": "1.5.0",
     "cookie-parser": "1.4.4",
     "cookie-parser": "1.4.4",
     "cors": "2.8.5",
     "cors": "2.8.5",
     "custom-error-instance": "2.1.1",
     "custom-error-instance": "2.1.1",
@@ -67,8 +67,8 @@
     "express": "4.17.1",
     "express": "4.17.1",
     "express-brute": "1.0.1",
     "express-brute": "1.0.1",
     "express-session": "1.17.0",
     "express-session": "1.17.0",
-    "file-type": "12.3.1",
-    "filesize": "5.0.3",
+    "file-type": "12.4.0",
+    "filesize": "6.0.1",
     "fs-extra": "8.1.0",
     "fs-extra": "8.1.0",
     "getos": "3.1.1",
     "getos": "3.1.1",
     "graphql": "14.5.8",
     "graphql": "14.5.8",
@@ -77,8 +77,8 @@
     "graphql-subscriptions": "1.1.0",
     "graphql-subscriptions": "1.1.0",
     "graphql-tools": "4.0.6",
     "graphql-tools": "4.0.6",
     "he": "1.2.0",
     "he": "1.2.0",
-    "highlight.js": "9.15.10",
-    "i18next": "18.0.1",
+    "highlight.js": "9.16.2",
+    "i18next": "19.0.1",
     "i18next-express-middleware": "1.8.2",
     "i18next-express-middleware": "1.8.2",
     "i18next-node-fs-backend": "2.1.3",
     "i18next-node-fs-backend": "2.1.3",
     "image-size": "0.8.3",
     "image-size": "0.8.3",
@@ -87,7 +87,7 @@
     "js-yaml": "3.13.1",
     "js-yaml": "3.13.1",
     "jsonwebtoken": "8.5.1",
     "jsonwebtoken": "8.5.1",
     "klaw": "3.0.0",
     "klaw": "3.0.0",
-    "knex": "0.20.0",
+    "knex": "0.20.3",
     "lodash": "4.17.15",
     "lodash": "4.17.15",
     "markdown-it": "10.0.0",
     "markdown-it": "10.0.0",
     "markdown-it-abbr": "1.0.4",
     "markdown-it-abbr": "1.0.4",
@@ -103,21 +103,21 @@
     "markdown-it-sup": "1.0.0",
     "markdown-it-sup": "1.0.0",
     "markdown-it-task-lists": "2.1.1",
     "markdown-it-task-lists": "2.1.1",
     "mathjax-node": "2.1.1",
     "mathjax-node": "2.1.1",
-    "mime-types": "2.1.24",
+    "mime-types": "2.1.25",
     "moment": "2.24.0",
     "moment": "2.24.0",
     "moment-timezone": "0.5.27",
     "moment-timezone": "0.5.27",
-    "mongodb": "3.3.3",
-    "mssql": "5.1.0",
+    "mongodb": "3.3.5",
+    "mssql": "6.0.1",
     "multer": "1.4.2",
     "multer": "1.4.2",
-    "mysql2": "1.7.0",
-    "nanoid": "2.1.6",
+    "mysql2": "2.0.1",
+    "nanoid": "2.1.7",
     "node-2fa": "1.1.2",
     "node-2fa": "1.1.2",
-    "node-cache": "5.0.0",
+    "node-cache": "5.0.2",
     "nodemailer": "6.3.1",
     "nodemailer": "6.3.1",
     "objection": "1.6.11",
     "objection": "1.6.11",
     "passport": "0.4.0",
     "passport": "0.4.0",
-    "passport-auth0": "1.2.1",
-    "passport-azure-ad": "4.1.0",
+    "passport-auth0": "1.3.0",
+    "passport-azure-ad": "4.2.0",
     "passport-cas": "0.1.1",
     "passport-cas": "0.1.1",
     "passport-discord": "0.1.3",
     "passport-discord": "0.1.3",
     "passport-dropbox-oauth2": "1.1.0",
     "passport-dropbox-oauth2": "1.1.0",
@@ -135,19 +135,19 @@
     "passport-saml": "1.2.0",
     "passport-saml": "1.2.0",
     "passport-twitch": "1.0.3",
     "passport-twitch": "1.0.3",
     "pem-jwk": "2.0.0",
     "pem-jwk": "2.0.0",
-    "pg": "7.12.1",
+    "pg": "7.14.0",
     "pg-hstore": "2.3.3",
     "pg-hstore": "2.3.3",
-    "pg-query-stream": "2.0.0",
+    "pg-query-stream": "2.0.1",
     "pg-tsquery": "8.0.5",
     "pg-tsquery": "8.0.5",
     "pug": "2.0.4",
     "pug": "2.0.4",
     "qr-image": "3.2.0",
     "qr-image": "3.2.0",
     "raven": "2.6.4",
     "raven": "2.6.4",
     "remove-markdown": "0.3.0",
     "remove-markdown": "0.3.0",
     "request": "2.88.0",
     "request": "2.88.0",
-    "request-promise": "4.2.4",
+    "request-promise": "4.2.5",
     "safe-regex": "2.1.1",
     "safe-regex": "2.1.1",
     "sanitize-filename": "1.6.3",
     "sanitize-filename": "1.6.3",
-    "scim-query-filter-parser": "2.0.1",
+    "scim-query-filter-parser": "2.0.2",
     "semver": "6.3.0",
     "semver": "6.3.0",
     "serve-favicon": "2.5.0",
     "serve-favicon": "2.5.0",
     "simple-git": "1.126.0",
     "simple-git": "1.126.0",
@@ -156,31 +156,31 @@
     "striptags": "3.1.1",
     "striptags": "3.1.1",
     "subscriptions-transport-ws": "0.9.16",
     "subscriptions-transport-ws": "0.9.16",
     "tar-fs": "2.0.0",
     "tar-fs": "2.0.0",
-    "twemoji": "12.1.3",
+    "twemoji": "12.1.4",
     "uslug": "1.0.4",
     "uslug": "1.0.4",
     "uuid": "3.3.3",
     "uuid": "3.3.3",
     "validate.js": "0.13.1",
     "validate.js": "0.13.1",
     "winston": "3.2.1",
     "winston": "3.2.1",
-    "yargs": "14.2.0"
+    "yargs": "15.0.2"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@babel/cli": "^7.6.4",
-    "@babel/core": "^7.6.4",
-    "@babel/plugin-proposal-class-properties": "^7.5.0",
-    "@babel/plugin-proposal-decorators": "^7.6.0",
-    "@babel/plugin-proposal-export-namespace-from": "^7.5.2",
-    "@babel/plugin-proposal-function-sent": "^7.5.0",
-    "@babel/plugin-proposal-json-strings": "^7.2.0",
-    "@babel/plugin-proposal-numeric-separator": "^7.2.0",
-    "@babel/plugin-proposal-throw-expressions": "^7.2.0",
-    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
-    "@babel/plugin-syntax-import-meta": "^7.2.0",
-    "@babel/polyfill": "^7.6.0",
-    "@babel/preset-env": "^7.6.3",
-    "@mdi/font": "4.5.95",
+    "@babel/cli": "^7.7.4",
+    "@babel/core": "^7.7.4",
+    "@babel/plugin-proposal-class-properties": "^7.7.4",
+    "@babel/plugin-proposal-decorators": "^7.7.4",
+    "@babel/plugin-proposal-export-namespace-from": "^7.7.4",
+    "@babel/plugin-proposal-function-sent": "^7.7.4",
+    "@babel/plugin-proposal-json-strings": "^7.7.4",
+    "@babel/plugin-proposal-numeric-separator": "^7.7.4",
+    "@babel/plugin-proposal-throw-expressions": "^7.7.4",
+    "@babel/plugin-syntax-dynamic-import": "^7.7.4",
+    "@babel/plugin-syntax-import-meta": "^7.7.4",
+    "@babel/polyfill": "^7.7.0",
+    "@babel/preset-env": "^7.7.4",
+    "@mdi/font": "4.6.95",
     "@panter/vue-i18next": "0.15.1",
     "@panter/vue-i18next": "0.15.1",
     "@requarks/ckeditor5": "12.4.0-wiki.14",
     "@requarks/ckeditor5": "12.4.0-wiki.14",
-    "@vue/babel-preset-app": "4.0.5",
+    "@vue/babel-preset-app": "4.1.1",
     "animate-sass": "0.8.2",
     "animate-sass": "0.8.2",
     "animated-number-vue": "1.0.0",
     "animated-number-vue": "1.0.0",
     "apollo-cache-inmemory": "1.6.3",
     "apollo-cache-inmemory": "1.6.3",
@@ -192,7 +192,7 @@
     "apollo-link-persisted-queries": "0.2.2",
     "apollo-link-persisted-queries": "0.2.2",
     "apollo-link-ws": "1.0.19",
     "apollo-link-ws": "1.0.19",
     "apollo-utilities": "1.3.2",
     "apollo-utilities": "1.3.2",
-    "autoprefixer": "9.6.4",
+    "autoprefixer": "9.7.3",
     "babel-eslint": "10.0.3",
     "babel-eslint": "10.0.3",
     "babel-jest": "24.9.0",
     "babel-jest": "24.9.0",
     "babel-loader": "^8.0.6",
     "babel-loader": "^8.0.6",
@@ -201,27 +201,27 @@
     "babel-plugin-prismjs": "1.1.1",
     "babel-plugin-prismjs": "1.1.1",
     "babel-plugin-transform-imports": "2.0.0",
     "babel-plugin-transform-imports": "2.0.0",
     "cache-loader": "4.1.0",
     "cache-loader": "4.1.0",
-    "chart.js": "2.9.0",
+    "chart.js": "2.9.3",
     "clean-webpack-plugin": "3.0.0",
     "clean-webpack-plugin": "3.0.0",
     "clipboard": "2.0.4",
     "clipboard": "2.0.4",
     "codemirror": "5.49.2",
     "codemirror": "5.49.2",
-    "copy-webpack-plugin": "5.0.4",
-    "core-js": "3.3.4",
+    "copy-webpack-plugin": "5.0.5",
+    "core-js": "3.4.5",
     "css-loader": "3.2.0",
     "css-loader": "3.2.0",
     "cssnano": "4.1.10",
     "cssnano": "4.1.10",
     "duplicate-package-checker-webpack-plugin": "3.0.0",
     "duplicate-package-checker-webpack-plugin": "3.0.0",
     "epic-spinners": "1.1.0",
     "epic-spinners": "1.1.0",
-    "eslint": "6.6.0",
+    "eslint": "6.7.2",
     "eslint-config-requarks": "1.0.7",
     "eslint-config-requarks": "1.0.7",
     "eslint-config-standard": "14.1.0",
     "eslint-config-standard": "14.1.0",
     "eslint-plugin-import": "2.18.2",
     "eslint-plugin-import": "2.18.2",
     "eslint-plugin-node": "10.0.0",
     "eslint-plugin-node": "10.0.0",
     "eslint-plugin-promise": "4.2.1",
     "eslint-plugin-promise": "4.2.1",
     "eslint-plugin-standard": "4.0.1",
     "eslint-plugin-standard": "4.0.1",
-    "eslint-plugin-vue": "5.2.3",
+    "eslint-plugin-vue": "6.0.1",
     "fibers": "4.0.2",
     "fibers": "4.0.2",
-    "file-loader": "4.2.0",
-    "filepond": "4.7.2",
+    "file-loader": "5.0.2",
+    "filepond": "4.7.4",
     "filepond-plugin-file-validate-type": "1.2.4",
     "filepond-plugin-file-validate-type": "1.2.4",
     "filesize.js": "2.0.0",
     "filesize.js": "2.0.0",
     "graphql-persisted-document-loader": "1.0.1",
     "graphql-persisted-document-loader": "1.0.1",
@@ -231,7 +231,7 @@
     "html-webpack-pug-plugin": "2.0.0",
     "html-webpack-pug-plugin": "2.0.0",
     "i18next-chained-backend": "2.0.0",
     "i18next-chained-backend": "2.0.0",
     "i18next-localstorage-backend": "3.0.0",
     "i18next-localstorage-backend": "3.0.0",
-    "i18next-xhr-backend": "3.2.0",
+    "i18next-xhr-backend": "3.2.2",
     "ignore-loader": "0.1.2",
     "ignore-loader": "0.1.2",
     "jest": "24.9.0",
     "jest": "24.9.0",
     "js-cookie": "2.2.1",
     "js-cookie": "2.2.1",
@@ -250,41 +250,41 @@
     "pug-lint": "2.6.0",
     "pug-lint": "2.6.0",
     "pug-loader": "2.4.0",
     "pug-loader": "2.4.0",
     "pug-plain-loader": "1.0.0",
     "pug-plain-loader": "1.0.0",
-    "raw-loader": "3.1.0",
-    "resolve-url-loader": "3.1.0",
-    "sass": "1.23.1",
+    "raw-loader": "4.0.0",
+    "resolve-url-loader": "3.1.1",
+    "sass": "1.23.7",
     "sass-loader": "8.0.0",
     "sass-loader": "8.0.0",
     "sass-resources-loader": "2.0.1",
     "sass-resources-loader": "2.0.1",
     "script-ext-html-webpack-plugin": "2.1.4",
     "script-ext-html-webpack-plugin": "2.1.4",
     "simple-progress-webpack-plugin": "1.1.2",
     "simple-progress-webpack-plugin": "1.1.2",
-    "style-loader": "1.0.0",
-    "terser": "4.3.9",
+    "style-loader": "1.0.1",
+    "terser": "4.4.0",
     "twemoji-awesome": "1.0.6",
     "twemoji-awesome": "1.0.6",
-    "url-loader": "2.2.0",
+    "url-loader": "3.0.0",
     "velocity-animate": "1.5.2",
     "velocity-animate": "1.5.2",
     "viz.js": "2.1.2",
     "viz.js": "2.1.2",
     "vue": "2.6.10",
     "vue": "2.6.10",
-    "vue-apollo": "3.0.0",
-    "vue-chartjs": "3.4.2",
+    "vue-apollo": "3.0.2",
+    "vue-chartjs": "3.5.0",
     "vue-clipboards": "1.3.0",
     "vue-clipboards": "1.3.0",
     "vue-filepond": "5.1.3",
     "vue-filepond": "5.1.3",
     "vue-hot-reload-api": "2.3.4",
     "vue-hot-reload-api": "2.3.4",
-    "vue-loader": "15.7.1",
+    "vue-loader": "15.7.2",
     "vue-moment": "4.0.0",
     "vue-moment": "4.0.0",
     "vue-router": "3.1.3",
     "vue-router": "3.1.3",
     "vue-status-indicator": "1.2.1",
     "vue-status-indicator": "1.2.1",
     "vue-template-compiler": "2.6.10",
     "vue-template-compiler": "2.6.10",
-    "vue2-animate": "2.1.2",
+    "vue2-animate": "2.1.3",
     "vuedraggable": "2.23.2",
     "vuedraggable": "2.23.2",
     "vuescroll": "4.14.4",
     "vuescroll": "4.14.4",
-    "vuetify": "2.1.7",
-    "vuetify-loader": "1.3.0",
-    "vuex": "3.1.1",
+    "vuetify": "2.1.12",
+    "vuetify-loader": "1.4.2",
+    "vuex": "3.1.2",
     "vuex-pathify": "1.4.0",
     "vuex-pathify": "1.4.0",
-    "vuex-persistedstate": "2.5.4",
+    "vuex-persistedstate": "2.7.0",
     "webpack": "4.41.2",
     "webpack": "4.41.2",
     "webpack-bundle-analyzer": "3.6.0",
     "webpack-bundle-analyzer": "3.6.0",
-    "webpack-cli": "3.3.9",
+    "webpack-cli": "3.3.10",
     "webpack-dev-middleware": "3.7.2",
     "webpack-dev-middleware": "3.7.2",
     "webpack-hot-middleware": "2.25.0",
     "webpack-hot-middleware": "2.25.0",
     "webpack-merge": "4.2.2",
     "webpack-merge": "4.2.2",
@@ -318,8 +318,7 @@
         ]
         ]
       },
       },
       "postcss-flexbugs-fixes": {},
       "postcss-flexbugs-fixes": {},
-      "postcss-flexibility": {},
-      "postcss-preset-env": {}
+      "postcss-flexibility": {}
     }
     }
   },
   },
   "pugLintConfig": {
   "pugLintConfig": {

+ 6 - 0
server/modules/editor/api/definition.yml

@@ -0,0 +1,6 @@
+key: api
+title: API Docs
+description: REST / GraphQL Editor
+contentType: yml
+author: requarks.io
+props: {}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 489 - 402
yarn.lock


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä