From 5374c1720ef33c8ef807a4150ac195dbe7e42da7 Mon Sep 17 00:00:00 2001 From: Nikolay Bonev Date: Thu, 28 Nov 2024 18:34:16 +0200 Subject: [PATCH] change how accepting invite works --- app/modules/invite/service.server.ts | 15 +-- app/routes/_auth+/accept-invite.$inviteId.tsx | 115 ++++++++++++++++-- 2 files changed, 109 insertions(+), 21 deletions(-) diff --git a/app/modules/invite/service.server.ts b/app/modules/invite/service.server.ts index 4eb087016..584c572de 100644 --- a/app/modules/invite/service.server.ts +++ b/app/modules/invite/service.server.ts @@ -375,10 +375,10 @@ export async function updateInviteStatus({ */ export async function checkUserAndInviteMatch({ context, - params, + invite, }: { context: AppLoadContext; - params: Params; + invite: Invite; }) { const authSession = context.getSession(); const { userId } = authSession; @@ -395,16 +395,7 @@ export async function checkUserAndInviteMatch({ }) .catch(() => null); - /** We get the invite based on the id of the params */ - const inv = await db.invite - .findFirst({ - where: { - id: params.inviteId, - }, - }) - .catch(() => null); - - if (user?.email !== inv?.inviteeEmail) { + if (user?.email !== invite?.inviteeEmail) { throw new ShelfError({ cause: null, title: "Wrong user", diff --git a/app/routes/_auth+/accept-invite.$inviteId.tsx b/app/routes/_auth+/accept-invite.$inviteId.tsx index fe6ba67b4..d0b5d1c26 100644 --- a/app/routes/_auth+/accept-invite.$inviteId.tsx +++ b/app/routes/_auth+/accept-invite.$inviteId.tsx @@ -1,8 +1,13 @@ import { InviteStatuses } from "@prisma/client"; import type { LoaderFunctionArgs } from "@remix-run/node"; import { json, redirect } from "@remix-run/node"; +import { Form, useActionData, useLoaderData } from "@remix-run/react"; import { z } from "zod"; +import { Button } from "~/components/shared/button"; import { Spinner } from "~/components/shared/spinner"; +import { db } from "~/database/db.server"; +import { useSearchParams } from "~/hooks/search-params"; +import { useDisabled } from "~/hooks/use-disabled"; import { signInWithEmail } from "~/modules/auth/service.server"; import { generateRandomCode } from "~/modules/invite/helpers"; import { @@ -13,20 +18,81 @@ import { setSelectedOrganizationIdCookie } from "~/modules/organization/context. import { setCookie } from "~/utils/cookies.server"; import { INVITE_TOKEN_SECRET } from "~/utils/env"; import { ShelfError, makeShelfError } from "~/utils/error"; -import { error, parseData, safeRedirect } from "~/utils/http.server"; +import { + data, + error, + getParams, + parseData, + safeRedirect, +} from "~/utils/http.server"; import jwt from "~/utils/jsonwebtoken.server"; -export async function loader({ context, request, params }: LoaderFunctionArgs) { +export async function loader({ context, params }: LoaderFunctionArgs) { + const { inviteId } = getParams(params, z.object({ inviteId: z.string() }), { + additionalData: { inviteId: params.inviteId }, + }); try { + /** We get the invite based on the id of the params */ + const invite = await db.invite + .findFirstOrThrow({ + where: { + id: inviteId, + }, + include: { + organization: { + select: { + name: true, + }, + }, + inviter: { + select: { + firstName: true, + lastName: true, + }, + }, + }, + }) + .catch((cause) => { + throw new ShelfError({ + cause, + title: "Invite not found", + message: + "The invitation you are trying to accept is either not found or expired", + label: "Invite", + }); + }); + /** Here we have to do a check based on the session of the current user * If the user is already signed in, we have to make sure the invite sent, is for the same user */ if (context.isAuthenticated) { - await checkUserAndInviteMatch({ context, params }); + await checkUserAndInviteMatch({ + context, + invite, + }); } + return json( + data({ + inviter: `${invite.inviter.firstName} ${invite.inviter.lastName}`, + workspace: `${invite.organization.name}`, + }) + ); + } catch (cause) { + const reason = makeShelfError(cause); + throw json( + error({ ...reason, title: reason.title || "Accept team invite" }), + { + status: reason.status, + } + ); + } +} + +export async function action({ context, request }: LoaderFunctionArgs) { + try { const { token } = parseData( - new URL(decodeURIComponent(request.url)).searchParams, + await request.formData(), z.object({ token: z.string() }), { message: @@ -103,7 +169,7 @@ export async function loader({ context, request, params }: LoaderFunctionArgs) { "The invitation link is invalid. Please try clicking the link in your email again or request a new invite. If the issue persists, feel free to contact support"; } - throw json( + return json( error({ ...reason, title: reason.title || "Accept team invite" }), { status: reason.status, @@ -113,10 +179,41 @@ export async function loader({ context, request, params }: LoaderFunctionArgs) { } export default function AcceptInvite() { + const { inviter, workspace } = useLoaderData(); + const [searchParams] = useSearchParams(); + const disabled = useDisabled(); + const actionData = useActionData(); + const error = actionData?.error; return ( -
- -

Validating token...

-
+ <> +
+

Accept invite

+

+ {inviter} invites you to join Shelf as a member of{" "} + {workspace}’s workspace. +

+
+ + {error && ( +

+ {error.message} +

+ )} + +
+
+
+

+ If you have any questions or need assistance, please don't hesitate to + contact our support team at support@shelf.nu. +

+
+ ); }