returns error message from server when add new member fails

This commit is contained in:
Mantey
2025-02-04 08:51:01 +00:00
parent aaa87389d3
commit f43bb15275
@@ -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>
</> </>
); )
} }