Loading analyst/src/components/ActionLog/index.tsx +6 −7 Original line number Diff line number Diff line Loading @@ -44,12 +44,10 @@ const getIcon = (logType: ActionLogFragment['type']) => { ) case 'EMAIL': return <Icon icon='envelope' /> default: throw new Error(`unknown actionLog type: ${logType}`) } } const getContent = (actionLog: ActionLogFragment) => { const getContent = (actionLog: ActionLogFragment): string => { switch (actionLog.details.__typename) { case 'InjectDetailsType': return textFromRenderedContent(actionLog.details.content.rendered) Loading @@ -67,8 +65,10 @@ const getContent = (actionLog: ActionLogFragment) => { .join(', ') return `${senderAddress} → ${recipients}: ${textFromRenderedContent(actionLog.details.content.rendered)}` } default: throw new Error(`unknown actionLog type`) case 'QuestionnaireSubmissionType': { const { teamQuestionnaireState } = actionLog.details return `${teamQuestionnaireState.questionnaire.displayName} submitted by ${teamQuestionnaireState.team.name}` } } } Loading Loading @@ -99,11 +99,10 @@ export const ActionLog: FC<ActionLogProps> = ({ selectedDispatch }) => { case 'CUSTOM_INJECT': case 'TOOL': case 'FORM': case 'FORM_SUBMISSION': return selectedDispatch({ type: 'selectActions', actionLog }) case 'EMAIL': return selectedDispatch({ type: 'selectEmails', actionLog }) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } }, [selectedDispatch] Loading analyst/src/components/Overview/ActionDetail.tsx +34 −31 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { RenderedContent, } from '@inject/frontend' import type { ActionLog, FileInfo } from '@inject/graphql' import { TickOrCross } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useContext } from 'react' Loading Loading @@ -144,45 +145,47 @@ const getContent = (actionLog: ActionLog, exerciseId: string) => { <tr> <th className={th}>Title</th> <td className={td}>{actionLog.details.questionnaire.displayName}</td> {/* TODO: */} </tr> ) case 'EmailType': details = actionLog.details case 'QuestionnaireSubmissionType': { const { teamQuestionnaireState, attempt, accepted } = actionLog.details const { questionnaire, team } = teamQuestionnaireState const { displayName, repeatable } = questionnaire const maxAttempts = repeatable?.maxAttempts return ( <> <tr> <th className={th}>Sender</th> <td className={td}>{details.sender.address}</td> <th className={th}>Subject</th> <td className={td}>{details.thread.subject}</td> <th className={th}>Questionnaire</th> <td className={td}>{displayName}</td> </tr> <tr> <th className={th}>Content</th> <th className={th}>Team</th> <td className={td}>{team.name}</td> </tr> <tr> <th className={th}>Accepted</th> <td className={td}> <RenderedContent exerciseId={exerciseId} renderedContent={details.content.rendered} inInstructor /> <TickOrCross value={accepted} /> </td> </tr> {details.content.attachments && ( <AttachementsRow attachments={details.content.attachments} getFileLink={fileId => ({ to: FilePageRoute.to, params: { exerciseId, fileId, }, })} /> )} <tr> <th className={th}>Attempt</th> <td className={td}>{attempt}</td> </tr> <tr> <th className={th}>Max attempts</th> <td className={td}> {repeatable ? (maxAttempts === -1 ? '∞' : maxAttempts) : '1'} </td> </tr> </> ) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } case 'EmailType': throw new Error( 'Email details should not be handled in ActionDetail, use EmailDetail instead' ) } } Loading analyst/src/components/Overview/ActionTooltip.tsx +9 −2 Original line number Diff line number Diff line Loading @@ -51,6 +51,15 @@ const getContent = (actionLog: ActionLog) => { <span>{details.questionnaire.displayName}</span> </> ) case 'QuestionnaireSubmissionType': return ( <> <strong>Questionnaire: </strong> <span> {details.teamQuestionnaireState.questionnaire.displayName} </span> </> ) case 'EmailType': return ( <> Loading @@ -64,8 +73,6 @@ const getContent = (actionLog: ActionLog) => { <span>{textFromRenderedContent(details.content.rendered)}</span> </> ) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } } Loading analyst/src/components/Overview/Detail.tsx +1 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ const Detail: FC<DetailsProps> = ({ selectedState, displayAuto }) => { teamId={selectedState.autoInjectsTeamId} /> ) default: case undefined: return ( <NonIdealState className={nonIdeal} Loading analyst/src/components/Overview/OverviewPlot.tsx +10 −9 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ import type { EmailDetails, EmailThread, MilestoneState, ToolDetails, } from '@inject/graphql' import { useChannelTypeEnabled } from '@inject/graphql' import { notEmpty } from '@inject/shared' Loading @@ -27,6 +26,7 @@ import { NOT_SELECTED_OPACITY, SELECTED_OPACITY, actionTypeColor, getQuestionnaireId, getTeamById, legendIconProps, } from '../utilities' Loading Loading @@ -105,18 +105,19 @@ const OverviewPlot: FC<OverviewPlotProps> = ({ return NOT_SELECTED_OPACITY if (!selectedState.actionLog) throw new Error('actionLog null when ACTIONS selected') if (selectedState.actionLog.type === actionLog.type) { if (selectedState.actionLog.type !== 'TOOL') { return SELECTED_OPACITY } if ( (selectedState.actionLog.details as ToolDetails).tool.id === (actionLog.details as ToolDetails).tool.id ) { // highlight forms and form submissions for the same questionnaire id const selectedQuestionnaireId = getQuestionnaireId( selectedState.actionLog ) const actionQuestionnaireId = getQuestionnaireId(actionLog) if (selectedQuestionnaireId && actionQuestionnaireId) { if (selectedQuestionnaireId === actionQuestionnaireId) { return SELECTED_OPACITY } return NOT_SELECTED_OPACITY } return NOT_SELECTED_OPACITY }, [selectedState] Loading Loading
analyst/src/components/ActionLog/index.tsx +6 −7 Original line number Diff line number Diff line Loading @@ -44,12 +44,10 @@ const getIcon = (logType: ActionLogFragment['type']) => { ) case 'EMAIL': return <Icon icon='envelope' /> default: throw new Error(`unknown actionLog type: ${logType}`) } } const getContent = (actionLog: ActionLogFragment) => { const getContent = (actionLog: ActionLogFragment): string => { switch (actionLog.details.__typename) { case 'InjectDetailsType': return textFromRenderedContent(actionLog.details.content.rendered) Loading @@ -67,8 +65,10 @@ const getContent = (actionLog: ActionLogFragment) => { .join(', ') return `${senderAddress} → ${recipients}: ${textFromRenderedContent(actionLog.details.content.rendered)}` } default: throw new Error(`unknown actionLog type`) case 'QuestionnaireSubmissionType': { const { teamQuestionnaireState } = actionLog.details return `${teamQuestionnaireState.questionnaire.displayName} submitted by ${teamQuestionnaireState.team.name}` } } } Loading Loading @@ -99,11 +99,10 @@ export const ActionLog: FC<ActionLogProps> = ({ selectedDispatch }) => { case 'CUSTOM_INJECT': case 'TOOL': case 'FORM': case 'FORM_SUBMISSION': return selectedDispatch({ type: 'selectActions', actionLog }) case 'EMAIL': return selectedDispatch({ type: 'selectEmails', actionLog }) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } }, [selectedDispatch] Loading
analyst/src/components/Overview/ActionDetail.tsx +34 −31 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { RenderedContent, } from '@inject/frontend' import type { ActionLog, FileInfo } from '@inject/graphql' import { TickOrCross } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useContext } from 'react' Loading Loading @@ -144,45 +145,47 @@ const getContent = (actionLog: ActionLog, exerciseId: string) => { <tr> <th className={th}>Title</th> <td className={td}>{actionLog.details.questionnaire.displayName}</td> {/* TODO: */} </tr> ) case 'EmailType': details = actionLog.details case 'QuestionnaireSubmissionType': { const { teamQuestionnaireState, attempt, accepted } = actionLog.details const { questionnaire, team } = teamQuestionnaireState const { displayName, repeatable } = questionnaire const maxAttempts = repeatable?.maxAttempts return ( <> <tr> <th className={th}>Sender</th> <td className={td}>{details.sender.address}</td> <th className={th}>Subject</th> <td className={td}>{details.thread.subject}</td> <th className={th}>Questionnaire</th> <td className={td}>{displayName}</td> </tr> <tr> <th className={th}>Content</th> <th className={th}>Team</th> <td className={td}>{team.name}</td> </tr> <tr> <th className={th}>Accepted</th> <td className={td}> <RenderedContent exerciseId={exerciseId} renderedContent={details.content.rendered} inInstructor /> <TickOrCross value={accepted} /> </td> </tr> {details.content.attachments && ( <AttachementsRow attachments={details.content.attachments} getFileLink={fileId => ({ to: FilePageRoute.to, params: { exerciseId, fileId, }, })} /> )} <tr> <th className={th}>Attempt</th> <td className={td}>{attempt}</td> </tr> <tr> <th className={th}>Max attempts</th> <td className={td}> {repeatable ? (maxAttempts === -1 ? '∞' : maxAttempts) : '1'} </td> </tr> </> ) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } case 'EmailType': throw new Error( 'Email details should not be handled in ActionDetail, use EmailDetail instead' ) } } Loading
analyst/src/components/Overview/ActionTooltip.tsx +9 −2 Original line number Diff line number Diff line Loading @@ -51,6 +51,15 @@ const getContent = (actionLog: ActionLog) => { <span>{details.questionnaire.displayName}</span> </> ) case 'QuestionnaireSubmissionType': return ( <> <strong>Questionnaire: </strong> <span> {details.teamQuestionnaireState.questionnaire.displayName} </span> </> ) case 'EmailType': return ( <> Loading @@ -64,8 +73,6 @@ const getContent = (actionLog: ActionLog) => { <span>{textFromRenderedContent(details.content.rendered)}</span> </> ) default: throw new Error(`unknown actionLog type: ${actionLog.type}`) } } Loading
analyst/src/components/Overview/Detail.tsx +1 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ const Detail: FC<DetailsProps> = ({ selectedState, displayAuto }) => { teamId={selectedState.autoInjectsTeamId} /> ) default: case undefined: return ( <NonIdealState className={nonIdeal} Loading
analyst/src/components/Overview/OverviewPlot.tsx +10 −9 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ import type { EmailDetails, EmailThread, MilestoneState, ToolDetails, } from '@inject/graphql' import { useChannelTypeEnabled } from '@inject/graphql' import { notEmpty } from '@inject/shared' Loading @@ -27,6 +26,7 @@ import { NOT_SELECTED_OPACITY, SELECTED_OPACITY, actionTypeColor, getQuestionnaireId, getTeamById, legendIconProps, } from '../utilities' Loading Loading @@ -105,18 +105,19 @@ const OverviewPlot: FC<OverviewPlotProps> = ({ return NOT_SELECTED_OPACITY if (!selectedState.actionLog) throw new Error('actionLog null when ACTIONS selected') if (selectedState.actionLog.type === actionLog.type) { if (selectedState.actionLog.type !== 'TOOL') { return SELECTED_OPACITY } if ( (selectedState.actionLog.details as ToolDetails).tool.id === (actionLog.details as ToolDetails).tool.id ) { // highlight forms and form submissions for the same questionnaire id const selectedQuestionnaireId = getQuestionnaireId( selectedState.actionLog ) const actionQuestionnaireId = getQuestionnaireId(actionLog) if (selectedQuestionnaireId && actionQuestionnaireId) { if (selectedQuestionnaireId === actionQuestionnaireId) { return SELECTED_OPACITY } return NOT_SELECTED_OPACITY } return NOT_SELECTED_OPACITY }, [selectedState] Loading