From f43bb1527585c8df8fa6f95c9df3434bc593826a Mon Sep 17 00:00:00 2001 From: Mantey Date: Tue, 4 Feb 2025 08:50:25 +0000 Subject: [PATCH 1/4] returns error message from server when add new member fails --- .../dashboard/src/pages/settings/members.tsx | 164 +++++++++--------- 1 file changed, 83 insertions(+), 81 deletions(-) diff --git a/packages/dashboard/src/pages/settings/members.tsx b/packages/dashboard/src/pages/settings/members.tsx index 206fa7f..687dd5d 100644 --- a/packages/dashboard/src/pages/settings/members.tsx +++ b/packages/dashboard/src/pages/settings/members.tsx @@ -1,36 +1,36 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { MembershipSchemas, type UtilitySchemas } from "@plunk/shared"; -import type { Project, Role } from "@prisma/client"; -import { motion } from "framer-motion"; -import { useRouter } from "next/router"; -import React, { useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from "../../components"; -import { Dashboard } from "../../layouts"; -import { useActiveProject, useActiveProjectMemberships, useProjects } from "../../lib/hooks/projects"; -import { useUser } from "../../lib/hooks/users"; -import { network } from "../../lib/network"; +import { zodResolver } from '@hookform/resolvers/zod' +import { MembershipSchemas, type UtilitySchemas } from '@plunk/shared' +import type { Project, Role } from '@prisma/client' +import { motion } from 'framer-motion' +import { useRouter } from 'next/router' +import React, { useState } from 'react' +import { useForm } from 'react-hook-form' +import { toast } from 'sonner' +import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from '../../components' +import { Dashboard } from '../../layouts' +import { useActiveProject, useActiveProjectMemberships, useProjects } from '../../lib/hooks/projects' +import { useUser } from '../../lib/hooks/users' +import { network } from '../../lib/network' interface EmailValues { - email: string; + email: string } /** * */ export default function Index() { - const [showInviteModal, setShowInviteModal] = useState(false); - const [showLeaveModal, setShowLeaveModal] = useState(false); + const [showInviteModal, setShowInviteModal] = useState(false) + const [showLeaveModal, setShowLeaveModal] = useState(false) - const [project, setProject] = useState(); + const [project, setProject] = useState() - const router = useRouter(); + const router = useRouter() - const activeProject = useActiveProject(); - const { data: user } = useUser(); - const { data: projects, mutate: projectMutate } = useProjects(); - const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships(); + const activeProject = useActiveProject() + const { data: user } = useUser() + const { data: projects, mutate: projectMutate } = useProjects() + const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships() const { register, @@ -38,89 +38,91 @@ export default function Index() { formState: { errors }, } = useForm({ resolver: zodResolver(MembershipSchemas.invite.omit({ id: true, role: true })), - }); + }) if (activeProject && !project) { - setProject(activeProject); + setProject(activeProject) } if (!project || !projects || !memberships || !user) { - return ; + return } if (!activeProject) { - return ; + return } const inviteAccount = (data: EmailValues) => { toast.promise( network.mock< { - success: true; + success: true members: { - userId: string; - email: string; - role: Role; - }[]; + userId: string + email: string + role: Role + }[] }, typeof MembershipSchemas.invite - >(project.secret, "POST", "/memberships/invite", { + >(project.secret, 'POST', '/memberships/invite', { id: project.id, email: data.email, - role: "ADMIN", + role: 'ADMIN', }), { - loading: "Adding new member", + loading: 'Adding new member', success: async (result) => { - await membershipMutate(result.members); - setShowInviteModal(false); + await membershipMutate(result.members) + 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) => { void network .fetch< { - success: true; + success: true members: { - userId: string; - email: string; - role: Role; - }[]; + userId: string + email: string + role: Role + }[] }, typeof MembershipSchemas.kick - >("POST", "/memberships/kick", { + >('POST', '/memberships/kick', { id: project.id, email, }) .then(async (res) => { - await membershipMutate(res.members); - }); - }; + await membershipMutate(res.members) + }) + } const leaveProject = () => { void network .fetch< { - success: true; - memberships: Project[]; + success: true + memberships: Project[] }, typeof UtilitySchemas.id - >("POST", "/memberships/leave", { + >('POST', '/memberships/leave', { id: project.id, }) .then(async (res) => { - await projectMutate(res.memberships); - localStorage.removeItem("project"); - await router.push("/"); - window.location.reload(); - }); - }; + await projectMutate(res.memberships) + localStorage.removeItem('project') + await router.push('/') + window.location.reload() + }) + } return ( <> @@ -128,29 +130,29 @@ export default function Index() { isOpen={showLeaveModal} onToggle={() => setShowLeaveModal(!showLeaveModal)} onAction={leaveProject} - type={"danger"} - title={"Are you sure?"} + type={'danger'} + title={'Are you sure?'} description={ memberships.length === 1 - ? "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." + ? '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.' } /> setShowInviteModal(!showInviteModal)} onAction={handleSubmit(inviteAccount)} - type={"info"} - title={"Invite a new member"} + type={'info'} + title={'Invite a new member'} 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.' } > - + - + { return { @@ -160,30 +162,30 @@ export default function Index() { Manage: membership.userId === user.id ? ( - ) : memberships.find((membership) => membership.userId === user.id)?.role === "OWNER" ? ( + ) : memberships.find((membership) => membership.userId === user.id)?.role === 'OWNER' ? ( ) : ( - "" + '' ), - }; + } })} /> -
-
-

Invite team

-

- By adding someone to your project you give them access to all data present in your project including emails and - your API key. +

+
+

Invite team

+

+ By adding someone to your project you give them access to all data present in your project including emails and your API + key.

@@ -191,7 +193,7 @@ export default function Index() { onClick={() => setShowInviteModal(true)} whileHover={{ scale: 1.05 }} 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 @@ -199,5 +201,5 @@ export default function Index() { - ); + ) } From 2f74c3af77dfb92b38537c49e099d279c341f42f Mon Sep 17 00:00:00 2001 From: Mantey Date: Tue, 4 Feb 2025 09:02:07 +0000 Subject: [PATCH 2/4] fixes prettier overriding linting --- .../dashboard/src/pages/settings/members.tsx | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/packages/dashboard/src/pages/settings/members.tsx b/packages/dashboard/src/pages/settings/members.tsx index 687dd5d..ca0b723 100644 --- a/packages/dashboard/src/pages/settings/members.tsx +++ b/packages/dashboard/src/pages/settings/members.tsx @@ -1,36 +1,36 @@ -import { zodResolver } from '@hookform/resolvers/zod' -import { MembershipSchemas, type UtilitySchemas } from '@plunk/shared' -import type { Project, Role } from '@prisma/client' -import { motion } from 'framer-motion' -import { useRouter } from 'next/router' -import React, { useState } from 'react' -import { useForm } from 'react-hook-form' -import { toast } from 'sonner' -import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from '../../components' -import { Dashboard } from '../../layouts' -import { useActiveProject, useActiveProjectMemberships, useProjects } from '../../lib/hooks/projects' -import { useUser } from '../../lib/hooks/users' -import { network } from '../../lib/network' +import { zodResolver } from "@hookform/resolvers/zod"; +import { MembershipSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Project, Role } from "@prisma/client"; +import { motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { Card, FullscreenLoader, Input, Modal, SettingTabs, Table } from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActiveProject, useActiveProjectMemberships, useProjects } from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; interface EmailValues { - email: string + email: string; } /** * */ export default function Index() { - const [showInviteModal, setShowInviteModal] = useState(false) - const [showLeaveModal, setShowLeaveModal] = useState(false) + const [showInviteModal, setShowInviteModal] = useState(false); + const [showLeaveModal, setShowLeaveModal] = useState(false); - const [project, setProject] = useState() + const [project, setProject] = useState(); - const router = useRouter() + const router = useRouter(); - const activeProject = useActiveProject() - const { data: user } = useUser() - const { data: projects, mutate: projectMutate } = useProjects() - const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships() + const activeProject = useActiveProject(); + const { data: user } = useUser(); + const { data: projects, mutate: projectMutate } = useProjects(); + const { data: memberships, mutate: membershipMutate } = useActiveProjectMemberships(); const { register, @@ -38,91 +38,91 @@ export default function Index() { formState: { errors }, } = useForm({ resolver: zodResolver(MembershipSchemas.invite.omit({ id: true, role: true })), - }) + }); if (activeProject && !project) { - setProject(activeProject) + setProject(activeProject); } if (!project || !projects || !memberships || !user) { - return + return ; } if (!activeProject) { - return + return ; } const inviteAccount = (data: EmailValues) => { toast.promise( network.mock< { - success: true + success: true; members: { - userId: string - email: string - role: Role - }[] + userId: string; + email: string; + role: Role; + }[]; }, typeof MembershipSchemas.invite - >(project.secret, 'POST', '/memberships/invite', { + >(project.secret, "POST", "/memberships/invite", { id: project.id, email: data.email, - role: 'ADMIN', + role: "ADMIN", }), { - loading: 'Adding new member', + loading: "Adding new member", success: async (result) => { - await membershipMutate(result.members) - setShowInviteModal(false) + await membershipMutate(result.members); + setShowInviteModal(false); - return 'Added new member' + return "Added new member"; }, - error: (e) => { - return e.message + error: (error) => { + return error.message }, - } - ) - } + }, + ); + }; const kickAccount = (email: string) => { void network .fetch< { - success: true + success: true; members: { - userId: string - email: string - role: Role - }[] + userId: string; + email: string; + role: Role; + }[]; }, typeof MembershipSchemas.kick - >('POST', '/memberships/kick', { + >("POST", "/memberships/kick", { id: project.id, email, }) .then(async (res) => { - await membershipMutate(res.members) - }) - } + await membershipMutate(res.members); + }); + }; const leaveProject = () => { void network .fetch< { - success: true - memberships: Project[] + success: true; + memberships: Project[]; }, typeof UtilitySchemas.id - >('POST', '/memberships/leave', { + >("POST", "/memberships/leave", { id: project.id, }) .then(async (res) => { - await projectMutate(res.memberships) - localStorage.removeItem('project') - await router.push('/') - window.location.reload() - }) - } + await projectMutate(res.memberships); + localStorage.removeItem("project"); + await router.push("/"); + window.location.reload(); + }); + }; return ( <> @@ -130,29 +130,29 @@ export default function Index() { isOpen={showLeaveModal} onToggle={() => setShowLeaveModal(!showLeaveModal)} onAction={leaveProject} - type={'danger'} - title={'Are you sure?'} + type={"danger"} + title={"Are you sure?"} description={ memberships.length === 1 - ? '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.' + ? "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." } /> setShowInviteModal(!showInviteModal)} onAction={handleSubmit(inviteAccount)} - type={'info'} - title={'Invite a new member'} + type={"info"} + title={"Invite a new member"} 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." } > - + - +
{ return { @@ -162,30 +162,30 @@ export default function Index() { Manage: membership.userId === user.id ? ( - ) : memberships.find((membership) => membership.userId === user.id)?.role === 'OWNER' ? ( + ) : memberships.find((membership) => membership.userId === user.id)?.role === "OWNER" ? ( ) : ( - '' + "" ), - } + }; })} /> -
-
-

Invite team

-

- By adding someone to your project you give them access to all data present in your project including emails and your API - key. +

+
+

Invite team

+

+ By adding someone to your project you give them access to all data present in your project including emails and + your API key.

@@ -193,7 +193,7 @@ export default function Index() { onClick={() => setShowInviteModal(true)} whileHover={{ scale: 1.05 }} 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 @@ -201,5 +201,5 @@ export default function Index() { - ) + ); } From 473d693a4429f276183834d47745e8ba9d3f5f47 Mon Sep 17 00:00:00 2001 From: Mantey Date: Tue, 4 Feb 2025 09:06:56 +0000 Subject: [PATCH 3/4] bumps up version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 096cbc7..8b217e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plunk", - "version": "1.0.11", + "version": "1.0.12", "private": true, "license": "agpl-3.0", "workspaces": { From 4d470718d8c1994f0b1dac60f2463c3dec0931ea Mon Sep 17 00:00:00 2001 From: Mantey Date: Thu, 6 Feb 2025 09:52:50 +0000 Subject: [PATCH 4/4] defaults to static error message if error message is not returned --- packages/dashboard/src/pages/settings/members.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dashboard/src/pages/settings/members.tsx b/packages/dashboard/src/pages/settings/members.tsx index ca0b723..1d1a481 100644 --- a/packages/dashboard/src/pages/settings/members.tsx +++ b/packages/dashboard/src/pages/settings/members.tsx @@ -78,7 +78,8 @@ export default function Index() { return "Added new member"; }, error: (error) => { - return error.message + const errorMessage = error?.message || 'We could not find that user, please ask them to sign up first.' + return errorMessage }, }, );