Browse Source

fix/gui-v2-user-management-added-api-token-management

pull/2854/head
Muhammed Mustafa 2 years ago
parent
commit
fc2e216491
  1. 201
      packages/nc-gui-v2/components/dashboard/settings/ApiTokenManagement.vue
  2. 3
      packages/nc-gui-v2/components/dashboard/settings/SettingsModal.vue
  3. 20
      packages/nc-gui-v2/components/dashboard/settings/UserManagement.vue

201
packages/nc-gui-v2/components/dashboard/settings/ApiTokenManagement.vue

@ -0,0 +1,201 @@
<script setup lang="ts">
import type { ApiTokenType } from '~~/../nocodb-sdk/build/main'
import { useToast } from 'vue-toastification'
import KebabIcon from '~icons/ic/baseline-more-vert'
import MdiPlusIcon from '~icons/mdi/plus'
import CloseIcon from '~icons/material-symbols/close-rounded'
import ReloadIcon from '~icons/mdi/reload'
import VisibilityOpenIcon from '~icons/material-symbols/visibility'
import VisibilityCloseIcon from '~icons/material-symbols/visibility-off'
import MdiDeleteOutlineIcon from '~icons/mdi/delete-outline'
import MdiContentCopyIcon from '~icons/mdi/content-copy'
import { extractSdkResponseErrorMsg } from '~~/utils/errorUtils'
import { copyTextToClipboard } from '~/utils/miscUtils'
const toast = useToast()
interface ApiToken extends ApiTokenType {
show?: boolean
}
const { $api, $e } = useNuxtApp()
const { project } = $(useProject())
let tokensInfo = $ref<ApiToken[] | undefined>([])
let showNewTokenModal = $ref(false)
let showDeleteTokenModal = $ref(false)
let selectedTokenData = $ref<ApiToken>({})
const loadApiTokens = async () => {
if (!project?.id) return
tokensInfo = await $api.apiToken.list(project.id)
}
const openNewTokenModal = () => {
showNewTokenModal = true
$e('c:api-token:generate')
}
const copyToken = (token: string | undefined) => {
if (!token) return
copyTextToClipboard(token)
toast.info('Copied to clipboard')
$e('c:api-token:copy')
}
const generateToken = async () => {
try {
if (!project?.id) return
await $api.apiToken.create(project.id, selectedTokenData)
showNewTokenModal = false
toast.success('Token generated successfully')
selectedTokenData = {}
await loadApiTokens()
} catch (e: any) {
console.error(e)
toast.error(await extractSdkResponseErrorMsg(e))
}
$e('a:api-token:generate')
}
const deleteToken = async () => {
try {
if (!project?.id || !selectedTokenData.token) return
await $api.apiToken.delete(project.id, selectedTokenData.token)
toast.success('Token deleted successfully')
await loadApiTokens()
showDeleteTokenModal = false
} catch (e: any) {
console.error(e)
toast.error(await extractSdkResponseErrorMsg(e))
}
$e('a:api-token:delete')
}
const openDeleteModal = (item: ApiToken) => {
selectedTokenData = item
showDeleteTokenModal = true
}
onMounted(() => {
loadApiTokens()
})
</script>
<template>
<a-modal v-model:visible="showNewTokenModal" :closable="false" width="28rem" centered :footer="null">
<div class="relative flex flex-col h-full">
<a-button type="text" class="!absolute top-0 right-0 rounded-md -mt-2 -mr-3" @click="showNewTokenModal = false">
<template #icon>
<CloseIcon class="flex mx-auto" />
</template>
</a-button>
<div class="flex flex-row justify-center w-full -mt-1">
<a-typography-title :level="5">Generate Token</a-typography-title>
</div>
<div class="flex flex-col mt-3 justify-center space-y-6">
<a-input v-model:value="selectedTokenData.description" placeholder="Description" />
<div class="flex flex-row justify-center">
<a-button type="primary" @click="generateToken"> Generate </a-button>
</div>
</div>
</div>
</a-modal>
<a-modal v-model:visible="showDeleteTokenModal" :closable="false" width="28rem" centered :footer="null">
<div class="flex flex-col h-full">
<div class="flex flex-row justify-center mt-2 text-center w-full text-base">This action will remove this API Token</div>
<div class="flex mt-6 justify-center space-x-2">
<a-button @click="showDeleteTokenModal = false"> Cancel </a-button>
<a-button type="primary" danger @click="deleteToken()"> Confirm </a-button>
</div>
</div>
</a-modal>
<div class="flex flex-col px-10 mt-6">
<div class="flex flex-row justify-end">
<div class="flex flex-row space-x-1">
<a-button size="middle" type="text" @click="loadApiTokens()">
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<ReloadIcon class="text-gray-500" />
<div class="text-gray-500">Reload</div>
</div>
</a-button>
<a-button size="middle" @click="openNewTokenModal">
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<MdiPlusIcon />
<div>Add New Token</div>
</div>
</a-button>
</div>
</div>
<div v-if="tokensInfo" class="w-full flex flex-col mt-2 px-1">
<div class="flex flex-row border-b-1 text-gray-600 text-xs pb-2 pt-2">
<div class="flex w-4/10 pl-2">Description</div>
<div class="flex w-4/10 justify-center">Token</div>
<div class="flex w-2/10 justify-end pr-2">Actions</div>
</div>
<div v-for="(item, index) in tokensInfo" :key="index" class="flex flex-col">
<div class="flex flex-row border-b-1 items-center px-2 py-2">
<div class="flex flex-row w-4/10 flex-wrap overflow-ellipsis">
{{ item.description }}
</div>
<div class="flex w-4/10 justify-center flex-wrap overflow-ellipsis">
<span v-if="item.show">{{ item.token }}</span>
<span v-else>****************************************</span>
</div>
<div class="flex flex-row w-2/10 justify-end">
<a-tooltip placement="bottom">
<template #title>
<span v-if="item.show">Hide API token </span>
<span v-else>Show API token </span>
</template>
<a-button type="text" class="!rounded-md" @click="item.show = !item.show">
<template #icon>
<VisibilityCloseIcon v-if="item.show" height="1.1rem" class="flex mx-auto" />
<VisibilityOpenIcon v-else height="1rem" class="flex mx-auto" />
</template>
</a-button>
</a-tooltip>
<a-tooltip placement="bottom">
<template #title> Copy token to clipboard </template>
<a-button type="text" class="!rounded-md" @click="item.show = !item.show">
<template #icon>
<MdiContentCopyIcon height="1rem" class="flex mx-auto" />
</template>
</a-button>
</a-tooltip>
<a-dropdown :trigger="['click']" class="flex">
<div class="flex flex-row items-center">
<a-button type="text" class="!px-0">
<div class="flex flex-row items-center">
<KebabIcon height="1.2rem" />
</div>
</a-button>
</div>
<template #overlay>
<a-menu>
<a-menu-item>
<a-button type="text" @click="openDeleteModal(item)">
<div class="flex flex-row items-center">
<MdiDeleteOutlineIcon height="1rem" class="flex" />
<div class="text-xs pl-2">Remove API Token</div>
</div>
</a-button>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
</div>
</div>
</div>
</template>

3
packages/nc-gui-v2/components/dashboard/settings/SettingsModal.vue

@ -5,6 +5,7 @@ import AppStore from './AppStore.vue'
import Metadata from './Metadata.vue' import Metadata from './Metadata.vue'
import UIAcl from './UIAcl.vue' import UIAcl from './UIAcl.vue'
import UserManagement from './UserManagement.vue' import UserManagement from './UserManagement.vue'
import ApiTokenManagement from './ApiTokenManagement.vue'
import StoreFrontOutline from '~icons/mdi/storefront-outline' import StoreFrontOutline from '~icons/mdi/storefront-outline'
import TeamFillIcon from '~icons/ri/team-fill' import TeamFillIcon from '~icons/ri/team-fill'
import MultipleTableIcon from '~icons/mdi/table-multiple' import MultipleTableIcon from '~icons/mdi/table-multiple'
@ -44,7 +45,7 @@ const tabsInfo: TabGroup = {
}, },
apiTokenManagement: { apiTokenManagement: {
title: 'API Token Management', title: 'API Token Management',
body: () => AuditTab, body: () => ApiTokenManagement,
}, },
}, },
}, },

20
packages/nc-gui-v2/components/dashboard/settings/UserManagement.vue

@ -142,17 +142,17 @@ watch(
<template> <template>
<div class="flex flex-col w-full px-6"> <div class="flex flex-col w-full px-6">
<UsersModal :key="showUserModal" :show="showUserModal" :selected-user="selectedUser" @closed="showUserModal = false" @reload="loadUsers()"/> <UsersModal :key="showUserModal" :show="showUserModal" :selected-user="selectedUser" @closed="showUserModal = false" @reload="loadUsers()"/>
<a-modal v-model:visible="showUserDeleteModal" :closable="false" width="28rem" centered :footer="null"> <a-modal v-model:visible="showUserDeleteModal" :closable="false" width="28rem" centered :footer="null">
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<div class="flex flex-row justify-center mt-2 text-center w-full text-base"> <div class="flex flex-row justify-center mt-2 text-center w-full text-base">
This action will remove this user from this project This action will remove this user from this project
</div> </div>
<div class="flex mt-6 justify-center space-x-2"> <div class="flex mt-6 justify-center space-x-2">
<a-button @click="showUserDeleteModal = false"> Cancel </a-button> <a-button @click="showUserDeleteModal = false"> Cancel </a-button>
<a-button type="primary" danger @click="deleteUser"> Confirm </a-button> <a-button type="primary" danger @click="deleteUser"> Confirm </a-button>
</div>
</div> </div>
</div> </a-modal>
</a-modal>
<div class="flex flex-row mb-4 mx-4 justify-between"> <div class="flex flex-row mb-4 mx-4 justify-between">
<div class="flex w-1/3" > <div class="flex w-1/3" >
<a-input v-model:value="searchText" placeholder="Filter by email" > <a-input v-model:value="searchText" placeholder="Filter by email" >

Loading…
Cancel
Save