diff --git a/src/apps/accounts/src/lib/userflow-survey.ts b/src/apps/accounts/src/lib/userflow-survey.ts index b86e2d62e..3ec728bdb 100644 --- a/src/apps/accounts/src/lib/userflow-survey.ts +++ b/src/apps/accounts/src/lib/userflow-survey.ts @@ -1,9 +1,7 @@ -import { TcUniNavFn } from 'universal-navigation' +import { getTcUniNav } from '~/apps/platform/src/utils' import { CES_SURVEY_ID } from '../config' -declare let tcUniNav: TcUniNavFn - export function triggerSurvey(): void { - tcUniNav('triggerFlow', CES_SURVEY_ID, {}) + getTcUniNav()?.('triggerFlow', CES_SURVEY_ID, {}) } diff --git a/src/apps/platform/src/components/app-footer/AppFooter.tsx b/src/apps/platform/src/components/app-footer/AppFooter.tsx index 1d5055c8a..dc1020f1d 100644 --- a/src/apps/platform/src/components/app-footer/AppFooter.tsx +++ b/src/apps/platform/src/components/app-footer/AppFooter.tsx @@ -1,7 +1,6 @@ import { FC, MutableRefObject, useEffect, useRef } from 'react' -import type { TcUniNavFn } from 'universal-navigation' -declare let tcUniNav: TcUniNavFn +import { getTcUniNav } from '../../utils' const APP_FOOTER_EL_ID: string = 'footer-nav-el' @@ -22,7 +21,7 @@ const AppFooter: FC<{}> = () => { return } - tcUniNav( + getTcUniNav()?.( 'init', APP_FOOTER_EL_ID, { diff --git a/src/apps/platform/src/components/app-header/AppHeader.tsx b/src/apps/platform/src/components/app-header/AppHeader.tsx index 48d81e331..6381cf483 100644 --- a/src/apps/platform/src/components/app-header/AppHeader.tsx +++ b/src/apps/platform/src/components/app-header/AppHeader.tsx @@ -11,7 +11,7 @@ import { useState, } from 'react' import { NavigateFunction, useNavigate } from 'react-router-dom' -import type { AuthUser as NavAuthUser, TcUniNavFn } from 'universal-navigation' +import type { AuthUser as NavAuthUser } from 'universal-navigation' import classNames from 'classnames' import { EnvironmentConfig, PageSubheaderPortalId } from '~/config' @@ -25,9 +25,10 @@ import { } from '~/libs/core' import { ConfigContextValue, useConfigContext } from '~/libs/shared' +import { getTcUniNav } from '../../utils' + import UniNavSnippet from './universal-nav-snippet' -declare let tcUniNav: TcUniNavFn UniNavSnippet(EnvironmentConfig.URLS.UNIVERSAL_NAV) interface NavigationRequest { @@ -86,7 +87,7 @@ const AppHeader: FC<{}> = () => { headerInit.current = true - tcUniNav( + getTcUniNav()?.( 'init', navElementId, { @@ -116,7 +117,7 @@ const AppHeader: FC<{}> = () => { // update uni-nav's tool details useEffect(() => { - tcUniNav( + getTcUniNav()?.( 'update', navElementId, { @@ -139,7 +140,7 @@ const AppHeader: FC<{}> = () => { return } - tcUniNav( + getTcUniNav()?.( 'update', navElementId, { diff --git a/src/apps/platform/src/utils/index.ts b/src/apps/platform/src/utils/index.ts new file mode 100644 index 000000000..6710c8dec --- /dev/null +++ b/src/apps/platform/src/utils/index.ts @@ -0,0 +1 @@ +export * from './other' diff --git a/src/apps/platform/src/utils/other.ts b/src/apps/platform/src/utils/other.ts new file mode 100644 index 000000000..14abc189c --- /dev/null +++ b/src/apps/platform/src/utils/other.ts @@ -0,0 +1,15 @@ +import { TcUniNavFn } from 'universal-navigation' + +declare let tcUniNav: TcUniNavFn + +/** + * Get tcUniNav + * @returns tcUniNav + */ +export function getTcUniNav(): TcUniNavFn | undefined { + if (typeof tcUniNav === 'undefined') { + return undefined + } + + return tcUniNav +} diff --git a/src/apps/profiles/src/lib/userflow-survey.ts b/src/apps/profiles/src/lib/userflow-survey.ts index b86e2d62e..3ec728bdb 100644 --- a/src/apps/profiles/src/lib/userflow-survey.ts +++ b/src/apps/profiles/src/lib/userflow-survey.ts @@ -1,9 +1,7 @@ -import { TcUniNavFn } from 'universal-navigation' +import { getTcUniNav } from '~/apps/platform/src/utils' import { CES_SURVEY_ID } from '../config' -declare let tcUniNav: TcUniNavFn - export function triggerSurvey(): void { - tcUniNav('triggerFlow', CES_SURVEY_ID, {}) + getTcUniNav()?.('triggerFlow', CES_SURVEY_ID, {}) } diff --git a/src/apps/review/src/config/index.config.ts b/src/apps/review/src/config/index.config.ts index f0de083f8..3786f4304 100644 --- a/src/apps/review/src/config/index.config.ts +++ b/src/apps/review/src/config/index.config.ts @@ -76,3 +76,5 @@ export const WINNERS = 'Winners' export const TAB = 'tab' export const FINISHTAB = [WINNERS] export const WITHOUT_APPEAL = [DESIGN, FIRST2FINISH] + +export const NO_RESOURCE_ID = 'noResource' diff --git a/src/apps/review/src/lib/components/Appeal/Appeal.tsx b/src/apps/review/src/lib/components/Appeal/Appeal.tsx index e6472f7dd..bea8d2be5 100644 --- a/src/apps/review/src/lib/components/Appeal/Appeal.tsx +++ b/src/apps/review/src/lib/components/Appeal/Appeal.tsx @@ -1,35 +1,51 @@ /** * AppealComment. */ -import { FC, useCallback, useState } from 'react' +import { FC, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { Controller, ControllerRenderProps, useForm, UseFormReturn, } from 'react-hook-form' -import _, { bind, includes } from 'lodash' +import { get } from 'lodash' import classNames from 'classnames' import { yupResolver } from '@hookform/resolvers/yup' import { MarkdownReview } from '../MarkdownReview' import { FieldMarkdownEditor } from '../FieldMarkdownEditor' -import { FormAppealResponse } from '../../models' -import { formAppealResponseSchema } from '../../utils' -import { ADMIN, COPILOT, FINISHTAB, ITERATIVE_REVIEW, TAB } from '../../../config/index.config' +import { AppealInfo, ChallengeDetailContextModel, FormAppealResponse } from '../../models' +import { formAppealResponseSchema, isAppealsPhase } from '../../utils' +import { ReviewItemComment } from '../../models/ReviewItemComment.model' +import { ChallengeDetailContext } from '../../contexts' import styles from './Appeal.module.scss' interface Props { className?: string - role?: string + appealInfo?: AppealInfo + commentItem: ReviewItemComment + isSavingAppeal: boolean + addAppeal: ( + content: string, + commentItem: ReviewItemComment, + success: () => void, + ) => void + doDeleteAppeal: ( + appealInfo: AppealInfo | undefined, + success: () => void, + ) => void } export const AppealComment: FC = (props: Props) => { const [appealResponse, setAppealResponse] = useState('') const [showResponseForm, setShowResponseForm] = useState(false) - const [showAppealResponse, setShowAppealResponse] = useState(false) + + const { challengeInfo }: ChallengeDetailContextModel = useContext( + ChallengeDetailContext, + ) + const canAddAppeal = useMemo(() => isAppealsPhase(challengeInfo), [challengeInfo]) const { handleSubmit, @@ -44,29 +60,54 @@ export const AppealComment: FC = (props: Props) => { }) const onSubmit = useCallback((data: FormAppealResponse) => { - setAppealResponse(data.response) - setShowResponseForm(false) - setShowAppealResponse(true) - }, []) + props.addAppeal(data.response, props.commentItem, () => { + setAppealResponse(data.response) + setShowResponseForm(false) + }) + }, [props.commentItem]) - if (includes(FINISHTAB, sessionStorage.getItem(TAB))) { - return <> - } + useEffect(() => { + if (props.appealInfo) { + setAppealResponse(props.appealInfo.content) + } + }, [props.appealInfo]) return (
- {showAppealResponse && ( + {appealResponse && !showResponseForm && (
Appeal Comment + {canAddAppeal && ( +
+ + +
+ )}
)} - {!showResponseForm - && !showAppealResponse - && !includes(FINISHTAB, sessionStorage.getItem(TAB)) - && !includes(sessionStorage.getItem(TAB), ITERATIVE_REVIEW) - && !includes([COPILOT, ADMIN], props.role) && ( + {!appealResponse && !showResponseForm && canAddAppeal && ( diff --git a/src/apps/review/src/lib/components/AppealComment/AppealComment.module.scss b/src/apps/review/src/lib/components/AppealComment/AppealComment.module.scss index 4633da960..dfbf9fb75 100644 --- a/src/apps/review/src/lib/components/AppealComment/AppealComment.module.scss +++ b/src/apps/review/src/lib/components/AppealComment/AppealComment.module.scss @@ -73,3 +73,10 @@ line-height: 20px; } } + +.blockReaponseAppealHeader { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} \ No newline at end of file diff --git a/src/apps/review/src/lib/components/AppealComment/AppealComment.tsx b/src/apps/review/src/lib/components/AppealComment/AppealComment.tsx index ef38d7401..36bf6b17d 100644 --- a/src/apps/review/src/lib/components/AppealComment/AppealComment.tsx +++ b/src/apps/review/src/lib/components/AppealComment/AppealComment.tsx @@ -1,36 +1,68 @@ /** * AppealComment. */ -import { FC, useCallback, useState } from 'react' +import { FC, useCallback, useContext, useMemo, useState } from 'react' import { Controller, ControllerRenderProps, useForm, UseFormReturn, } from 'react-hook-form' -import _, { bind, includes } from 'lodash' +import { bind, get } from 'lodash' +import Select, { SingleValue } from 'react-select' import classNames from 'classnames' import { yupResolver } from '@hookform/resolvers/yup' import { MarkdownReview } from '../MarkdownReview' import { FieldMarkdownEditor } from '../FieldMarkdownEditor' -import { AppealInfo, FormAppealResponse } from '../../models' -import { formAppealResponseSchema } from '../../utils' -import { ADMIN, COPILOT, FINISHTAB, TAB } from '../../../config/index.config' +import { + AppealInfo, + ChallengeDetailContextModel, + FormAppealResponse, + ReviewItemInfo, + ScorecardQuestion, + SelectOption, +} from '../../models' +import { formAppealResponseSchema, isAppealsResponsePhase } from '../../utils' +import { + QUESTION_YES_NO_OPTIONS, +} from '../../../config/index.config' +import { ChallengeDetailContext } from '../../contexts' import styles from './AppealComment.module.scss' interface Props { className?: string data: AppealInfo - role?: string + scorecardQuestion: ScorecardQuestion + isSavingAppealResponse: boolean + reviewItem: ReviewItemInfo + appealInfo?: AppealInfo + addAppealResponse: ( + content: string, + updatedResponse: string, + appeal: AppealInfo, + reviewItem: ReviewItemInfo, + success: () => void, + ) => void } export const AppealComment: FC = (props: Props) => { const [appealResponse, setAppealResponse] = useState('') const [showResponseForm, setShowResponseForm] = useState(false) - const [showAppealResponse, setShowAppealResponse] = useState(false) + + const { challengeInfo }: ChallengeDetailContextModel = useContext( + ChallengeDetailContext, + ) + const canAddAppealResponse = useMemo(() => isAppealsResponsePhase(challengeInfo), [challengeInfo]) + + const [updatedResponse, setUpdatedResponse] = useState< + SingleValue<{ + label: string + value: string + }> + >() const { handleSubmit, @@ -45,10 +77,42 @@ export const AppealComment: FC = (props: Props) => { }) const onSubmit = useCallback((data: FormAppealResponse) => { - setAppealResponse(data.response) - setShowResponseForm(false) - setShowAppealResponse(true) - }, []) + if (props.appealInfo) { + props.addAppealResponse( + data.response, + updatedResponse?.value ?? '', + props.appealInfo, + props.reviewItem, + () => { + setAppealResponse(data.response) + setShowResponseForm(false) + }, + ) + } + }, [updatedResponse, props.appealInfo, props.addAppealResponse, props.reviewItem]) + + const responseOptions = useMemo(() => { + if (props.scorecardQuestion.type === 'SCALE') { + const length + = props.scorecardQuestion.scaleMax + - props.scorecardQuestion.scaleMin + + 1 + return Array.from( + new Array(length), + (x, i) => `${i + props.scorecardQuestion.scaleMin}`, + ) + .map(item => ({ + label: item, + value: item, + })) + } + + if (props.scorecardQuestion.type === 'YES_NO') { + return QUESTION_YES_NO_OPTIONS + } + + return [] + }, [props.scorecardQuestion]) return (
@@ -56,17 +120,27 @@ export const AppealComment: FC = (props: Props) => { Appeal Comment
- {showAppealResponse && ( + {!showResponseForm && appealResponse && (
Appeal Response + {canAddAppealResponse && ( +
+ +
+ )}
)} - {!showResponseForm - && !showAppealResponse - && !includes(FINISHTAB, sessionStorage.getItem(TAB)) - && !includes([COPILOT, ADMIN], props.role) && ( + {!showResponseForm && !appealResponse && canAddAppealResponse && (