feat: environment quick peek (#3119)

This commit is contained in:
Liyas Thomas
2023-08-15 19:30:37 +05:30
committed by GitHub
parent 15c2c7bb5b
commit e255c46455
9 changed files with 329 additions and 382 deletions

View File

@@ -204,13 +204,18 @@
"deleted": "Environment deletion", "deleted": "Environment deletion",
"edit": "Edit Environment", "edit": "Edit Environment",
"global": "Global", "global": "Global",
"empty_variables": "No variables",
"global_variables": "Global variables",
"invalid_name": "Please provide a name for the environment", "invalid_name": "Please provide a name for the environment",
"list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
"name": "Name", "name": "Name",
"nested_overflow": "nested environment variables are limited to 10 levels", "nested_overflow": "nested environment variables are limited to 10 levels",
"new": "New Environment", "new": "New Environment",
"no_active_environment": "No active environment",
"no_environment": "No environment", "no_environment": "No environment",
"no_environment_description": "No environments were selected. Choose what to do with the following variables.", "no_environment_description": "No environments were selected. Choose what to do with the following variables.",
"quick_peek": "Environment Quick Peek",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Replace with variable",
"scope": "Scope", "scope": "Scope",
"select": "Select environment", "select": "Select environment",

View File

@@ -77,29 +77,6 @@ declare module '@vue/runtime-core' {
History: typeof import('./components/history/index.vue')['default'] History: typeof import('./components/history/index.vue')['default']
HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default'] HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default']
HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default'] HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default']
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand']
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing']
HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup']
HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow']
HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows']
HttpAuthorization: typeof import('./components/http/Authorization.vue')['default'] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']
HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default'] HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default']
HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default'] HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default']
@@ -119,25 +96,13 @@ declare module '@vue/runtime-core' {
HttpResponse: typeof import('./components/http/Response.vue')['default'] HttpResponse: typeof import('./components/http/Response.vue')['default']
HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default'] HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default']
HttpSidebar: typeof import('./components/http/Sidebar.vue')['default'] HttpSidebar: typeof import('./components/http/Sidebar.vue')['default']
HttpTabHead: typeof import('./components/http/TabHead.vue')['default']
HttpTestResult: typeof import('./components/http/TestResult.vue')['default'] HttpTestResult: typeof import('./components/http/TestResult.vue')['default']
HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default'] HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default']
HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default'] HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default']
HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default'] HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default']
HttpTests: typeof import('./components/http/Tests.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default']
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default'] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideInfo: typeof import('~icons/lucide/info')['default']
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideUsers: typeof import('~icons/lucide/users')['default']
LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default'] LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default']
LensesRenderersAudioLensRenderer: typeof import('./components/lenses/renderers/AudioLensRenderer.vue')['default'] LensesRenderersAudioLensRenderer: typeof import('./components/lenses/renderers/AudioLensRenderer.vue')['default']

View File

@@ -18,14 +18,14 @@
</div> </div>
<div class="inline-flex items-center justify-center flex-1 space-x-2"> <div class="inline-flex items-center justify-center flex-1 space-x-2">
<button <button
class="flex flex-1 items-center justify-between px-2 py-1 bg-primaryDark transition text-secondaryLight cursor-text rounded border border-dividerDark max-w-xs hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary" class="flex flex-1 items-center justify-between px-2 py-1 self-stretch bg-primaryDark transition text-secondaryLight cursor-text rounded border border-dividerDark max-w-60 hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary"
@click="invokeAction('modals.search.toggle')" @click="invokeAction('modals.search.toggle')"
> >
<span class="inline-flex flex-1 items-center"> <span class="inline-flex flex-1 items-center">
<icon-lucide-search class="mr-2 svg-icons" /> <icon-lucide-search class="mr-2 svg-icons" />
{{ t("app.search") }} {{ t("app.search") }}
</span> </span>
<span class="flex"> <span class="flex space-x-1">
<kbd class="shortcut-key">{{ getPlatformSpecialKey() }}</kbd> <kbd class="shortcut-key">{{ getPlatformSpecialKey() }}</kbd>
<kbd class="shortcut-key">K</kbd> <kbd class="shortcut-key">K</kbd>
</span> </span>

View File

@@ -1,203 +1,301 @@
<template> <template>
<tippy <div class="flex divide-x divide-dividerLight">
interactive <tippy
trigger="click" interactive
theme="popover" trigger="click"
:on-shown="() => tippyActions!.focus()" theme="popover"
> :on-shown="() => envSelectorActions!.focus()"
<span
v-tippy="{ theme: 'tooltip' }"
:title="`${t('environment.select')}`"
class="bg-transparent select-wrapper"
> >
<HoppButtonSecondary <span
:icon="IconLayers" v-tippy="{ theme: 'tooltip' }"
:label=" :title="`${t('environment.select')}`"
mdAndLarger class="bg-transparent border-b border-dividerLight select-wrapper"
? selectedEnv.type !== 'NO_ENV_SELECTED'
? selectedEnv.name
: `${t('environment.select')}`
: ''
"
class="flex-1 !justify-start pr-8 rounded-none"
/>
</span>
<template #content="{ hide }">
<div
ref="tippyActions"
role="menu"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
> >
<HoppSmartItem <HoppButtonSecondary
v-if="!isScopeSelector" :icon="IconLayers"
:label="`${t('environment.no_environment')}`" :label="
:info-icon=" mdAndLarger
selectedEnvironmentIndex.type === 'NO_ENV_SELECTED' ? selectedEnv.type !== 'NO_ENV_SELECTED'
? IconCheck ? selectedEnv.name
: undefined : `${t('environment.select')}`
" : ''
:active-info-icon="
selectedEnvironmentIndex.type === 'NO_ENV_SELECTED'
"
@click="
() => {
selectedEnvironmentIndex = { type: 'NO_ENV_SELECTED' }
hide()
}
" "
class="flex-1 !justify-start pr-8 rounded-none"
/> />
<HoppSmartItem </span>
v-else-if="isScopeSelector && modelValue" <template #content="{ hide }">
:label="t('environment.global')" <div
:icon="IconGlobe" ref="envSelectorActions"
:info-icon="modelValue.type === 'global' ? IconCheck : undefined" role="menu"
:active-info-icon="modelValue.type === 'global'" class="flex flex-col focus:outline-none"
@click=" tabindex="0"
() => { @keyup.escape="hide()"
$emit('update:modelValue', {
type: 'global',
})
hide()
}
"
/>
<HoppSmartTabs
v-model="selectedEnvTab"
styles="sticky overflow-x-auto my-2 border border-divider rounded flex-shrink-0 z-10 top-0 bg-primary"
render-inactive-tabs
> >
<HoppSmartTab <HoppSmartItem
:id="'my-environments'" :label="`${t('environment.no_environment')}`"
:label="`${t('environment.my_environments')}`" :info-icon="
selectedEnvironmentIndex.type === 'NO_ENV_SELECTED'
? IconCheck
: undefined
"
:active-info-icon="
selectedEnvironmentIndex.type === 'NO_ENV_SELECTED'
"
@click="
() => {
selectedEnvironmentIndex = { type: 'NO_ENV_SELECTED' }
hide()
}
"
/>
<HoppSmartTabs
v-model="selectedEnvTab"
styles="sticky overflow-x-auto my-2 border border-divider rounded flex-shrink-0 z-0 top-0 bg-primary"
render-inactive-tabs
> >
<HoppSmartItem <HoppSmartTab
v-for="(gen, index) in myEnvironments" :id="'my-environments'"
:key="`gen-${index}`" :label="`${t('environment.my_environments')}`"
:icon="IconLayers"
:label="gen.name"
:info-icon="isEnvActive(index) ? IconCheck : undefined"
:active-info-icon="isEnvActive(index)"
@click="
() => {
handleEnvironmentChange(index, {
type: 'my-environment',
environment: gen,
})
hide()
}
"
/>
<HoppSmartPlaceholder
v-if="myEnvironments.length === 0"
:src="`/images/states/${colorMode.value}/blockchain.svg`"
:alt="`${t('empty.environments')}`"
:text="t('empty.environments')"
> >
</HoppSmartPlaceholder>
</HoppSmartTab>
<HoppSmartTab
:id="'team-environments'"
:label="`${t('environment.team_environments')}`"
:disabled="!isTeamSelected || workspace.type === 'personal'"
>
<div
v-if="teamListLoading"
class="flex flex-col items-center justify-center p-4"
>
<HoppSmartSpinner class="my-4" />
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
</div>
<div v-else-if="isTeamSelected" class="flex flex-col">
<HoppSmartItem <HoppSmartItem
v-for="(gen, index) in teamEnvironmentList" v-for="(gen, index) in myEnvironments"
:key="`gen-team-${index}`" :key="`gen-${index}`"
:icon="IconLayers" :icon="IconLayers"
:label="gen.environment.name" :label="gen.name"
:info-icon="isEnvActive(gen.id) ? IconCheck : undefined" :info-icon="index === selectedEnv.index ? IconCheck : undefined"
:active-info-icon="isEnvActive(gen.id)" :active-info-icon="index === selectedEnv.index"
@click=" @click="
() => { () => {
handleEnvironmentChange(index, { selectedEnvironmentIndex = {
type: 'team-environment', type: 'MY_ENV',
environment: gen, index: index,
}) }
hide() hide()
} }
" "
/> />
<div
<HoppSmartPlaceholder v-if="myEnvironments.length === 0"
v-if="teamEnvironmentList.length === 0" class="flex flex-col items-center justify-center text-secondaryLight"
:src="`/images/states/${colorMode.value}/blockchain.svg`"
:alt="`${t('empty.environments')}`"
:text="t('empty.environments')"
> >
</HoppSmartPlaceholder> <img
:src="`/images/states/${colorMode.value}/blockchain.svg`"
loading="lazy"
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-2"
:alt="`${t('empty.environments')}`"
/>
<span class="pb-2 text-center">
{{ t("empty.environments") }}
</span>
</div>
</HoppSmartTab>
<HoppSmartTab
:id="'team-environments'"
:label="`${t('environment.team_environments')}`"
:disabled="!isTeamSelected || workspace.type === 'personal'"
>
<div
v-if="teamListLoading"
class="flex flex-col items-center justify-center p-4"
>
<HoppSmartSpinner class="my-4" />
<span class="text-secondaryLight">
{{ t("state.loading") }}
</span>
</div>
<div v-if="isTeamSelected" class="flex flex-col">
<HoppSmartItem
v-for="(gen, index) in teamEnvironmentList"
:key="`gen-team-${index}`"
:icon="IconLayers"
:label="gen.environment.name"
:info-icon="
gen.id === selectedEnv.teamEnvID ? IconCheck : undefined
"
:active-info-icon="gen.id === selectedEnv.teamEnvID"
@click="
() => {
selectedEnvironmentIndex = {
type: 'TEAM_ENV',
teamEnvID: gen.id,
teamID: gen.teamID,
environment: gen.environment,
}
hide()
}
"
/>
<div
v-if="teamEnvironmentList.length === 0"
class="flex flex-col items-center justify-center text-secondaryLight"
>
<img
:src="`/images/states/${colorMode.value}/blockchain.svg`"
loading="lazy"
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-2"
:alt="`${t('empty.environments')}`"
/>
<span class="pb-2 text-center">
{{ t("empty.environments") }}
</span>
</div>
</div>
<div
v-if="!teamListLoading && teamAdapterError"
class="flex flex-col items-center py-4"
>
<icon-lucide-help-circle class="mb-4 svg-icons" />
{{ getErrorMessage(teamAdapterError) }}
</div>
</HoppSmartTab>
</HoppSmartTabs>
</div>
</template>
</tippy>
<span class="flex">
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => envQuickPeekActions!.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="`${t('environment.quick_peek')}`"
:icon="IconEye"
class="!px-4"
/>
<template #content="{ hide }">
<div
ref="envQuickPeekActions"
role="menu"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<div
class="sticky top-0 font-semibold truncate flex items-center justify-between text-secondaryDark bg-primary border border-divider rounded pl-4"
>
{{ t("environment.global_variables") }}
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.edit')"
:icon="IconEdit"
@click="
() => {
editGlobalEnv()
hide()
}
"
/>
</div>
<div class="my-2 flex flex-col flex-1 space-y-2 pl-4">
<div class="flex flex-1 space-x-4">
<span class="w-20 truncate text-tiny font-semibold">
{{ t("environment.name") }}
</span>
<span class="w-36 truncate text-tiny font-semibold">
{{ t("environment.value") }}
</span>
</div>
<div
v-for="(variable, index) in globalEnvs"
:key="index"
class="flex flex-1 space-x-4"
>
<span class="text-secondaryLight w-20 truncate">
{{ variable.key }}
</span>
<span class="text-secondaryLight w-36 truncate">
{{ variable.value }}
</span>
</div>
<div v-if="globalEnvs.length === 0" class="text-secondaryLight">
{{ t("environment.empty_variables") }}
</div>
</div> </div>
<div <div
v-if="!teamListLoading && teamAdapterError" class="sticky top-0 font-semibold truncate flex items-center justify-between text-secondaryDark bg-primary border border-divider rounded pl-4"
class="flex flex-col items-center py-4" :class="{
'py-2 pr-4': !selectedEnv.variables,
}"
> >
<icon-lucide-help-circle class="mb-4 svg-icons" /> {{ t("environment.list") }}
{{ getErrorMessage(teamAdapterError) }} <HoppButtonSecondary
v-if="selectedEnv.variables"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.edit')"
:icon="IconEdit"
@click="
() => {
editEnv()
hide()
}
"
/>
</div> </div>
</HoppSmartTab> <div
</HoppSmartTabs> v-if="selectedEnv.type === 'NO_ENV_SELECTED'"
</div> class="text-secondaryLight my-2 flex flex-col flex-1 pl-4"
</template> >
</tippy> {{ t("environment.no_active_environment") }}
</div>
<div v-else class="my-2 flex flex-col flex-1 space-y-2 pl-4">
<div class="flex flex-1 space-x-4">
<span class="w-20 truncate text-tiny font-semibold">
{{ t("environment.name") }}
</span>
<span class="w-36 truncate text-tiny font-semibold">
{{ t("environment.value") }}
</span>
</div>
<div
v-for="(variable, index) in environmentVariables"
:key="index"
class="flex flex-1 space-x-4"
>
<span class="text-secondaryLight w-20 truncate">
{{ variable.key }}
</span>
<span class="text-secondaryLight w-36 truncate">
{{ variable.value }}
</span>
</div>
<div
v-if="environmentVariables.length === 0"
class="text-secondaryLight"
>
{{ t("environment.empty_variables") }}
</div>
</div>
</div>
</template>
</tippy>
</span>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref, watch } from "vue" import { computed, ref, watch } from "vue"
import IconCheck from "~icons/lucide/check" import IconCheck from "~icons/lucide/check"
import IconLayers from "~icons/lucide/layers" import IconLayers from "~icons/lucide/layers"
import IconGlobe from "~icons/lucide/globe" import IconEye from "~icons/lucide/eye"
import IconEdit from "~icons/lucide/edit"
import { TippyComponent } from "vue-tippy" import { TippyComponent } from "vue-tippy"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { GQLError } from "~/helpers/backend/GQLClient" import { GQLError } from "~/helpers/backend/GQLClient"
import { useReadonlyStream, useStream } from "~/composables/stream" import { useReadonlyStream, useStream } from "~/composables/stream"
import { import {
environments$, environments$,
globalEnv$,
selectedEnvironmentIndex$, selectedEnvironmentIndex$,
setSelectedEnvironmentIndex, setSelectedEnvironmentIndex,
} from "~/newstore/environments" } from "~/newstore/environments"
import { changeWorkspace, workspaceStatus$ } from "~/newstore/workspace" import { workspaceStatus$ } from "~/newstore/workspace"
import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter" import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter"
import { useColorMode } from "@composables/theming" import { useColorMode } from "@composables/theming"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core" import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import TeamListAdapter from "~/helpers/teams/TeamListAdapter" import { invokeAction } from "~/helpers/actions"
import { useLocalState } from "~/newstore/localstate"
import { onLoggedIn } from "~/composables/auth"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { TeamEnvironment } from "~/helpers/teams/TeamEnvironment"
import { Environment } from "@hoppscotch/data"
type Scope =
| {
type: "global"
}
| {
type: "my-environment"
environment: Environment
index: number
}
| {
type: "team-environment"
environment: TeamEnvironment
}
const props = defineProps<{
isScopeSelector?: boolean
modelValue?: Scope
}>()
const emit = defineEmits<{
(e: "update:modelValue", data: Scope): void
}>()
const breakpoints = useBreakpoints(breakpointsTailwind) const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md") const mdAndLarger = breakpoints.greater("md")
@@ -212,39 +310,6 @@ const myEnvironments = useReadonlyStream(environments$, [])
const workspace = useReadonlyStream(workspaceStatus$, { type: "personal" }) const workspace = useReadonlyStream(workspaceStatus$, { type: "personal" })
// TeamList-Adapter
const teamListAdapter = new TeamListAdapter(true)
const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const teamListFetched = ref(false)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
onLoggedIn(() => {
!teamListAdapter.isInitialized && teamListAdapter.initialize()
})
const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
REMEMBERED_TEAM_ID.value = team.id
changeWorkspace({
teamID: team.id,
teamName: team.name,
type: "team",
})
}
watch(
() => myTeams.value,
(newTeams) => {
if (newTeams && !teamListFetched.value) {
teamListFetched.value = true
if (REMEMBERED_TEAM_ID.value) {
const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
if (team) switchToTeamWorkspace(team)
}
}
}
)
// TeamEnv List Adapter
const teamEnvListAdapter = new TeamEnvironmentAdapter(undefined) const teamEnvListAdapter = new TeamEnvironmentAdapter(undefined)
const teamListLoading = useReadonlyStream(teamEnvListAdapter.loading$, false) const teamListLoading = useReadonlyStream(teamEnvListAdapter.loading$, false)
const teamAdapterError = useReadonlyStream(teamEnvListAdapter.error$, null) const teamAdapterError = useReadonlyStream(teamEnvListAdapter.error$, null)
@@ -279,157 +344,41 @@ watch(
} }
) )
const handleEnvironmentChange = (
index: number,
env?:
| {
type: "my-environment"
environment: Environment
}
| {
type: "team-environment"
environment: TeamEnvironment
}
) => {
if (props.isScopeSelector && env) {
if (env.type === "my-environment") {
emit("update:modelValue", {
type: "my-environment",
environment: env.environment,
index,
})
} else if (env.type === "team-environment") {
emit("update:modelValue", {
type: "team-environment",
environment: env.environment,
})
}
} else {
if (env && env.type === "my-environment") {
selectedEnvironmentIndex.value = {
type: "MY_ENV",
index,
}
} else if (env && env.type === "team-environment") {
selectedEnvironmentIndex.value = {
type: "TEAM_ENV",
teamEnvID: env.environment.id,
teamID: env.environment.teamID,
environment: env.environment.environment,
}
}
}
}
const isEnvActive = (id: string | number) => {
if (props.isScopeSelector) {
if (props.modelValue?.type === "my-environment") {
return props.modelValue.index === id
} else if (props.modelValue?.type === "team-environment") {
return (
props.modelValue?.type === "team-environment" &&
props.modelValue.environment &&
props.modelValue.environment.id === id
)
}
} else {
if (selectedEnvironmentIndex.value.type === "MY_ENV") {
return selectedEnv.value.index === id
} else {
return (
selectedEnvironmentIndex.value.type === "TEAM_ENV" &&
selectedEnv.value.teamEnvID === id
)
}
}
}
const selectedEnv = computed(() => { const selectedEnv = computed(() => {
if (props.isScopeSelector) { if (selectedEnvironmentIndex.value.type === "MY_ENV") {
if (props.modelValue?.type === "my-environment") { const environment =
return { myEnvironments.value[selectedEnvironmentIndex.value.index]
type: "MY_ENV", return {
index: props.modelValue.index, type: "MY_ENV",
name: props.modelValue.environment?.name, index: selectedEnvironmentIndex.value.index,
} name: environment.name,
} else if (props.modelValue?.type === "team-environment") { variables: environment.variables,
}
} else if (selectedEnvironmentIndex.value.type === "TEAM_ENV") {
const teamEnv = teamEnvironmentList.value.find(
(env) =>
env.id ===
(selectedEnvironmentIndex.value.type === "TEAM_ENV" &&
selectedEnvironmentIndex.value.teamEnvID)
)
if (teamEnv) {
return { return {
type: "TEAM_ENV", type: "TEAM_ENV",
name: props.modelValue.environment.environment.name, name: teamEnv.environment.name,
teamEnvID: props.modelValue.environment.id, teamEnvID: selectedEnvironmentIndex.value.teamEnvID,
} variables: teamEnv.environment.variables,
} else {
return { type: "global", name: "Global" }
}
} else {
if (selectedEnvironmentIndex.value.type === "MY_ENV") {
return {
type: "MY_ENV",
index: selectedEnvironmentIndex.value.index,
name: myEnvironments.value[selectedEnvironmentIndex.value.index].name,
}
} else if (selectedEnvironmentIndex.value.type === "TEAM_ENV") {
const teamEnv = teamEnvironmentList.value.find(
(env) =>
env.id ===
(selectedEnvironmentIndex.value.type === "TEAM_ENV" &&
selectedEnvironmentIndex.value.teamEnvID)
)
if (teamEnv) {
return {
type: "TEAM_ENV",
name: teamEnv.environment.name,
teamEnvID: selectedEnvironmentIndex.value.teamEnvID,
}
} else {
return { type: "NO_ENV_SELECTED" }
} }
} else { } else {
return { type: "NO_ENV_SELECTED" } return { type: "NO_ENV_SELECTED" }
} }
} } else {
}) return { type: "NO_ENV_SELECTED" }
// Set the selected environment as initial scope value
onMounted(() => {
if (props.isScopeSelector) {
if (
selectedEnvironmentIndex.value.type === "MY_ENV" &&
selectedEnvironmentIndex.value.index !== undefined
) {
emit("update:modelValue", {
type: "my-environment",
environment: myEnvironments.value[selectedEnvironmentIndex.value.index],
index: selectedEnvironmentIndex.value.index,
})
} else if (
selectedEnvironmentIndex.value.type === "TEAM_ENV" &&
selectedEnvironmentIndex.value.teamEnvID &&
teamEnvironmentList.value &&
teamEnvironmentList.value.length > 0
) {
const teamEnv = teamEnvironmentList.value.find(
(env) =>
env.id ===
(selectedEnvironmentIndex.value.type === "TEAM_ENV" &&
selectedEnvironmentIndex.value.teamEnvID)
)
if (teamEnv) {
emit("update:modelValue", {
type: "team-environment",
environment: teamEnv,
})
}
} else {
emit("update:modelValue", {
type: "global",
})
}
} }
}) })
// Template refs // Template refs
const tippyActions = ref<TippyComponent | null>(null) const envSelectorActions = ref<TippyComponent | null>(null)
const envQuickPeekActions = ref<TippyComponent | null>(null)
const getErrorMessage = (err: GQLError<string>) => { const getErrorMessage = (err: GQLError<string>) => {
if (err.type === "network_error") { if (err.type === "network_error") {
@@ -443,4 +392,32 @@ const getErrorMessage = (err: GQLError<string>) => {
} }
} }
} }
const globalEnvs = useReadonlyStream(globalEnv$, [])
const environmentVariables = computed(() => {
if (selectedEnv.value.variables) {
return selectedEnv.value.variables
} else {
return []
}
})
const editGlobalEnv = () => {
invokeAction("modals.my.environment.edit", {
envName: "Global",
})
}
const editEnv = () => {
if (selectedEnv.value.type === "MY_ENV" && selectedEnv.value.name) {
invokeAction("modals.my.environment.edit", {
envName: selectedEnv.value.name,
})
} else if (selectedEnv.value.type === "TEAM_ENV" && selectedEnv.value.name) {
invokeAction("modals.team.environment.edit", {
envName: selectedEnv.value.name,
})
}
}
</script> </script>

View File

@@ -11,9 +11,9 @@
@edit-environment="editEnvironment('Global')" @edit-environment="editEnvironment('Global')"
/> />
</div> </div>
<EnvironmentsMy v-if="environmentType.type === 'my-environments'" /> <EnvironmentsMy v-show="environmentType.type === 'my-environments'" />
<EnvironmentsTeams <EnvironmentsTeams
v-if="environmentType.type === 'team-environments'" v-show="environmentType.type === 'team-environments'"
:team="environmentType.selectedTeam" :team="environmentType.selectedTeam"
:team-environments="teamEnvironmentList" :team-environments="teamEnvironmentList"
:loading="loading" :loading="loading"
@@ -201,7 +201,7 @@ const resetSelectedData = () => {
defineActionHandler( defineActionHandler(
"modals.my.environment.edit", "modals.my.environment.edit",
({ envName, variableName }) => { ({ envName, variableName }) => {
editingVariableName.value = variableName if (variableName) editingVariableName.value = variableName
envName === "Global" && editEnvironment("Global") envName === "Global" && editEnvironment("Global")
} }
) )

View File

@@ -109,7 +109,7 @@ const resetSelectedData = () => {
defineActionHandler( defineActionHandler(
"modals.my.environment.edit", "modals.my.environment.edit",
({ envName, variableName }) => { ({ envName, variableName }) => {
editingVariableName.value = variableName if (variableName) editingVariableName.value = variableName
const envIndex: number = environments.value.findIndex( const envIndex: number = environments.value.findIndex(
(environment: Environment) => { (environment: Environment) => {
return environment.name === envName return environment.name === envName

View File

@@ -178,7 +178,7 @@ const getErrorMessage = (err: GQLError<string>) => {
defineActionHandler( defineActionHandler(
"modals.team.environment.edit", "modals.team.environment.edit",
({ envName, variableName }) => { ({ envName, variableName }) => {
editingVariableName.value = variableName if (variableName) editingVariableName.value = variableName
const teamEnvToEdit = props.teamEnvironments.find( const teamEnvToEdit = props.teamEnvironments.find(
(environment) => environment.environment.name === envName (environment) => environment.environment.name === envName
) )

View File

@@ -67,11 +67,11 @@ type HoppActionArgsMap = {
} }
"modals.my.environment.edit": { "modals.my.environment.edit": {
envName: string envName: string
variableName: string variableName?: string
} }
"modals.team.environment.edit": { "modals.team.environment.edit": {
envName: string envName: string
variableName: string variableName?: string
} }
"rest.request.open": { "rest.request.open": {
doc: HoppRESTDocument doc: HoppRESTDocument

View File

@@ -100,7 +100,7 @@
</div> </div>
</div> </div>
<div v-if="hasActions" :class="mdAndLarger ? 'w-64' : 'w-16'"> <div v-if="hasActions" :class="mdAndLarger ? 'w-64' : 'w-28'">
<slot name="actions" /> <slot name="actions" />
</div> </div>
@@ -116,7 +116,7 @@
:style="[ :style="[
`--thumb-width: ${scrollThumb.width}px`, `--thumb-width: ${scrollThumb.width}px`,
`width: calc(100% - ${ `width: calc(100% - ${
hasActions ? (mdAndLarger ? '19rem' : '7rem') : '3rem' hasActions ? (mdAndLarger ? '19rem' : '10rem') : '3rem'
})`, })`,
]" ]"
id="myRange" id="myRange"