returns error message from server when add new member fails
This commit is contained in:
@@ -1,36 +1,36 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from '@hookform/resolvers/zod'
|
||||||
import { MembershipSchemas, type UtilitySchemas } from "@plunk/shared";
|
import { MembershipSchemas, type UtilitySchemas } from '@plunk/shared'
|
||||||
import type { Project, Role } from "@prisma/client";
|
import type { Project, Role } from '@prisma/client'
|
||||||
import { motion } from "framer-motion";
|
import { motion } from 'framer-motion'
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router'
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from 'react-hook-form'
|
||||||
import { toast } from "sonner";
|
import { toast } from 'sonner'
|
||||||
import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from "../../components";
|
import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from '../../components'
|
||||||
import { Dashboard } from "../../layouts";
|
import { Dashboard } from '../../layouts'
|
||||||
import { useActiveProject, useActiveProjectMemberships, useProjects } from "../../lib/hooks/projects";
|
import { useActiveProject, useActiveProjectMemberships, useProjects } from '../../lib/hooks/projects'
|
||||||
import { useUser } from "../../lib/hooks/users";
|
import { useUser } from '../../lib/hooks/users'
|
||||||
import { network } from "../../lib/network";
|
import { network } from '../../lib/network'
|
||||||
|
|
||||||
interface EmailValues {
|
interface EmailValues {
|
||||||
email: string;
|
email: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const [showInviteModal, setShowInviteModal] = useState(false);
|
const [showInviteModal, setShowInviteModal] = useState(false)
|
||||||
const [showLeaveModal, setShowLeaveModal] = useState(false);
|
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
||||||
|
|
||||||
const [project, setProject] = useState<Project>();
|
const [project, setProject] = useState<Project>()
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
|
|
||||||
const activeProject = useActiveProject();
|
const activeProject = useActiveProject()
|
||||||
const { data: user } = useUser();
|
const { data: user } = useUser()
|
||||||
const { data: projects, mutate: projectMutate } = useProjects();
|
const { data: projects, mutate: projectMutate } = useProjects()
|
||||||
const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships();
|
const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -38,89 +38,91 @@ export default function Index() {
|
|||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<EmailValues>({
|
} = useForm<EmailValues>({
|
||||||
resolver: zodResolver(MembershipSchemas.invite.omit({ id: true, role: true })),
|
resolver: zodResolver(MembershipSchemas.invite.omit({ id: true, role: true })),
|
||||||
});
|
})
|
||||||
|
|
||||||
if (activeProject && !project) {
|
if (activeProject && !project) {
|
||||||
setProject(activeProject);
|
setProject(activeProject)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!project || !projects || !memberships || !user) {
|
if (!project || !projects || !memberships || !user) {
|
||||||
return <FullscreenLoader />;
|
return <FullscreenLoader />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activeProject) {
|
if (!activeProject) {
|
||||||
return <FullscreenLoader />;
|
return <FullscreenLoader />
|
||||||
}
|
}
|
||||||
|
|
||||||
const inviteAccount = (data: EmailValues) => {
|
const inviteAccount = (data: EmailValues) => {
|
||||||
toast.promise(
|
toast.promise(
|
||||||
network.mock<
|
network.mock<
|
||||||
{
|
{
|
||||||
success: true;
|
success: true
|
||||||
members: {
|
members: {
|
||||||
userId: string;
|
userId: string
|
||||||
email: string;
|
email: string
|
||||||
role: Role;
|
role: Role
|
||||||
}[];
|
}[]
|
||||||
},
|
},
|
||||||
typeof MembershipSchemas.invite
|
typeof MembershipSchemas.invite
|
||||||
>(project.secret, "POST", "/memberships/invite", {
|
>(project.secret, 'POST', '/memberships/invite', {
|
||||||
id: project.id,
|
id: project.id,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
role: "ADMIN",
|
role: 'ADMIN',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
loading: "Adding new member",
|
loading: 'Adding new member',
|
||||||
success: async (result) => {
|
success: async (result) => {
|
||||||
await membershipMutate(result.members);
|
await membershipMutate(result.members)
|
||||||
setShowInviteModal(false);
|
setShowInviteModal(false)
|
||||||
|
|
||||||
return "Added new member";
|
return 'Added new member'
|
||||||
},
|
},
|
||||||
error: "Could not add new member!",
|
error: (e) => {
|
||||||
},
|
return e.message
|
||||||
);
|
},
|
||||||
};
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const kickAccount = (email: string) => {
|
const kickAccount = (email: string) => {
|
||||||
void network
|
void network
|
||||||
.fetch<
|
.fetch<
|
||||||
{
|
{
|
||||||
success: true;
|
success: true
|
||||||
members: {
|
members: {
|
||||||
userId: string;
|
userId: string
|
||||||
email: string;
|
email: string
|
||||||
role: Role;
|
role: Role
|
||||||
}[];
|
}[]
|
||||||
},
|
},
|
||||||
typeof MembershipSchemas.kick
|
typeof MembershipSchemas.kick
|
||||||
>("POST", "/memberships/kick", {
|
>('POST', '/memberships/kick', {
|
||||||
id: project.id,
|
id: project.id,
|
||||||
email,
|
email,
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
await membershipMutate(res.members);
|
await membershipMutate(res.members)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const leaveProject = () => {
|
const leaveProject = () => {
|
||||||
void network
|
void network
|
||||||
.fetch<
|
.fetch<
|
||||||
{
|
{
|
||||||
success: true;
|
success: true
|
||||||
memberships: Project[];
|
memberships: Project[]
|
||||||
},
|
},
|
||||||
typeof UtilitySchemas.id
|
typeof UtilitySchemas.id
|
||||||
>("POST", "/memberships/leave", {
|
>('POST', '/memberships/leave', {
|
||||||
id: project.id,
|
id: project.id,
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
await projectMutate(res.memberships);
|
await projectMutate(res.memberships)
|
||||||
localStorage.removeItem("project");
|
localStorage.removeItem('project')
|
||||||
await router.push("/");
|
await router.push('/')
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -128,29 +130,29 @@ export default function Index() {
|
|||||||
isOpen={showLeaveModal}
|
isOpen={showLeaveModal}
|
||||||
onToggle={() => setShowLeaveModal(!showLeaveModal)}
|
onToggle={() => setShowLeaveModal(!showLeaveModal)}
|
||||||
onAction={leaveProject}
|
onAction={leaveProject}
|
||||||
type={"danger"}
|
type={'danger'}
|
||||||
title={"Are you sure?"}
|
title={'Are you sure?'}
|
||||||
description={
|
description={
|
||||||
memberships.length === 1
|
memberships.length === 1
|
||||||
? "You are the last person in this project, if you leave it we will automatically delete it!"
|
? 'You are the last person in this project, if you leave it we will automatically delete it!'
|
||||||
: "Leaving a project is permanent, you will lose access to the data and will need to be reinvited again."
|
: 'Leaving a project is permanent, you will lose access to the data and will need to be reinvited again.'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={showInviteModal}
|
isOpen={showInviteModal}
|
||||||
onToggle={() => setShowInviteModal(!showInviteModal)}
|
onToggle={() => setShowInviteModal(!showInviteModal)}
|
||||||
onAction={handleSubmit(inviteAccount)}
|
onAction={handleSubmit(inviteAccount)}
|
||||||
type={"info"}
|
type={'info'}
|
||||||
title={"Invite a new member"}
|
title={'Invite a new member'}
|
||||||
description={
|
description={
|
||||||
"Enter the email of the account you want to invite to this project. The person you want to invite needs to have an account on Plunk."
|
'Enter the email of the account you want to invite to this project. The person you want to invite needs to have an account on Plunk.'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Input register={register("email")} error={errors.email} label={"Email"} placeholder={"[email protected]"} />
|
<Input register={register('email')} error={errors.email} label={'Email'} placeholder={'[email protected]'} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Dashboard>
|
<Dashboard>
|
||||||
<SettingTabs />
|
<SettingTabs />
|
||||||
<Card title={"Project members"}>
|
<Card title={'Project members'}>
|
||||||
<Table
|
<Table
|
||||||
values={memberships.map((membership) => {
|
values={memberships.map((membership) => {
|
||||||
return {
|
return {
|
||||||
@@ -160,30 +162,30 @@ export default function Index() {
|
|||||||
Manage:
|
Manage:
|
||||||
membership.userId === user.id ? (
|
membership.userId === user.id ? (
|
||||||
<button
|
<button
|
||||||
className={"mb-2 text-sm text-neutral-400 underline transition ease-in-out hover:text-neutral-700"}
|
className={'mb-2 text-sm text-neutral-400 underline transition ease-in-out hover:text-neutral-700'}
|
||||||
onClick={() => setShowLeaveModal(true)}
|
onClick={() => setShowLeaveModal(true)}
|
||||||
>
|
>
|
||||||
Leave
|
Leave
|
||||||
</button>
|
</button>
|
||||||
) : memberships.find((membership) => membership.userId === user.id)?.role === "OWNER" ? (
|
) : memberships.find((membership) => membership.userId === user.id)?.role === 'OWNER' ? (
|
||||||
<button
|
<button
|
||||||
className={"mb-2 text-sm text-neutral-400 underline transition ease-in-out hover:text-neutral-700"}
|
className={'mb-2 text-sm text-neutral-400 underline transition ease-in-out hover:text-neutral-700'}
|
||||||
onClick={() => kickAccount(membership.email)}
|
onClick={() => kickAccount(membership.email)}
|
||||||
>
|
>
|
||||||
Kick
|
Kick
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
""
|
''
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<div className={"mt-9 flex items-center"}>
|
<div className={'mt-9 flex items-center'}>
|
||||||
<div className={"w-2/3"}>
|
<div className={'w-2/3'}>
|
||||||
<p className={"text-sm font-semibold text-neutral-800"}>Invite team</p>
|
<p className={'text-sm font-semibold text-neutral-800'}>Invite team</p>
|
||||||
<p className={"text-sm text-neutral-400"}>
|
<p className={'text-sm text-neutral-400'}>
|
||||||
By adding someone to your project you give them access to all data present in your project including emails and
|
By adding someone to your project you give them access to all data present in your project including emails and your API
|
||||||
your API key.
|
key.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -191,7 +193,7 @@ export default function Index() {
|
|||||||
onClick={() => setShowInviteModal(true)}
|
onClick={() => setShowInviteModal(true)}
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.05 }}
|
||||||
whileTap={{ scale: 0.9 }}
|
whileTap={{ scale: 0.9 }}
|
||||||
className={"ml-auto mt-4 self-end rounded bg-neutral-800 px-8 py-2.5 text-sm font-medium text-white"}
|
className={'ml-auto mt-4 self-end rounded bg-neutral-800 px-8 py-2.5 text-sm font-medium text-white'}
|
||||||
>
|
>
|
||||||
Invite user
|
Invite user
|
||||||
</motion.button>
|
</motion.button>
|
||||||
@@ -199,5 +201,5 @@ export default function Index() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Dashboard>
|
</Dashboard>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user