Skip to content

Commit

Permalink
user profile view drawer setup w headlessui
Browse files Browse the repository at this point in the history
  • Loading branch information
17coincooker committed Sep 16, 2023
1 parent 4e1285f commit debbad8
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 22 deletions.
54 changes: 33 additions & 21 deletions app/conversations/[conversationId]/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import Avatar from '@/app/components/Avatar';
import useOtherUser from '@/app/hooks/useOtherUser';
import { Conversation, User } from '@prisma/client';
import Link from 'next/link';
import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { HiChevronLeft } from 'react-icons/hi';
import { HiEllipsisHorizontal } from 'react-icons/hi2';
import ProfileDrawer from './ProfileDrawer';

interface HeaderProps {
conversation: Conversation & {
Expand All @@ -17,6 +18,8 @@ interface HeaderProps {
const Header: React.FC<HeaderProps> = ({ conversation }) => {
const otherUser = useOtherUser(conversation);

const [drawerOpen, setDrawerOpen] = useState(false);

const statusText = useMemo(() => {
if (conversation.isGroup) {
return `${conversation.users.length} members`;
Expand All @@ -26,29 +29,38 @@ const Header: React.FC<HeaderProps> = ({ conversation }) => {
}, []);

return (
<div className="bg-white w-full flex border-b-[1px] sm:px-4 py-3 px-4 lg:px-6 justify-between items-center shadow-sm">
<div className="flex gap-3 items-center">
<Link
className="lg:hidden block text-sky-500 hover:text-sky-600 transition cursor-pointer"
href="/conversations"
>
<HiChevronLeft size={32} />
</Link>
<Avatar user={otherUser} />
<div className="flex flex-col">
<div>{conversation.name || otherUser?.name || otherUser?.email}</div>
<div className="text-sm font-light text-neutral-500">
{statusText}
<>
<ProfileDrawer
data={conversation}
isOpen={drawerOpen}
onClose={() => setDrawerOpen(false)}
/>
<div className="bg-white w-full flex border-b-[1px] sm:px-4 py-3 px-4 lg:px-6 justify-between items-center shadow-sm">
<div className="flex gap-3 items-center">
<Link
className="lg:hidden block text-sky-500 hover:text-sky-600 transition cursor-pointer"
href="/conversations"
>
<HiChevronLeft size={32} />
</Link>
<Avatar user={otherUser} />
<div className="flex flex-col">
<div>
{conversation.name || otherUser?.name || otherUser?.email}
</div>
<div className="text-sm font-light text-neutral-500">
{statusText}
</div>
</div>
</div>
</div>

<HiEllipsisHorizontal
className="text-sky-500 cursor-pointer hover:text-sky-600 transition"
size={32}
onClick={() => {}}
/>
</div>
<HiEllipsisHorizontal
className="text-sky-500 cursor-pointer hover:text-sky-600 transition"
size={32}
onClick={() => setDrawerOpen(true)}
/>
</div>
</>
);
};
export default Header;
156 changes: 156 additions & 0 deletions app/conversations/[conversationId]/components/ProfileDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
'use client';

import Avatar from '@/app/components/Avatar';
import useOtherUser from '@/app/hooks/useOtherUser';
import { Transition, Dialog } from '@headlessui/react';
import { Conversation, User } from '@prisma/client';
import { format } from 'date-fns';
import { useMemo, Fragment } from 'react';
import { IoClose, IoTrash } from 'react-icons/io5';

interface ProfileDrawerProps {
isOpen: boolean;
onClose: () => void;
data: Conversation & {
users: User[];
};
}

const ProfileDrawer: React.FC<ProfileDrawerProps> = ({
isOpen,
onClose,
data,
}) => {
const otherUser = useOtherUser(data);

const joinedDate = useMemo(() => {
return format(new Date(data.createdAt), 'PP');
}, [data.createdAt]);

const title = useMemo(() => {
if (data.isGroup) {
return data.name;
}

return otherUser?.name || otherUser?.email;
}, [data.isGroup, data.name, otherUser?.email, otherUser?.name]);

const statusText = useMemo(() => {
if (data.isGroup) {
return `${data.users.length} members`;
}

return 'Active';
}, [data.isGroup, data.users.length]);

return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-50" onClose={onClose}>
<Transition.Child
as={Fragment}
enter="ease-out duration-500"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-40" />
</Transition.Child>

<div className="fixed inset-0 overflow-hidden">
<div className="absolute inset-0 overflow-hidden">
<div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveTo="translate-x-full"
>
<Dialog.Panel className="pointer-events-auto w-screen max-w-md">
<div className="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl">
<div className="px-4 sm:px-6">
<div className="flex items-start justify-end">
<div className="ml-3 flex h-7 items-center">
<button
onClick={onClose}
className="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2"
>
<span className="sr-only">Close panel</span>
<IoClose size={24} />
</button>
</div>
</div>
</div>

<div className="relative mt-6 flex-1 px-4 sm:px-6">
<div className="flex flex-col items-center">
<div className="mb-2">
<Avatar user={otherUser} />
</div>

<div>{title}</div>

<div className="text-sm text-gray-500">
{statusText}
</div>

<div className="flex gap-10 my-8">
<div
onClick={() => {}}
className="flex flex-col gap-3 items-center cursor-pointer hover:opacity-75"
>
<div className="w-10 h-10 bg-neutral-100 rounded-full flex items-center justify-center">
<IoTrash size={20} />
</div>
<div className="text-sm font-light text-neutral-600">
Delete
</div>
</div>
</div>

<div className="w-full pb-5 pt-5 sm:px-0 sm:pt-0">
<dl className="space-y-8 px-4 sm:space-y-6 sm:px-6">
{!data.isGroup && (
<div>
<dt className="text-sm font-medium text-gray-500 sm:w-40 sm:flex-shrink-0">
Email
</dt>
<dd className="mt-1 font-medium text-sm text-gray-900 sm:col-span-2">
{otherUser?.email}
</dd>
</div>
)}

{!data.isGroup && (
<>
<hr />
<div>
<dt className="text-sm font-medium text-gray-500 sm:w-40 sm:flex-shrink-0">
Joined
</dt>
<dd className="mt-1 font-medium text-sm text-gray-900 sm:col-span-2">
<time dateTime={joinedDate}>
{joinedDate}
</time>
</dd>
</div>
</>
)}
</dl>
</div>
</div>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
);
};
export default ProfileDrawer;
2 changes: 1 addition & 1 deletion middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export default withAuth({
});

export const config = {
matcher: ['/users/:path*'], // pages that should be protected
matcher: ['/users/:path*', '/conversations/:path*'], // pages that should be protected
};
1 change: 1 addition & 0 deletions notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- react-hot-toast
- date-fns
- next-cloudinary
- @headlessui/react

## ToDos

Expand Down
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@headlessui/react": "^1.7.17",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.2.0",
"@tailwindcss/forms": "^0.5.4",
Expand Down

0 comments on commit debbad8

Please sign in to comment.