Files
hoppscotch/packages/hoppscotch-common/src/components/collections/index.vue
2023-04-21 20:20:22 +05:30

2001 lines
57 KiB
Vue

<template>
<div
:class="{
'rounded border border-divider': saveRequest,
'bg-primaryDark':
draggingToRoot && currentReorderingStatus.type !== 'request',
}"
class="flex-1"
@drop.prevent="dropToRoot"
@dragover.prevent="draggingToRoot = true"
@dragend="draggingToRoot = false"
>
<div
class="sticky z-10 flex flex-col flex-shrink-0 overflow-x-auto border-b bg-primary border-dividerLight"
:class="{ 'rounded-t': saveRequest }"
:style="
saveRequest ? 'top: calc(-1 * var(--line-height-body))' : 'top: 0'
"
>
<WorkspaceCurrent :section="t('tab.collections')" />
<input
v-model="filterTexts"
type="search"
autocomplete="off"
:placeholder="t('action.search')"
class="py-2 pl-4 pr-2 bg-transparent"
:disabled="collectionsType.type === 'team-collections'"
/>
</div>
<CollectionsMyCollections
v-if="collectionsType.type === 'my-collections'"
:collections-type="collectionsType"
:filtered-collections="filteredCollections"
:filter-text="filterTexts"
:save-request="saveRequest"
:picked="picked"
@add-folder="addFolder"
@add-request="addRequest"
@edit-collection="editCollection"
@edit-folder="editFolder"
@export-data="exportData"
@remove-collection="removeCollection"
@remove-folder="removeFolder"
@drop-collection="dropCollection"
@update-request-order="updateRequestOrder"
@update-collection-order="updateCollectionOrder"
@edit-request="editRequest"
@duplicate-request="duplicateRequest"
@remove-request="removeRequest"
@select-request="selectRequest"
@select="selectPicked"
@drop-request="dropRequest"
@display-modal-add="displayModalAdd(true)"
@display-modal-import-export="displayModalImportExport(true)"
/>
<CollectionsTeamCollections
v-else
:collections-type="collectionsType"
:team-collection-list="teamCollectionList"
:team-loading-collections="teamLoadingCollections"
:export-loading="exportLoading"
:duplicate-loading="duplicateLoading"
:save-request="saveRequest"
:picked="picked"
:collection-move-loading="collectionMoveLoading"
:request-move-loading="requestMoveLoading"
@add-request="addRequest"
@add-folder="addFolder"
@edit-collection="editCollection"
@edit-folder="editFolder"
@export-data="exportData"
@remove-collection="removeCollection"
@remove-folder="removeFolder"
@edit-request="editRequest"
@duplicate-request="duplicateRequest"
@remove-request="removeRequest"
@select-request="selectRequest"
@select="selectPicked"
@drop-request="dropRequest"
@drop-collection="dropCollection"
@update-request-order="updateRequestOrder"
@update-collection-order="updateCollectionOrder"
@expand-team-collection="expandTeamCollection"
@display-modal-add="displayModalAdd(true)"
@display-modal-import-export="displayModalImportExport(true)"
/>
<div
class="hidden bg-primaryDark flex-col flex-1 items-center py-15 justify-center px-4 text-secondaryLight"
:class="{
'!flex': draggingToRoot && currentReorderingStatus.type !== 'request',
}"
>
<icon-lucide-list-end class="svg-icons !w-8 !h-8" />
</div>
<CollectionsAdd
:show="showModalAdd"
:loading-state="modalLoadingState"
@submit="addNewRootCollection"
@hide-modal="displayModalAdd(false)"
/>
<CollectionsAddRequest
:show="showModalAddRequest"
:loading-state="modalLoadingState"
@add-request="onAddRequest"
@hide-modal="displayModalAddRequest(false)"
/>
<CollectionsAddFolder
:show="showModalAddFolder"
:loading-state="modalLoadingState"
@add-folder="onAddFolder"
@hide-modal="displayModalAddFolder(false)"
/>
<CollectionsEdit
:show="showModalEditCollection"
:editing-collection-name="editingCollectionName ?? ''"
:loading-state="modalLoadingState"
@hide-modal="displayModalEditCollection(false)"
@submit="updateEditingCollection"
/>
<CollectionsEditFolder
:show="showModalEditFolder"
:editing-folder-name="editingFolderName ?? ''"
:loading-state="modalLoadingState"
@submit="updateEditingFolder"
@hide-modal="displayModalEditFolder(false)"
/>
<CollectionsEditRequest
:show="showModalEditRequest"
:editing-request-name="editingRequest ? editingRequest.name : ''"
:loading-state="modalLoadingState"
@submit="updateEditingRequest"
@hide-modal="displayModalEditRequest(false)"
/>
<HoppSmartConfirmModal
:show="showConfirmModal"
:title="confirmModalTitle"
:loading-state="modalLoadingState"
@hide-modal="showConfirmModal = false"
@resolve="resolveConfirmModal"
/>
<CollectionsImportExport
:show="showModalImportExport"
:collections-type="collectionsType.type"
:exporting-team-collections="exportingTeamCollections"
:creating-gist-collection="creatingGistCollection"
:importing-my-collections="importingMyCollections"
@export-json-collection="exportJSONCollection"
@create-collection-gist="createCollectionGist"
@import-to-teams="importToTeams"
@hide-modal="displayModalImportExport(false)"
/>
<TeamsAdd
:show="showTeamModalAdd"
@hide-modal="displayTeamModalAdd(false)"
/>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, ref, watch } from "vue"
import { useToast } from "@composables/toast"
import { useI18n } from "@composables/i18n"
import { Picked } from "~/helpers/types/HoppPicked"
import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
import { useReadonlyStream } from "~/composables/stream"
import { useLocalState } from "~/newstore/localstate"
import { onLoggedIn } from "~/composables/auth"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import {
addRESTCollection,
addRESTFolder,
editRESTCollection,
editRESTFolder,
editRESTRequest,
moveRESTRequest,
removeRESTCollection,
removeRESTFolder,
removeRESTRequest,
restCollections$,
saveRESTRequestAs,
updateRESTRequestOrder,
updateRESTCollectionOrder,
moveRESTFolder,
navigateToFolderWithIndexPath,
restCollectionStore,
} from "~/newstore/collections"
import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
import {
HoppCollection,
HoppRESTRequest,
makeCollection,
} from "@hoppscotch/data"
import { cloneDeep, isEqual } from "lodash-es"
import { GQLError } from "~/helpers/backend/GQLClient"
import {
createNewRootCollection,
createChildCollection,
renameCollection,
deleteCollection,
importJSONToTeam,
moveRESTTeamCollection,
updateOrderRESTTeamCollection,
} from "~/helpers/backend/mutations/TeamCollection"
import {
updateTeamRequest,
createRequestInCollection,
deleteTeamRequest,
moveRESTTeamRequest,
updateOrderRESTTeamRequest,
} from "~/helpers/backend/mutations/TeamRequest"
import { TeamCollection } from "~/helpers/teams/TeamCollection"
import { Collection as NodeCollection } from "./MyCollections.vue"
import {
getCompleteCollectionTree,
getTeamCollectionJSON,
teamCollToHoppRESTColl,
} from "~/helpers/backend/helpers"
import * as E from "fp-ts/Either"
import { platform } from "~/platform"
import { createCollectionGists } from "~/helpers/gist"
import { workspaceStatus$ } from "~/newstore/workspace"
import {
createNewTab,
currentActiveTab,
currentTabID,
getTabRefWithSaveContext,
} from "~/helpers/rest/tab"
import {
getRequestsByPath,
resolveSaveContextOnRequestReorder,
} from "~/helpers/collection/request"
import {
getFoldersByPath,
resolveSaveContextOnCollectionReorder,
updateSaveContextForAffectedRequests,
resetTeamRequestsContext,
} from "~/helpers/collection/collection"
import { currentReorderingStatus$ } from "~/newstore/reordering"
const t = useI18n()
const toast = useToast()
const props = defineProps({
saveRequest: {
type: Boolean,
default: false,
required: false,
},
picked: {
type: Object as PropType<Picked | null>,
default: null,
required: false,
},
})
const emit = defineEmits<{
(event: "select", payload: Picked | null): void
(event: "update-team", team: SelectedTeam): void
(event: "update-collection-type", type: CollectionType["type"]): void
}>()
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type CollectionType =
| {
type: "team-collections"
selectedTeam: SelectedTeam
}
| { type: "my-collections"; selectedTeam: undefined }
const collectionsType = ref<CollectionType>({
type: "my-collections",
selectedTeam: undefined,
})
// Collection Data
const editingCollection = ref<
HoppCollection<HoppRESTRequest> | TeamCollection | null
>(null)
const editingCollectionName = ref<string | null>(null)
const editingCollectionIndex = ref<number | null>(null)
const editingCollectionID = ref<string | null>(null)
const editingFolder = ref<
HoppCollection<HoppRESTRequest> | TeamCollection | null
>(null)
const editingFolderName = ref<string | null>(null)
const editingFolderPath = ref<string | null>(null)
const editingRequest = ref<HoppRESTRequest | null>(null)
const editingRequestIndex = ref<number | null>(null)
const editingRequestID = ref<string | null>(null)
const confirmModalTitle = ref<string | null>(null)
const filterTexts = ref("")
const currentUser = useReadonlyStream(
platform.auth.getCurrentUserStream(),
platform.auth.getCurrentUser()
)
const myCollections = useReadonlyStream(restCollections$, [], "deep")
// Draging
const draggingToRoot = ref(false)
const collectionMoveLoading = ref<string[]>([])
const requestMoveLoading = ref<string[]>([])
// Export - Import refs
const collectionJSON = ref("")
const exportingTeamCollections = ref(false)
const creatingGistCollection = ref(false)
const importingMyCollections = ref(false)
// TeamList-Adapter
const teamListAdapter = new TeamListAdapter(true)
const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false)
// Team Collection Adapter
const teamCollectionAdapter = new TeamCollectionAdapter(null)
const teamCollectionList = useReadonlyStream(
teamCollectionAdapter.collections$,
[]
)
const teamLoadingCollections = useReadonlyStream(
teamCollectionAdapter.loadingCollections$,
[]
)
watch(
() => myTeams.value,
(newTeams) => {
if (newTeams && !teamListFetched.value) {
teamListFetched.value = true
if (REMEMBERED_TEAM_ID.value && currentUser.value) {
const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
if (team) updateSelectedTeam(team)
}
}
}
)
watch(
() => collectionsType.value.selectedTeam,
(newTeam) => {
if (newTeam) {
teamCollectionAdapter.changeTeamID(newTeam.id)
}
}
)
const switchToMyCollections = () => {
collectionsType.value.type = "my-collections"
collectionsType.value.selectedTeam = undefined
teamCollectionAdapter.changeTeamID(null)
}
const expandTeamCollection = (collectionID: string) => {
teamCollectionAdapter.expandCollection(collectionID)
}
const updateSelectedTeam = (team: SelectedTeam) => {
if (team) {
collectionsType.value.type = "team-collections"
collectionsType.value.selectedTeam = team
REMEMBERED_TEAM_ID.value = team.id
emit("update-team", team)
emit("update-collection-type", "team-collections")
}
}
onLoggedIn(() => {
!teamListAdapter.isInitialized && teamListAdapter.initialize()
})
const workspace = useReadonlyStream(workspaceStatus$, { type: "personal" })
// Used to switch collection type and team when user switch workspace in the global workspace switcher
// Check if there is a teamID in the workspace, if yes, switch to team collection and select the team
// If there is no teamID, switch to my environment
watch(
() => workspace.value.teamID,
(teamID) => {
if (!teamID) {
switchToMyCollections()
} else if (teamID) {
const team = myTeams.value?.find((t) => t.id === teamID)
if (team) updateSelectedTeam(team)
}
}
)
// Switch to my-collections and reset the team collection when user logout
watch(
() => currentUser.value,
(user) => {
if (!user) {
switchToMyCollections()
}
}
)
const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
type: "collection",
id: "",
parentID: "",
})
const hasTeamWriteAccess = computed(() => {
if (!collectionsType.value.selectedTeam) return false
if (
collectionsType.value.type === "team-collections" &&
collectionsType.value.selectedTeam.myRole !== "VIEWER"
)
return true
else return false
})
const filteredCollections = computed(() => {
const collections =
collectionsType.value.type === "my-collections" ? myCollections.value : []
if (filterTexts.value === "") return collections
if (collectionsType.value.type === "team-collections") return []
const filterText = filterTexts.value.toLowerCase()
const filteredCollections = []
const isMatch = (text: string) => text.toLowerCase().includes(filterText)
for (const collection of collections) {
const filteredRequests = []
const filteredFolders = []
for (const request of collection.requests) {
if (isMatch(request.name)) filteredRequests.push(request)
}
for (const folder of collection.folders) {
if (isMatch(folder.name)) filteredFolders.push(folder)
const filteredFolderRequests = []
for (const request of folder.requests) {
if (isMatch(request.name)) filteredFolderRequests.push(request)
}
if (filteredFolderRequests.length > 0) {
const filteredFolder = Object.assign({}, folder)
filteredFolder.requests = filteredFolderRequests
filteredFolders.push(filteredFolder)
}
}
if (
filteredRequests.length + filteredFolders.length > 0 ||
isMatch(collection.name)
) {
const filteredCollection = Object.assign({}, collection)
filteredCollection.requests = filteredRequests
filteredCollection.folders = filteredFolders
filteredCollections.push(filteredCollection)
}
}
return filteredCollections
})
const isSelected = ({
collectionIndex,
folderPath,
requestIndex,
collectionID,
folderID,
requestID,
}: {
collectionIndex?: number | undefined
folderPath?: string | undefined
requestIndex?: number | undefined
collectionID?: string | undefined
folderID?: string | undefined
requestID?: string | undefined
}) => {
if (collectionIndex !== undefined) {
return (
props.picked &&
props.picked.pickedType === "my-collection" &&
props.picked.collectionIndex === collectionIndex
)
} else if (requestIndex !== undefined && folderPath !== undefined) {
return (
props.picked &&
props.picked.pickedType === "my-request" &&
props.picked.folderPath === folderPath &&
props.picked.requestIndex === requestIndex
)
} else if (folderPath !== undefined) {
return (
props.picked &&
props.picked.pickedType === "my-folder" &&
props.picked.folderPath === folderPath
)
} else if (collectionID !== undefined) {
return (
props.picked &&
props.picked.pickedType === "teams-collection" &&
props.picked.collectionID === collectionID
)
} else if (requestID !== undefined) {
return (
props.picked &&
props.picked.pickedType === "teams-request" &&
props.picked.requestID === requestID
)
} else if (folderID !== undefined) {
return (
props.picked &&
props.picked.pickedType === "teams-folder" &&
props.picked.folderID === folderID
)
}
}
const modalLoadingState = ref(false)
const exportLoading = ref(false)
const duplicateLoading = ref(false)
const showModalAdd = ref(false)
const showModalAddRequest = ref(false)
const showModalAddFolder = ref(false)
const showModalEditCollection = ref(false)
const showModalEditFolder = ref(false)
const showModalEditRequest = ref(false)
const showModalImportExport = ref(false)
const showConfirmModal = ref(false)
const showTeamModalAdd = ref(false)
const displayModalAdd = (show: boolean) => {
showModalAdd.value = show
if (!show) resetSelectedData()
}
const displayModalAddRequest = (show: boolean) => {
showModalAddRequest.value = show
if (!show) resetSelectedData()
}
const displayModalAddFolder = (show: boolean) => {
showModalAddFolder.value = show
if (!show) resetSelectedData()
}
const displayModalEditCollection = (show: boolean) => {
showModalEditCollection.value = show
if (!show) resetSelectedData()
}
const displayModalEditFolder = (show: boolean) => {
showModalEditFolder.value = show
if (!show) resetSelectedData()
}
const displayModalEditRequest = (show: boolean) => {
showModalEditRequest.value = show
if (!show) resetSelectedData()
}
const displayModalImportExport = (show: boolean) => {
showModalImportExport.value = show
if (!show) resetSelectedData()
}
const displayConfirmModal = (show: boolean) => {
showConfirmModal.value = show
if (!show) resetSelectedData()
}
const displayTeamModalAdd = (show: boolean) => {
showTeamModalAdd.value = show
teamListAdapter.fetchList()
}
const addNewRootCollection = (name: string) => {
if (collectionsType.value.type === "my-collections") {
addRESTCollection(
makeCollection({
name,
folders: [],
requests: [],
})
)
displayModalAdd(false)
} else if (hasTeamWriteAccess.value) {
if (!collectionsType.value.selectedTeam) return
modalLoadingState.value = true
pipe(
createNewRootCollection(name, collectionsType.value.selectedTeam.id),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("collection.created"))
displayModalAdd(false)
}
)
)()
}
}
const addRequest = (payload: {
path: string
folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
const { path, folder } = payload
editingFolder.value = folder
editingFolderPath.value = path
displayModalAddRequest(true)
}
const onAddRequest = (requestName: string) => {
const newRequest = {
...cloneDeep(currentActiveTab.value.document.request),
name: requestName,
}
if (collectionsType.value.type === "my-collections") {
const path = editingFolderPath.value
if (!path) return
const insertionIndex = saveRESTRequestAs(path, newRequest)
createNewTab({
request: newRequest,
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: insertionIndex,
},
})
displayModalAddRequest(false)
} else if (hasTeamWriteAccess.value) {
const folder = editingFolder.value
if (!folder || !collectionsType.value.selectedTeam) return
if (!folder.id) return
modalLoadingState.value = true
const data = {
request: JSON.stringify(newRequest),
teamID: collectionsType.value.selectedTeam.id,
title: requestName,
}
pipe(
createRequestInCollection(folder.id, data),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
(result) => {
const { createRequestInCollection } = result
createNewTab({
request: newRequest,
isDirty: false,
saveContext: {
originLocation: "team-collection",
requestID: createRequestInCollection.id,
collectionID: createRequestInCollection.collection.id,
teamID: createRequestInCollection.collection.team.id,
},
})
modalLoadingState.value = false
displayModalAddRequest(false)
}
)
)()
}
}
const addFolder = (payload: {
path: string
folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
const { path, folder } = payload
editingFolder.value = folder
editingFolderPath.value = path
displayModalAddFolder(true)
}
const onAddFolder = (folderName: string) => {
const path = editingFolderPath.value
if (collectionsType.value.type === "my-collections") {
if (!path) return
addRESTFolder(folderName, path)
displayModalAddFolder(false)
} else if (hasTeamWriteAccess.value) {
const folder = editingFolder.value
if (!folder || !folder.id) return
modalLoadingState.value = true
pipe(
createChildCollection(folderName, folder.id),
TE.match(
(err: GQLError<string>) => {
if (err.error === "team_coll/short_title") {
toast.error(t("folder.name_length_insufficient"))
} else {
toast.error(`${getErrorMessage(err)}`)
}
modalLoadingState.value = false
},
() => {
toast.success(t("folder.created"))
modalLoadingState.value = false
displayModalAddFolder(false)
}
)
)()
}
}
const editCollection = (payload: {
collectionIndex: string
collection: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
const { collectionIndex, collection } = payload
editingCollection.value = collection
if (collectionsType.value.type === "my-collections") {
editingCollectionIndex.value = parseInt(collectionIndex)
editingCollectionName.value = (
collection as HoppCollection<HoppRESTRequest>
).name
} else {
editingCollectionName.value = (collection as TeamCollection).title
}
displayModalEditCollection(true)
}
const updateEditingCollection = (newName: string) => {
if (!editingCollection.value) return
if (!newName) {
toast.error(t("collection.invalid_name"))
return
}
if (collectionsType.value.type === "my-collections") {
const collectionIndex = editingCollectionIndex.value
if (collectionIndex === null) return
const collectionUpdated = {
...editingCollection.value,
name: newName,
}
editRESTCollection(
collectionIndex,
collectionUpdated as NodeCollection["data"]["data"]
)
displayModalEditCollection(false)
} else if (hasTeamWriteAccess.value) {
if (!editingCollection.value.id) return
modalLoadingState.value = true
pipe(
renameCollection(editingCollection.value.id, newName),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("collection.renamed"))
displayModalEditCollection(false)
}
)
)()
}
}
const editFolder = (payload: {
folderPath: string | undefined
folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
const { folderPath, folder } = payload
editingFolder.value = folder
if (collectionsType.value.type === "my-collections" && folderPath) {
editingFolderPath.value = folderPath
editingFolderName.value = (folder as HoppCollection<HoppRESTRequest>).name
} else {
editingFolderName.value = (folder as TeamCollection).title
}
displayModalEditFolder(true)
}
const updateEditingFolder = (newName: string) => {
if (!editingFolder.value) return
if (collectionsType.value.type === "my-collections") {
if (!editingFolderPath.value) return
editRESTFolder(editingFolderPath.value, {
...(editingFolder.value as HoppCollection<HoppRESTRequest>),
name: newName,
})
displayModalEditFolder(false)
} else if (hasTeamWriteAccess.value) {
if (!editingFolder.value.id) return
modalLoadingState.value = true
/* renameCollection can be used to rename both collections and folders
since folder is treated as collection in the BE. */
pipe(
renameCollection(editingFolder.value.id, newName),
TE.match(
(err: GQLError<string>) => {
if (err.error === "team_coll/short_title") {
toast.error(t("folder.name_length_insufficient"))
} else {
toast.error(`${getErrorMessage(err)}`)
}
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("folder.renamed"))
displayModalEditFolder(false)
}
)
)()
}
}
const editRequest = (payload: {
folderPath: string | undefined
requestIndex: string
request: HoppRESTRequest
}) => {
const { folderPath, requestIndex, request } = payload
editingRequest.value = request
if (collectionsType.value.type === "my-collections" && folderPath) {
editingFolderPath.value = folderPath
editingRequestIndex.value = parseInt(requestIndex)
} else {
editingRequestID.value = requestIndex
}
displayModalEditRequest(true)
}
const updateEditingRequest = (newName: string) => {
const request = editingRequest.value
if (!request) return
const requestUpdated = {
...request,
name: newName || request.name,
}
if (collectionsType.value.type === "my-collections") {
const folderPath = editingFolderPath.value
const requestIndex = editingRequestIndex.value
if (folderPath === null || requestIndex === null) return
const possibleActiveTab = getTabRefWithSaveContext({
originLocation: "user-collection",
requestIndex,
folderPath,
})
editRESTRequest(folderPath, requestIndex, requestUpdated)
if (possibleActiveTab) {
possibleActiveTab.value.document.request.name = requestUpdated.name
}
displayModalEditRequest(false)
} else if (hasTeamWriteAccess.value) {
modalLoadingState.value = true
const requestID = editingRequestID.value
const requestName = newName || request.name
if (!requestID) return
const data = {
request: JSON.stringify(requestUpdated),
title: requestName,
}
pipe(
updateTeamRequest(requestID, data),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("request.renamed"))
displayModalEditRequest(false)
}
)
)()
const possibleTab = getTabRefWithSaveContext({
originLocation: "team-collection",
requestID,
})
if (possibleTab) {
possibleTab.value.document.request.name = requestName
}
}
}
const duplicateRequest = (payload: {
folderPath: string
request: HoppRESTRequest
}) => {
const { folderPath, request } = payload
if (!folderPath) return
const newRequest = {
...cloneDeep(request),
name: `${request.name} - ${t("action.duplicate")}`,
}
if (collectionsType.value.type === "my-collections") {
saveRESTRequestAs(folderPath, newRequest)
toast.success(t("request.duplicated"))
} else if (hasTeamWriteAccess.value) {
duplicateLoading.value = true
if (!collectionsType.value.selectedTeam) return
const data = {
request: JSON.stringify(newRequest),
teamID: collectionsType.value.selectedTeam.id,
title: `${request.name} - ${t("action.duplicate")}`,
}
pipe(
createRequestInCollection(folderPath, data),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
duplicateLoading.value = false
},
() => {
duplicateLoading.value = false
toast.success(t("request.duplicated"))
displayModalAddRequest(false)
}
)
)()
}
}
const removeCollection = (id: string) => {
if (collectionsType.value.type === "my-collections")
editingCollectionIndex.value = parseInt(id)
else editingCollectionID.value = id
confirmModalTitle.value = `${t("confirm.remove_collection")}`
displayConfirmModal(true)
}
/**
* Used to delete both collections and folders
* since folder is treated as collection in the BE.
* @param collectionID - ID of the collection or folder to be deleted.
*/
const removeTeamCollectionOrFolder = async (collectionID: string) => {
modalLoadingState.value = true
await pipe(
deleteCollection(collectionID),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
)
)()
}
const onRemoveCollection = () => {
if (collectionsType.value.type === "my-collections") {
const collectionIndex = editingCollectionIndex.value
const collectionToRemove =
collectionIndex || collectionIndex == 0
? navigateToFolderWithIndexPath(restCollectionStore.value.state, [
collectionIndex,
])
: undefined
if (collectionIndex === null) return
if (
isSelected({
collectionIndex,
})
) {
emit("select", null)
}
removeRESTCollection(
collectionIndex,
collectionToRemove ? collectionToRemove.id : undefined
)
resolveSaveContextOnCollectionReorder({
lastIndex: collectionIndex,
newIndex: -1,
folderPath: "", // root folder
length: myCollections.value.length,
})
toast.success(t("state.deleted"))
displayConfirmModal(false)
} else if (hasTeamWriteAccess.value) {
const collectionID = editingCollectionID.value
if (!collectionID) return
if (
isSelected({
collectionID,
})
) {
emit("select", null)
}
removeTeamCollectionOrFolder(collectionID).then(() => {
resetTeamRequestsContext()
})
}
}
const removeFolder = (id: string) => {
if (collectionsType.value.type === "my-collections")
editingFolderPath.value = id
else editingCollectionID.value = id
confirmModalTitle.value = `${t("confirm.remove_folder")}`
displayConfirmModal(true)
}
const onRemoveFolder = () => {
if (collectionsType.value.type === "my-collections") {
const folderPath = editingFolderPath.value
if (!folderPath) return
if (
isSelected({
folderPath,
})
) {
emit("select", null)
}
const folderToRemove = folderPath
? navigateToFolderWithIndexPath(
restCollectionStore.value.state,
folderPath.split("/").map((i) => parseInt(i))
)
: undefined
removeRESTFolder(folderPath, folderToRemove ? folderToRemove.id : undefined)
const parentFolder = folderPath.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
resolveSaveContextOnCollectionReorder({
lastIndex: pathToLastIndex(folderPath),
newIndex: -1,
folderPath: parentFolder,
length: getFoldersByPath(myCollections.value, parentFolder).length,
})
toast.success(t("state.deleted"))
displayConfirmModal(false)
} else if (hasTeamWriteAccess.value) {
const collectionID = editingCollectionID.value
if (!collectionID) return
if (
isSelected({
collectionID,
})
) {
emit("select", null)
}
removeTeamCollectionOrFolder(collectionID).then(() => {
resetTeamRequestsContext()
})
}
}
const removeRequest = (payload: {
folderPath: string | null
requestIndex: string
}) => {
const { folderPath, requestIndex } = payload
if (collectionsType.value.type === "my-collections" && folderPath) {
editingFolderPath.value = folderPath
editingRequestIndex.value = parseInt(requestIndex)
} else {
editingRequestID.value = requestIndex
}
confirmModalTitle.value = `${t("confirm.remove_request")}`
displayConfirmModal(true)
}
const onRemoveRequest = () => {
if (collectionsType.value.type === "my-collections") {
const folderPath = editingFolderPath.value
const requestIndex = editingRequestIndex.value
if (folderPath === null || requestIndex === null) return
if (
isSelected({
folderPath,
requestIndex,
})
) {
emit("select", null)
}
const possibleTab = getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath,
requestIndex,
})
// If there is a tab attached to this request, dissociate its state and mark it dirty
if (possibleTab) {
possibleTab.value.document.saveContext = null
possibleTab.value.document.isDirty = true
}
const requestToRemove = navigateToFolderWithIndexPath(
restCollectionStore.value.state,
folderPath.split("/").map((i) => parseInt(i))
)?.requests[requestIndex]
removeRESTRequest(folderPath, requestIndex, requestToRemove?.id)
// the same function is used to reorder requests since after removing, it's basically doing reorder
resolveSaveContextOnRequestReorder({
lastIndex: requestIndex,
newIndex: -1,
folderPath,
length: getRequestsByPath(myCollections.value, folderPath).length,
})
toast.success(t("state.deleted"))
displayConfirmModal(false)
} else if (hasTeamWriteAccess.value) {
const requestID = editingRequestID.value
if (!requestID) return
if (
isSelected({
requestID,
})
) {
emit("select", null)
}
modalLoadingState.value = true
pipe(
deleteTeamRequest(requestID),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
)
)()
// If there is a tab attached to this request, dissociate its state and mark it dirty
const possibleTab = getTabRefWithSaveContext({
originLocation: "team-collection",
requestID,
})
if (possibleTab) {
possibleTab.value.document.saveContext = null
possibleTab.value.document.isDirty = true
}
}
}
// The request is picked in the save request as modal
const selectPicked = (payload: Picked | null) => {
emit("select", payload)
}
/**
* This function is called when the user clicks on a request
* @param selectedRequest The request that the user clicked on emited from the collection tree
*/
const selectRequest = (selectedRequest: {
request: HoppRESTRequest
folderPath: string | undefined
requestIndex: string
isActive: boolean
}) => {
const { request, folderPath, requestIndex } = selectedRequest
// If there is a request with this save context, switch into it
let possibleTab = null
if (collectionsType.value.type === "team-collections") {
possibleTab = getTabRefWithSaveContext({
originLocation: "team-collection",
requestID: requestIndex,
})
if (possibleTab) {
currentTabID.value = possibleTab.value.id
} else {
createNewTab({
request: cloneDeep(request),
isDirty: false,
saveContext: {
originLocation: "team-collection",
requestID: requestIndex,
},
})
}
} else {
possibleTab = getTabRefWithSaveContext({
originLocation: "user-collection",
requestIndex: parseInt(requestIndex),
folderPath: folderPath!,
})
if (possibleTab) {
currentTabID.value = possibleTab.value.id
} else {
// If not, open the request in a new tab
createNewTab({
request: cloneDeep(request),
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: folderPath!,
requestIndex: parseInt(requestIndex),
},
})
}
}
}
/**
* Used to get the index of the request from the path
* @param path The path of the request
* @returns The index of the request
*/
const pathToLastIndex = (path: string) => {
const pathArr = path.split("/")
return parseInt(pathArr[pathArr.length - 1])
}
/**
* This function is called when the user drops the request inside a collection
* @param payload Object that contains the folder path, request index and the destination collection index
*/
const dropRequest = (payload: {
folderPath?: string | undefined
requestIndex: string
destinationCollectionIndex: string
}) => {
const { folderPath, requestIndex, destinationCollectionIndex } = payload
if (!requestIndex || !destinationCollectionIndex) return
if (collectionsType.value.type === "my-collections" && folderPath) {
moveRESTRequest(
folderPath,
pathToLastIndex(requestIndex),
destinationCollectionIndex
)
const possibleTab = getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath,
requestIndex: pathToLastIndex(requestIndex),
})
// If there is a tab attached to this request, change save its save context
if (possibleTab) {
possibleTab.value.document.saveContext = {
originLocation: "user-collection",
folderPath: destinationCollectionIndex,
requestIndex: getRequestsByPath(
myCollections.value,
destinationCollectionIndex
).length,
}
}
// When it's drop it's basically getting deleted from last folder. reordering last folder accordingly
resolveSaveContextOnRequestReorder({
lastIndex: pathToLastIndex(requestIndex),
newIndex: -1, // being deleted from last folder
folderPath,
length: getRequestsByPath(myCollections.value, folderPath).length,
})
toast.success(`${t("request.moved")}`)
draggingToRoot.value = false
} else if (hasTeamWriteAccess.value) {
// add the request index to the loading array
requestMoveLoading.value.push(requestIndex)
pipe(
moveRESTTeamRequest(destinationCollectionIndex, requestIndex),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
requestMoveLoading.value.splice(
requestMoveLoading.value.indexOf(requestIndex),
1
)
},
() => {
// remove the request index from the loading array
requestMoveLoading.value.splice(
requestMoveLoading.value.indexOf(requestIndex),
1
)
const possibleTab = getTabRefWithSaveContext({
originLocation: "team-collection",
requestID: requestIndex,
})
if (possibleTab) {
possibleTab.value.document.saveContext = {
originLocation: "team-collection",
requestID: requestIndex,
}
}
toast.success(`${t("request.moved")}`)
}
)
)()
}
}
/**
* @param path The path of the collection or request
* @returns The index of the collection or request
*/
const pathToIndex = (path: string) => {
const pathArr = path.split("/")
return pathArr
}
/**
* Used to check if the collection exist as the parent of the childrens
* @param collectionIndexDragged The index of the collection dragged
* @param destinationCollectionIndex The index of the destination collection
* @returns True if the collection exist as the parent of the childrens
*/
const checkIfCollectionIsAParentOfTheChildren = (
collectionIndexDragged: string,
destinationCollectionIndex: string
) => {
const collectionDraggedPath = pathToIndex(collectionIndexDragged)
const destinationCollectionPath = pathToIndex(destinationCollectionIndex)
if (collectionDraggedPath.length < destinationCollectionPath.length) {
const slicedDestinationCollectionPath = destinationCollectionPath.slice(
0,
collectionDraggedPath.length
)
if (isEqual(slicedDestinationCollectionPath, collectionDraggedPath)) {
return true
} else {
return false
}
}
return false
}
const isMoveToSameLocation = (
draggedItemPath: string,
destinationPath: string
) => {
const draggedItemPathArr = pathToIndex(draggedItemPath)
const destinationPathArr = pathToIndex(destinationPath)
if (draggedItemPathArr.length > 0) {
const draggedItemParentPathArr = draggedItemPathArr.slice(
0,
draggedItemPathArr.length - 1
)
if (isEqual(draggedItemParentPathArr, destinationPathArr)) {
return true
} else {
return false
}
}
}
/**
* This function is called when the user moves the collection
* to a different collection or folder
* @param payload - object containing the collection index dragged and the destination collection index
*/
const dropCollection = (payload: {
collectionIndexDragged: string
destinationCollectionIndex: string
}) => {
const { collectionIndexDragged, destinationCollectionIndex } = payload
if (!collectionIndexDragged || !destinationCollectionIndex) return
if (collectionIndexDragged === destinationCollectionIndex) return
if (collectionsType.value.type === "my-collections") {
if (
checkIfCollectionIsAParentOfTheChildren(
collectionIndexDragged,
destinationCollectionIndex
)
) {
toast.error(`${t("team.parent_coll_move")}`)
return
}
//check if the collection is being moved to its own parent
if (
isMoveToSameLocation(collectionIndexDragged, destinationCollectionIndex)
) {
return
}
const parentFolder = collectionIndexDragged
.split("/")
.slice(0, -1)
.join("/") // remove last folder to get parent folder
const totalFoldersOfDestinationCollection =
getFoldersByPath(myCollections.value, destinationCollectionIndex).length -
(parentFolder === destinationCollectionIndex ? 1 : 0)
moveRESTFolder(collectionIndexDragged, destinationCollectionIndex)
resolveSaveContextOnCollectionReorder(
{
lastIndex: pathToLastIndex(collectionIndexDragged),
newIndex: -1,
folderPath: parentFolder,
length: getFoldersByPath(myCollections.value, parentFolder).length,
},
"drop"
)
updateSaveContextForAffectedRequests(
collectionIndexDragged,
`${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
)
draggingToRoot.value = false
toast.success(`${t("collection.moved")}`)
} else if (hasTeamWriteAccess.value) {
// add the collection index to the loading array
collectionMoveLoading.value.push(collectionIndexDragged)
pipe(
moveRESTTeamCollection(
collectionIndexDragged,
destinationCollectionIndex
),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(collectionIndexDragged),
1
)
},
() => {
toast.success(`${t("collection.moved")}`)
// remove the collection index from the loading array
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(collectionIndexDragged),
1
)
}
)
)()
}
}
/**
* Checks if the collection is already in the root
* @param id - path of the collection
* @returns boolean - true if the collection is already in the root
*/
const isAlreadyInRoot = (id: string) => {
const indexPath = pathToIndex(id)
return indexPath.length === 1
}
/**
* This function is called when the user drops the collection
* to the root
* @param payload - object containing the collection index dragged
*/
const dropToRoot = ({ dataTransfer }: DragEvent) => {
if (dataTransfer) {
const collectionIndexDragged = dataTransfer.getData("collectionIndex")
if (!collectionIndexDragged) return
if (collectionsType.value.type === "my-collections") {
// check if the collection is already in the root
if (isAlreadyInRoot(collectionIndexDragged)) {
toast.error(`${t("collection.invalid_root_move")}`)
} else {
moveRESTFolder(collectionIndexDragged, null)
toast.success(`${t("collection.moved")}`)
}
draggingToRoot.value = false
} else if (hasTeamWriteAccess.value) {
// add the collection index to the loading array
collectionMoveLoading.value.push(collectionIndexDragged)
// destination collection index is null since we are moving to root
pipe(
moveRESTTeamCollection(collectionIndexDragged, null),
TE.match(
(err: GQLError<string>) => {
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(collectionIndexDragged),
1
)
toast.error(`${getErrorMessage(err)}`)
},
() => {
// remove the collection index from the loading array
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(collectionIndexDragged),
1
)
toast.success(`${t("collection.moved")}`)
}
)
)()
}
}
}
/**
* Used to check if the request/collection is being moved to the same parent since reorder is only allowed within the same parent
* @param draggedItem - path index of the dragged request
* @param destinationItem - path index of the destination request
* @param destinationCollectionIndex - index of the destination collection
* @returns boolean - true if the request is being moved to the same parent
*/
const isSameSameParent = (
draggedItemPath: string,
destinationItemPath: string | null,
destinationCollectionIndex: string | null
) => {
const draggedItemIndex = pathToIndex(draggedItemPath)
// if the destinationItemPath and destinationCollectionIndex is null, it means the request is being moved to the root
if (destinationItemPath === null && destinationCollectionIndex === null) {
return draggedItemIndex.length === 1
} else if (
destinationItemPath === null &&
destinationCollectionIndex !== null &&
draggedItemIndex.length === 1
) {
return draggedItemIndex[0] === destinationCollectionIndex
} else if (
destinationItemPath === null &&
draggedItemIndex.length !== 1 &&
destinationCollectionIndex !== null
) {
const dragedItemParent = draggedItemIndex.slice(0, -1)
return dragedItemParent.join("/") === destinationCollectionIndex
} else {
if (destinationItemPath === null) return false
const destinationItemIndex = pathToIndex(destinationItemPath)
// length of 1 means the request is in the root
if (draggedItemIndex.length === 1 && destinationItemIndex.length === 1) {
return true
} else if (draggedItemIndex.length === destinationItemIndex.length) {
const dragedItemParent = draggedItemIndex.slice(0, -1)
const destinationItemParent = destinationItemIndex.slice(0, -1)
if (isEqual(dragedItemParent, destinationItemParent)) {
return true
} else {
return false
}
} else {
return false
}
}
}
/**
* This function is called when the user updates the request order in a collection
* @param payload - object containing the request index dragged and the destination request index
* with the destination collection index
*/
const updateRequestOrder = (payload: {
dragedRequestIndex: string
destinationRequestIndex: string | null
destinationCollectionIndex: string
}) => {
const {
dragedRequestIndex,
destinationRequestIndex,
destinationCollectionIndex,
} = payload
if (!dragedRequestIndex || !destinationCollectionIndex) return
if (dragedRequestIndex === destinationRequestIndex) return
if (collectionsType.value.type === "my-collections") {
if (
!isSameSameParent(
dragedRequestIndex,
destinationRequestIndex,
destinationCollectionIndex
)
) {
toast.error(`${t("collection.different_parent")}`)
} else {
updateRESTRequestOrder(
pathToLastIndex(dragedRequestIndex),
destinationRequestIndex
? pathToLastIndex(destinationRequestIndex)
: null,
destinationCollectionIndex
)
toast.success(`${t("request.order_changed")}`)
}
} else if (hasTeamWriteAccess.value) {
// add the request index to the loading array
requestMoveLoading.value.push(dragedRequestIndex)
pipe(
updateOrderRESTTeamRequest(
dragedRequestIndex,
destinationRequestIndex,
destinationCollectionIndex
),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
requestMoveLoading.value.splice(
requestMoveLoading.value.indexOf(dragedRequestIndex),
1
)
},
() => {
toast.success(`${t("request.order_changed")}`)
// remove the request index from the loading array
requestMoveLoading.value.splice(
requestMoveLoading.value.indexOf(dragedRequestIndex),
1
)
}
)
)()
}
}
/**
* This function is called when the user updates the collection or folder order
* @param payload - object containing the collection index dragged and the destination collection index
*/
const updateCollectionOrder = (payload: {
dragedCollectionIndex: string
destinationCollection: {
destinationCollectionIndex: string | null
destinationCollectionParentIndex: string | null
}
}) => {
const { dragedCollectionIndex, destinationCollection } = payload
const { destinationCollectionIndex, destinationCollectionParentIndex } =
destinationCollection
if (!dragedCollectionIndex) return
if (dragedCollectionIndex === destinationCollectionIndex) return
if (collectionsType.value.type === "my-collections") {
if (
!isSameSameParent(
dragedCollectionIndex,
destinationCollectionIndex,
destinationCollectionParentIndex
)
) {
toast.error(`${t("collection.different_parent")}`)
} else {
updateRESTCollectionOrder(
dragedCollectionIndex,
destinationCollectionIndex
)
resolveSaveContextOnCollectionReorder({
lastIndex: pathToLastIndex(dragedCollectionIndex),
newIndex: pathToLastIndex(
destinationCollectionIndex ? destinationCollectionIndex : ""
),
folderPath: dragedCollectionIndex.split("/").slice(0, -1).join("/"),
})
toast.success(`${t("collection.order_changed")}`)
}
} else if (hasTeamWriteAccess.value) {
collectionMoveLoading.value.push(dragedCollectionIndex)
pipe(
updateOrderRESTTeamCollection(
dragedCollectionIndex,
destinationCollectionIndex
),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(dragedCollectionIndex),
1
)
},
() => {
toast.success(`${t("collection.order_changed")}`)
collectionMoveLoading.value.splice(
collectionMoveLoading.value.indexOf(dragedCollectionIndex),
1
)
}
)
)()
}
}
// Import - Export Collection functions
/**
* Export the whole my collection or specific team collection to JSON
*/
const getJSONCollection = async () => {
if (collectionsType.value.type === "my-collections") {
collectionJSON.value = JSON.stringify(myCollections.value, null, 2)
} else {
if (!collectionsType.value.selectedTeam) return
exportingTeamCollections.value = true
pipe(
await getTeamCollectionJSON(collectionsType.value.selectedTeam.id),
E.match(
(err) => {
toast.error(`${getErrorMessage(err)}`)
exportingTeamCollections.value = false
},
(result) => {
const { exportCollectionsToJSON } = result
collectionJSON.value = exportCollectionsToJSON
exportingTeamCollections.value = false
}
)
)
}
return collectionJSON.value
}
/**
* Create a downloadable file from a collection and prompts the user to download it.
* @param collectionJSON - JSON string of the collection
* @param name - Name of the collection set as the file name
*/
const initializeDownloadCollection = (
collectionJSON: string,
name: string | null
) => {
const file = new Blob([collectionJSON], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
if (name) {
a.download = `${name}.json`
} else {
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
}
document.body.appendChild(a)
a.click()
toast.success(t("state.download_started").toString())
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
}
/**
* Export a specific collection or folder
* Triggered by the export button in the tippy menu
* @param collection - Collection or folder to be exported
*/
const exportData = async (
collection: HoppCollection<HoppRESTRequest> | TeamCollection
) => {
if (collectionsType.value.type === "my-collections") {
const collectionJSON = JSON.stringify(collection)
const name = (collection as HoppCollection<HoppRESTRequest>).name
initializeDownloadCollection(collectionJSON, name)
} else {
if (!collection.id) return
exportLoading.value = true
pipe(
getCompleteCollectionTree(collection.id),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
exportLoading.value = false
return
},
(coll) => {
const hoppColl = teamCollToHoppRESTColl(coll)
const collectionJSONString = JSON.stringify(hoppColl)
initializeDownloadCollection(collectionJSONString, hoppColl.name)
exportLoading.value = false
}
)
)()
}
}
const exportJSONCollection = async () => {
await getJSONCollection()
initializeDownloadCollection(collectionJSON.value, null)
}
const createCollectionGist = async () => {
if (!currentUser.value || !currentUser.value.accessToken) {
toast.error(t("profile.no_permission").toString())
return
}
creatingGistCollection.value = true
await getJSONCollection()
pipe(
createCollectionGists(collectionJSON.value, currentUser.value.accessToken),
TE.match(
(err) => {
toast.error(t("error.something_went_wrong").toString())
console.error(err)
creatingGistCollection.value = false
},
(result) => {
toast.success(t("export.gist_created").toString())
creatingGistCollection.value = false
window.open(result.data.html_url)
}
)
)()
}
const importToTeams = async (collection: HoppCollection<HoppRESTRequest>[]) => {
if (!hasTeamWriteAccess.value) {
toast.error(t("team.no_access").toString())
return
}
if (!collectionsType.value.selectedTeam) return
importingMyCollections.value = true
pipe(
importJSONToTeam(
JSON.stringify(collection),
collectionsType.value.selectedTeam.id
),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
importingMyCollections.value = false
},
() => {
importingMyCollections.value = false
displayModalImportExport(false)
}
)
)()
}
const resolveConfirmModal = (title: string | null) => {
if (title === `${t("confirm.remove_collection")}`) onRemoveCollection()
else if (title === `${t("confirm.remove_request")}`) onRemoveRequest()
else if (title === `${t("confirm.remove_folder")}`) onRemoveFolder()
else {
console.error(
`Confirm modal title ${title} is not handled by the component`
)
toast.error(t("error.something_went_wrong"))
displayConfirmModal(false)
}
}
const resetSelectedData = () => {
editingCollection.value = null
editingCollectionIndex.value = null
editingCollectionID.value = null
editingFolder.value = null
editingFolderPath.value = null
editingRequest.value = null
editingRequestIndex.value = null
editingRequestID.value = null
confirmModalTitle.value = null
}
const getErrorMessage = (err: GQLError<string>) => {
console.error(err)
if (err.type === "network_error") {
return t("error.network_error")
} else {
switch (err.error) {
case "team_coll/short_title":
return t("collection.name_length_insufficient")
case "team/invalid_coll_id":
case "bug/team_coll/no_coll_id":
case "team_req/invalid_target_id":
return t("team.invalid_coll_id")
case "team/not_required_role":
return t("profile.no_permission")
case "team_req/not_required_role":
return t("profile.no_permission")
case "Forbidden resource":
return t("profile.no_permission")
case "team_req/not_found":
return t("team.no_request_found")
case "bug/team_req/no_req_id":
return t("team.no_request_found")
case "team/collection_is_parent_coll":
return t("team.parent_coll_move")
case "team/target_and_destination_collection_are_same":
return t("team.same_target_destination")
case "team/target_collection_is_already_root_collection":
return t("collection.invalid_root_move")
case "team_req/requests_not_from_same_collection":
return t("request.different_collection")
case "team/team_collections_have_different_parents":
return t("collection.different_parent")
default:
return t("error.something_went_wrong")
}
}
}
</script>