feat: invite system error states and user prompts
This commit is contained in:
@@ -95,12 +95,15 @@ body {
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
@apply flex-shrink-0;
|
||||
|
||||
font-size: var(--body-line-height) !important;
|
||||
width: var(--body-line-height);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-icons {
|
||||
@apply flex-shrink-0;
|
||||
|
||||
height: var(--body-line-height);
|
||||
width: var(--body-line-height);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<SmartItem
|
||||
svg="log-out"
|
||||
:label="`${$t('auth.logout')}`"
|
||||
:outline="outline"
|
||||
@click.native="
|
||||
() => {
|
||||
$emit('confirm-logout')
|
||||
@@ -24,6 +25,12 @@ import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { signOutUser } from "~/helpers/fb/auth"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
confirmLogout: false,
|
||||
|
||||
@@ -11,21 +11,39 @@
|
||||
{{ $t("team.we_sent_invite_link_description") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex space-y-2 px-4 pt-8 flex-col">
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
border border-dividerLight
|
||||
mt-8
|
||||
rounded
|
||||
flex-col
|
||||
space-y-6
|
||||
p-4
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(invitee, index) in sendInvitesResult"
|
||||
:key="`invitee-${index}`"
|
||||
class="flex items-center"
|
||||
>
|
||||
<i
|
||||
class="material-icons mr-4"
|
||||
:class="
|
||||
invitee.status === 'error' ? 'text-red-500' : 'text-green-500'
|
||||
"
|
||||
>
|
||||
{{ invitee.status === "error" ? "error" : "check_circle" }}
|
||||
</i>
|
||||
<span class="flex truncate">{{ invitee.email }}</span>
|
||||
<p class="flex items-center">
|
||||
<i
|
||||
class="material-icons mr-4"
|
||||
:class="
|
||||
invitee.status === 'error' ? 'text-red-500' : 'text-green-500'
|
||||
"
|
||||
>
|
||||
{{
|
||||
invitee.status === "error"
|
||||
? "error_outline"
|
||||
: "mark_email_read"
|
||||
}}
|
||||
</i>
|
||||
<span class="truncate">{{ invitee.email }}</span>
|
||||
</p>
|
||||
<p v-if="invitee.status === 'error'" class="ml-8 text-red-500 mt-2">
|
||||
{{ getErrorMessage(invitee.error) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -375,18 +393,18 @@ const removeNewInvitee = (id: number) => {
|
||||
newInvites.value.splice(id, 1)
|
||||
}
|
||||
|
||||
type SendInvitesErrorType = {
|
||||
email: Email
|
||||
status: "error"
|
||||
error: GQLError<CreateTeamInvitationErrors>
|
||||
} | {
|
||||
email: Email
|
||||
status: "success"
|
||||
}
|
||||
type SendInvitesErrorType =
|
||||
| {
|
||||
email: Email
|
||||
status: "error"
|
||||
error: GQLError<CreateTeamInvitationErrors>
|
||||
}
|
||||
| {
|
||||
email: Email
|
||||
status: "success"
|
||||
}
|
||||
|
||||
const sendInvitesResult = ref<
|
||||
Array<SendInvitesErrorType>
|
||||
>([])
|
||||
const sendInvitesResult = ref<Array<SendInvitesErrorType>>([])
|
||||
|
||||
const sendingInvites = ref<boolean>(false)
|
||||
|
||||
@@ -428,7 +446,7 @@ const sendInvites = async () => {
|
||||
(err) => ({
|
||||
status: "error" as const,
|
||||
email: newInvites.value[i].key as Email,
|
||||
error: err
|
||||
error: err,
|
||||
}),
|
||||
() => ({
|
||||
status: "success" as const,
|
||||
@@ -445,6 +463,23 @@ const sendInvites = async () => {
|
||||
sendingInvites.value = false
|
||||
}
|
||||
|
||||
const getErrorMessage = (error: SendInvitesErrorType) => {
|
||||
if (error.type === "network_error") {
|
||||
return t("error.network_error")
|
||||
} else {
|
||||
switch (error.error) {
|
||||
case "team/invalid_id":
|
||||
return t("team.invalid_id")
|
||||
case "team/member_not_found":
|
||||
return t("team.member_not_found")
|
||||
case "team_invite/already_member":
|
||||
return t("team.already_member")
|
||||
case "team_invite/member_has_invite":
|
||||
return t("team.member_has_invite")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hideModal = () => {
|
||||
sendingInvites.value = false
|
||||
sendInvitesResult.value = []
|
||||
|
||||
@@ -201,7 +201,9 @@ export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
|
||||
(gqlErr) =>
|
||||
<GQLError<DocErrorType>>{
|
||||
type: "gql_error",
|
||||
error: parseGQLErrorString(gqlErr ?? "") as DocErrorType,
|
||||
error: parseGQLErrorString(
|
||||
gqlErr ?? ""
|
||||
) as DocErrorType,
|
||||
},
|
||||
// The right case (it was a GraphQL Error)
|
||||
(networkErr) =>
|
||||
@@ -244,7 +246,8 @@ export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
|
||||
return response
|
||||
}
|
||||
|
||||
const parseGQLErrorString = (s: string) => s.startsWith("[GraphQL] ") ? s.split("[GraphQL] ")[1] : s
|
||||
const parseGQLErrorString = (s: string) =>
|
||||
s.startsWith("[GraphQL] ") ? s.split("[GraphQL] ")[1] : s
|
||||
|
||||
export const runMutation = <
|
||||
DocType,
|
||||
@@ -273,13 +276,13 @@ export const runMutation = <
|
||||
// Result is null
|
||||
pipe(
|
||||
result.error?.networkError,
|
||||
E.fromNullable(result.error?.name),
|
||||
E.fromNullable(result.error?.message),
|
||||
E.match(
|
||||
// The left case (network error was null)
|
||||
(gqlErr) =>
|
||||
<GQLError<DocErrors>>{
|
||||
type: "gql_error",
|
||||
error: gqlErr,
|
||||
error: parseGQLErrorString(gqlErr ?? ""),
|
||||
},
|
||||
// The right case (it was a network error)
|
||||
(networkErr) =>
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
"gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again",
|
||||
"incorrect_email": "Incorrect email",
|
||||
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
|
||||
"network_error": "There seems to be a network error. Please try again.",
|
||||
"network_fail": "Could not send request",
|
||||
"no_duration": "No duration",
|
||||
"something_went_wrong": "Something went wrong"
|
||||
@@ -470,19 +471,21 @@
|
||||
"websocket": "WebSocket"
|
||||
},
|
||||
"team": {
|
||||
"already_member": "You are already a member of this team. Contact your team owner.",
|
||||
"create_new": "Create new team",
|
||||
"deleted": "Team deleted",
|
||||
"edit": "Edit Team",
|
||||
"email": "E-mail",
|
||||
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
|
||||
"exit": "Exit Team",
|
||||
"exit_disabled": "Only owner cannot exit the team",
|
||||
"invalid_email_format": "Email format is invalid",
|
||||
"invalid_member_permission": "Please provide a valid permission to the team member",
|
||||
"invalid_id": "Invalid team ID. Contact your team owner.",
|
||||
"invalid_invite_link": "Invalid invite link",
|
||||
"invalid_invite_link_description": "The link you followed is invalid. Contact your team owner.",
|
||||
"invalid_member_permission": "Please provide a valid permission to the team member",
|
||||
"invite": "Invite",
|
||||
"login_to_continue": "Login to continue",
|
||||
"login_to_continue_description": "You need to be logged in to join a team.",
|
||||
"logout_and_try_again": "Logout and sign in with another account",
|
||||
"invite_more": "Invite more",
|
||||
"invite_tooltip": "Invite people to this workspace",
|
||||
"invited_to_team": "{owner} invited you to join {team}",
|
||||
@@ -490,6 +493,10 @@
|
||||
"join_beta": "Join the beta program to access teams.",
|
||||
"join_team": "Join {team}",
|
||||
"left": "You left the team",
|
||||
"login_to_continue": "Login to continue",
|
||||
"login_to_continue_description": "You need to be logged in to join a team.",
|
||||
"member_has_invite": "This email ID already has an invite. Contact your team owner.",
|
||||
"member_not_found": "Member not found. Contact your team owner.",
|
||||
"member_removed": "User removed",
|
||||
"member_role_updated": "User roles updated",
|
||||
"members": "Members",
|
||||
@@ -498,6 +505,9 @@
|
||||
"new_created": "New team created",
|
||||
"new_name": "My New Team",
|
||||
"no_access": "You do not have edit access to these collections",
|
||||
"no_invite_found": "Invitation not found. Contact your team owner.",
|
||||
"not_invitee": "Invite link doesn't match with your account details. Contact your team owner.",
|
||||
"not_valid_viewer": "You are not a valid viewer. Contact your team owner.",
|
||||
"pending_invites": "Pending invites",
|
||||
"permissions": "Permissions",
|
||||
"saved": "Team saved",
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
v-if="invalidLink"
|
||||
class="flex flex-1 items-center justify-center flex-col"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">report</i>
|
||||
<i class="opacity-75 pb-2 material-icons">error_outline</i>
|
||||
<h1 class="heading text-center">
|
||||
{{ $t("team.invalid_invite_link") }}
|
||||
</h1>
|
||||
<p class="text-center">
|
||||
<p class="text-center mt-2">
|
||||
{{ $t("team.invalid_invite_link_description") }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -36,8 +36,31 @@
|
||||
v-if="!inviteDetails.loading && E.isLeft(inviteDetails.data)"
|
||||
class="flex flex-col p-4 items-center"
|
||||
>
|
||||
<i class="mb-4 material-icons">help_outline</i>
|
||||
{{ $t("error.something_went_wrong") }}
|
||||
<i class="mb-4 material-icons">error_outline</i>
|
||||
<p>
|
||||
{{ getErrorMessage(inviteDetails.data.left.error) }}
|
||||
</p>
|
||||
<p
|
||||
class="
|
||||
p-4
|
||||
items-center
|
||||
mt-8
|
||||
rounded
|
||||
flex-col
|
||||
border border-dividerLight
|
||||
flex
|
||||
"
|
||||
>
|
||||
<span class="mb-4">
|
||||
{{ $t("team.logout_and_try_again") }}
|
||||
</span>
|
||||
<span class="flex">
|
||||
<FirebaseLogout
|
||||
v-if="inviteDetails.data.left.type === 'gql_error'"
|
||||
outline
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-if="!inviteDetails.loading && E.isRight(inviteDetails.data)"
|
||||
@@ -91,7 +114,7 @@ import { defineComponent, useRoute } from "@nuxtjs/composition-api"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import { useGQLQuery } from "~/helpers/backend/GQLClient"
|
||||
import { GQLError, useGQLQuery } from "~/helpers/backend/GQLClient"
|
||||
import {
|
||||
GetInviteDetailsDocument,
|
||||
GetInviteDetailsQuery,
|
||||
@@ -123,11 +146,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
onLoggedIn(() => {
|
||||
console.log("loog aayi")
|
||||
|
||||
if (typeof route.value.query.id === "string") {
|
||||
console.log("query run aayi", route.value.query.id)
|
||||
|
||||
inviteDetails.execute({
|
||||
inviteID: route.value.query.id,
|
||||
})
|
||||
@@ -180,6 +199,28 @@ export default defineComponent({
|
||||
)
|
||||
)()
|
||||
},
|
||||
getErrorMessage(error: GQLError<GetInviteDetailsError>) {
|
||||
if (error.type === "network_error") {
|
||||
return this.$t("error.network_error")
|
||||
} else {
|
||||
switch (error) {
|
||||
case "team_invite/not_valid_viewer":
|
||||
return this.$t("team.not_valid_viewer")
|
||||
case "team_invite/not_found":
|
||||
return this.$t("team.not_found")
|
||||
case "team_invite/no_invite_found":
|
||||
return this.$t("team.no_invite_found")
|
||||
case "team_invite/not_invitee":
|
||||
return this.$t("team.not_invitee")
|
||||
case "team_invite/already_member":
|
||||
return this.$t("team.already_member")
|
||||
case "team_invite/email_do_not_match":
|
||||
return this.$t("team.email_do_not_match")
|
||||
default:
|
||||
return this.$t("error.something_went_wrong")
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user