Skip to content

Commit

Permalink
UI cleanup: UserCard, UsersGrid, Groups. New component UserNameDisplay.
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Hendrik Scheufen <j.h.scheufen@gmail.com>
  • Loading branch information
j-h-scheufen committed Oct 13, 2024
1 parent 661737f commit 7ca368b
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 106 deletions.
12 changes: 8 additions & 4 deletions packages/quilombo/components/GroupProfile/GroupView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Link } from '@nextui-org/link';
import { Spinner } from '@nextui-org/spinner';
import { useAtomValue } from 'jotai';
import { MailIcon } from 'lucide-react';

import ContactInfo from '@/components/ContactInfo';
import GroupMembers from '@/components/GroupMembers';
Expand All @@ -22,12 +23,15 @@ const GroupView = () => {
<GroupBanner />
<div className="mt-5 xs:flex xs:gap-5">
<GroupLogo url={getImageUrl(group.logo)} />
<div className="">
<div className="flex flex-col gap-2">
<GroupDescription description={group.description} />
{group.email && (
<Link href={`mailto:${group.email}`} className="text-small tracking-tight text-default-400">
{group.email}
</Link>
<div className="flex gap-1 items-center text-default-400">
<MailIcon className="h-4 w-4" />
<Link href={`mailto:${group.email}`} className="text-small tracking-tight text-default-400">
{group.email}
</Link>
</div>
)}
<ContactInfo links={group.links} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/quilombo/components/Groups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const Groups = () => {
});

return (
<div className="flex flex-col gap-4 pt-5">
<div className="flex flex-col gap-4">
<div className="flex h-fit items-center justify-between gap-3">
<Input
ref={inputRef}
Expand Down
16 changes: 6 additions & 10 deletions packages/quilombo/components/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
'use client';

import { Tab, Tabs } from '@nextui-org/tabs';
import { useAtom } from 'jotai';

import useOverviewQueries from '@/hooks/useOverviewQueries';
import { searchTabAtom } from '@/hooks/state/app';
import Groups from './Groups';
import Users from './Users';

const Overview = () => {
const [query, setQuery] = useOverviewQueries();

const { tab } = query;
const [searchTab, setSearchTab] = useAtom(searchTabAtom);

return (
<div>
Expand All @@ -18,13 +17,10 @@ const Overview = () => {
aria-label="Options"
className="mb-3"
classNames={{ tabList: 'mb-5' }}
defaultSelectedKey={tab || undefined}
onSelectionChange={(key) => {
const tab = key.toString() as 'users' | 'groups';
setQuery({ tab, searchTerm: null });
}}
defaultSelectedKey={searchTab || undefined}
onSelectionChange={(key) => setSearchTab(key.toString() as 'users' | 'groups')}
>
<Tab key="users" title="Users">
<Tab key="users" title="People">
<Users />
</Tab>
<Tab key="groups" title="Groups">
Expand Down
3 changes: 2 additions & 1 deletion packages/quilombo/components/Profile/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const Profile = () => {
<div className="flex flex-1 flex-col gap-3">
<div className="flex flex-col items-center justify-center gap-5 md:gap-10 xs:flex-row">
<div className="text-center xs:text-left">
<div className="text-small capitalize text-default-500 flex justify-center xs:justify-start gap-2 mt-2">
<div className="text-small capitalize text-default-500 flex justify-center xs:justify-start mt-2">
{user.title}
</div>
<h3 className="text-lg font-medium">{getUserDisplayName(user)}</h3>
Expand All @@ -45,6 +45,7 @@ const Profile = () => {
</Button>
</div>
</div>
<div>Email: {user.email}</div>
<ContactInfo links={user.links} />
</div>
</div>
Expand Down
56 changes: 24 additions & 32 deletions packages/quilombo/components/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,42 @@
'use client';

import { Avatar, AvatarProps } from '@nextui-org/avatar';
import { Card, CardBody, CardProps } from '@nextui-org/card';
import { Link } from '@nextui-org/link';
import clsx from 'clsx';
import { UserRoundIcon } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { ForwardedRef, ReactNode, forwardRef } from 'react';
import { ReactNode } from 'react';

import { PATHS } from '@/config/constants';
import { User } from '@/types/model';
import { getImageUrl } from '@/utils';
import UserNameDisplay from './UserNameDisplay';

export type UserCardProps = CardProps & {
user?: User | null;
user: User;
className?: string;
endContent?: ReactNode | null;
avatarProps?: AvatarProps;
};

const UserCard = (
{ user, endContent = null, avatarProps, ...props }: UserCardProps,
ref: ForwardedRef<HTMLDivElement | null>,
) => {
const router = useRouter();
if (!user) return 'Missing User';

const UserCard = ({ user, className = '', endContent = null, avatarProps }: UserCardProps) => {
return (
<Link as="div" onPress={() => router.push(`${PATHS.users}/${user.id}`)} className="inline-block cursor-pointer">
<Card ref={ref} {...props}>
<CardBody className="flex gap-3 justify-between">
<Avatar
radius="full"
size="md"
src={getImageUrl(user.avatar)}
fallback={<UserRoundIcon className="w-5 h-5 text-default-400" strokeWidth={1.25} />}
className="min-w-10"
{...avatarProps}
/>
<div className="flex-1 flex flex-col items-start justify-center gap-1">
<h4 className="text-small font-semibold leading-none text-default-600">{user.name}</h4>
<h5 className="text-small tracking-tight text-default-400">{user.email}</h5>
</div>
{endContent}
</CardBody>
</Card>
</Link>
<Card as={Link} href={`${PATHS.users}/${user.id}`}>
<CardBody className={clsx('flex flex-row gap-3 p-2', className)}>
<Avatar
radius="full"
size="lg"
src={getImageUrl(user.avatar)}
fallback={<UserRoundIcon className="w-5 h-5 text-default-400" strokeWidth={1.25} />}
className="min-w-10"
{...avatarProps}
/>
<div className="flex-1 flex flex-col items-start justify-top gap-1">
<div className="text-small capitalize text-default-500 flex justify-start">{user.title}</div>
<UserNameDisplay user={user} />
</div>
{endContent}
</CardBody>
</Card>
);
};

export default forwardRef(UserCard);
export default UserCard;
16 changes: 5 additions & 11 deletions packages/quilombo/components/UserCardWithFetch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@

import { AvatarProps } from '@nextui-org/avatar';
import { CardProps } from '@nextui-org/card';
import { ReactNode, Suspense } from 'react';
import { ReactNode } from 'react';

import { useFetchUser } from '@/query/user';
import UserCardSkeleton from './skeletons/UserCardSkeleton';
import UserCard from './UserCard';

export type UserCardDynamicProps = CardProps & {
export type UserCardWithFetchProps = CardProps & {
userId: string;
endContent?: ReactNode | null;
avatarProps?: AvatarProps;
};

const DynamicUserCard = ({ userId, ...props }: UserCardDynamicProps) => {
const { data: user } = useFetchUser(userId);
return <UserCard user={user} {...props} />;
const UserCardWithFetch = ({ userId, ...props }: UserCardWithFetchProps) => {
const { data: user, isLoading, isFetching } = useFetchUser(userId);
return isLoading || isFetching || !user ? <UserCardSkeleton /> : <UserCard user={user} {...props} />;
};

const UserCardWithFetch = (props: UserCardDynamicProps) => (
<Suspense fallback={<UserCardSkeleton {...props} />}>
<DynamicUserCard {...props} />
</Suspense>
);

export default UserCardWithFetch;
17 changes: 17 additions & 0 deletions packages/quilombo/components/UserNameDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { User } from '@/types/model';
import { getUserDisplayName } from '@/utils';

type Props = { user: User };

const UserNameDisplay = ({ user }: Props) => {
return (
<div className="flex flex-col gap-1">
<span className="font-semibold leading-none text-default-700">{getUserDisplayName(user)}</span>
{user.nickname && user.name && (
<span className="text-small font-semibold leading-none text-default-500 mt-1">{user.name}</span>
)}
</div>
);
};

export default UserNameDisplay;
2 changes: 1 addition & 1 deletion packages/quilombo/components/Users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const Users = () => {
});

return (
<div className="flex flex-col gap-4 -mt-5">
<div className="flex flex-col gap-4">
<div className="flex h-fit flex-col items-start justify-start gap-3 md:flex-row md:items-end">
<Input
ref={inputRef}
Expand Down
40 changes: 9 additions & 31 deletions packages/quilombo/components/UsersGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,22 @@
import { User } from '@/types/model';
import { cn } from '@/utils/tailwind';
import React, { ReactNode, RefObject } from 'react';
import { RefObject } from 'react';

import UserCard, { UserCardProps } from './UserCard';
import UserCard from './UserCard';
import UserCardSkeleton from './skeletons/UserCardSkeleton';

type Props = {
users?: User[];
emptyContent?: ReactNode;
className?: string;
userCardProps?: UserCardProps;
scrollerRef?: RefObject<HTMLElement>;
isLoading?: boolean;
numSkeletons?: number;
userComponent?: React.FC<UserCardProps>;
scrollerRef?: RefObject<HTMLElement>;
};
const UsersGrid = ({
users,
emptyContent,
className = '',
userCardProps,
scrollerRef,
isLoading = false,
numSkeletons = 20,
userComponent: UserComponent,
}: Props) => {
const UsersGrid = ({ users = [], isLoading = false, scrollerRef }: Props) => {
return (
<div className={cn('grid w-full grid-cols-1 gap-5 xs:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', className)}>
{users?.length
? users.map((user, i) => {
if (UserComponent)
return <UserComponent key={i} {...((userCardProps as UserCardProps) || {})} user={user} />;
return <UserCard key={i} {...((userCardProps as UserCardProps) || {})} user={user} />;
})
: !isLoading && <div className="mb-3 text-small text-default-400">{emptyContent || 'No users to display'}</div>}
<div className="grid w-full grid-cols-1 gap-5 xs:grid-cols-2 lg:grid-cols-3">
{users.map((user, i) => (
<UserCard key={i} user={user} />
))}
{scrollerRef && <div ref={scrollerRef as RefObject<HTMLDivElement>} className="hidden" hidden></div>}
{isLoading &&
[...Array(numSkeletons)].map((_, i) => (
<UserCardSkeleton key={i} {...((userCardProps as UserCardProps) || {})} />
))}
{isLoading && [...Array(20)].map((_, i) => <UserCardSkeleton key={i} />)}
</div>
);
};
Expand Down
2 changes: 2 additions & 0 deletions packages/quilombo/hooks/state/app.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { atom } from 'jotai';

export const breadcrumbsHistoryAtom = atom<string[]>([]);

export const searchTabAtom = atom<'users' | 'groups'>('users');
12 changes: 0 additions & 12 deletions packages/quilombo/hooks/useOverviewQueries.ts

This file was deleted.

5 changes: 2 additions & 3 deletions packages/quilombo/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ export const getUserDisplayName = (user?: User): string => {
let displayName = '';
if (user.nickname) {
displayName = user.nickname;
}
if (user.name) {
displayName += displayName === '' ? user.name : ` (${user.name})`;
} else if (user.name) {
displayName = user.name;
}
return displayName || `User (${abbreviateAddress(user.walletAddress)})`;
};
Expand Down

0 comments on commit 7ca368b

Please sign in to comment.