From 3fae0afdcfcb8d4f7e959f9a9b935dcda0a4e53b Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Thu, 20 Nov 2025 01:02:48 +0100
Subject: [PATCH 1/6] feat: likes and dislikes on run items and comments
---
.../lib/assets/icons/icon-thumb-up-filled.svg | 3 +
.../src/lib/assets/icons/icon-thumb-up.svg | 3 +
.../assets/icons/icon-thumbs-down-filled.svg | 3 +
.../src/lib/assets/icons/icon-thumbs-down.svg | 3 +
src/apps/review/src/lib/assets/icons/index.ts | 8 +
.../AiFeedback/AiFeedback.module.scss | 45 ++++
.../AiFeedback/AiFeedback.tsx | 15 +-
.../AiFeedbackActions.module.scss | 40 +++
.../AiFeedbackActions/AiFeedbackActions.tsx | 227 ++++++++++++++++++
.../AiFeedbackComments.module.scss | 29 +++
.../AiFeedbackComments/AiFeedbackComments.tsx | 57 +++++
.../src/lib/services/scorecards.service.ts | 29 ++-
12 files changed, 459 insertions(+), 3 deletions(-)
create mode 100644 src/apps/review/src/lib/assets/icons/icon-thumb-up-filled.svg
create mode 100644 src/apps/review/src/lib/assets/icons/icon-thumb-up.svg
create mode 100644 src/apps/review/src/lib/assets/icons/icon-thumbs-down-filled.svg
create mode 100644 src/apps/review/src/lib/assets/icons/icon-thumbs-down.svg
create mode 100644 src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.module.scss
create mode 100644 src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
create mode 100644 src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.module.scss
create mode 100644 src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
diff --git a/src/apps/review/src/lib/assets/icons/icon-thumb-up-filled.svg b/src/apps/review/src/lib/assets/icons/icon-thumb-up-filled.svg
new file mode 100644
index 000000000..e862a480c
--- /dev/null
+++ b/src/apps/review/src/lib/assets/icons/icon-thumb-up-filled.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/apps/review/src/lib/assets/icons/icon-thumb-up.svg b/src/apps/review/src/lib/assets/icons/icon-thumb-up.svg
new file mode 100644
index 000000000..8f31f2966
--- /dev/null
+++ b/src/apps/review/src/lib/assets/icons/icon-thumb-up.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/apps/review/src/lib/assets/icons/icon-thumbs-down-filled.svg b/src/apps/review/src/lib/assets/icons/icon-thumbs-down-filled.svg
new file mode 100644
index 000000000..5f8c2c03f
--- /dev/null
+++ b/src/apps/review/src/lib/assets/icons/icon-thumbs-down-filled.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/apps/review/src/lib/assets/icons/icon-thumbs-down.svg b/src/apps/review/src/lib/assets/icons/icon-thumbs-down.svg
new file mode 100644
index 000000000..abe7616a6
--- /dev/null
+++ b/src/apps/review/src/lib/assets/icons/icon-thumbs-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/apps/review/src/lib/assets/icons/index.ts b/src/apps/review/src/lib/assets/icons/index.ts
index dffc1a502..de1e549c0 100644
--- a/src/apps/review/src/lib/assets/icons/index.ts
+++ b/src/apps/review/src/lib/assets/icons/index.ts
@@ -15,6 +15,10 @@ import { ReactComponent as IconPremium } from './icon-premium.svg'
import { ReactComponent as IconComment } from './icon-comment.svg'
import { ReactComponent as IconEdit } from './icon-edit.svg'
import { ReactComponent as IconFile } from './icon-file.svg'
+import { ReactComponent as IconThumbsUp } from './icon-thumb-up.svg'
+import { ReactComponent as IconThumbsDown } from './icon-thumbs-down.svg'
+import { ReactComponent as IconThumbsUpFilled } from './icon-thumb-up-filled.svg'
+import { ReactComponent as IconThumbsDownFilled } from './icon-thumbs-down-filled.svg'
export * from './editor/bold'
export * from './editor/code'
@@ -49,6 +53,10 @@ export {
IconComment,
IconEdit,
IconFile,
+ IconThumbsUp,
+ IconThumbsDown,
+ IconThumbsUpFilled,
+ IconThumbsDownFilled,
}
export const phasesIcons = {
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.module.scss b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.module.scss
index fe1718d67..bd3e71bc7 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.module.scss
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.module.scss
@@ -8,4 +8,49 @@
margin-bottom: $sp-2;
}
}
+
+ .comment {
+ margin-top: $sp-3;
+ padding-left: $sp-4;
+ border-left: 2px solid black;
+ }
+
+ .commentContent {
+ margin-bottom: $sp-1;
+ }
+
+ .commentActions {
+ display: flex;
+ gap: $sp-2;
+ margin-bottom: $sp-2;
+ }
+
+ .commentBtn {
+ display: inline-flex;
+ align-items: center;
+ gap: $sp-1;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: $sp-1;
+ }
+
+ .replies {
+ margin-left: $sp-4;
+ margin-top: $sp-2;
+ }
+
+ .reply {
+ margin-top: $sp-2;
+ }
+
+ .count {
+ font-size: 12px;
+ margin-left: $sp-1;
+ }
+
+ .active {
+ color: blue;
+ font-weight: 600;
+ }
}
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.tsx
index 5b57e6f8f..2e1007dd5 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedback/AiFeedback.tsx
@@ -7,6 +7,8 @@ import { ScorecardViewerContextValue, useScorecardViewerContext } from '../../Sc
import { ScorecardQuestionRow } from '../ScorecardQuestionRow'
import { ScorecardScore } from '../../ScorecardScore'
import { MarkdownReview } from '../../../../MarkdownReview'
+import { AiFeedbackActions } from '../AiFeedbackActions/AiFeedbackActions'
+import { AiFeedbackComments } from '../AiFeedbackComments/AiFeedbackComments'
import styles from './AiFeedback.module.scss'
@@ -16,10 +18,12 @@ interface AiFeedbackProps {
const AiFeedback: FC = props => {
const { aiFeedbackItems, scoreMap }: ScorecardViewerContextValue = useScorecardViewerContext()
- const feedback = useMemo(() => (
- aiFeedbackItems?.find(r => r.scorecardQuestionId === props.question.id)
+ const feedback: any = useMemo(() => (
+ aiFeedbackItems?.find((r: any) => r.scorecardQuestionId === props.question.id)
), [props.question.id, aiFeedbackItems])
+ const commentsArr: any[] = (feedback?.comments) || []
+
if (!aiFeedbackItems?.length || !feedback) {
return <>>
}
@@ -43,7 +47,14 @@ const AiFeedback: FC = props => {
{feedback.questionScore ? 'Yes' : 'No'}
)}
+
+
+
+
+ {commentsArr.length > 0 && (
+
+ )}
)
}
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.module.scss b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.module.scss
new file mode 100644
index 000000000..ba7b4fcec
--- /dev/null
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.module.scss
@@ -0,0 +1,40 @@
+@import '@libs/ui/styles/includes';
+
+.actions {
+ display: flex;
+ gap: $sp-2;
+ margin-top: $sp-3;
+ .count {
+ font-family: $font-roboto;
+ color: #0D61BF;
+ font-weight: 700;
+ font-size: 14px;
+ }
+}
+
+.actionBtn {
+ display: inline-flex;
+ align-items: center;
+ gap: $sp-2;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ color: black;
+ padding: $sp-1 $sp-2;
+ &.active {
+ svg {
+ path {
+ fill: $link-blue-dark;
+ }
+ }
+ }
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+
+ &:hover {
+ opacity: 0.85;
+ }
+}
\ No newline at end of file
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
new file mode 100644
index 000000000..d7e71567b
--- /dev/null
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
@@ -0,0 +1,227 @@
+/* eslint-disable react/jsx-no-bind */
+import { FC, useCallback, useContext, useEffect, useState } from 'react'
+import { mutate } from 'swr'
+
+import {
+ IconThumbsDown,
+ IconThumbsDownFilled,
+ IconThumbsUp,
+ IconThumbsUpFilled,
+} from '~/apps/review/src/lib/assets/icons'
+import { ReviewAppContext } from '~/apps/review/src/lib/contexts'
+import { useReviewsContext } from '~/apps/review/src/pages/reviews/ReviewsContext'
+import { updateLikesOrDislikesOnRunItem, updateLikesOrDislikesOnRunItemComment } from '~/apps/review/src/lib/services'
+import { EnvironmentConfig } from '~/config'
+import { ReviewAppContextModel, ReviewsContextModel } from '~/apps/review/src/lib/models'
+
+import { AiFeedbackComment } from '../AiFeedbackComments/AiFeedbackComments'
+
+import styles from './AiFeedbackActions.module.scss'
+
+export enum VOTE_TYPE {
+ UPVOTE = 'UPVOTE',
+ DOWNVOTE = 'DOWNVOTE'
+}
+
+interface AiFeedbackActionsProps {
+ actionType: 'comment' | 'runItem'
+ comment?: AiFeedbackComment
+ feedback?: any
+}
+
+export const AiFeedbackActions: FC = props => {
+
+ const [userVote, setUserVote] = useState(undefined)
+ const [upVotes, setUpVotes] = useState(0)
+ const [downVotes, setDownVotes] = useState(0)
+
+ const { loginUserInfo }: ReviewAppContextModel = useContext(ReviewAppContext)
+ const { workflowId, workflowRun }: ReviewsContextModel = useReviewsContext()
+
+ const votesArr: any[] = (props.actionType === 'runItem' ? (props.feedback?.votes) : (props.comment?.votes)) || []
+
+ const setInitialVotesForFeedback = (): void => {
+ const initialUp = props.feedback?.upVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = props.feedback?.downVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }
+
+ const setInitialVotesForComment = (): void => {
+ const initialUp = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }
+
+ useEffect(() => {
+ if (props.actionType === 'runItem') {
+ setInitialVotesForFeedback()
+ } else {
+ setInitialVotesForComment()
+ }
+ }, [props.actionType, props.feedback?.id, votesArr.length, loginUserInfo?.userId])
+
+ const voteOnItem = useCallback(async (type: VOTE_TYPE) => {
+ if (!workflowId || !workflowRun?.id) return
+ const current = userVote
+ let up = false
+ let down = false
+
+ if (current === type) {
+ // remove vote
+ up = false
+ down = false
+ } else if (!current) {
+ up = type === VOTE_TYPE.UPVOTE
+ down = type === VOTE_TYPE.DOWNVOTE
+ } else {
+ // switch vote
+ up = type === VOTE_TYPE.UPVOTE
+ down = type === VOTE_TYPE.DOWNVOTE
+ }
+
+ // optimistic update
+ const prevUserVote = userVote
+ const prevUp = upVotes
+ const prevDown = downVotes
+
+ if (current === type) {
+ // removing
+ if (type === VOTE_TYPE.UPVOTE) setUpVotes(Math.max(0, upVotes - 1))
+ else setDownVotes(Math.max(0, downVotes - 1))
+ setUserVote(undefined)
+ } else if (!current) {
+ if (type === VOTE_TYPE.UPVOTE) setUpVotes(upVotes + 1)
+ else setDownVotes(downVotes + 1)
+ setUserVote(type)
+ } else {
+ // switch
+ if (type === VOTE_TYPE.UPVOTE) {
+ setUpVotes(upVotes + 1)
+ setDownVotes(Math.max(0, downVotes - 1))
+ } else {
+ setDownVotes(downVotes + 1)
+ setUpVotes(Math.max(0, upVotes - 1))
+ }
+
+ setUserVote(type)
+ }
+
+ try {
+ await updateLikesOrDislikesOnRunItem(workflowId, workflowRun.id, props.feedback.id, {
+ downVote: down,
+ upVote: up,
+ })
+ } catch (err) {
+ // rollback
+ setUserVote(prevUserVote)
+ setUpVotes(prevUp)
+ setDownVotes(prevDown)
+ }
+ }, [workflowId, workflowRun, props.feedback?.id, userVote, upVotes, downVotes])
+
+ const voteOnComment = useCallback(async (c: any, type: VOTE_TYPE) => {
+ if (!workflowId || !workflowRun?.id) return
+ const votes = (c.votes || [])
+ const my = votes.find((v: any) => String(v.createdBy) === String(loginUserInfo?.userId))
+ const current = my?.voteType ?? undefined
+
+ let up = false
+ let down = false
+
+ if (current === type) {
+ up = false
+ down = false
+ } else if (!current) {
+ up = type === VOTE_TYPE.UPVOTE
+ down = type === VOTE_TYPE.DOWNVOTE
+ } else {
+ up = type === VOTE_TYPE.UPVOTE
+ down = type === VOTE_TYPE.DOWNVOTE
+ }
+
+ const prevUserVote = userVote
+ const prevUp = upVotes
+ const prevDown = downVotes
+
+ if (current === type) {
+ // removing
+ if (type === VOTE_TYPE.UPVOTE) setUpVotes(Math.max(0, upVotes - 1))
+ else setDownVotes(Math.max(0, downVotes - 1))
+ setUserVote(undefined)
+ } else if (!current) {
+ if (type === VOTE_TYPE.UPVOTE) setUpVotes(upVotes + 1)
+ else setDownVotes(downVotes + 1)
+ setUserVote(type)
+ } else {
+ // switch
+ if (type === VOTE_TYPE.UPVOTE) {
+ setUpVotes(upVotes + 1)
+ setDownVotes(Math.max(0, downVotes - 1))
+ } else {
+ setDownVotes(downVotes + 1)
+ setUpVotes(Math.max(0, upVotes - 1))
+ }
+
+ setUserVote(type)
+
+ }
+
+ try {
+ await updateLikesOrDislikesOnRunItemComment(workflowId, workflowRun.id, props.feedback.id, c.id, {
+ downVote: down,
+ upVote: up,
+ })
+ await mutate(`${EnvironmentConfig.API.V6}/workflows/${workflowId}/runs/${workflowRun.id}/items`)
+ } catch (err) {
+ setUserVote(prevUserVote)
+ setUpVotes(prevUp)
+ setDownVotes(prevDown)
+ }
+ }, [workflowId, workflowRun, props.feedback?.id, loginUserInfo])
+
+ const onVote = (action: VOTE_TYPE): void => {
+ if (props.actionType === 'comment') {
+ voteOnComment(props.comment as AiFeedbackComment, action)
+ } else {
+ voteOnItem(action)
+ }
+ }
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.module.scss b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.module.scss
new file mode 100644
index 000000000..1a465a9b1
--- /dev/null
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.module.scss
@@ -0,0 +1,29 @@
+@import '@libs/ui/styles/includes';
+
+.comments {
+ font-family: "Nunito Sans", sans-serif;
+ .comment {
+ margin-top: 32px;
+ background-color: #E0E4E8;
+ padding: 16px;
+ .info {
+ margin: 16px 0;
+ font-size: 14px;
+ .reply {
+ color: #0A0A0A;
+ font-weight: $font-weight-bold;
+ }
+ .text {
+ font-weight: $font-weight-normal;
+ color: #767676;
+ }
+ .name {
+ color: #0A0A0A;
+ font-weight: $font-weight-bold;
+ }
+ .date {
+ color: #0A0A0A;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
new file mode 100644
index 000000000..28c9a1414
--- /dev/null
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
@@ -0,0 +1,57 @@
+import { FC } from 'react'
+
+import { AiFeedbackActions } from '../AiFeedbackActions/AiFeedbackActions'
+
+import styles from './AiFeedbackComments.module.scss'
+
+export interface AiFeedbackVote {
+ id: string
+ parentId: string
+ voteType: string
+ createdAt: string
+ createdBy: string
+}
+export interface AiFeedbackComment {
+ id: string
+ content: string
+ parentId: string
+ createdBy: string
+ createdAt: string
+ createdUser: {
+ userId: string
+ handle: string
+ ratingColor: string
+ }
+ votes: AiFeedbackVote[]
+}
+
+interface AiFeedbackCommentsProps {
+ comments: AiFeedbackComment[]
+ feedback: any
+}
+
+export const AiFeedbackComments: FC = props => (
+
+ {props.comments.filter(c => !c.parentId)
+ .map((comment: AiFeedbackComment) => (
+
+
+ Reply
+ by
+
+ {comment.createdUser.handle}
+
+ on
+ {comment.createdAt}
+
+
{comment.content}
+
+
+ ))}
+
+)
diff --git a/src/apps/review/src/lib/services/scorecards.service.ts b/src/apps/review/src/lib/services/scorecards.service.ts
index 909527b3d..6ff09fb73 100644
--- a/src/apps/review/src/lib/services/scorecards.service.ts
+++ b/src/apps/review/src/lib/services/scorecards.service.ts
@@ -1,7 +1,7 @@
/**
* Scorecards service
*/
-import { xhrPostAsync, xhrPutAsync } from '~/libs/core'
+import { xhrPatchAsync, xhrPostAsync, xhrPutAsync } from '~/libs/core'
import { EnvironmentConfig } from '~/config'
import { Scorecard } from '../models'
@@ -29,3 +29,30 @@ export const saveScorecard = async (scorecard: Scorecard): Promise =>
return xhrPutAsync(`${baseUrl}/${scorecard.id}`, scorecard)
}
+
+export const updateLikesOrDislikesOnRunItem = (
+ workflowId: string,
+ runId: string,
+ feedbackId: string,
+ body: {
+ upVote: boolean
+ downVote: boolean
+ },
+): Promise => xhrPatchAsync(
+ `${EnvironmentConfig.API.V6}/workflows/${workflowId}/runs/${runId}/items/${feedbackId}`,
+ body,
+)
+
+export const updateLikesOrDislikesOnRunItemComment = (
+ workflowId: string,
+ runId: string,
+ feedbackId: string,
+ commentId: string,
+ body: {
+ upVote: boolean
+ downVote: boolean
+ },
+): Promise => xhrPatchAsync(
+ `${EnvironmentConfig.API.V6}/workflows/${workflowId}/runs/${runId}/items/${feedbackId}/comments/${commentId}`,
+ body,
+)
From d383431df84accd261832fee29f004a324581ba5 Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Thu, 20 Nov 2025 01:10:37 +0100
Subject: [PATCH 2/6] feat: likes and dislikes on run items and comments
---
.../AiFeedbackComments/AiFeedbackComments.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
index 28c9a1414..5916279fe 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
@@ -1,4 +1,5 @@
import { FC } from 'react'
+import moment from 'moment'
import { AiFeedbackActions } from '../AiFeedbackActions/AiFeedbackActions'
@@ -47,7 +48,7 @@ export const AiFeedbackComments: FC = props => (
{comment.createdUser.handle}
on
- {comment.createdAt}
+ { moment(comment.createdAt).local().format('MMM DD, hh:mm A')}
{comment.content}
From 5dd1dc499f46491f862ddeb394ab5093614c842c Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Thu, 20 Nov 2025 01:11:50 +0100
Subject: [PATCH 3/6] feat: likes and dislikes on run items and comments
---
.../AiFeedbackComments/AiFeedbackComments.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
index 5916279fe..46bb0ec91 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackComments/AiFeedbackComments.tsx
@@ -48,7 +48,11 @@ export const AiFeedbackComments: FC = props => (
{comment.createdUser.handle}
on
- { moment(comment.createdAt).local().format('MMM DD, hh:mm A')}
+
+ { moment(comment.createdAt)
+ .local()
+ .format('MMM DD, hh:mm A')}
+
{comment.content}
From 564381fe71e188a5121ba7998b6006d02113a8f3 Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Fri, 21 Nov 2025 00:24:57 +0100
Subject: [PATCH 4/6] fix: moved setInitialVotesForFeedback into useEffect
---
.../AiFeedbackActions/AiFeedbackActions.tsx | 56 +++++++++----------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
index d7e71567b..306338459 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
@@ -40,35 +40,35 @@ export const AiFeedbackActions: FC = props => {
const votesArr: any[] = (props.actionType === 'runItem' ? (props.feedback?.votes) : (props.comment?.votes)) || []
- const setInitialVotesForFeedback = (): void => {
- const initialUp = props.feedback?.upVotes ?? votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('up')).length
- const initialDown = props.feedback?.downVotes ?? votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('down')).length
-
- const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
- setUpVotes(initialUp)
- setDownVotes(initialDown)
- setUserVote(myVote?.voteType ?? undefined)
- }
-
- const setInitialVotesForComment = (): void => {
- const initialUp = votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('up')).length
- const initialDown = votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('down')).length
-
- const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
- setUpVotes(initialUp)
- setDownVotes(initialDown)
- setUserVote(myVote?.voteType ?? undefined)
- }
-
useEffect(() => {
+ const setInitialVotesForFeedback = (): void => {
+ const initialUp = props.feedback?.upVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = props.feedback?.downVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }
+
+ const setInitialVotesForComment = (): void => {
+ const initialUp = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }
+
if (props.actionType === 'runItem') {
setInitialVotesForFeedback()
} else {
From 79091296716af3800e4a123c13ab29bb3e345072 Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Fri, 21 Nov 2025 00:27:27 +0100
Subject: [PATCH 5/6] fix: moved setInitialVotesForFeedback into useEffect
---
.../AiFeedbackActions/AiFeedbackActions.tsx | 56 +++++++++----------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
index 306338459..4601cbe91 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
@@ -40,35 +40,35 @@ export const AiFeedbackActions: FC = props => {
const votesArr: any[] = (props.actionType === 'runItem' ? (props.feedback?.votes) : (props.comment?.votes)) || []
+ const setInitialVotesForFeedback = useCallback((): void => {
+ const initialUp = props.feedback?.upVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = props.feedback?.downVotes ?? votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }, [])
+
+ const setInitialVotesForComment = useCallback((): void => {
+ const initialUp = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('up')).length
+ const initialDown = votesArr.filter(v => String(v.voteType)
+ .toLowerCase()
+ .includes('down')).length
+
+ const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
+ setUpVotes(initialUp)
+ setDownVotes(initialDown)
+ setUserVote(myVote?.voteType ?? undefined)
+ }, [])
+
useEffect(() => {
- const setInitialVotesForFeedback = (): void => {
- const initialUp = props.feedback?.upVotes ?? votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('up')).length
- const initialDown = props.feedback?.downVotes ?? votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('down')).length
-
- const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
- setUpVotes(initialUp)
- setDownVotes(initialDown)
- setUserVote(myVote?.voteType ?? undefined)
- }
-
- const setInitialVotesForComment = (): void => {
- const initialUp = votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('up')).length
- const initialDown = votesArr.filter(v => String(v.voteType)
- .toLowerCase()
- .includes('down')).length
-
- const myVote = votesArr.find(v => String(v.createdBy) === String(loginUserInfo?.userId))
- setUpVotes(initialUp)
- setDownVotes(initialDown)
- setUserVote(myVote?.voteType ?? undefined)
- }
-
if (props.actionType === 'runItem') {
setInitialVotesForFeedback()
} else {
From f9c98adb0a551947a040d7ca1cd44be5bfd40001 Mon Sep 17 00:00:00 2001
From: Hentry Martin
Date: Fri, 21 Nov 2025 00:29:38 +0100
Subject: [PATCH 6/6] fix: review comments
---
.../ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
index 4601cbe91..ab89c9263 100644
--- a/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
+++ b/src/apps/review/src/lib/components/Scorecard/ScorecardViewer/ScorecardQuestion/AiFeedbackActions/AiFeedbackActions.tsx
@@ -52,7 +52,7 @@ export const AiFeedbackActions: FC = props => {
setUpVotes(initialUp)
setDownVotes(initialDown)
setUserVote(myVote?.voteType ?? undefined)
- }, [])
+ }, [votesArr, props.feedback])
const setInitialVotesForComment = useCallback((): void => {
const initialUp = votesArr.filter(v => String(v.voteType)
@@ -66,7 +66,7 @@ export const AiFeedbackActions: FC = props => {
setUpVotes(initialUp)
setDownVotes(initialDown)
setUserVote(myVote?.voteType ?? undefined)
- }, [])
+ }, [votesArr])
useEffect(() => {
if (props.actionType === 'runItem') {