|
|
@ -8,7 +8,7 @@ import MidAccountIcon from '~icons/mdi/account-outline' |
|
|
|
import ContentCopyIcon from '~icons/mdi/content-copy' |
|
|
|
import ContentCopyIcon from '~icons/mdi/content-copy' |
|
|
|
import type { User } from '~/lib/types' |
|
|
|
import type { User } from '~/lib/types' |
|
|
|
import { ProjectRole } from '~/lib/enums' |
|
|
|
import { ProjectRole } from '~/lib/enums' |
|
|
|
import { extractSdkResponseErrorMsg, isEmail, projectRoleTagColors } from '~/utils' |
|
|
|
import { extractSdkResponseErrorMsg, projectRoles, isEmail, projectRoleTagColors } from '~/utils' |
|
|
|
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
interface Props { |
|
|
|
show: boolean |
|
|
|
show: boolean |
|
|
@ -27,8 +27,9 @@ const emit = defineEmits(['closed', 'reload']) |
|
|
|
const { project } = useProject() |
|
|
|
const { project } = useProject() |
|
|
|
const { $api, $e } = useNuxtApp() |
|
|
|
const { $api, $e } = useNuxtApp() |
|
|
|
const { copy } = useClipboard() |
|
|
|
const { copy } = useClipboard() |
|
|
|
|
|
|
|
const { dashboardUrl } = $(useDashboard()) |
|
|
|
|
|
|
|
|
|
|
|
const usersData = $ref<Users>({ emails: undefined, role: ProjectRole.Guest, invitationToken: undefined }) |
|
|
|
const usersData = $ref<Users>({ emails: undefined, role: ProjectRole.Viewer, invitationToken: undefined }) |
|
|
|
const formRef = ref() |
|
|
|
const formRef = ref() |
|
|
|
|
|
|
|
|
|
|
|
const useForm = Form.useForm |
|
|
|
const useForm = Form.useForm |
|
|
@ -100,9 +101,7 @@ const saveUser = async () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const inviteUrl = $computed(() => |
|
|
|
const inviteUrl = $computed(() => |
|
|
|
usersData.invitationToken |
|
|
|
usersData.invitationToken ? `${dashboardUrl}/user/authentication/signup/${usersData.invitationToken}` : null, |
|
|
|
? `${location.origin}${location.pathname}#/user/authentication/signup/${usersData.invitationToken}` |
|
|
|
|
|
|
|
: null, |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
const copyUrl = async () => { |
|
|
|
const copyUrl = async () => { |
|
|
@ -119,7 +118,7 @@ const copyUrl = async () => { |
|
|
|
const clickInviteMore = () => { |
|
|
|
const clickInviteMore = () => { |
|
|
|
$e('c:user:invite-more') |
|
|
|
$e('c:user:invite-more') |
|
|
|
usersData.invitationToken = undefined |
|
|
|
usersData.invitationToken = undefined |
|
|
|
usersData.role = ProjectRole.Guest |
|
|
|
usersData.role = ProjectRole.Viewer |
|
|
|
usersData.emails = undefined |
|
|
|
usersData.emails = undefined |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|
</script> |
|
|
@ -128,7 +127,7 @@ const clickInviteMore = () => { |
|
|
|
<a-modal :footer="null" centered :visible="show" :closable="false" width="max(50vw, 44rem)" @cancel="emit('closed')"> |
|
|
|
<a-modal :footer="null" centered :visible="show" :closable="false" width="max(50vw, 44rem)" @cancel="emit('closed')"> |
|
|
|
<div class="flex flex-col"> |
|
|
|
<div class="flex flex-col"> |
|
|
|
<div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full"> |
|
|
|
<div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full"> |
|
|
|
<a-typography-title class="select-none" :level="4"> Share: {{ project.title }} </a-typography-title> |
|
|
|
<a-typography-title class="select-none" :level="4"> {{ $t('activity.share') }}: {{ project.title }} </a-typography-title> |
|
|
|
<a-button type="text" class="!rounded-md mr-1 -mt-1.5" @click="emit('closed')"> |
|
|
|
<a-button type="text" class="!rounded-md mr-1 -mt-1.5" @click="emit('closed')"> |
|
|
|
<template #icon> |
|
|
|
<template #icon> |
|
|
|
<CloseIcon class="flex mx-auto" /> |
|
|
|
<CloseIcon class="flex mx-auto" /> |
|
|
@ -146,11 +145,11 @@ const clickInviteMore = () => { |
|
|
|
|
|
|
|
|
|
|
|
<a-alert class="mt-1" type="success" show-icon> |
|
|
|
<a-alert class="mt-1" type="success" show-icon> |
|
|
|
<template #message> |
|
|
|
<template #message> |
|
|
|
<div class="flex flex-row w-full justify-between items-center"> |
|
|
|
<div class="flex flex-row justify-between items-center py-1"> |
|
|
|
<div class="flex pl-2 text-green-700"> |
|
|
|
<div class="flex pl-2 text-green-700 text-xs"> |
|
|
|
{{ inviteUrl }} |
|
|
|
{{ inviteUrl }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<a-button type="text" class="!rounded-md mr-1" @click="copyUrl"> |
|
|
|
<a-button type="text" class="!rounded-md -mt-0.5" @click="copyUrl"> |
|
|
|
<template #icon> |
|
|
|
<template #icon> |
|
|
|
<ContentCopyIcon class="flex mx-auto text-green-700 h-[1rem]" /> |
|
|
|
<ContentCopyIcon class="flex mx-auto text-green-700 h-[1rem]" /> |
|
|
|
</template> |
|
|
|
</template> |
|
|
@ -158,15 +157,16 @@ const clickInviteMore = () => { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
</a-alert> |
|
|
|
</a-alert> |
|
|
|
|
|
|
|
|
|
|
|
<div class="flex text-xs text-gray-500 mt-2 justify-start ml-2"> |
|
|
|
<div class="flex text-xs text-gray-500 mt-2 justify-start ml-2"> |
|
|
|
Looks like you have not configured mailer yet! Please copy above invite link and send it to |
|
|
|
{{ $t('msg.info.userInviteNoSMTP') }} |
|
|
|
{{ usersData.invitationToken && usersData.emails }} |
|
|
|
{{ usersData.invitationToken && usersData.emails }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="flex flex-row justify-start mt-4 ml-2"> |
|
|
|
<div class="flex flex-row justify-start mt-4 ml-2"> |
|
|
|
<a-button size="small" outlined @click="clickInviteMore"> |
|
|
|
<a-button size="small" outlined @click="clickInviteMore"> |
|
|
|
<div class="flex flex-row justify-center items-center space-x-0.5"> |
|
|
|
<div class="flex flex-row justify-center items-center space-x-0.5"> |
|
|
|
<SendIcon class="flex mx-auto text-gray-600 h-[0.8rem]" /> |
|
|
|
<SendIcon class="flex mx-auto text-gray-600 h-[0.8rem]" /> |
|
|
|
<div class="text-xs text-gray-600">Invite more</div> |
|
|
|
<div class="text-xs text-gray-600">{{ $t('activity.inviteMore') }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</a-button> |
|
|
|
</a-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -197,18 +197,21 @@ const clickInviteMore = () => { |
|
|
|
<a-input |
|
|
|
<a-input |
|
|
|
v-model:value="usersData.emails" |
|
|
|
v-model:value="usersData.emails" |
|
|
|
validate-trigger="onBlur" |
|
|
|
validate-trigger="onBlur" |
|
|
|
placeholder="Email" |
|
|
|
:placeholder="$t('labels.email')" |
|
|
|
:disabled="!!selectedUser" |
|
|
|
:disabled="!!selectedUser" |
|
|
|
/> |
|
|
|
/> |
|
|
|
</a-form-item> |
|
|
|
</a-form-item> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="flex flex-col w-1/4"> |
|
|
|
<div class="flex flex-col w-1/4"> |
|
|
|
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]"> |
|
|
|
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]"> |
|
|
|
<div class="ml-1 mb-1 text-xs text-gray-500">Select User Role:</div> |
|
|
|
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div> |
|
|
|
<a-select v-model:value="usersData.role"> |
|
|
|
<a-select v-model:value="usersData.role"> |
|
|
|
<a-select-option v-for="(role, index) in Object.keys(projectRoleTagColors)" :key="index" :value="role"> |
|
|
|
<a-select-option v-for="(role, index) in projectRoles" :key="index" :value="role"> |
|
|
|
<div class="flex flex-row h-full justify-start items-center"> |
|
|
|
<div class="flex flex-row h-full justify-start items-center"> |
|
|
|
<div :class="`px-2 py-1 flex rounded-full text-xs bg-[${projectRoleTagColors[role]}]`"> |
|
|
|
<div |
|
|
|
|
|
|
|
class="px-2 py-1 flex rounded-full text-xs" |
|
|
|
|
|
|
|
:style="{ backgroundColor: projectRoleTagColors[role] }" |
|
|
|
|
|
|
|
> |
|
|
|
{{ role }} |
|
|
|
{{ role }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -219,10 +222,10 @@ const clickInviteMore = () => { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="flex flex-row justify-center"> |
|
|
|
<div class="flex flex-row justify-center"> |
|
|
|
<a-button type="primary" html-type="submit"> |
|
|
|
<a-button type="primary" html-type="submit"> |
|
|
|
<div v-if="selectedUser">Save</div> |
|
|
|
<div v-if="selectedUser">{{ $t('general.save') }}</div> |
|
|
|
<div v-else class="flex flex-row justify-center items-center space-x-1.5"> |
|
|
|
<div v-else class="flex flex-row justify-center items-center space-x-1.5"> |
|
|
|
<SendIcon class="flex h-[0.8rem]" /> |
|
|
|
<SendIcon class="flex h-[0.8rem]" /> |
|
|
|
<div>Invite</div> |
|
|
|
<div>{{ $t('activity.invite') }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</a-button> |
|
|
|
</a-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|