| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 | <template lang='pug'>  v-container(fluid, grid-list-lg)    v-layout(row, wrap)      v-flex(xs12)        .admin-header          img.animated.fadeInUp(src='/_assets/svg/icon-browse-page.svg', alt='Dashboard', style='width: 80px;')          .admin-header-title            .headline.primary--text.animated.fadeInLeft {{ $t('admin:dashboard.title') }}            .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin:dashboard.subtitle') }}      v-flex(xs12 md6 lg4 xl3 d-flex)        v-card.primary.dashboard-card.animated.fadeInUp(dark)          v-card-text            v-icon.dashboard-icon mdi-file-document-outline            .overline {{$t('admin:dashboard.pages')}}            animated-number.display-1(              :value='info.pagesTotal'              :duration='2000'              :formatValue='round'              easing='easeOutQuint'              )      v-flex(xs12 md6 lg4 xl3 d-flex)        v-card.blue.darken-3.dashboard-card.animated.fadeInUp.wait-p2s(dark)          v-card-text            v-icon.dashboard-icon mdi-account            .overline {{$t('admin:dashboard.users')}}            animated-number.display-1(              :value='info.usersTotal'              :duration='2000'              :formatValue='round'              easing='easeOutQuint'              )      v-flex(xs12 md6 lg4 xl3 d-flex)        v-card.blue.darken-4.dashboard-card.animated.fadeInUp.wait-p4s(dark)          v-card-text            v-icon.dashboard-icon mdi-account-group            .overline {{$t('admin:dashboard.groups')}}            animated-number.display-1(              :value='info.groupsTotal'              :duration='2000'              :formatValue='round'              easing='easeOutQuint'              )      v-flex(xs12 md6 lg12 xl3 d-flex)        v-card.dashboard-card.animated.fadeInUp.wait-p6s(          :class='isLatestVersion ? "green" : "red lighten-2"'          dark          )          v-btn.btn-animate-wrench(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, to='system', v-if='hasPermission(`manage:system`)')            v-icon(:color='isLatestVersion ? `green` : `red darken-4`', small) mdi-wrench          v-card-text            v-icon.dashboard-icon mdi-blur            .subtitle-1 Wiki.js {{info.currentVersion}}            .body-2(v-if='isLatestVersion') {{$t('admin:dashboard.versionLatest')}}            .body-2(v-else) {{$t('admin:dashboard.versionNew', { version: info.latestVersion })}}      v-flex(xs12, xl6)        v-card.radius-7.animated.fadeInUp.wait-p2s          v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)            v-spacer            .overline {{$t('admin:dashboard.recentPages')}}            v-spacer          v-data-table.pb-2(            :items='recentPages'            :headers='recentPagesHeaders'            :loading='recentPagesLoading'            hide-default-footer            hide-default-header            )            template(slot='item', slot-scope='props')              tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')                td                  .body-2: strong {{ props.item.title }}                td.admin-pages-path                  v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}                  span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}                td.text-right.caption(width='250') {{ props.item.updatedAt | moment('calendar') }}      v-flex(xs12, xl6)        v-card.radius-7.animated.fadeInUp.wait-p4s          v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)            v-spacer            .overline {{$t('admin:dashboard.lastLogins')}}            v-spacer          v-data-table.pb-2(            :items='lastLogins'            :headers='lastLoginsHeaders'            :loading='lastLoginsLoading'            hide-default-footer            hide-default-header            )            template(slot='item', slot-scope='props')              tr.is-clickable(:active='props.selected', @click='$router.push(`/users/` + props.item.id)')                td                  .body-2: strong {{ props.item.name }}                td.text-right.caption(width='250') {{ props.item.lastLoginAt | moment('calendar') }}      v-flex(xs12)        v-card.dashboard-contribute.animated.fadeInUp.wait-p4s          v-card-text            img(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='height: 80px;')            .pl-5              .subtitle-1 {{$t('admin:contribute.title')}}              .body-2.mt-3: strong {{$t('admin:dashboard.contributeSubtitle')}}              .body-2 {{$t('admin:dashboard.contributeHelp')}}              v-btn.mx-0.mt-4(:color='$vuetify.theme.dark ? `indigo lighten-3` : `indigo`', outlined, small, to='/contribute')                .caption: strong {{$t('admin:dashboard.contributeLearnMore')}}</template><script>import _ from 'lodash'import AnimatedNumber from 'animated-number-vue'import { get } from 'vuex-pathify'import gql from 'graphql-tag'import semverLte from 'semver/functions/lte'export default {  components: {    AnimatedNumber  },  data() {    return {      recentPages: [],      recentPagesLoading: false,      recentPagesHeaders: [        { text: 'Title', value: 'title' },        { text: 'Path', value: 'path' },        { text: 'Last Updated', value: 'updatedAt', width: 250 }      ],      lastLogins: [],      lastLoginsLoading: false,      lastLoginsHeaders: [        { text: 'User', value: 'displayName' },        { text: 'Last Login', value: 'lastLoginAt', width: 250 }      ]    }  },  computed: {    isLatestVersion() {      if (this.info.latestVersion === 'n/a' || this.info.currentVersion === 'n/a') {        return true      } else {        return semverLte(this.info.latestVersion, this.info.currentVersion)      }    },    info: get('admin/info'),    permissions: get('user/permissions')  },  methods: {    round(val) { return Math.round(val) },    hasPermission(prm) {      if (_.isArray(prm)) {        return _.some(prm, p => {          return _.includes(this.permissions, p)        })      } else {        return _.includes(this.permissions, prm)      }    }  },  apollo: {    recentPages: {      query: gql`        query {          pages {            list(limit: 10, orderBy: UPDATED, orderByDirection: DESC) {              id              locale              path              title              description              contentType              isPublished              isPrivate              privateNS              createdAt              updatedAt            }          }        }      `,      update: (data) => data.pages.list,      watchLoading (isLoading) {        this.recentPagesLoading = isLoading        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dashboard-recentpages')      }    },    lastLogins: {      query: gql`        query {          users {            lastLogins {              id              name              lastLoginAt            }          }        }      `,      fetchPolicy: 'network-only',      update: (data) => data.users.lastLogins,      watchLoading (isLoading) {        this.lastLoginsLoading = isLoading        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-dashboard-lastlogins')      }    }  }}</script><style lang='scss'>.dashboard-card {  display: flex;  width: 100%;  border-radius: 7px;  .v-card__text {    overflow: hidden;    position: relative;  }}.dashboard-contribute {  background-color: #FFF;  background-image: linear-gradient(to bottom, #FFF 0%, lighten(mc('indigo', '50'), 3%) 100%);  border-radius: 7px;  @at-root .theme--dark & {    background-color: mc('grey', '800');    background-image: linear-gradient(to bottom, mc('grey', '800') 0%, darken(mc('grey', '800'), 6%) 100%);  }  .v-card__text {    display: flex;    align-items: center;    color: mc('indigo', '500') !important;    @at-root .theme--dark & {      color: mc('grey', '300') !important;    }  }}.v-icon.dashboard-icon {  position: absolute !important;  right: 0;  top: 12px;  font-size: 100px !important;  opacity: .25;  @at-root .v-application--is-rtl & {    left: 0;    right: initial;  }}</style>
 |