index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <template>
  2. <div :class="{ 'rounded border border-divider': savingMode }">
  3. <div
  4. class="sticky top-0 z-10 flex flex-col border-b divide-dividerLight divide-y border-dividerLight"
  5. :class="{ 'bg-primary': !savingMode }"
  6. >
  7. <input
  8. v-if="showCollActions"
  9. v-model="filterText"
  10. type="search"
  11. autocomplete="off"
  12. :placeholder="$t('action.search')"
  13. class="flex px-4 py-2 bg-transparent"
  14. />
  15. <div class="flex justify-between flex-1">
  16. <ButtonSecondary
  17. svg="plus"
  18. :label="$t('action.new')"
  19. class="!rounded-none"
  20. @click.native="displayModalAdd(true)"
  21. />
  22. <div class="flex">
  23. <ButtonSecondary
  24. v-tippy="{ theme: 'tooltip' }"
  25. to="https://docs.hoppscotch.io/features/collections"
  26. blank
  27. :title="$t('app.wiki')"
  28. svg="help-circle"
  29. />
  30. <ButtonSecondary
  31. v-if="showCollActions"
  32. v-tippy="{ theme: 'tooltip' }"
  33. :title="$t('modal.import_export')"
  34. svg="archive"
  35. @click.native="displayModalImportExport(true)"
  36. />
  37. </div>
  38. </div>
  39. </div>
  40. <div class="flex-col">
  41. <CollectionsGraphqlCollection
  42. v-for="(collection, index) in filteredCollections"
  43. :key="`collection-${index}`"
  44. :picked="picked"
  45. :name="collection.name"
  46. :collection-index="index"
  47. :collection="collection"
  48. :doc="doc"
  49. :is-filtered="filterText.length > 0"
  50. :saving-mode="savingMode"
  51. @edit-collection="editCollection(collection, index)"
  52. @add-request="addRequest($event)"
  53. @add-folder="addFolder($event)"
  54. @edit-folder="editFolder($event)"
  55. @edit-request="editRequest($event)"
  56. @duplicate-request="duplicateRequest($event)"
  57. @select-collection="$emit('use-collection', collection)"
  58. @select="$emit('select', $event)"
  59. />
  60. </div>
  61. <div
  62. v-if="collections.length === 0"
  63. class="flex flex-col items-center justify-center p-4 text-secondaryLight"
  64. >
  65. <img
  66. :src="`/images/states/${$colorMode.value}/pack.svg`"
  67. loading="lazy"
  68. class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
  69. :alt="$t('empty.collections')"
  70. />
  71. <span class="pb-4 text-center">
  72. {{ $t("empty.collections") }}
  73. </span>
  74. <ButtonSecondary
  75. :label="$t('add.new')"
  76. filled
  77. @click.native="displayModalAdd(true)"
  78. />
  79. </div>
  80. <div
  81. v-if="!(filteredCollections.length !== 0 || collections.length === 0)"
  82. class="flex flex-col items-center justify-center p-4 text-secondaryLight"
  83. >
  84. <i class="pb-2 opacity-75 material-icons">manage_search</i>
  85. <span class="my-2 text-center">
  86. {{ $t("state.nothing_found") }} "{{ filterText }}"
  87. </span>
  88. </div>
  89. <CollectionsGraphqlAdd
  90. :show="showModalAdd"
  91. @hide-modal="displayModalAdd(false)"
  92. />
  93. <CollectionsGraphqlEdit
  94. :show="showModalEdit"
  95. :editing-collection="editingCollection"
  96. :editing-collection-index="editingCollectionIndex"
  97. :editing-collection-name="editingCollection ? editingCollection.name : ''"
  98. @hide-modal="displayModalEdit(false)"
  99. />
  100. <CollectionsGraphqlAddRequest
  101. :show="showModalAddRequest"
  102. :folder-path="editingFolderPath"
  103. @add-request="onAddRequest($event)"
  104. @hide-modal="displayModalAddRequest(false)"
  105. />
  106. <CollectionsGraphqlAddFolder
  107. :show="showModalAddFolder"
  108. :folder-path="editingFolderPath"
  109. @add-folder="onAddFolder($event)"
  110. @hide-modal="displayModalAddFolder(false)"
  111. />
  112. <CollectionsGraphqlEditFolder
  113. :show="showModalEditFolder"
  114. :collection-index="editingCollectionIndex"
  115. :folder="editingFolder"
  116. :folder-index="editingFolderIndex"
  117. :folder-path="editingFolderPath"
  118. :editing-folder-name="editingFolder ? editingFolder.name : ''"
  119. @hide-modal="displayModalEditFolder(false)"
  120. />
  121. <CollectionsGraphqlEditRequest
  122. :show="showModalEditRequest"
  123. :folder-path="editingFolderPath"
  124. :request="editingRequest"
  125. :request-index="editingRequestIndex"
  126. :editing-request-name="editingRequest ? editingRequest.name : ''"
  127. @hide-modal="displayModalEditRequest(false)"
  128. />
  129. <CollectionsGraphqlImportExport
  130. :show="showModalImportExport"
  131. @hide-modal="displayModalImportExport(false)"
  132. />
  133. </div>
  134. </template>
  135. <script>
  136. import { defineComponent } from "@nuxtjs/composition-api"
  137. import cloneDeep from "lodash/cloneDeep"
  138. import clone from "lodash/clone"
  139. import { useReadonlyStream } from "~/helpers/utils/composables"
  140. import {
  141. graphqlCollections$,
  142. addGraphqlFolder,
  143. saveGraphqlRequestAs,
  144. } from "~/newstore/collections"
  145. import { getGQLSession, setGQLSession } from "~/newstore/GQLSession"
  146. export default defineComponent({
  147. props: {
  148. // Whether to activate the ability to pick items (activates 'select' events)
  149. savingMode: { type: Boolean, default: false },
  150. doc: { type: Boolean, default: false },
  151. picked: { type: Object, default: null },
  152. // Whether to show the 'New' and 'Import/Export' actions
  153. showCollActions: { type: Boolean, default: true },
  154. },
  155. setup() {
  156. return {
  157. collections: useReadonlyStream(graphqlCollections$, []),
  158. }
  159. },
  160. data() {
  161. return {
  162. showModalAdd: false,
  163. showModalEdit: false,
  164. showModalImportExport: false,
  165. showModalAddRequest: false,
  166. showModalAddFolder: false,
  167. showModalEditFolder: false,
  168. showModalEditRequest: false,
  169. editingCollection: undefined,
  170. editingCollectionIndex: undefined,
  171. editingFolder: undefined,
  172. editingFolderName: undefined,
  173. editingFolderIndex: undefined,
  174. editingFolderPath: undefined,
  175. editingRequest: undefined,
  176. editingRequestIndex: undefined,
  177. filterText: "",
  178. }
  179. },
  180. computed: {
  181. filteredCollections() {
  182. const collections = clone(this.collections)
  183. if (!this.filterText) return collections
  184. const filterText = this.filterText.toLowerCase()
  185. const filteredCollections = []
  186. for (const collection of collections) {
  187. const filteredRequests = []
  188. const filteredFolders = []
  189. for (const request of collection.requests) {
  190. if (request.name.toLowerCase().includes(filterText))
  191. filteredRequests.push(request)
  192. }
  193. for (const folder of collection.folders) {
  194. const filteredFolderRequests = []
  195. for (const request of folder.requests) {
  196. if (request.name.toLowerCase().includes(filterText))
  197. filteredFolderRequests.push(request)
  198. }
  199. if (filteredFolderRequests.length > 0) {
  200. const filteredFolder = Object.assign({}, folder)
  201. filteredFolder.requests = filteredFolderRequests
  202. filteredFolders.push(filteredFolder)
  203. }
  204. }
  205. if (filteredRequests.length + filteredFolders.length > 0) {
  206. const filteredCollection = Object.assign({}, collection)
  207. filteredCollection.requests = filteredRequests
  208. filteredCollection.folders = filteredFolders
  209. filteredCollections.push(filteredCollection)
  210. }
  211. }
  212. return filteredCollections
  213. },
  214. },
  215. methods: {
  216. displayModalAdd(shouldDisplay) {
  217. this.showModalAdd = shouldDisplay
  218. },
  219. displayModalEdit(shouldDisplay) {
  220. this.showModalEdit = shouldDisplay
  221. if (!shouldDisplay) this.resetSelectedData()
  222. },
  223. displayModalImportExport(shouldDisplay) {
  224. this.showModalImportExport = shouldDisplay
  225. },
  226. displayModalAddRequest(shouldDisplay) {
  227. this.showModalAddRequest = shouldDisplay
  228. if (!shouldDisplay) this.resetSelectedData()
  229. },
  230. displayModalAddFolder(shouldDisplay) {
  231. this.showModalAddFolder = shouldDisplay
  232. if (!shouldDisplay) this.resetSelectedData()
  233. },
  234. displayModalEditFolder(shouldDisplay) {
  235. this.showModalEditFolder = shouldDisplay
  236. if (!shouldDisplay) this.resetSelectedData()
  237. },
  238. displayModalEditRequest(shouldDisplay) {
  239. this.showModalEditRequest = shouldDisplay
  240. if (!shouldDisplay) this.resetSelectedData()
  241. },
  242. editCollection(collection, collectionIndex) {
  243. this.$data.editingCollection = collection
  244. this.$data.editingCollectionIndex = collectionIndex
  245. this.displayModalEdit(true)
  246. },
  247. onAddRequest({ name, path }) {
  248. const newRequest = {
  249. ...getGQLSession().request,
  250. name,
  251. }
  252. saveGraphqlRequestAs(path, newRequest)
  253. setGQLSession({
  254. request: newRequest,
  255. schema: "",
  256. response: "",
  257. })
  258. this.displayModalAddRequest(false)
  259. },
  260. addRequest(payload) {
  261. const { path } = payload
  262. this.$data.editingFolderPath = path
  263. this.displayModalAddRequest(true)
  264. },
  265. onAddFolder({ name, path }) {
  266. addGraphqlFolder(name, path)
  267. this.displayModalAddFolder(false)
  268. },
  269. addFolder(payload) {
  270. const { path } = payload
  271. this.$data.editingFolderPath = path
  272. this.displayModalAddFolder(true)
  273. },
  274. editFolder(payload) {
  275. const { folder, folderPath } = payload
  276. this.editingFolder = folder
  277. this.editingFolderPath = folderPath
  278. this.displayModalEditFolder(true)
  279. },
  280. editRequest(payload) {
  281. const {
  282. collectionIndex,
  283. folderIndex,
  284. folderName,
  285. request,
  286. requestIndex,
  287. folderPath,
  288. } = payload
  289. this.$data.editingFolderPath = folderPath
  290. this.$data.editingCollectionIndex = collectionIndex
  291. this.$data.editingFolderIndex = folderIndex
  292. this.$data.editingFolderName = folderName
  293. this.$data.editingRequest = request
  294. this.$data.editingRequestIndex = requestIndex
  295. this.displayModalEditRequest(true)
  296. },
  297. resetSelectedData() {
  298. this.$data.editingCollection = undefined
  299. this.$data.editingCollectionIndex = undefined
  300. this.$data.editingFolder = undefined
  301. this.$data.editingFolderIndex = undefined
  302. this.$data.editingRequest = undefined
  303. this.$data.editingRequestIndex = undefined
  304. },
  305. duplicateRequest({ folderPath, request }) {
  306. saveGraphqlRequestAs(folderPath, {
  307. ...cloneDeep(request),
  308. name: `${request.name} - ${this.$t("action.duplicate")}`,
  309. })
  310. },
  311. },
  312. })
  313. </script>