AdminExtensions.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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='siteStore.docsBase + `/system/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-es'
  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. // QUASAR
  107. const $q = useQuasar()
  108. // STORES
  109. const adminStore = useAdminStore()
  110. const siteStore = useSiteStore()
  111. // I18N
  112. const { t } = useI18n()
  113. // META
  114. useMeta({
  115. title: t('admin.extensions.title')
  116. })
  117. // DATA
  118. const state = reactive({
  119. loading: false,
  120. extensions: []
  121. })
  122. // METHODS
  123. async function load () {
  124. state.loading++
  125. $q.loading.show()
  126. const resp = await APOLLO_CLIENT.query({
  127. query: gql`
  128. query fetchExtensions {
  129. systemExtensions {
  130. key
  131. title
  132. description
  133. isInstalled
  134. isInstallable
  135. isCompatible
  136. }
  137. }
  138. `,
  139. fetchPolicy: 'network-only'
  140. })
  141. state.extensions = cloneDeep(resp?.data?.systemExtensions)
  142. $q.loading.hide()
  143. state.loading--
  144. }
  145. async function install (ext) {
  146. $q.loading.show({
  147. message: t('admin.extensions.installing') + '<br>' + t('admin.extensions.installingHint'),
  148. html: true
  149. })
  150. try {
  151. const respRaw = await APOLLO_CLIENT.mutate({
  152. mutation: gql`
  153. mutation installExtension (
  154. $key: String!
  155. ) {
  156. installExtension (
  157. key: $key
  158. ) {
  159. operation {
  160. succeeded
  161. message
  162. }
  163. }
  164. }
  165. `,
  166. variables: {
  167. key: ext.key
  168. }
  169. })
  170. if (respRaw.data?.installExtension?.operation?.succeeded) {
  171. $q.notify({
  172. type: 'positive',
  173. message: t('admin.extensions.installSuccess')
  174. })
  175. ext.isInstalled = true
  176. // this.$forceUpdate()
  177. } else {
  178. throw new Error(respRaw.data?.installExtension?.operation?.message || 'An unexpected error occured')
  179. }
  180. } catch (err) {
  181. $q.notify({
  182. type: 'negative',
  183. message: t('admin.extensions.installFailed'),
  184. caption: err.message
  185. })
  186. }
  187. $q.loading.hide()
  188. }
  189. // MOUNTED
  190. onMounted(() => {
  191. load()
  192. })
  193. </script>
  194. <style lang='scss'>
  195. .admin-extensions {
  196. .q-expansion-item__content .q-card {
  197. @at-root .body--light & {
  198. background-color: $grey-1;
  199. }
  200. @at-root .body--dark & {
  201. background-color: $dark-3;
  202. }
  203. }
  204. }
  205. </style>