From 0666c991513882fdb68690da5738d3dddfadc06c Mon Sep 17 00:00:00 2001
From: Keane Chan <e0425601@u.nus.edu>
Date: Sat, 15 Oct 2022 12:41:38 +0800
Subject: [PATCH] [resumes][feat] add isStarredByUser field to Resumes (#381)

---
 .../resumes/browse/ResumeListItem.tsx         | 18 +++----------
 .../resumes/browse/ResumeListItems.tsx        |  4 +--
 apps/portal/src/pages/resumes/[resumeId].tsx  |  3 ---
 .../resumes/resumes-comments-user-router.ts   |  2 +-
 .../router/resumes/resumes-resume-router.ts   |  9 +++++++
 .../resumes/resumes-resume-user-router.ts     | 27 +++++++------------
 apps/portal/src/types/resume.d.ts             |  1 +
 7 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/apps/portal/src/components/resumes/browse/ResumeListItem.tsx b/apps/portal/src/components/resumes/browse/ResumeListItem.tsx
index 0d0a2756..b314376a 100644
--- a/apps/portal/src/components/resumes/browse/ResumeListItem.tsx
+++ b/apps/portal/src/components/resumes/browse/ResumeListItem.tsx
@@ -1,17 +1,14 @@
 import formatDistanceToNow from 'date-fns/formatDistanceToNow';
 import Link from 'next/link';
-import { useSession } from 'next-auth/react';
 import type { UrlObject } from 'url';
-import { ChevronRightIcon } from '@heroicons/react/20/solid';
 import {
   AcademicCapIcon,
   BriefcaseIcon,
+  ChevronRightIcon,
   StarIcon as ColouredStarIcon,
 } from '@heroicons/react/20/solid';
 import { ChatBubbleLeftIcon, StarIcon } from '@heroicons/react/24/outline';
 
-import { trpc } from '~/utils/trpc';
-
 import type { Resume } from '~/types/resume';
 
 type Props = Readonly<{
@@ -19,16 +16,7 @@ type Props = Readonly<{
   resumeInfo: Resume;
 }>;
 
-export default function BrowseListItem({ href, resumeInfo }: Props) {
-  const { data: sessionData } = useSession();
-
-  // Find out if user has starred this particular resume
-  const resumeId = resumeInfo.id;
-  const isStarredQuery = trpc.useQuery([
-    'resumes.resume.user.isResumeStarred',
-    { resumeId },
-  ]);
-
+export default function ResumeListItem({ href, resumeInfo }: Props) {
   return (
     <Link href={href}>
       <div className="grid grid-cols-8 gap-4 border-b border-slate-200 p-4 hover:bg-slate-100">
@@ -56,7 +44,7 @@ export default function BrowseListItem({ href, resumeInfo }: Props) {
               {resumeInfo.numComments} comments
             </div>
             <div className="flex gap-2">
-              {isStarredQuery.data && sessionData?.user ? (
+              {resumeInfo.isStarredByUser ? (
                 <ColouredStarIcon className="w-4 text-yellow-400" />
               ) : (
                 <StarIcon className="w-4" />
diff --git a/apps/portal/src/components/resumes/browse/ResumeListItems.tsx b/apps/portal/src/components/resumes/browse/ResumeListItems.tsx
index 1216e36b..07f61770 100644
--- a/apps/portal/src/components/resumes/browse/ResumeListItems.tsx
+++ b/apps/portal/src/components/resumes/browse/ResumeListItems.tsx
@@ -1,6 +1,6 @@
 import { Spinner } from '@tih/ui';
 
-import ResumseListItem from './ResumeListItem';
+import ResumeListItem from './ResumeListItem';
 
 import type { Resume } from '~/types/resume';
 
@@ -22,7 +22,7 @@ export default function ResumeListItems({ isLoading, resumes }: Props) {
     <ul role="list">
       {resumes.map((resumeObj: Resume) => (
         <li key={resumeObj.id}>
-          <ResumseListItem
+          <ResumeListItem
             href={`/resumes/${resumeObj.id}`}
             resumeInfo={resumeObj}
           />
diff --git a/apps/portal/src/pages/resumes/[resumeId].tsx b/apps/portal/src/pages/resumes/[resumeId].tsx
index 611f0204..79fbd077 100644
--- a/apps/portal/src/pages/resumes/[resumeId].tsx
+++ b/apps/portal/src/pages/resumes/[resumeId].tsx
@@ -49,7 +49,6 @@ export default function ResumeReviewPage() {
       utils.invalidateQueries(['resumes.resume.findOne']);
     },
   });
-
   const userIsOwner =
     session?.user?.id != null && session.user.id === detailsQuery.data?.userId;
 
@@ -61,8 +60,6 @@ export default function ResumeReviewPage() {
       return;
     }
 
-    // Star button only rendered if resume exists
-    // Star button only clickable if user exists
     if (detailsQuery.data?.stars.length) {
       unstarMutation.mutate({
         resumeId: resumeId as string,
diff --git a/apps/portal/src/server/router/resumes/resumes-comments-user-router.ts b/apps/portal/src/server/router/resumes/resumes-comments-user-router.ts
index 479c9390..0e083222 100644
--- a/apps/portal/src/server/router/resumes/resumes-comments-user-router.ts
+++ b/apps/portal/src/server/router/resumes/resumes-comments-user-router.ts
@@ -22,7 +22,7 @@ export const resumesCommentsUserRouter = createProtectedRouter().mutation(
       skills: z.string(),
     }),
     async resolve({ ctx, input }) {
-      const userId = ctx.session?.user?.id;
+      const userId = ctx.session.user.id;
       const { resumeId, education, experience, general, projects, skills } =
         input;
 
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 34d7d6f0..4f5c33d8 100644
--- a/apps/portal/src/server/router/resumes/resumes-resume-router.ts
+++ b/apps/portal/src/server/router/resumes/resumes-resume-router.ts
@@ -7,6 +7,7 @@ import type { Resume } from '~/types/resume';
 export const resumesRouter = createRouter()
   .query('findAll', {
     async resolve({ ctx }) {
+      const userId = ctx.session?.user?.id;
       const resumesData = await ctx.prisma.resumesResume.findMany({
         include: {
           _count: {
@@ -15,6 +16,13 @@ export const resumesRouter = createRouter()
               stars: true,
             },
           },
+          stars: {
+            where: {
+              OR: {
+                userId,
+              },
+            },
+          },
           user: {
             select: {
               name: true,
@@ -31,6 +39,7 @@ export const resumesRouter = createRouter()
           createdAt: r.createdAt,
           experience: r.experience,
           id: r.id,
+          isStarredByUser: r.stars.length > 0,
           location: r.location,
           numComments: r._count.comments,
           numStars: r._count.stars,
diff --git a/apps/portal/src/server/router/resumes/resumes-resume-user-router.ts b/apps/portal/src/server/router/resumes/resumes-resume-user-router.ts
index 366385ad..a61aa3ac 100644
--- a/apps/portal/src/server/router/resumes/resumes-resume-user-router.ts
+++ b/apps/portal/src/server/router/resumes/resumes-resume-user-router.ts
@@ -17,7 +17,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
       url: z.string(),
     }),
     async resolve({ ctx, input }) {
-      const userId = ctx.session?.user.id;
+      const userId = ctx.session.user.id;
 
       return await ctx.prisma.resumesResume.upsert({
         create: {
@@ -46,7 +46,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
   })
   .query('findUserStarred', {
     async resolve({ ctx }) {
-      const userId = ctx.session?.user?.id;
+      const userId = ctx.session.user.id;
       const resumeStarsData = await ctx.prisma.resumesStar.findMany({
         include: {
           resume: {
@@ -78,6 +78,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
           createdAt: rs.resume.createdAt,
           experience: rs.resume.experience,
           id: rs.resume.id,
+          isStarredByUser: true,
           location: rs.resume.location,
           numComments: rs.resume._count.comments,
           numStars: rs.resume._count.stars,
@@ -92,7 +93,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
   })
   .query('findUserCreated', {
     async resolve({ ctx }) {
-      const userId = ctx.session?.user?.id;
+      const userId = ctx.session.user.id;
       const resumesData = await ctx.prisma.resumesResume.findMany({
         include: {
           _count: {
@@ -101,6 +102,11 @@ export const resumesResumeUserRouter = createProtectedRouter()
               stars: true,
             },
           },
+          stars: {
+            where: {
+              userId,
+            },
+          },
           user: {
             select: {
               name: true,
@@ -120,6 +126,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
           createdAt: r.createdAt,
           experience: r.experience,
           id: r.id,
+          isStarredByUser: r.stars.length > 0,
           location: r.location,
           numComments: r._count.comments,
           numStars: r._count.stars,
@@ -131,18 +138,4 @@ export const resumesResumeUserRouter = createProtectedRouter()
         return resume;
       });
     },
-  })
-  .query('isResumeStarred', {
-    input: z.object({
-      resumeId: z.string(),
-    }),
-    async resolve({ ctx, input }) {
-      const userId = ctx.session?.user?.id;
-      const { resumeId } = input;
-      return await ctx.prisma.resumesStar.findUnique({
-        where: {
-          userId_resumeId: { resumeId, userId },
-        },
-      });
-    },
   });
diff --git a/apps/portal/src/types/resume.d.ts b/apps/portal/src/types/resume.d.ts
index 5b2a33a9..39e782bb 100644
--- a/apps/portal/src/types/resume.d.ts
+++ b/apps/portal/src/types/resume.d.ts
@@ -3,6 +3,7 @@ export type Resume = {
   createdAt: Date;
   experience: string;
   id: string;
+  isStarredByUser: boolean;
   location: string;
   numComments: number;
   numStars: number;