Details.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <SmartModal
  3. v-if="show"
  4. dialog
  5. :title="$t(`environment.${action}`)"
  6. @close="hideModal"
  7. >
  8. <template #body>
  9. <div class="flex flex-col px-2">
  10. <div class="relative flex">
  11. <input
  12. id="selectLabelEnvEdit"
  13. v-model="name"
  14. v-focus
  15. class="input floating-input"
  16. placeholder=" "
  17. type="text"
  18. autocomplete="off"
  19. :disabled="editingEnvironmentIndex === 'Global'"
  20. @keyup.enter="saveEnvironment"
  21. />
  22. <label for="selectLabelEnvEdit">
  23. {{ $t("action.label") }}
  24. </label>
  25. </div>
  26. <div class="flex items-center justify-between flex-1">
  27. <label for="variableList" class="p-4">
  28. {{ $t("environment.variable_list") }}
  29. </label>
  30. <div class="flex">
  31. <ButtonSecondary
  32. v-tippy="{ theme: 'tooltip' }"
  33. :title="$t('action.clear_all')"
  34. :svg="clearIcon"
  35. @click.native="clearContent()"
  36. />
  37. <ButtonSecondary
  38. v-tippy="{ theme: 'tooltip' }"
  39. svg="plus"
  40. :title="$t('add.new')"
  41. @click.native="addEnvironmentVariable"
  42. />
  43. </div>
  44. </div>
  45. <div
  46. v-if="evnExpandError"
  47. class="w-full px-4 py-2 mb-2 overflow-auto font-mono text-red-400 whitespace-normal rounded bg-primaryLight"
  48. >
  49. {{ $t("environment.nested_overflow") }}
  50. </div>
  51. <div class="border rounded divide-y divide-dividerLight border-divider">
  52. <div
  53. v-for="(variable, index) in vars"
  54. :key="`variable-${index}`"
  55. class="flex divide-x divide-dividerLight"
  56. >
  57. <input
  58. v-model="variable.key"
  59. class="flex flex-1 px-4 py-2 bg-transparent"
  60. :placeholder="`${$t('count.variable', { count: index + 1 })}`"
  61. :name="'param' + index"
  62. />
  63. <SmartEnvInput
  64. v-model="variable.value"
  65. :placeholder="`${$t('count.value', { count: index + 1 })}`"
  66. :envs="liveEnvs"
  67. :name="'value' + index"
  68. />
  69. <div class="flex">
  70. <ButtonSecondary
  71. id="variable"
  72. v-tippy="{ theme: 'tooltip' }"
  73. :title="$t('action.remove')"
  74. svg="trash"
  75. color="red"
  76. @click.native="removeEnvironmentVariable(index)"
  77. />
  78. </div>
  79. </div>
  80. <div
  81. v-if="vars.length === 0"
  82. class="flex flex-col items-center justify-center p-4 text-secondaryLight"
  83. >
  84. <img
  85. :src="`/images/states/${$colorMode.value}/blockchain.svg`"
  86. loading="lazy"
  87. class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
  88. :alt="`${$t('empty.environments')}`"
  89. />
  90. <span class="pb-4 text-center">
  91. {{ $t("empty.environments") }}
  92. </span>
  93. <ButtonSecondary
  94. :label="`${$t('add.new')}`"
  95. filled
  96. class="mb-4"
  97. @click.native="addEnvironmentVariable"
  98. />
  99. </div>
  100. </div>
  101. </div>
  102. </template>
  103. <template #footer>
  104. <span>
  105. <ButtonPrimary
  106. :label="`${$t('action.save')}`"
  107. @click.native="saveEnvironment"
  108. />
  109. <ButtonSecondary
  110. :label="`${$t('action.cancel')}`"
  111. @click.native="hideModal"
  112. />
  113. </span>
  114. </template>
  115. </SmartModal>
  116. </template>
  117. <script lang="ts">
  118. import clone from "lodash/clone"
  119. import { computed, defineComponent, PropType } from "@nuxtjs/composition-api"
  120. import * as E from "fp-ts/Either"
  121. import { Environment, parseTemplateStringE } from "@hoppscotch/data"
  122. import {
  123. createEnvironment,
  124. environments$,
  125. getEnviroment,
  126. getGlobalVariables,
  127. globalEnv$,
  128. setCurrentEnvironment,
  129. setGlobalEnvVariables,
  130. updateEnvironment,
  131. } from "~/newstore/environments"
  132. import { useReadonlyStream } from "~/helpers/utils/composables"
  133. export default defineComponent({
  134. props: {
  135. show: Boolean,
  136. action: {
  137. type: String as PropType<"new" | "edit">,
  138. default: "edit",
  139. },
  140. editingEnvironmentIndex: {
  141. type: [Number, String] as PropType<number | "Global" | null>,
  142. default: null,
  143. },
  144. envVars: {
  145. type: Function as PropType<() => Environment["variables"]>,
  146. default: () => [],
  147. },
  148. },
  149. setup(props) {
  150. const globalVars = useReadonlyStream(globalEnv$, [])
  151. const workingEnv = computed(() => {
  152. if (props.editingEnvironmentIndex === "Global") {
  153. return {
  154. name: "Global",
  155. variables: getGlobalVariables(),
  156. } as Environment
  157. } else if (props.action === "new") {
  158. return {
  159. name: "",
  160. variables: props.envVars(),
  161. }
  162. } else if (props.editingEnvironmentIndex !== null) {
  163. return getEnviroment(props.editingEnvironmentIndex)
  164. } else {
  165. return null
  166. }
  167. })
  168. return {
  169. globalVars,
  170. workingEnv,
  171. envList: useReadonlyStream(environments$, []) || props.envVars(),
  172. }
  173. },
  174. data() {
  175. return {
  176. name: null as string | null,
  177. vars: [] as { key: string; value: string }[],
  178. clearIcon: "trash-2",
  179. }
  180. },
  181. computed: {
  182. evnExpandError(): boolean {
  183. for (const variable of this.vars) {
  184. const result = parseTemplateStringE(variable.value, this.vars)
  185. if (E.isLeft(result)) {
  186. console.error("error", result.left)
  187. return true
  188. }
  189. }
  190. return false
  191. },
  192. liveEnvs(): Array<{ key: string; value: string; source: string }> {
  193. if (this.evnExpandError) {
  194. return []
  195. }
  196. if (this.$props.editingEnvironmentIndex === "Global") {
  197. return [...this.vars.map((x) => ({ ...x, source: this.name! }))]
  198. } else {
  199. return [
  200. ...this.vars.map((x) => ({ ...x, source: this.name! })),
  201. ...this.globalVars.map((x) => ({ ...x, source: "Global" })),
  202. ]
  203. }
  204. },
  205. },
  206. watch: {
  207. show() {
  208. this.name = this.workingEnv?.name ?? null
  209. this.vars = clone(this.workingEnv?.variables ?? [])
  210. },
  211. },
  212. methods: {
  213. clearContent() {
  214. this.vars = []
  215. this.clearIcon = "check"
  216. this.$toast.success(`${this.$t("state.cleared")}`)
  217. setTimeout(() => (this.clearIcon = "trash-2"), 1000)
  218. },
  219. addEnvironmentVariable() {
  220. this.vars.push({
  221. key: "",
  222. value: "",
  223. })
  224. },
  225. removeEnvironmentVariable(index: number) {
  226. this.vars.splice(index, 1)
  227. },
  228. saveEnvironment() {
  229. if (!this.name) {
  230. this.$toast.error(`${this.$t("environment.invalid_name")}`)
  231. return
  232. }
  233. if (this.action === "new") {
  234. createEnvironment(this.name)
  235. setCurrentEnvironment(this.envList.length - 1)
  236. }
  237. const environmentUpdated: Environment = {
  238. name: this.name,
  239. variables: this.vars,
  240. }
  241. if (this.editingEnvironmentIndex === "Global")
  242. setGlobalEnvVariables(environmentUpdated.variables)
  243. else if (this.action === "new") {
  244. updateEnvironment(this.envList.length - 1, environmentUpdated)
  245. } else {
  246. updateEnvironment(this.editingEnvironmentIndex!, environmentUpdated)
  247. }
  248. this.hideModal()
  249. },
  250. hideModal() {
  251. this.name = null
  252. this.$emit("hide-modal")
  253. },
  254. },
  255. })
  256. </script>