feat(frontend): add member pagination
This commit is contained in:
		
							parent
							
								
									a4a62fa6b6
								
							
						
					
					
						commit
						6ea8861da2
					
				
					 2 changed files with 150 additions and 118 deletions
				
			
		|  | @ -3,7 +3,7 @@ import { Link, redirect, useLoaderData, useRouteLoaderData } from "@remix-run/re | ||||||
| import { UserWithMembers } from "~/lib/api/user"; | import { UserWithMembers } from "~/lib/api/user"; | ||||||
| import serverRequest from "~/lib/request.server"; | import serverRequest from "~/lib/request.server"; | ||||||
| import { loader as rootLoader } from "~/root"; | import { loader as rootLoader } from "~/root"; | ||||||
| import { Alert, Button } from "react-bootstrap"; | import { Alert, Button, Pagination } from "react-bootstrap"; | ||||||
| import { Trans, useTranslation } from "react-i18next"; | import { Trans, useTranslation } from "react-i18next"; | ||||||
| import { renderMarkdown } from "~/lib/markdown"; | import { renderMarkdown } from "~/lib/markdown"; | ||||||
| import ProfileLink from "~/components/ProfileLink"; | import ProfileLink from "~/components/ProfileLink"; | ||||||
|  | @ -11,6 +11,7 @@ import ProfileField from "~/components/ProfileField"; | ||||||
| import { PersonPlusFill } from "react-bootstrap-icons"; | import { PersonPlusFill } from "react-bootstrap-icons"; | ||||||
| import { defaultAvatarUrl } from "~/lib/utils"; | import { defaultAvatarUrl } from "~/lib/utils"; | ||||||
| import MemberCard from "~/routes/$username/MemberCard"; | import MemberCard from "~/routes/$username/MemberCard"; | ||||||
|  | import { ReactNode } from "react"; | ||||||
| 
 | 
 | ||||||
| export const meta: MetaFunction<typeof loader> = ({ data }) => { | export const meta: MetaFunction<typeof loader> = ({ data }) => { | ||||||
| 	const { user } = data!; | 	const { user } = data!; | ||||||
|  | @ -34,17 +35,44 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { | ||||||
| 		memberPage = 0; | 		memberPage = 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	console.log(JSON.stringify(members)); | ||||||
|  | 
 | ||||||
| 	return json({ user, members, currentPage: memberPage, pageCount }); | 	return json({ user, members, currentPage: memberPage, pageCount }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default function UserPage() { | export default function UserPage() { | ||||||
| 	const { t } = useTranslation(); | 	const { t } = useTranslation(); | ||||||
| 	const { user } = useLoaderData<typeof loader>(); | 	const { user, members, currentPage, pageCount } = useLoaderData<typeof loader>(); | ||||||
| 	const { meUser } = useRouteLoaderData<typeof rootLoader>("root") || { meUser: undefined }; | 	const { meUser } = useRouteLoaderData<typeof rootLoader>("root") || { meUser: undefined }; | ||||||
| 
 | 
 | ||||||
| 	const isMeUser = meUser && meUser.id === user.id; | 	const isMeUser = meUser && meUser.id === user.id; | ||||||
| 	const bio = renderMarkdown(user.bio); | 	const bio = renderMarkdown(user.bio); | ||||||
| 
 | 
 | ||||||
|  | 	const paginationItems: ReactNode[] = []; | ||||||
|  | 	for (let i = 0; i < pageCount; i++) { | ||||||
|  | 		paginationItems.push( | ||||||
|  | 			<Pagination.Item key={i} as={Link} to={`/@${user.username}?page=${i}`}> | ||||||
|  | 				{i + 1} | ||||||
|  | 			</Pagination.Item>, | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const pagination = ( | ||||||
|  | 		<Pagination className="justify-content-center"> | ||||||
|  | 			<Pagination.Prev | ||||||
|  | 				as={Link} | ||||||
|  | 				to={`/@${user.username}?page=${currentPage - 1}`} | ||||||
|  | 				disabled={currentPage === 0} | ||||||
|  | 			/> | ||||||
|  | 			{paginationItems} | ||||||
|  | 			<Pagination.Next | ||||||
|  | 				as={Link} | ||||||
|  | 				to={`/@${user.username}?page=${currentPage + 1}`} | ||||||
|  | 				disabled={currentPage === pageCount - 1} | ||||||
|  | 			/> | ||||||
|  | 		</Pagination> | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
| 	return ( | 	return ( | ||||||
| 		<> | 		<> | ||||||
| 			{isMeUser && ( | 			{isMeUser && ( | ||||||
|  | @ -120,24 +148,27 @@ export default function UserPage() { | ||||||
| 					))} | 					))} | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			{user.members.length > 0 || | 			{(members.length > 0 || isMeUser) && ( | ||||||
| 				(isMeUser && ( |  | ||||||
| 				<> | 				<> | ||||||
| 					<hr /> | 					<hr /> | ||||||
| 					<h2> | 					<h2> | ||||||
| 						{user.member_title || t("user.heading.members")}{" "} | 						{user.member_title || t("user.heading.members")}{" "} | ||||||
| 							{/* @ts-expect-error using as=Link causes an error here, even though it runs completely fine */} | 						{isMeUser && ( | ||||||
|  | 							// @ts-expect-error using as=Link causes an error here, even though it runs completely fine
 | ||||||
| 							<Button as={Link} to="/settings/members/create" variant="success"> | 							<Button as={Link} to="/settings/members/create" variant="success"> | ||||||
| 								<PersonPlusFill /> {t("user.create-member-button")} | 								<PersonPlusFill /> {t("user.create-member-button")} | ||||||
| 							</Button> | 							</Button> | ||||||
|  | 						)} | ||||||
| 					</h2> | 					</h2> | ||||||
|  | 					{pageCount > 1 && pagination} | ||||||
| 					<div className="grid"> | 					<div className="grid"> | ||||||
| 							{user.members.length === 0 ? ( | 						{members.length === 0 ? ( | ||||||
| 							<div> | 							<div> | ||||||
| 								<Trans t={t} i18nKey="user.no-members-blurb"> | 								<Trans t={t} i18nKey="user.no-members-blurb"> | ||||||
| 									You don't have any members yet. | 									You don't have any members yet. | ||||||
| 									<br /> | 									<br /> | ||||||
| 										Members are sub-profiles that can have their own avatar, names, pronouns, and preferred terms. | 									Members are sub-profiles that can have their own avatar, names, pronouns, and | ||||||
|  | 									preferred terms. | ||||||
| 									<br /> | 									<br /> | ||||||
| 									You can create a new member with the "Create member" button above.{" "} | 									You can create a new member with the "Create member" button above.{" "} | ||||||
| 									<span className="text-muted">(only you can see this)</span> | 									<span className="text-muted">(only you can see this)</span> | ||||||
|  | @ -145,14 +176,15 @@ export default function UserPage() { | ||||||
| 							</div> | 							</div> | ||||||
| 						) : ( | 						) : ( | ||||||
| 							<div className="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 text-center"> | 							<div className="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 text-center"> | ||||||
| 									{user.members.map((member, i) => ( | 								{members.map((member, i) => ( | ||||||
| 									<MemberCard user={user} member={member} key={i} /> | 									<MemberCard user={user} member={member} key={i} /> | ||||||
| 								))} | 								))} | ||||||
| 							</div> | 							</div> | ||||||
| 						)} | 						)} | ||||||
| 					</div> | 					</div> | ||||||
|  | 					{pageCount > 1 && pagination} | ||||||
| 				</> | 				</> | ||||||
| 				))} | 			)} | ||||||
| 		</> | 		</> | ||||||
| 	); | 	); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue