AdminExtensions.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <template lang='pug'>
  2. q-page.admin-extensions
  3. .row.q-pa-md.items-center
  4. .col-auto
  5. img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-module.svg')
  6. .col.q-pl-md
  7. .text-h5.text-primary.animated.fadeInLeft {{ t('admin.extensions.title') }}
  8. .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.extensions.subtitle') }}
  9. .col-auto
  10. q-btn.acrylic-btn.q-mr-sm(
  11. icon='las la-question-circle'
  12. flat
  13. color='grey'
  14. href='https://docs.js.wiki/admin/extensions'
  15. target='_blank'
  16. type='a'
  17. )
  18. q-btn.acrylic-btn(
  19. icon='las la-redo-alt'
  20. flat
  21. color='secondary'
  22. :loading='state.loading > 0'
  23. @click='load'
  24. )
  25. q-separator(inset)
  26. .row.q-pa-md.q-col-gutter-md
  27. .col-12
  28. q-card.shadow-1
  29. q-list(separator)
  30. q-item(
  31. v-for='(ext, idx) of state.extensions'
  32. :key='`ext-` + ext.key'
  33. )
  34. blueprint-icon(icon='module')
  35. q-item-section
  36. q-item-label {{ext.title}}
  37. q-item-label(caption) {{ext.description}}
  38. q-item-section(side)
  39. .row
  40. q-btn-group(unelevated)
  41. q-btn(
  42. icon='las la-check'
  43. size='sm'
  44. color='positive'
  45. padding='xs sm'
  46. v-if='ext.isInstalled'
  47. :ripple='false'
  48. )
  49. q-tooltip(
  50. anchor='center left'
  51. self='center right'
  52. ) {{t('admin.extensions.installed')}}
  53. q-btn(
  54. :label='t(`admin.extensions.install`)'
  55. color='blue-7'
  56. v-if='ext.isCompatible && !ext.isInstalled && ext.isInstallable'
  57. @click='install(ext)'
  58. no-caps
  59. )
  60. q-btn(
  61. v-else-if='ext.isCompatible && ext.isInstalled && ext.isInstallable'
  62. :label='t(`admin.extensions.reinstall`)'
  63. color='blue-7'
  64. @click='install(ext)'
  65. no-caps
  66. )
  67. q-btn(
  68. v-else-if='ext.isCompatible && ext.isInstalled && !ext.isInstallable'
  69. :label='t(`admin.extensions.installed`)'
  70. color='positive'
  71. no-caps
  72. :ripple='false'
  73. )
  74. q-btn(
  75. v-else-if='ext.isCompatible'
  76. :label='t(`admin.extensions.instructions`)'
  77. icon='las la-info-circle'
  78. color='indigo'
  79. outline
  80. type='a'
  81. :href='`https://docs.js.wiki/admin/extensions/` + ext.key'
  82. target='_blank'
  83. no-caps
  84. )
  85. q-tooltip(
  86. anchor='center left'
  87. self='center right'
  88. ) {{t('admin.extensions.instructionsHint')}}
  89. q-btn(
  90. v-else
  91. color='negative'
  92. outline
  93. :label='t(`admin.extensions.incompatible`)'
  94. no-caps
  95. :ripple='false'
  96. )
  97. </template>
  98. <script setup>
  99. import gql from 'graphql-tag'
  100. import cloneDeep from 'lodash/cloneDeep'
  101. import { useI18n } from 'vue-i18n'
  102. import { useMeta, useQuasar } from 'quasar'
  103. import { computed, onMounted, reactive, watch } from 'vue'
  104. import { useAdminStore } from 'src/stores/admin'
  105. import { useSiteStore } from 'src/stores/site'
  106. import { useDataStore } from 'src/stores/data'
  107. // QUASAR
  108. const $q = useQuasar()
  109. // STORES
  110. const adminStore = useAdminStore()
  111. const siteStore = useSiteStore()
  112. const dataStore = useDataStore()
  113. // I18N
  114. const { t } = useI18n()
  115. // META
  116. useMeta({
  117. title: t('admin.extensions.title')
  118. })
  119. // DATA
  120. const state = reactive({
  121. loading: false,
  122. extensions: []
  123. })
  124. // METHODS
  125. async function load () {
  126. state.loading++
  127. $q.loading.show()
  128. const resp = await APOLLO_CLIENT.query({
  129. query: gql`
  130. query fetchExtensions {
  131. systemExtensions {
  132. key
  133. title
  134. description
  135. isInstalled
  136. isInstallable
  137. isCompatible
  138. }
  139. }
  140. `,
  141. fetchPolicy: 'network-only'
  142. })
  143. state.extensions = cloneDeep(resp?.data?.systemExtensions)
  144. $q.loading.hide()
  145. state.loading--
  146. }
  147. async function install (ext) {
  148. $q.loading.show({
  149. message: t('admin.extensions.installing') + '<br>' + t('admin.extensions.installingHint'),
  150. html: true
  151. })
  152. try {
  153. const respRaw = await APOLLO_CLIENT.mutate({
  154. mutation: gql`
  155. mutation installExtension (
  156. $key: String!
  157. ) {
  158. installExtension (
  159. key: $key
  160. ) {
  161. status {
  162. succeeded
  163. message
  164. }
  165. }
  166. }
  167. `,
  168. variables: {
  169. key: ext.key
  170. }
  171. })
  172. if (respRaw.data?.installExtension?.status?.succeeded) {
  173. $q.notify({
  174. type: 'positive',
  175. message: t('admin.extensions.installSuccess')
  176. })
  177. ext.isInstalled = true
  178. // this.$forceUpdate()
  179. } else {
  180. throw new Error(respRaw.data?.installExtension?.status?.message || 'An unexpected error occured')
  181. }
  182. } catch (err) {
  183. $q.notify({
  184. type: 'negative',
  185. message: t('admin.extensions.installFailed'),
  186. caption: err.message
  187. })
  188. }
  189. $q.loading.hide()
  190. }
  191. // MOUNTED
  192. onMounted(() => {
  193. load()
  194. })
  195. </script>
  196. <style lang='scss'>
  197. .admin-extensions {
  198. .q-expansion-item__content .q-card {
  199. @at-root .body--light & {
  200. background-color: $grey-1;
  201. }
  202. @at-root .body--dark & {
  203. background-color: $dark-3;
  204. }
  205. }
  206. }
  207. </style>