Revert "[resumes][refactor] tweak resume review page UI"

This reverts commit 29abea1cd9.
pull/509/head
Yangshun Tay 2 years ago
parent 29abea1cd9
commit 6d3c29e6c4

@ -194,7 +194,7 @@ export default function AppShell({ children }: Props) {
<span className="sr-only">Open sidebar</span> <span className="sr-only">Open sidebar</span>
<Bars3BottomLeftIcon aria-hidden="true" className="h-6 w-6" /> <Bars3BottomLeftIcon aria-hidden="true" className="h-6 w-6" />
</button> </button>
<div className="flex flex-1 justify-between px-4 sm:px-6 lg:px-8"> <div className="flex flex-1 justify-between px-4 sm:px-6">
<div className="flex flex-1 items-center"> <div className="flex flex-1 items-center">
<ProductNavigation <ProductNavigation
items={currentProductNavigation.navigation} items={currentProductNavigation.navigation}

@ -36,9 +36,9 @@ export default function ResumePdf({ url }: Props) {
return ( return (
<div className="w-full" id="pdfView"> <div className="w-full" id="pdfView">
<div className="group relative bg-slate-100"> <div className="group relative">
<Document <Document
className="flex h-[calc(100vh-16rem)] flex-row justify-center overflow-auto py-8" className="flex h-[calc(100vh-16rem)] flex-row justify-center overflow-auto"
file={url} file={url}
loading={<Spinner display="block" size="lg" />} loading={<Spinner display="block" size="lg" />}
noData="" noData=""
@ -79,7 +79,7 @@ export default function ResumePdf({ url }: Props) {
</div> </div>
</Document> </Document>
</div> </div>
<div className="flex justify-center border-t border-slate-200 bg-white py-4"> <div className="flex justify-center p-4">
<Pagination <Pagination
current={pageNumber} current={pageNumber}
end={numPages} end={numPages}

@ -39,22 +39,18 @@ export default function ResumeUserBadges({ userId }: Props) {
topUpvotedCommentCount: userTopUpvotedCommentCountQuery.data ?? 0, topUpvotedCommentCount: userTopUpvotedCommentCountQuery.data ?? 0,
}; };
const badges = RESUME_USER_BADGES.filter((badge) => badge.isValid(payload));
if (badges.length === 0) {
return null;
}
return ( return (
<div className="flex items-center justify-center gap-1"> <div className="flex items-center justify-center gap-1">
{badges.map((badge) => ( {RESUME_USER_BADGES.filter((badge) => badge.isValid(payload)).map(
<ResumeUserBadge (badge) => (
key={badge.id} <ResumeUserBadge
description={badge.description} key={badge.id}
icon={badge.icon} description={badge.description}
title={badge.title} icon={badge.icon}
/> title={badge.title}
))} />
),
)}
</div> </div>
); );
} }

@ -1,6 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
import { useState } from 'react'; import { useState } from 'react';
import { ChevronUpIcon } from '@heroicons/react/20/solid';
import { FaceSmileIcon } from '@heroicons/react/24/outline'; import { FaceSmileIcon } from '@heroicons/react/24/outline';
import ResumeCommentEditForm from './comment/ResumeCommentEditForm'; import ResumeCommentEditForm from './comment/ResumeCommentEditForm';
@ -11,10 +12,10 @@ import ResumeExpandableText from '../shared/ResumeExpandableText';
import type { ResumeComment } from '~/types/resume-comments'; import type { ResumeComment } from '~/types/resume-comments';
type ResumeCommentListItemProps = Readonly<{ type ResumeCommentListItemProps = {
comment: ResumeComment; comment: ResumeComment;
userId: string | undefined; userId: string | undefined;
}>; };
export default function ResumeCommentListItem({ export default function ResumeCommentListItem({
comment, comment,
@ -27,14 +28,14 @@ export default function ResumeCommentListItem({
return ( return (
<div className="min-w-fit"> <div className="min-w-fit">
<div className="flex flex-row space-x-3 align-top"> <div className="flex flex-row space-x-2 p-1 align-top">
{/* Image Icon */} {/* Image Icon */}
{comment.user.image ? ( {comment.user.image ? (
<img <img
alt={comment.user.name ?? 'Reviewer'} alt={comment.user.name ?? 'Reviewer'}
className={clsx( className={clsx(
'mt-1 rounded-full', 'mt-1 rounded-full',
comment.parentId ? 'h-8 w-8' : 'h-10 w-10', comment.parentId ? 'h-6 w-6' : 'h-8 w-8 ',
)} )}
src={comment.user.image!} src={comment.user.image!}
/> />
@ -49,18 +50,24 @@ export default function ResumeCommentListItem({
<div className="flex w-full flex-col space-y-1"> <div className="flex w-full flex-col space-y-1">
{/* Name and creation time */} {/* Name and creation time */}
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row justify-between">
<p className={clsx('text-sm font-medium text-slate-800')}> <div className="flex flex-row items-center space-x-1">
{comment.user.name ?? 'Reviewer ABC'} <p
</p> className={clsx(
{isCommentOwner && ( 'font-medium text-gray-800',
<span className="bg-primary-100 text-primary-800 rounded-md py-0.5 px-1 text-xs"> !!comment.parentId && 'text-sm',
Me )}>
</span> {comment.user.name ?? 'Reviewer ABC'}
)} </p>
<ResumeUserBadges userId={comment.user.userId} />
<span className="font-medium text-slate-500">&middot;</span> <p className="text-primary-800 text-xs font-medium">
<div className="text-xs text-slate-500"> {isCommentOwner ? '(Me)' : ''}
</p>
<ResumeUserBadges userId={comment.user.userId} />
</div>
<div className="px-2 text-xs text-slate-600">
{formatDistanceToNow(comment.createdAt, { {formatDistanceToNow(comment.createdAt, {
addSuffix: true, addSuffix: true,
})} })}
@ -74,7 +81,7 @@ export default function ResumeCommentListItem({
setIsEditingComment={setIsEditingComment} setIsEditingComment={setIsEditingComment}
/> />
) : ( ) : (
<div className="text-slate-800"> <div className="text-gray-800">
<ResumeExpandableText <ResumeExpandableText
key={comment.description} key={comment.description}
text={comment.description} text={comment.description}
@ -83,54 +90,31 @@ export default function ResumeCommentListItem({
)} )}
{/* Upvote and edit */} {/* Upvote and edit */}
<div className="flex flex-row space-x-2 pt-1 align-middle"> <div className="flex flex-row space-x-1 pt-1 align-middle">
<ResumeCommentVoteButtons commentId={comment.id} userId={userId} /> <ResumeCommentVoteButtons commentId={comment.id} userId={userId} />
{/* Action buttons; only present for authenticated user when not editing/replying */} {/* Action buttons; only present for authenticated user when not editing/replying */}
{userId && !isEditingComment && !isReplyingComment && ( {userId && !isEditingComment && !isReplyingComment && (
<> <>
{isCommentOwner && ( {isCommentOwner && (
<> <button
<span className="font-medium text-slate-500">&middot;</span>{' '} className="text-primary-800 hover:text-primary-400 px-1 text-xs"
<button type="button"
className="px-1 text-xs font-medium text-slate-500 hover:text-slate-600" onClick={() => setIsEditingComment(true)}>
type="button" Edit
onClick={() => setIsEditingComment(true)}> </button>
Edit
</button>
</>
)} )}
{!comment.parentId && ( {!comment.parentId && (
<> <button
<span className="font-medium text-slate-500">&middot;</span>{' '} className="text-primary-800 hover:text-primary-400 px-1 text-xs"
<button type="button"
className="px-1 text-xs font-medium text-slate-500 hover:text-slate-600" onClick={() => setIsReplyingComment(true)}>
type="button" Reply
onClick={() => setIsReplyingComment(true)}> </button>
Reply
</button>
</>
)} )}
</> </>
)} )}
{comment.children.length > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button
className="flex items-center space-x-1 rounded-md text-xs font-medium text-slate-500 hover:text-slate-600"
type="button"
onClick={() => setShowReplies(!showReplies)}>
<span>
{showReplies
? `Hide ${
comment.children.length === 1 ? 'reply' : 'replies'
}`
: `Show ${comment.children.length} ${
comment.children.length === 1 ? 'reply' : 'replies'
}`}
</span>
</button>
</>
)}
</div> </div>
{/* Reply Form */} {/* Reply Form */}
@ -144,21 +128,48 @@ export default function ResumeCommentListItem({
)} )}
{/* Replies */} {/* Replies */}
{comment.children.length > 0 && showReplies && ( {comment.children.length > 0 && (
<div className="min-w-fit space-y-1 pt-2"> <div className="min-w-fit space-y-1 pt-2">
<div className="flex flex-row border-l-2 border-slate-200 pl-2"> <button
<div className="flex flex-1 flex-col space-y-1"> className="text-primary-800 hover:text-primary-300 flex items-center space-x-1 rounded-md text-xs font-medium"
{comment.children.map((child) => { type="button"
return ( onClick={() => setShowReplies(!showReplies)}>
<ResumeCommentListItem <ChevronUpIcon
key={child.id} className={clsx(
comment={child} 'h-5 w-5 ',
userId={userId} !showReplies && 'rotate-180 transform',
/> )}
); />
})} <span>
{showReplies
? `Hide ${
comment.children.length === 1 ? 'reply' : 'replies'
}`
: `Show ${comment.children.length} ${
comment.children.length === 1 ? 'reply' : 'replies'
}`}
</span>
</button>
{showReplies && (
<div className="flex flex-row">
<div className="relative flex flex-col px-2 py-2">
<div className="flex-grow border-r border-slate-300" />
</div>
<div className="flex flex-1 flex-col space-y-1">
{comment.children.map((child) => {
return (
<ResumeCommentListItem
key={child.id}
comment={child}
userId={userId}
/>
);
})}
</div>
</div> </div>
</div> )}
</div> </div>
)} )}
</div> </div>

@ -115,12 +115,10 @@ export default function ResumeCommentsForm({
}; };
return ( return (
<div className="overflow-y-auto py-8 px-4 lg:h-[calc(100vh-13rem)]"> <div className="h-[calc(100vh-13rem)] overflow-y-auto pb-4">
<h2 className="text-xl font-medium text-slate-800"> <h2 className="text-xl font-semibold text-slate-800">Add your review</h2>
Contribute a review <p className="text-slate-800">
</h2> Please fill in at least one section to submit your review
<p className="mt-1 text-slate-600">
Please fill in at least one section to submit a review.
</p> </p>
<form <form

@ -1,7 +1,9 @@
import clsx from 'clsx';
import { useSession } from 'next-auth/react'; import { useSession } from 'next-auth/react';
import { import {
BookOpenIcon, BookOpenIcon,
BriefcaseIcon, BriefcaseIcon,
ChatBubbleLeftRightIcon,
CodeBracketSquareIcon, CodeBracketSquareIcon,
FaceSmileIcon, FaceSmileIcon,
IdentificationIcon, IdentificationIcon,
@ -29,7 +31,7 @@ export default function ResumeCommentsList({
const commentsQuery = trpc.useQuery(['resumes.comments.list', { resumeId }]); const commentsQuery = trpc.useQuery(['resumes.comments.list', { resumeId }]);
const renderIcon = (section: ResumesSection) => { const renderIcon = (section: ResumesSection) => {
const className = 'h-5 w-5'; const className = 'h-7 w-7';
switch (section) { switch (section) {
case ResumesSection.GENERAL: case ResumesSection.GENERAL:
return <IdentificationIcon className={className} />; return <IdentificationIcon className={className} />;
@ -55,7 +57,7 @@ export default function ResumeCommentsList({
} }
return ( return (
<div className="flow-root w-full space-y-4 overflow-y-auto overflow-x-hidden px-4 lg:h-[calc(100vh-12rem)] lg:py-8"> <div className="flow-root w-full flex-col space-y-10 overflow-y-auto overflow-x-hidden lg:h-[calc(100vh-13rem)]">
{RESUME_COMMENTS_SECTIONS.map(({ label, value }) => { {RESUME_COMMENTS_SECTIONS.map(({ label, value }) => {
const comments = commentsQuery.data const comments = commentsQuery.data
? commentsQuery.data.filter((comment: ResumeComment) => { ? commentsQuery.data.filter((comment: ResumeComment) => {
@ -65,19 +67,22 @@ export default function ResumeCommentsList({
const commentCount = comments.length; const commentCount = comments.length;
return ( return (
<div <div key={value} className="space-y-4 pr-4">
key={value}
className="rounded-lg border border-slate-200 bg-white shadow-sm">
{/* CommentHeader Section */} {/* CommentHeader Section */}
<div className="flex items-center space-x-2 border-b border-slate-200 px-4 py-3 font-medium text-slate-700"> <div className="text-primary-800 flex items-center space-x-2">
<hr className="flex-grow border-slate-800" />
{renderIcon(value)} {renderIcon(value)}
<span className="w-fit text-sm font-medium uppercase tracking-wide">
{label} <span className="w-fit text-lg font-medium">{label}</span>
</span> <hr className="flex-grow border-slate-800" />
</div> </div>
{/* Comment Section */} {/* Comment Section */}
<div className="space-y-4 px-4 py-3"> <div
className={clsx(
'space-y-2 rounded-md border-2 bg-white px-4 py-3 drop-shadow-md',
commentCount ? 'border-slate-300' : 'border-slate-300',
)}>
{commentCount > 0 ? ( {commentCount > 0 ? (
comments.map((comment) => { comments.map((comment) => {
return ( return (
@ -90,8 +95,10 @@ export default function ResumeCommentsList({
}) })
) : ( ) : (
<div className="flex flex-row items-center text-sm"> <div className="flex flex-row items-center text-sm">
<ChatBubbleLeftRightIcon className="mr-2 h-6 w-6 text-slate-500" />
<div className="text-slate-500"> <div className="text-slate-500">
There are no comments for this section. There are no comments for this section yet!
</div> </div>
</div> </div>
)} )}

@ -77,37 +77,39 @@ export default function ResumeCommentReplyForm({
}; };
return ( return (
<form className="space-y-2" onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<TextArea <div className="flex-column space-y-2 pt-2">
{...(register('description', { <TextArea
required: 'Reply cannot be empty!', {...(register('description', {
}), required: 'Reply cannot be empty!',
{})} }),
defaultValue="" {})}
disabled={commentReplyMutation.isLoading} defaultValue=""
errorMessage={errors.description?.message}
label=""
placeholder="Leave your reply here"
onChange={setFormValue}
/>
<div className="flex-row space-x-2">
<Button
disabled={commentReplyMutation.isLoading} disabled={commentReplyMutation.isLoading}
label="Cancel" errorMessage={errors.description?.message}
size="sm" label=""
variant="tertiary" placeholder="Leave your reply here"
onClick={onCancel} onChange={setFormValue}
/> />
<Button <div className="flex-row space-x-2">
disabled={!isDirty || commentReplyMutation.isLoading} <Button
isLoading={commentReplyMutation.isLoading} disabled={commentReplyMutation.isLoading}
label="Confirm" label="Cancel"
size="sm" size="sm"
type="submit" variant="tertiary"
variant="primary" onClick={onCancel}
/> />
<Button
disabled={!isDirty || commentReplyMutation.isLoading}
isLoading={commentReplyMutation.isLoading}
label="Confirm"
size="sm"
type="submit"
variant="primary"
/>
</div>
</div> </div>
</form> </form>
); );

@ -1,7 +1,10 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; import { useState } from 'react';
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/solid'; import {
ArrowDownCircleIcon,
ArrowUpCircleIcon,
} from '@heroicons/react/20/solid';
import { Vote } from '@prisma/client'; import { Vote } from '@prisma/client';
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
@ -89,9 +92,8 @@ export default function ResumeCommentVoteButtons({
}; };
return ( return (
<div className="flex items-center"> <>
<button <button
className="-m-1 rounded-full p-1 hover:bg-slate-100"
disabled={ disabled={
commentVotesQuery.isLoading || commentVotesQuery.isLoading ||
commentVotesUpsertMutation.isLoading || commentVotesUpsertMutation.isLoading ||
@ -99,36 +101,27 @@ export default function ResumeCommentVoteButtons({
} }
type="button" type="button"
onClick={() => onVote(Vote.UPVOTE, setUpvoteAnimation)}> onClick={() => onVote(Vote.UPVOTE, setUpvoteAnimation)}>
<ChevronUpIcon <ArrowUpCircleIcon
className={clsx( className={clsx(
'h-5 w-5', 'h-4 w-4',
commentVotesQuery.data?.userVote?.value === Vote.UPVOTE || commentVotesQuery.data?.userVote?.value === Vote.UPVOTE ||
upvoteAnimation upvoteAnimation
? 'text-primary-500' ? 'fill-primary-500'
: 'text-slate-400', : 'fill-slate-400',
userId && userId &&
!downvoteAnimation && !downvoteAnimation &&
!upvoteAnimation && !upvoteAnimation &&
'hover:text-primary-500', 'hover:fill-primary-500',
upvoteAnimation && 'animate-[bounce_0.5s_infinite] cursor-default', upvoteAnimation && 'animate-[bounce_0.5s_infinite] cursor-default',
)} )}
/> />
</button> </button>
<div className="mx-1 flex min-w-[1rem] justify-center text-xs font-semibold text-gray-700">
<div className="flex min-w-[1rem] justify-center text-xs font-semibold text-gray-700">
{commentVotesQuery.data?.numVotes ?? 0} {commentVotesQuery.data?.numVotes ?? 0}
</div> </div>
<button <button
className={clsx(
'-m-1 rounded-full p-1 hover:bg-slate-100',
commentVotesQuery.data?.userVote?.value === Vote.DOWNVOTE ||
downvoteAnimation
? 'text-danger-500'
: 'text-slate-400',
userId &&
!downvoteAnimation &&
!upvoteAnimation &&
'hover:text-danger-500',
)}
disabled={ disabled={
commentVotesQuery.isLoading || commentVotesQuery.isLoading ||
commentVotesUpsertMutation.isLoading || commentVotesUpsertMutation.isLoading ||
@ -136,14 +129,22 @@ export default function ResumeCommentVoteButtons({
} }
type="button" type="button"
onClick={() => onVote(Vote.DOWNVOTE, setDownvoteAnimation)}> onClick={() => onVote(Vote.DOWNVOTE, setDownvoteAnimation)}>
<ChevronDownIcon <ArrowDownCircleIcon
className={clsx( className={clsx(
'h-5 w-5', 'h-4 w-4',
commentVotesQuery.data?.userVote?.value === Vote.DOWNVOTE ||
downvoteAnimation
? 'fill-danger-500'
: 'fill-slate-400',
userId &&
!downvoteAnimation &&
!upvoteAnimation &&
'hover:fill-danger-500',
downvoteAnimation && downvoteAnimation &&
'animate-[bounce_0.5s_infinite] cursor-default', 'animate-[bounce_0.5s_infinite] cursor-default',
)} )}
/> />
</button> </button>
</div> </>
); );
} }

@ -29,14 +29,14 @@ export default function ResumeExpandableText({
<span <span
ref={ref} ref={ref}
className={clsx( className={clsx(
'line-clamp-3 whitespace-pre-wrap text-xs sm:text-sm', 'line-clamp-3 whitespace-pre-wrap text-sm',
isExpanded ? 'line-clamp-none' : '', isExpanded ? 'line-clamp-none' : '',
)}> )}>
{text} {text}
</span> </span>
{descriptionOverflow && ( {descriptionOverflow && (
<p <p
className="text-primary-500 hover:text-primary-300 mt-1 cursor-pointer text-xs sm:text-sm" className="text-primary-500 hover:text-primary-300 mt-1 cursor-pointer text-xs"
onClick={onSeeActionClicked}> onClick={onSeeActionClicked}>
{isExpanded ? 'See Less' : 'See More'} {isExpanded ? 'See Less' : 'See More'}
</p> </p>

@ -180,9 +180,10 @@ export default function ResumeReviewPage() {
}; };
const renderReviewButton = () => { const renderReviewButton = () => {
if (session == null) { if (session === null) {
return ( return (
<Button <Button
className="h-10 shadow-md"
display="block" display="block"
href={loginPageHref()} href={loginPageHref()}
label="Log in to join discussion" label="Log in to join discussion"
@ -190,9 +191,9 @@ export default function ResumeReviewPage() {
/> />
); );
} }
return ( return (
<Button <Button
className="h-10 shadow-md"
display="block" display="block"
label="Add your review" label="Add your review"
variant="primary" variant="primary"
@ -223,192 +224,182 @@ export default function ResumeReviewPage() {
return ( return (
<> <>
{(detailsQuery.isError || detailsQuery.data == null) && ErrorPage} {(detailsQuery.isError || detailsQuery.data === null) && ErrorPage}
{detailsQuery.isLoading && ( {detailsQuery.isLoading && (
<div className="w-full pt-4"> <div className="w-full pt-4">
<Spinner display="block" size="lg" /> {' '}
<Spinner display="block" size="lg" />{' '}
</div> </div>
)} )}
{detailsQuery.isFetched && detailsQuery.data && ( {detailsQuery.isFetched && detailsQuery.data && (
<> <>
<Head> <Head>
<title>{`${detailsQuery.data.title} | Resume Review`}</title> <title>{detailsQuery.data.title}</title>
</Head> </Head>
<main className="h-full w-full bg-white"> <main className="h-full flex-1 space-y-2 py-4 px-8 xl:px-12 2xl:pr-16">
<div className="mx-auto space-y-4 border-b border-slate-200 px-4 py-6 sm:px-6 md:space-y-2 lg:px-8"> <div className="flex flex-wrap justify-between">
<div className="flex flex-wrap justify-between space-y-4 lg:space-y-0"> <h1 className="w-[60%] pr-2 text-2xl font-semibold leading-7 text-slate-900">
<h1 className="pr-2 text-xl font-medium leading-7 text-slate-900 sm:text-2xl lg:w-[60%]"> {detailsQuery.data.title}
{detailsQuery.data.title} </h1>
</h1> <div className="flex gap-3 xl:pr-4">
<div className="flex gap-3"> {userIsOwner && (
{userIsOwner && ( <>
<> <Button
<Button addonPosition="start"
addonPosition="start" className="h-10 shadow-md"
icon={PencilSquareIcon} icon={PencilSquareIcon}
label="Edit" label="Edit"
variant="tertiary" variant="tertiary"
onClick={onEditButtonClick} onClick={onEditButtonClick}
/>
<button
className="isolate inline-flex items-center space-x-4 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={resolveMutation.isLoading}
type="button"
onClick={onResolveButtonClick}>
<div className="-ml-1 mr-2 h-5 w-5">
{resolveMutation.isLoading ? (
<Spinner className="mt-0.5" size="xs" />
) : (
<CheckCircleIcon
aria-hidden="true"
className={
isResumeResolved
? 'text-slate-500'
: 'text-success-600'
}
/>
)}
</div>
{isResumeResolved
? 'Reopen for review'
: 'Mark as reviewed'}
</button>
</>
)}
<button
className="isolate inline-flex h-10 items-center space-x-4 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={
starMutation.isLoading || unstarMutation.isLoading
}
type="button"
onClick={onStarButtonClick}>
<div className="-ml-1 mr-2 h-5 w-5">
{starMutation.isLoading ||
unstarMutation.isLoading ||
detailsQuery.isLoading ? (
<Spinner className="mt-0.5" size="xs" />
) : (
<StarIcon
aria-hidden="true"
className={clsx(
detailsQuery.data?.stars.length
? 'text-orange-400'
: 'text-slate-400',
)}
/>
)}
</div>
{detailsQuery.data?.stars.length ? 'Starred' : 'Star'}
<span className="relative -ml-px inline-flex">
{detailsQuery.data?._count.stars}
</span>
</button>
<div className="hidden xl:block">{renderReviewButton()}</div>
</div>
</div>
<div className="space-y-2">
<div className="grid grid-cols-2 gap-2 md:flex md:flex-wrap md:space-x-8">
<div className="col-span-1 flex items-center text-xs text-slate-600 sm:text-sm">
<BriefcaseIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/> />
<button <button
className="hover:text-primary-800 underline" className="isolate inline-flex h-10 items-center space-x-4 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-md hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={resolveMutation.isLoading}
type="button" type="button"
onClick={() => onClick={onResolveButtonClick}>
onInfoTagClick({ <div className="-ml-1 mr-2 h-5 w-5">
roleLabel: detailsQuery.data?.role, {resolveMutation.isLoading ? (
}) <Spinner className="mt-0.5" size="xs" />
}> ) : (
{getFilterLabel( <CheckCircleIcon
ROLES, aria-hidden="true"
detailsQuery.data.role as RoleFilter, className={
)} isResumeResolved
? 'text-slate-500'
: 'text-success-600'
}
/>
)}
</div>
{isResumeResolved
? 'Reopen for review'
: 'Mark as reviewed'}
</button> </button>
</div> </>
<div className="col-span-1 flex items-center text-xs text-slate-600 sm:text-sm">
<MapPinIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<button
className="hover:text-primary-800 underline"
type="button"
onClick={() =>
onInfoTagClick({
locationLabel: detailsQuery.data?.location,
})
}>
{getFilterLabel(
LOCATIONS,
detailsQuery.data.location as LocationFilter,
)}
</button>
</div>
<div className="col-span-1 flex items-center text-xs text-slate-600 sm:text-sm">
<AcademicCapIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<button
className="hover:text-primary-800 underline"
type="button"
onClick={() =>
onInfoTagClick({
experienceLabel: detailsQuery.data?.experience,
})
}>
{getFilterLabel(
EXPERIENCES,
detailsQuery.data.experience as ExperienceFilter,
)}
</button>
</div>
<div className="col-span-1 flex items-center text-xs text-slate-600 sm:text-sm">
<CalendarIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{`Uploaded ${formatDistanceToNow(
detailsQuery.data.createdAt,
{
addSuffix: true,
},
)} by ${detailsQuery.data.user.name}`}
</div>
</div>
{detailsQuery.data.additionalInfo && (
<div className="col-span-2 flex items-start whitespace-pre-wrap pt-2 text-slate-600 xl:pt-1">
<InformationCircleIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<ResumeExpandableText
key={detailsQuery.data.additionalInfo}
text={detailsQuery.data.additionalInfo}
/>
</div>
)} )}
<button
className="isolate inline-flex h-10 items-center space-x-4 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-md hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={starMutation.isLoading || unstarMutation.isLoading}
type="button"
onClick={onStarButtonClick}>
<div className="-ml-1 mr-2 h-5 w-5">
{starMutation.isLoading ||
unstarMutation.isLoading ||
detailsQuery.isLoading ? (
<Spinner className="mt-0.5" size="xs" />
) : (
<StarIcon
aria-hidden="true"
className={clsx(
detailsQuery.data?.stars.length
? 'text-orange-400'
: 'text-slate-400',
)}
/>
)}
</div>
{detailsQuery.data?.stars.length ? 'Starred' : 'Star'}
<span className="relative -ml-px inline-flex">
{detailsQuery.data?._count.stars}
</span>
</button>
<div className="hidden xl:block">{renderReviewButton()}</div>
</div>
</div>
<div className="flex flex-col lg:mt-0 lg:flex-row lg:flex-wrap lg:space-x-8">
<div className="mt-2 flex items-center text-sm text-slate-600 xl:mt-1">
<BriefcaseIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<button
className="hover:text-primary-800 underline"
type="button"
onClick={() =>
onInfoTagClick({
roleLabel: detailsQuery.data?.role,
})
}>
{getFilterLabel(ROLES, detailsQuery.data.role as RoleFilter)}
</button>
</div>
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<MapPinIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<button
className="hover:text-primary-800 underline"
type="button"
onClick={() =>
onInfoTagClick({
locationLabel: detailsQuery.data?.location,
})
}>
{getFilterLabel(
LOCATIONS,
detailsQuery.data.location as LocationFilter,
)}
</button>
</div>
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<AcademicCapIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<button
className="hover:text-primary-800 underline"
type="button"
onClick={() =>
onInfoTagClick({
experienceLabel: detailsQuery.data?.experience,
})
}>
{getFilterLabel(
EXPERIENCES,
detailsQuery.data.experience as ExperienceFilter,
)}
</button>
</div>
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<CalendarIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{`Uploaded ${formatDistanceToNow(detailsQuery.data.createdAt, {
addSuffix: true,
})} by ${detailsQuery.data.user.name}`}
</div> </div>
</div> </div>
<div className="flex w-full flex-col divide-x divide-slate-200 py-4 lg:flex-row xl:py-0"> {detailsQuery.data.additionalInfo && (
<div className="w-full bg-slate-100 lg:w-1/2"> <div className="flex items-start whitespace-pre-wrap pt-2 text-sm text-slate-600 xl:pt-1">
<InformationCircleIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<ResumeExpandableText
key={detailsQuery.data.additionalInfo}
text={detailsQuery.data.additionalInfo}
/>
</div>
)}
<div className="flex w-full flex-col gap-6 py-4 xl:flex-row xl:py-0">
<div className="w-full xl:w-1/2">
<ResumePdf url={detailsQuery.data.url} /> <ResumePdf url={detailsQuery.data.url} />
</div> </div>
<div className="grow border-t border-slate-200 bg-slate-50 lg:border-none"> <div className="grow">
<div className="divide-y divide-slate-200 lg:hidden"> <div className="mb-6 space-y-4 xl:hidden">
<div className="bg-white p-4 lg:p-0"> {renderReviewButton()}
{renderReviewButton()} <div className="flex items-center space-x-2">
<hr className="flex-grow border-slate-300" />
<span className="bg-slate-50 px-3 text-lg font-medium text-slate-900">
Reviews
</span>
<hr className="flex-grow border-slate-300" />
</div> </div>
{!showCommentsForm && (
<div className="p-4 lg:p-0">
<h2 className="text-xl font-medium text-slate-900">
Reviews
</h2>
</div>
)}
</div> </div>
{showCommentsForm ? ( {showCommentsForm ? (
<ResumeCommentsForm <ResumeCommentsForm
resumeId={resumeId as string} resumeId={resumeId as string}

Loading…
Cancel
Save