From 41d51702252a6d736d09532858239b9532e1cae0 Mon Sep 17 00:00:00 2001 From: Keane Chan Date: Thu, 20 Oct 2022 18:25:26 +0800 Subject: [PATCH] [resumes][feat] scaffold for resume badges (#399) * [resumes][fix] reduce font size in comments * [resumes][feat] add queries for resume badges * [resumes][feat] add scaffold for resume badges * [resumes][chore] remove unused query --- .../reviewer/BronzeReviewerBadgeIcon.tsx | 7 ++- .../reviewer/GoldReviewerBadgeIcon.tsx | 7 ++- .../reviewer/SilverReviewerBadgeIcon.tsx | 7 ++- .../resumes/badges/ResumeUserBadge.tsx | 31 ++++++++++ .../resumes/badges/ResumeUserBadges.tsx | 36 ++++++++++++ .../resumes/badges/resumeBadgeConstants.ts | 57 +++++++++++++++++++ .../comments/ResumeCommentListItem.tsx | 11 ++-- .../resumes/comments/ResumeCommentsList.tsx | 6 +- .../router/resumes/resumes-comments-router.ts | 1 - .../router/resumes/resumes-resume-router.ts | 16 ++++++ 10 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 apps/portal/src/components/resumes/badges/ResumeUserBadge.tsx create mode 100644 apps/portal/src/components/resumes/badges/ResumeUserBadges.tsx create mode 100644 apps/portal/src/components/resumes/badges/resumeBadgeConstants.ts diff --git a/apps/portal/src/components/resumes/badgeIcons/reviewer/BronzeReviewerBadgeIcon.tsx b/apps/portal/src/components/resumes/badgeIcons/reviewer/BronzeReviewerBadgeIcon.tsx index 16871299..bd5ae146 100644 --- a/apps/portal/src/components/resumes/badgeIcons/reviewer/BronzeReviewerBadgeIcon.tsx +++ b/apps/portal/src/components/resumes/badgeIcons/reviewer/BronzeReviewerBadgeIcon.tsx @@ -1,7 +1,12 @@ -export default function BronzeReviewerBadgeIcon() { +type Props = Readonly<{ + className: string; +}>; + +export default function BronzeReviewerBadgeIcon({ className }: Props) { return (
+
+ +

{toolTip}

+

{description}

+
+ +
+ ); +} diff --git a/apps/portal/src/components/resumes/badges/ResumeUserBadges.tsx b/apps/portal/src/components/resumes/badges/ResumeUserBadges.tsx new file mode 100644 index 00000000..8c772d8f --- /dev/null +++ b/apps/portal/src/components/resumes/badges/ResumeUserBadges.tsx @@ -0,0 +1,36 @@ +import { trpc } from '~/utils/trpc'; + +import type { BadgePayload } from './resumeBadgeConstants'; +import { RESUME_USER_BADGES } from './resumeBadgeConstants'; +import ResumeUserBadge from './ResumeUserBadge'; + +type Props = Readonly<{ + userId: string; +}>; + +export default function ResumeUserBadges({ userId }: Props) { + const userReviewedResumesCountQuery = trpc.useQuery([ + 'resumes.resume.findUserReviewedResumeCount', + { userId }, + ]); + + // TODO: Add other badges in + const payload: BadgePayload = { + reviewedResumesCount: userReviewedResumesCountQuery.data ?? 0, + }; + + return ( + <> + {RESUME_USER_BADGES.filter((badge) => badge.isValid(payload)).map( + (badge) => ( + + ), + )} + + ); +} diff --git a/apps/portal/src/components/resumes/badges/resumeBadgeConstants.ts b/apps/portal/src/components/resumes/badges/resumeBadgeConstants.ts new file mode 100644 index 00000000..92086204 --- /dev/null +++ b/apps/portal/src/components/resumes/badges/resumeBadgeConstants.ts @@ -0,0 +1,57 @@ +import BronzeReviewerBadgeIcon from '../badgeIcons/reviewer/BronzeReviewerBadgeIcon'; +import GoldReviewerBadgeIcon from '../badgeIcons/reviewer/GoldReviewerBadgeIcon'; +import SilverReviewerBadgeIcon from '../badgeIcons/reviewer/SilverReviewerBadgeIcon'; + +export type BadgeIcon = ( + props: React.ComponentProps< + | typeof BronzeReviewerBadgeIcon + | typeof GoldReviewerBadgeIcon + | typeof SilverReviewerBadgeIcon + >, +) => JSX.Element; + +export type BadgeInfo = { + description: string; + icon: BadgeIcon; + id: string; + isValid: (payload: BadgePayload) => boolean; + toolTip: string; +}; + +// TODO: Add other badges in +export type BadgePayload = { + reviewedResumesCount: number; +}; + +const GOLD_TIER = 20; +const SILVER_TIER = 10; +const BRONZE_TIER = 5; + +export const RESUME_USER_BADGES: Array = [ + { + description: `User has reviewed over ${GOLD_TIER} resumes`, + icon: GoldReviewerBadgeIcon, + id: 'Superhero', + isValid: (payload: BadgePayload) => + payload.reviewedResumesCount >= GOLD_TIER, + toolTip: 'True saviour of the people', + }, + { + description: `User has reviewed over ${SILVER_TIER} resumes`, + icon: SilverReviewerBadgeIcon, + id: 'Detective', + isValid: (payload: BadgePayload) => + payload.reviewedResumesCount >= SILVER_TIER && + payload.reviewedResumesCount < GOLD_TIER, + toolTip: 'Keen eye for details like a private eye', + }, + { + description: `User has reviewed over ${BRONZE_TIER} resumes`, + icon: BronzeReviewerBadgeIcon, + id: 'Eagle', + isValid: (payload: BadgePayload) => + payload.reviewedResumesCount >= BRONZE_TIER && + payload.reviewedResumesCount < SILVER_TIER, + toolTip: 'As sharp as an eagle', + }, +]; diff --git a/apps/portal/src/components/resumes/comments/ResumeCommentListItem.tsx b/apps/portal/src/components/resumes/comments/ResumeCommentListItem.tsx index 28f6a224..833c53c2 100644 --- a/apps/portal/src/components/resumes/comments/ResumeCommentListItem.tsx +++ b/apps/portal/src/components/resumes/comments/ResumeCommentListItem.tsx @@ -13,6 +13,7 @@ import { Button, TextArea } from '@tih/ui'; import { trpc } from '~/utils/trpc'; +import ResumeUserBadges from '../badges/ResumeUserBadges'; import ResumeExpandableText from '../shared/ResumeExpandableText'; import type { ResumeComment } from '~/types/resume-comments'; @@ -152,13 +153,15 @@ export default function ResumeCommentListItem({ {/* Name and creation time */}
-
+

{comment.user.name ?? 'Reviewer ABC'} -

+

-
+

{isCommentOwner ? '(Me)' : ''} -

+

+ +
diff --git a/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx b/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx index 23f8ad02..677678e6 100644 --- a/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx +++ b/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx @@ -33,7 +33,7 @@ export default function ResumeCommentsList({ const commentsQuery = trpc.useQuery(['resumes.comments.list', { resumeId }]); const renderIcon = (section: ResumesSection) => { - const className = 'h-8 w-8'; + const className = 'h-7 w-7'; switch (section) { case ResumesSection.GENERAL: return ; @@ -83,11 +83,11 @@ export default function ResumeCommentsList({ const commentCount = comments.length; return ( -
+
{renderIcon(value)} -
{label}
+
{label}
{commentCount > 0 ? ( diff --git a/apps/portal/src/server/router/resumes/resumes-comments-router.ts b/apps/portal/src/server/router/resumes/resumes-comments-router.ts index 33d6256a..096e8323 100644 --- a/apps/portal/src/server/router/resumes/resumes-comments-router.ts +++ b/apps/portal/src/server/router/resumes/resumes-comments-router.ts @@ -13,7 +13,6 @@ export const resumeCommentsRouter = createRouter().query('list', { // For this resume, we retrieve every comment's information, along with: // The user's name and image to render - // Number of votes, and whether the user (if-any) has voted const comments = await ctx.prisma.resumesComment.findMany({ include: { user: { diff --git a/apps/portal/src/server/router/resumes/resumes-resume-router.ts b/apps/portal/src/server/router/resumes/resumes-resume-router.ts index 00c9f13b..00f20b20 100644 --- a/apps/portal/src/server/router/resumes/resumes-resume-router.ts +++ b/apps/portal/src/server/router/resumes/resumes-resume-router.ts @@ -138,4 +138,20 @@ export const resumesRouter = createRouter() }, }); }, + }) + .query('findUserReviewedResumeCount', { + input: z.object({ + userId: z.string(), + }), + async resolve({ ctx, input }) { + return await ctx.prisma.resumesResume.count({ + where: { + comments: { + some: { + userId: input.userId, + }, + }, + }, + }); + }, });