Skip to content

Commit

Permalink
User invitiations listing (#42)
Browse files Browse the repository at this point in the history
* User invitiations listing

* Fix helper

* Fix wording
  • Loading branch information
Lucieo authored Oct 3, 2023
1 parent d6f5cd9 commit 919b4cf
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 37 deletions.
12 changes: 6 additions & 6 deletions src/app/(routes)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react';
import { getCurrentUser } from '@/lib/sessions';
import PageContainer from '@/components/layout/PageContainer';
import AccountForm from '@/components/account/AccountForm';
import SectionContainer from '@/components/layout/SectionContainer';
import { notFound } from 'next/navigation';
import UserInvitations from '@/components/account/UserInvitations';
import { getUserInvitations } from '@/lib/queries';

export const dynamic = 'force-dynamic';
export const metadata = {
Expand All @@ -13,15 +14,14 @@ export const metadata = {
const AccountPage = async () => {
const user = await getCurrentUser();
if (!user) return notFound();
const invitations = await getUserInvitations(user!.email as string);

return (
<PageContainer>
<SectionContainer
title="My account"
className="flex justify-center max-w-2xl m-auto"
>
<div className="flex flex-col space-y-6 content-stretch">
<AccountForm user={user!} />
</SectionContainer>
<UserInvitations invitations={invitations} />
</div>
</PageContainer>
);
};
Expand Down
66 changes: 36 additions & 30 deletions src/components/account/AccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useMutation } from 'react-query';
import Button from '../Button';
import { Input } from '../Input';
import { DangerZoneAccount } from '../teams/form/DangerZone';
import SectionContainer from '../layout/SectionContainer';

interface FormElements extends HTMLFormControlsCollection {
email: HTMLInputElement;
Expand Down Expand Up @@ -44,38 +45,43 @@ const AccountForm = ({ user }: Props) => {
};

return (
<form
className="flex flex-col items-end mt-4 gap-6 w-full max-w-2xl"
onSubmit={handleSubmit}
<SectionContainer
title="My Account"
className="flex justify-center max-w-2xl m-auto w-full"
>
<fieldset className="flex flex-col gap-2 w-full">
<label htmlFor="email">Email address</label>
<Input
readOnly
disabled
defaultValue={user?.email!}
type="email"
name="email"
/>
</fieldset>
<fieldset className="flex flex-col gap-2 w-full">
<label htmlFor="name">Full name</label>
<Input
defaultValue={user?.name!}
type="text"
name="name"
minLength={2}
/>
</fieldset>
<div className="flex justify-start gap-4 w-full items-center">
<Button type="submit" isLoading={isLoading}>
Save
</Button>
<div className="flex-2">
<DangerZoneAccount user={user} />
<form
className="flex flex-col items-end mt-4 gap-6 w-full max-w-2xl"
onSubmit={handleSubmit}
>
<fieldset className="flex flex-col gap-2 w-full">
<label htmlFor="email">Email address</label>
<Input
readOnly
disabled
defaultValue={user?.email!}
type="email"
name="email"
/>
</fieldset>
<fieldset className="flex flex-col gap-2 w-full">
<label htmlFor="name">Full name</label>
<Input
defaultValue={user?.name!}
type="text"
name="name"
minLength={2}
/>
</fieldset>
<div className="flex justify-start gap-4 w-full items-center">
<Button type="submit" isLoading={isLoading}>
Save
</Button>
<div className="flex-2">
<DangerZoneAccount user={user} />
</div>
</div>
</div>
</form>
</form>
</SectionContainer>
);
};

Expand Down
65 changes: 65 additions & 0 deletions src/components/account/UserInvitationItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use client';

import Button from '../Button';
import useTransitionRefresh from '@/hooks/useTransitionRefresh';
import { useMutation } from 'react-query';
import api from '@/lib/api';
import { routes } from '@/core/constants';
import { UserInvitationItem } from '@/lib/queries';
import useCustomToast from '@/hooks/useCustomToast';
import { useRouter } from 'next/navigation';

const UserInvitationItem = ({
invitation,
}: {
invitation: UserInvitationItem;
}) => {
const router = useRouter();
const { successToast, errorToast } = useCustomToast();
const { refresh, isRefreshing } = useTransitionRefresh();
const { mutate: joinTeam, isLoading } = useMutation(
'join-team',
({ teamId, invitationId }: { teamId: string; invitationId: string }) =>
api.get(`${routes.TEAMS}/${teamId}/invitations/${invitationId}/accept`),
{
onSuccess: () => {
successToast('Welcome to your new team !');
refresh();
router.push(`${routes.TEAMS}/${invitation?.membership?.team?.slug}`, {
forceOptimisticNavigation: true,
});
},
onError: () => {
errorToast('Something went wrong...');
},
}
);

return (
<div role="group" className="flex gap-2 p-2 rounded-md bg-gray-50">
<div className="w-full flex gap-2">
<div className="flex gap-2 justify-between items-center w-full">
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap max-w-[14rem] text-gray-900 font-semibold text-sm">
{invitation?.membership?.team?.name}
</p>
<div className="flex gap-2 justify-between self-end items-center">
<Button
variant="outline"
isLoading={isLoading || isRefreshing}
onClick={() =>
joinTeam({
teamId: invitation?.membership?.team?.id,
invitationId: invitation?.id,
})
}
>
Accept
</Button>
</div>
</div>
</div>
</div>
);
};

export default UserInvitationItem;
31 changes: 31 additions & 0 deletions src/components/account/UserInvitations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client';

import SectionContainer from '../layout/SectionContainer';
import { UserInvitationsResults } from '@/lib/queries';
import UserInvitationItem from './UserInvitationItem';

const UserInvitations = ({
invitations,
}: {
invitations: UserInvitationsResults;
}) => {
return (
<SectionContainer
title="My invitations"
className="flex justify-center max-w-2xl m-auto w-full"
>
{invitations?.length ? (
<p className="pb-4">Find all pending invitations below</p>
) : (
<p>You have no pending invitations</p>
)}
<div className="flex flex-col space-y-4">
{invitations?.map((invitation) => (
<UserInvitationItem key={invitation?.id} invitation={invitation} />
))}
</div>
</SectionContainer>
);
};

export default UserInvitations;
10 changes: 9 additions & 1 deletion src/lib/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export const getUserInvitations = (email: string) =>
db.invitation.findMany({
select: {
id: true,
membership: { select: { team: { select: { name: true } } } },
membership: {
select: { team: { select: { name: true, id: true, slug: true } } },
},
},
where: {
membership: { invitedEmail: email },
Expand Down Expand Up @@ -476,6 +478,12 @@ export type TeamInvitation = Awaited<
ReturnType<typeof getTeamInvitations>
>[number];

export type UserInvitationsResults = Awaited<
ReturnType<typeof getUserInvitations>
>;

export type UserInvitationItem = UserInvitationsResults[number];

export type TeamDigestsResult = Awaited<
ReturnType<typeof getTeamDigests>
>[number];
Expand Down

1 comment on commit 919b4cf

@vercel
Copy link

@vercel vercel bot commented on 919b4cf Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

digestclub – ./

digestclub-premieroctet.vercel.app
digestclub-git-main-premieroctet.vercel.app
digestclub.vercel.app

Please sign in to comment.