admin-navigation.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row wrap)
  4. v-flex(xs12)
  5. .admin-header
  6. v-icon(size='80', color='grey lighten-2') near_me
  7. .admin-header-title
  8. .headline.primary--text {{$t('navigation.title')}}
  9. .subheading.grey--text {{$t('navigation.subtitle')}}
  10. v-spacer
  11. v-btn(outline, color='grey', @click='refresh', large)
  12. v-icon refresh
  13. v-btn(color='success', depressed, @click='save', large)
  14. v-icon(left) check
  15. span {{$t('common:actions.apply')}}
  16. v-container.pa-0.mt-3(fluid, grid-list-lg)
  17. v-layout(row)
  18. v-flex(style='flex: 0 0 350px;')
  19. v-card
  20. v-list.py-2(dense, dark, :class='navTree.length < 1 ? "grey lighten-4" : "primary"')
  21. v-list-tile(v-if='navTree.length < 1')
  22. v-list-tile-avatar: v-icon(color='grey') explore_off
  23. v-list-tile-content
  24. .caption.grey--text {{$t('navigation.emptyList')}}
  25. draggable(v-model='navTree')
  26. template(v-for='navItem in navTree')
  27. v-list-tile(
  28. v-if='navItem.kind === "link"'
  29. :key='navItem.id'
  30. :class='(navItem === current) ? "blue" : ""'
  31. @click='selectItem(navItem)'
  32. )
  33. v-list-tile-avatar: v-icon {{navItem.icon}}
  34. v-list-tile-title {{navItem.label}}
  35. .py-2.clickable(
  36. v-else-if='navItem.kind === "divider"'
  37. :key='navItem.id'
  38. :class='(navItem === current) ? "blue" : ""'
  39. @click='selectItem(navItem)'
  40. )
  41. v-divider
  42. v-subheader.pl-4.clickable(
  43. v-else-if='navItem.kind === "header"'
  44. :key='navItem.id'
  45. :class='(navItem === current) ? "blue" : ""'
  46. @click='selectItem(navItem)'
  47. ) {{navItem.label}}
  48. v-card-chin
  49. v-menu(offset-y, bottom, min-width='200px', style='flex: 1 1;')
  50. v-btn(slot='activator', color='primary', depressed, block)
  51. v-icon(left) add
  52. span {{$t('common:actions.add')}}
  53. v-list
  54. v-list-tile(@click='addItem("link")')
  55. v-list-tile-avatar: v-icon link
  56. v-list-tile-title {{$t('navigation.link')}}
  57. v-list-tile(@click='addItem("header")')
  58. v-list-tile-avatar: v-icon title
  59. v-list-tile-title {{$t('navigation.header')}}
  60. v-list-tile(@click='addItem("divider")')
  61. v-list-tile-avatar: v-icon power_input
  62. v-list-tile-title {{$t('navigation.divider')}}
  63. v-flex
  64. v-card(v-if='current.kind === "link"')
  65. v-toolbar(dense, color='blue', flat, dark)
  66. .subheading {{$t('navigation.edit', { kind: $t('navigation.link') })}}
  67. v-card-text
  68. v-text-field(
  69. outline
  70. background-color='grey lighten-2'
  71. :label='$t("navigation.label")'
  72. prepend-icon='title'
  73. v-model='current.label'
  74. )
  75. v-text-field(
  76. outline
  77. background-color='grey lighten-2'
  78. :label='$t("navigation.icon")'
  79. prepend-icon='casino'
  80. v-model='current.icon'
  81. )
  82. v-select(
  83. outline
  84. background-color='grey lighten-2'
  85. :label='$t("navigation.targetType")'
  86. prepend-icon='near_me'
  87. :items='navTypes'
  88. v-model='current.targetType'
  89. )
  90. v-text-field(
  91. v-if='current.targetType === "external"'
  92. outline
  93. background-color='grey lighten-2'
  94. :label='$t("navigation.target")'
  95. prepend-icon='near_me'
  96. v-model='current.target'
  97. )
  98. v-card-chin
  99. v-spacer
  100. v-btn(color='red', outline, @click='deleteItem(current)')
  101. v-icon(left) delete
  102. span {{$t('navigation.delete', { kind: $t('navigation.link') })}}
  103. v-card(v-else-if='current.kind === "header"')
  104. v-toolbar(dense, color='blue', flat, dark)
  105. .subheading {{$t('navigation.edit', { kind: $t('navigation.header') })}}
  106. v-card-text
  107. v-text-field(
  108. outline
  109. background-color='grey lighten-2'
  110. :label='$t("navigation.label")'
  111. prepend-icon='title'
  112. v-model='current.label'
  113. )
  114. v-card-chin
  115. v-spacer
  116. v-btn(color='red', outline, @click='deleteItem(current)')
  117. v-icon(left) delete
  118. span {{$t('navigation.delete', { kind: $t('navigation.header') })}}
  119. div(v-else-if='current.kind === "divider"')
  120. v-btn.mt-0(color='red', outline, @click='deleteItem(current)')
  121. v-icon(left) delete
  122. span {{$t('navigation.delete', { kind: $t('navigation.divider') })}}
  123. v-card(v-else)
  124. v-card-text.grey--text(v-if='navTree.length > 0') {{$t('navigation.noSelectionText')}}
  125. v-card-text.grey--text(v-else) {{$t('navigation.noItemsText')}}
  126. </template>
  127. <script>
  128. import _ from 'lodash'
  129. import uuid from 'uuid/v4'
  130. import treeSaveMutation from 'gql/admin/navigation/navigation-mutation-save-tree.gql'
  131. import treeQuery from 'gql/admin/navigation/navigation-query-tree.gql'
  132. import draggable from 'vuedraggable'
  133. export default {
  134. components: {
  135. draggable
  136. },
  137. data() {
  138. return {
  139. navTree: [],
  140. current: {}
  141. }
  142. },
  143. computed: {
  144. navTypes() {
  145. return [
  146. { text: this.$t('navigation.navType.external'), value: 'external' },
  147. { text: this.$t('navigation.navType.home'), value: 'home' },
  148. { text: this.$t('navigation.navType.page'), value: 'page' },
  149. { text: this.$t('navigation.navType.searchQuery'), value: 'search' }
  150. ]
  151. }
  152. },
  153. methods: {
  154. addItem(kind) {
  155. let newItem = {
  156. id: uuid(),
  157. kind
  158. }
  159. switch (kind) {
  160. case 'link':
  161. newItem = {
  162. ...newItem,
  163. label: this.$t('navigation.untitled', { kind: this.$t(`navigation.link`) }),
  164. icon: 'chevron_right',
  165. targetType: 'home',
  166. target: '/'
  167. }
  168. break
  169. case 'header':
  170. newItem.label = this.$t('navigation.untitled', { kind: this.$t(`navigation.header`) })
  171. break
  172. }
  173. this.navTree.push(newItem)
  174. this.current = newItem
  175. },
  176. deleteItem(item) {
  177. this.navTree = _.pull(this.navTree, item)
  178. this.current = {}
  179. },
  180. selectItem(item) {
  181. this.current = item
  182. },
  183. async save() {
  184. this.$store.commit(`loadingStart`, 'admin-navigation-save')
  185. try {
  186. const resp = await this.$apollo.mutate({
  187. mutation: treeSaveMutation,
  188. variables: {
  189. tree: this.navTree
  190. }
  191. })
  192. if (_.get(resp, 'data.navigation.updateTree.responseResult.succeeded', false)) {
  193. this.$store.commit('showNotification', {
  194. message: this.$t('navigation.saveSuccess'),
  195. style: 'success',
  196. icon: 'check'
  197. })
  198. } else {
  199. throw new Error(_.get(resp, 'data.navigation.updateTree.responseResult.message', 'An unexpected error occured.'))
  200. }
  201. } catch (err) {
  202. this.$store.commit('showNotification', {
  203. message: err.message,
  204. style: 'red',
  205. icon: 'warning'
  206. })
  207. }
  208. this.$store.commit(`loadingStop`, 'admin-navigation-save')
  209. },
  210. async refresh() {
  211. await this.$apollo.queries.navTree.refetch()
  212. this.current = {}
  213. this.$store.commit('showNotification', {
  214. message: 'Navigation has been refreshed.',
  215. style: 'success',
  216. icon: 'cached'
  217. })
  218. }
  219. },
  220. apollo: {
  221. navTree: {
  222. query: treeQuery,
  223. fetchPolicy: 'network-only',
  224. update: (data) => _.cloneDeep(data.navigation.tree),
  225. watchLoading (isLoading) {
  226. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-tree')
  227. }
  228. }
  229. }
  230. }
  231. </script>
  232. <style lang='scss' scoped>
  233. .clickable {
  234. cursor: pointer;
  235. &:hover {
  236. background-color: rgba(mc('blue', '500'), .25);
  237. }
  238. }
  239. </style>