Files
speckle-server/packages/frontend-2/components/invite/Banner.vue
T
2024-08-27 15:20:36 +02:00

144 lines
3.8 KiB
Vue

<template>
<div :class="mainClasses">
<div :class="mainInfoBlockClasses">
<UserAvatar v-if="invite.invitedBy" :user="invite.invitedBy" :size="avatarSize" />
<WorkspaceAvatar
v-if="invite.workspace"
:logo="invite.workspace.logo"
:default-logo-index="invite.workspace.defaultLogoIndex"
/>
<div class="text-foreground">
<slot name="message" />
</div>
</div>
<div class="flex space-x-2 w-full sm:w-auto shrink-0">
<div v-if="isLoggedIn" class="flex items-center justify-end w-full space-x-2">
<FormButton
v-if="!invite.workspace"
:size="buttonSize"
color="subtle"
text
:full-width="block"
:disabled="loading"
@click="$emit('processed', false, token)"
>
Decline
</FormButton>
<FormButton
:full-width="block"
:size="buttonSize"
color="outline"
class="px-4"
:icon-left="CheckIcon"
:disabled="loading"
@click="$emit('processed', true, token)"
>
Accept
</FormButton>
</div>
<template v-else>
<FormButton
:size="buttonSize"
color="outline"
full-width
:disabled="loading"
@click.stop.prevent="onLoginSignupClick"
>
{{ isForRegisteredUser ? 'Log in' : 'Sign up' }}
</FormButton>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import type { MaybeNullOrUndefined, Optional } from '@speckle/shared'
import type { AvatarUserType } from '~/lib/user/composables/avatar'
import { CheckIcon } from '@heroicons/vue/24/solid'
import { usePostAuthRedirect } from '~/lib/auth/composables/postAuthRedirect'
import {
useNavigateToLogin,
useNavigateToRegistration
} from '~/lib/common/helpers/route'
defineEmits<{
processed: [accept: boolean, token: Optional<string>]
}>()
type GenericInviteItem = {
invitedBy?: AvatarUserType
workspace?: {
id: string
logo?: string
defaultLogoIndex: number
}
user?: MaybeNullOrUndefined<{
id: string
}>
token?: MaybeNullOrUndefined<string>
}
const props = defineProps<{
invite: GenericInviteItem
/**
* Render this as a big block, instead of a small row. Used in full-page project access error pages.
*/
block?: boolean
loading?: boolean
}>()
const route = useRoute()
const { isLoggedIn } = useActiveUser()
const postAuthRedirect = usePostAuthRedirect()
const goToLogin = useNavigateToLogin()
const goToSignUp = useNavigateToRegistration()
const token = computed(
() => props.invite?.token || (route.query.token as Optional<string>)
)
const mainClasses = computed(() => {
const classParts = [
'flex flex-col space-y-4 px-4 py-5 transition border-x border-b first:border-t first:rounded-t-lg last:rounded-b-lg'
]
if (props.block) {
classParts.push('')
} else {
classParts.push('hover:bg-primary-muted')
classParts.push('sm:space-y-0 sm:space-x-2 sm:items-center sm:flex-row sm:py-2')
}
return classParts.join(' ')
})
const mainInfoBlockClasses = computed(() => {
const classParts = ['flex grow items-center']
if (props.block) {
classParts.push('flex-col space-y-2')
} else {
classParts.push('flex-row space-x-2 text-body-xs')
}
return classParts.join(' ')
})
const avatarSize = computed(() => (props.block ? 'xxl' : 'base'))
const buttonSize = computed(() => (props.block ? 'lg' : 'sm'))
const isForRegisteredUser = computed(() => !!props.invite.user?.id)
const onLoginSignupClick = async () => {
postAuthRedirect.setCurrentRoute()
const query = {
token: token.value || undefined
}
if (isForRegisteredUser.value) {
await goToLogin({
query
})
} else {
await goToSignUp({ query })
}
}
</script>