AdminDashboard.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <template lang='pug'>
  2. q-page.admin-dashboard
  3. .row.q-pa-md.items-center
  4. .col-auto
  5. img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-apps-tab.svg')
  6. .col.q-pl-md
  7. .text-h5.text-primary.animated.fadeInLeft {{ t('admin.dashboard.title') }}
  8. .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.dashboard.subtitle') }}
  9. .row.q-px-md.q-col-gutter-md
  10. .col-12.col-sm-6.col-lg-3
  11. q-card
  12. q-card-section.admin-dashboard-card
  13. img(src='/_assets/icons/fluent-change-theme.svg')
  14. div
  15. strong {{ t('admin.sites.title') }}
  16. span {{adminStore.sites.length}}
  17. q-separator
  18. q-card-actions(align='right')
  19. q-btn(
  20. flat
  21. color='primary'
  22. icon='las la-plus-circle'
  23. :label='t(`common.actions.new`)'
  24. :disable='!userStore.can(`manage:sites`)'
  25. @click='newSite'
  26. )
  27. q-separator.q-mx-sm(vertical)
  28. q-btn(
  29. flat
  30. color='primary'
  31. icon='las la-sitemap'
  32. :label='t(`common.actions.manage`)'
  33. :disable='!userStore.can(`manage:sites`)'
  34. to='/_admin/sites'
  35. )
  36. .col-12.col-sm-6.col-lg-3
  37. q-card
  38. q-card-section.admin-dashboard-card
  39. img(src='/_assets/icons/fluent-account.svg')
  40. div
  41. strong {{ t('admin.users.title') }}
  42. span {{adminStore.info.usersTotal}}
  43. q-separator
  44. q-card-actions(align='right')
  45. q-btn(
  46. flat
  47. color='primary'
  48. icon='las la-user-plus'
  49. :label='t(`common.actions.new`)'
  50. :disable='!userStore.can(`manage:users`)'
  51. @click='newUser'
  52. )
  53. q-separator.q-mx-sm(vertical)
  54. q-btn(
  55. flat
  56. color='primary'
  57. icon='las la-users'
  58. :label='t(`common.actions.manage`)'
  59. :disable='!userStore.can(`manage:users`)'
  60. to='/_admin/users'
  61. )
  62. .col-12.col-sm-6.col-lg-3
  63. q-card
  64. q-card-section.admin-dashboard-card
  65. img(src='/_assets/icons/fluent-female-working-with-a-laptop.svg')
  66. div
  67. strong Logins
  68. small {{adminStore.info.loginsPastDay}} #[i / last 24h]
  69. q-separator
  70. q-card-actions(align='right')
  71. q-btn(
  72. flat
  73. color='primary'
  74. icon='las la-chart-area'
  75. :label='t(`admin.analytics.title`)'
  76. :disable='!userStore.can(`manage:sites`)'
  77. :to='`/_admin/` + adminStore.currentSiteId + `/analytics`'
  78. )
  79. .col-12.col-sm-6.col-lg-3
  80. q-card
  81. q-card-section.admin-dashboard-card
  82. img(src='/_assets/icons/fluent-ssd-animated.svg')
  83. div
  84. strong {{ t('admin.storage.title') }}
  85. small.text-positive Operational
  86. q-separator
  87. q-card-actions(align='right')
  88. q-btn(
  89. flat
  90. color='primary'
  91. icon='las la-server'
  92. :label='t(`common.actions.manage`)'
  93. :disable='!userStore.can(`manage:sites`)'
  94. :to='`/_admin/` + adminStore.currentSiteId + `/storage`'
  95. )
  96. .col-12
  97. q-banner.bg-positive.text-white(
  98. :class='adminStore.isVersionLatest ? `bg-positive` : `bg-warning`'
  99. inline-actions
  100. rounded
  101. )
  102. i.las.la-check.q-mr-sm
  103. span.text-weight-medium(v-if='adminStore.isVersionLatest') Your Wiki.js server is running the latest version!
  104. span.text-weight-medium(v-else) A new version of Wiki.js is available. Please update to the latest version.
  105. template(#action, v-if='userStore.can(`manage:system`)')
  106. q-btn(
  107. flat
  108. :label='t(`admin.system.checkForUpdates`)'
  109. @click='checkForUpdates'
  110. )
  111. q-separator.q-mx-sm(vertical, dark)
  112. q-btn(
  113. flat
  114. :label='t(`admin.system.title`)'
  115. to='/_admin/system'
  116. )
  117. .col-12
  118. q-card
  119. q-card-section ---
  120. //- v-container(fluid, grid-list-lg)
  121. //- v-layout(row, wrap)
  122. //- v-flex(xs12)
  123. //- .admin-header
  124. //- img.animated.fadeInUp(src='/_assets/svg/icon-browse-page.svg', alt='Dashboard', style='width: 80px;')
  125. //- .admin-header-title
  126. //- .headline.primary--text.animated.fadeInLeft {{ $t('admin.dashboard.title') }}
  127. //- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin.dashboard.subtitle') }}
  128. //- v-flex(xs12 md6 lg4 xl3 d-flex)
  129. //- v-card.primary.dashboard-card.animated.fadeInUp(dark)
  130. //- v-card-text
  131. //- v-icon.dashboard-icon mdi-file-document-outline
  132. //- .overline {{$t('admin.dashboard.pages')}}
  133. //- animated-number.display-1(
  134. //- :value='info.pagesTotal'
  135. //- :duration='2000'
  136. //- :formatValue='round'
  137. //- easing='easeOutQuint'
  138. //- )
  139. //- v-flex(xs12 md6 lg4 xl3 d-flex)
  140. //- v-card.blue.darken-3.dashboard-card.animated.fadeInUp.wait-p2s(dark)
  141. //- v-card-text
  142. //- v-icon.dashboard-icon mdi-account
  143. //- .overline {{$t('admin.dashboard.users')}}
  144. //- animated-number.display-1(
  145. //- :value='info.usersTotal'
  146. //- :duration='2000'
  147. //- :formatValue='round'
  148. //- easing='easeOutQuint'
  149. //- )
  150. //- v-flex(xs12 md6 lg4 xl3 d-flex)
  151. //- v-card.blue.darken-4.dashboard-card.animated.fadeInUp.wait-p4s(dark)
  152. //- v-card-text
  153. //- v-icon.dashboard-icon mdi-account-group
  154. //- .overline {{$t('admin.dashboard.groups')}}
  155. //- animated-number.display-1(
  156. //- :value='info.groupsTotal'
  157. //- :duration='2000'
  158. //- :formatValue='round'
  159. //- easing='easeOutQuint'
  160. //- )
  161. //- v-flex(xs12 md6 lg12 xl3 d-flex)
  162. //- v-card.dashboard-card.animated.fadeInUp.wait-p6s(
  163. //- :class='isLatestVersion ? "green" : "red lighten-2"'
  164. //- dark
  165. //- )
  166. //- v-btn.btn-animate-wrench(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, to='system', v-if='hasPermission(`manage:system`)')
  167. //- v-icon(:color='isLatestVersion ? `green` : `red darken-4`', small) mdi-wrench
  168. //- v-card-text
  169. //- v-icon.dashboard-icon mdi-blur
  170. //- .subtitle-1 Wiki.js {{info.currentVersion}}
  171. //- .body-2(v-if='isLatestVersion') {{$t('admin.dashboard.versionLatest')}}
  172. //- .body-2(v-else) {{$t('admin.dashboard.versionNew', { version: info.latestVersion })}}
  173. //- v-flex(xs12, xl6)
  174. //- v-card.radius-7.animated.fadeInUp.wait-p2s
  175. //- v-toolbar(:color='$q.dark.isActive ? `grey darken-2` : `grey lighten-5`', dense, flat)
  176. //- v-spacer
  177. //- .overline {{$t('admin.dashboard.recentPages')}}
  178. //- v-spacer
  179. //- v-data-table.pb-2(
  180. //- :items='recentPages'
  181. //- :headers='recentPagesHeaders'
  182. //- :loading='recentPagesLoading'
  183. //- hide-default-footer
  184. //- hide-default-header
  185. //- )
  186. //- template(slot='item', slot-scope='props')
  187. //- tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
  188. //- td
  189. //- .body-2: strong {{ props.item.title }}
  190. //- td.admin-pages-path
  191. //- v-chip(label, small, :color='$q.dark.isActive ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
  192. //- span.ml-2.grey--text(:class='$q.dark.isActive ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
  193. //- td.text-right.caption(width='250') {{ props.item.updatedAt | moment('calendar') }}
  194. //- v-flex(xs12, xl6)
  195. //- v-card.radius-7.animated.fadeInUp.wait-p4s
  196. //- v-toolbar(:color='$q.dark.isActive ? `grey darken-2` : `grey lighten-5`', dense, flat)
  197. //- v-spacer
  198. //- .overline {{$t('admin.dashboard.lastLogins')}}
  199. //- v-spacer
  200. //- v-data-table.pb-2(
  201. //- :items='lastLogins'
  202. //- :headers='lastLoginsHeaders'
  203. //- :loading='lastLoginsLoading'
  204. //- hide-default-footer
  205. //- hide-default-header
  206. //- )
  207. //- template(slot='item', slot-scope='props')
  208. //- tr.is-clickable(:active='props.selected', @click='$router.push(`/users/` + props.item.id)')
  209. //- td
  210. //- .body-2: strong {{ props.item.name }}
  211. //- td.text-right.caption(width='250') {{ props.item.lastLoginAt | moment('calendar') }}
  212. //- v-flex(xs12)
  213. //- v-card.dashboard-contribute.animated.fadeInUp.wait-p4s
  214. //- v-card-text
  215. //- img(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='height: 80px;')
  216. //- .pl-5
  217. //- .subtitle-1 {{$t('admin.contribute.title')}}
  218. //- .body-2.mt-3: strong {{$t('admin.dashboard.contributeSubtitle')}}
  219. //- .body-2 {{$t('admin.dashboard.contributeHelp')}}
  220. //- v-btn.mx-0.mt-4(:color='$q.dark.isActive ? `indigo lighten-3` : `indigo`', outlined, small, to='/contribute')
  221. //- .caption: strong {{$t('admin.dashboard.contributeLearnMore')}}
  222. </template>
  223. <script setup>
  224. import { useMeta, useQuasar } from 'quasar'
  225. import { useI18n } from 'vue-i18n'
  226. import { useRouter } from 'vue-router'
  227. import { useAdminStore } from '../stores/admin'
  228. import { useUserStore } from 'src/stores/user'
  229. // COMPONENTS
  230. import CheckUpdateDialog from '../components/CheckUpdateDialog.vue'
  231. import SiteCreateDialog from '../components/SiteCreateDialog.vue'
  232. import UserCreateDialog from '../components/UserCreateDialog.vue'
  233. // QUASAR
  234. const $q = useQuasar()
  235. // STORES
  236. const adminStore = useAdminStore()
  237. const userStore = useUserStore()
  238. // ROUTER
  239. const router = useRouter()
  240. // I18N
  241. const { t } = useI18n()
  242. // META
  243. useMeta({
  244. title: t('admin.dashboard.title')
  245. })
  246. // METHODS
  247. function newSite () {
  248. $q.dialog({
  249. component: SiteCreateDialog
  250. }).onOk(() => {
  251. router.push('/_admin/sites')
  252. })
  253. }
  254. function newUser () {
  255. $q.dialog({
  256. component: UserCreateDialog
  257. }).onOk(() => {
  258. router.push('/_admin/users')
  259. })
  260. }
  261. function checkForUpdates () {
  262. $q.dialog({
  263. component: CheckUpdateDialog
  264. })
  265. }
  266. </script>
  267. <style lang='scss'>
  268. .admin-dashboard {
  269. &-card {
  270. display: flex;
  271. align-items: center;
  272. img {
  273. width: 64px;
  274. margin-right: 12px;
  275. }
  276. strong {
  277. font-size: 1.1rem;
  278. font-weight: 300;
  279. display: block;
  280. line-height: 1.2rem;
  281. padding-left: 2px;
  282. }
  283. span {
  284. font-size: 2rem;
  285. line-height: 2rem;
  286. font-weight: 500;
  287. color: var(--q-secondary);
  288. display: block;
  289. }
  290. small {
  291. font-size: 1.4rem;
  292. line-height: 2rem;
  293. font-weight: 400;
  294. color: var(--q-secondary);
  295. display: block;
  296. i {
  297. font-size: 1rem;
  298. font-style: normal;
  299. }
  300. }
  301. }
  302. }
  303. </style>