Loading analyst/src/components/ExerciseSelector/index.tsx +3 −3 Original line number Diff line number Diff line import type { ButtonProps } from '@blueprintjs/core' import { Button, Dialog, DialogBody } from '@blueprintjs/core' import { ExerciseList, getExerciseColumns } from '@inject/frontend' import type { Exercise } from '@inject/graphql' import type { ExerciseSimple } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { dialogBody, maximizedDialog } from '@inject/shared' import type { FC } from 'react' Loading @@ -11,8 +11,8 @@ interface ExerciseSelectorProps { buttonProps?: ButtonProps | undefined className?: string openByDefault?: boolean onSelect: (exercise: Exercise) => void isSelected: (exercise: Exercise) => boolean onSelect: (exercise: ExerciseSimple) => void isSelected: (exercise: ExerciseSimple) => boolean } export const ExerciseSelector: FC<ExerciseSelectorProps> = ({ Loading analyst/src/components/NavigationBar/index.tsx +2 −2 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ import { synchronousExerciseState, useSubscribedTeams, } from '@inject/frontend' import type { Exercise } from '@inject/graphql' import type { ExerciseSimple } from '@inject/graphql' import { useChannelTypeEnabled } from '@inject/graphql' import type { Section } from '@inject/shared' import { Loading Loading @@ -64,7 +64,7 @@ export const NavigationBar: FC<NavigationBarProps> = ({ // TODO: keep the rest of the path in the URL const nav = useNavigate() const handleSelect = useCallback( (exercise: Exercise) => { (exercise: ExerciseSimple) => { nav({ to: SelectTeamsPageRoute.to, params: { exerciseId: exercise.id } }) }, [nav] Loading analyst/src/components/utilities.ts +0 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,6 @@ export const DUMMY_EXERCISE: Exercise = { exerciseDuration: 0, updateIntervalS: 0, enabledFeatures: [], instructorNotes: null, }, states: [], onDemand: true, Loading frontend/src/components/DefinitionList/DefinitionButtons.tsx +3 −3 Original line number Diff line number Diff line import { Button, ButtonGroup, Classes } from '@blueprintjs/core' import { css } from '@emotion/css' import type { Definition } from '@inject/graphql' import type { DefinitionSimple } from '@inject/graphql' import { DeleteDefinition, useHost, useTypedMutation } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { Loading Loading @@ -28,7 +28,7 @@ const buttonGroup = css` ` interface DefinitionButtonsProps { definition: Definition definition: DefinitionSimple } const DefinitionButtons: FC<DefinitionButtonsProps> = ({ definition }) => { Loading Loading @@ -90,7 +90,7 @@ const DefinitionButtons: FC<DefinitionButtonsProps> = ({ definition }) => { icon='trash' title={t('exercisePanel.definitionManager.delete.buttonTitle')} /> <InfoButton definition={definition} /> <InfoButton definitionId={definition.id} /> </ButtonGroup> <ConfirmAlert Loading frontend/src/components/DefinitionList/DefinitionDetail.tsx 0 → 100644 +146 −0 Original line number Diff line number Diff line import { Colors } from '@blueprintjs/core' import { Cross } from '@blueprintjs/icons' import { css } from '@emotion/css' import { GetDefinition, useTypedQuery } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { CenteredSpinner, fullWidthTable, useFormatTimestamp, } from '@inject/shared' import { type FC } from 'react' import ConfigInfo from '../ConfigInfo' import { UserLabel } from '../UserLabel' const configClass = css` margin-top: 1rem; ` interface DefinitionDetailProps { definitionId: string } export const DefinitionDetail: FC<DefinitionDetailProps> = ({ definitionId, }) => { const { t } = useTranslationFrontend() const [{ data, fetching }] = useTypedQuery({ query: GetDefinition, variables: { definitionId }, }) const timestampCreatedFormatted = useFormatTimestamp({ timestamp: data?.definition.timestampCreated || null, inExerciseTime: null, }) if (fetching || !data?.definition) { return <CenteredSpinner /> } const definition = data.definition const { id, name, description, version, roles, channels, uploadedBy, prerequisites, targetAudience, config, } = definition return ( <> <table className={fullWidthTable(14)}> <tbody> <tr> <th>ID:</th> <td>{id}</td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.name')}:</th> <td>{name}</td> </tr> {description && ( <tr> <th>{t('exercisePanel.definitionManager.info.description')}:</th> <td>{description}</td> </tr> )} <tr> <th>{t('exercisePanel.definitionManager.info.version')}:</th> <td>{version}</td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.roles')}:</th> <td> {/* TODO: add Description */} {roles.length ? ( roles.map((role, index) => ( <div key={role.id} >{`${role.displayName}${index < roles.length - 1 ? ',' : ''}`}</div> )) ) : ( <Cross color={Colors.RED3} /> )} </td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.channels')}:</th> <td> {/* TODO: add Description */} {channels.map((channel, index) => ( <div key={channel.id} title={channel.description || channel.displayName} >{`${channel.displayName} (${channel.channelType})${index < channels.length - 1 ? ',' : ''}`}</div> ))} </td> </tr> {prerequisites && ( <tr> <th> {t('exercisePanel.definitionManager.info.prerequisites')}: </th> <td> <ul> {prerequisites.map(prerequisite => ( <li key={prerequisite}>{prerequisite}</li> ))} </ul> </td> </tr> )} {targetAudience && ( <tr> <th> {t('exercisePanel.definitionManager.info.targetAudience')}: </th> <td>{targetAudience}</td> </tr> )} <tr> <th>{t('exercisePanel.definitionManager.info.uploadedBy')}:</th> <td> {uploadedBy ? <UserLabel user={uploadedBy} /> : 'a deleted user'} </td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.uploadedAt')}:</th> <td>{timestampCreatedFormatted}</td> </tr> </tbody> </table> <ConfigInfo config={config} className={configClass} tableClassName={fullWidthTable(25)} /> </> ) } Loading
analyst/src/components/ExerciseSelector/index.tsx +3 −3 Original line number Diff line number Diff line import type { ButtonProps } from '@blueprintjs/core' import { Button, Dialog, DialogBody } from '@blueprintjs/core' import { ExerciseList, getExerciseColumns } from '@inject/frontend' import type { Exercise } from '@inject/graphql' import type { ExerciseSimple } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { dialogBody, maximizedDialog } from '@inject/shared' import type { FC } from 'react' Loading @@ -11,8 +11,8 @@ interface ExerciseSelectorProps { buttonProps?: ButtonProps | undefined className?: string openByDefault?: boolean onSelect: (exercise: Exercise) => void isSelected: (exercise: Exercise) => boolean onSelect: (exercise: ExerciseSimple) => void isSelected: (exercise: ExerciseSimple) => boolean } export const ExerciseSelector: FC<ExerciseSelectorProps> = ({ Loading
analyst/src/components/NavigationBar/index.tsx +2 −2 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ import { synchronousExerciseState, useSubscribedTeams, } from '@inject/frontend' import type { Exercise } from '@inject/graphql' import type { ExerciseSimple } from '@inject/graphql' import { useChannelTypeEnabled } from '@inject/graphql' import type { Section } from '@inject/shared' import { Loading Loading @@ -64,7 +64,7 @@ export const NavigationBar: FC<NavigationBarProps> = ({ // TODO: keep the rest of the path in the URL const nav = useNavigate() const handleSelect = useCallback( (exercise: Exercise) => { (exercise: ExerciseSimple) => { nav({ to: SelectTeamsPageRoute.to, params: { exerciseId: exercise.id } }) }, [nav] Loading
analyst/src/components/utilities.ts +0 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,6 @@ export const DUMMY_EXERCISE: Exercise = { exerciseDuration: 0, updateIntervalS: 0, enabledFeatures: [], instructorNotes: null, }, states: [], onDemand: true, Loading
frontend/src/components/DefinitionList/DefinitionButtons.tsx +3 −3 Original line number Diff line number Diff line import { Button, ButtonGroup, Classes } from '@blueprintjs/core' import { css } from '@emotion/css' import type { Definition } from '@inject/graphql' import type { DefinitionSimple } from '@inject/graphql' import { DeleteDefinition, useHost, useTypedMutation } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { Loading Loading @@ -28,7 +28,7 @@ const buttonGroup = css` ` interface DefinitionButtonsProps { definition: Definition definition: DefinitionSimple } const DefinitionButtons: FC<DefinitionButtonsProps> = ({ definition }) => { Loading Loading @@ -90,7 +90,7 @@ const DefinitionButtons: FC<DefinitionButtonsProps> = ({ definition }) => { icon='trash' title={t('exercisePanel.definitionManager.delete.buttonTitle')} /> <InfoButton definition={definition} /> <InfoButton definitionId={definition.id} /> </ButtonGroup> <ConfirmAlert Loading
frontend/src/components/DefinitionList/DefinitionDetail.tsx 0 → 100644 +146 −0 Original line number Diff line number Diff line import { Colors } from '@blueprintjs/core' import { Cross } from '@blueprintjs/icons' import { css } from '@emotion/css' import { GetDefinition, useTypedQuery } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { CenteredSpinner, fullWidthTable, useFormatTimestamp, } from '@inject/shared' import { type FC } from 'react' import ConfigInfo from '../ConfigInfo' import { UserLabel } from '../UserLabel' const configClass = css` margin-top: 1rem; ` interface DefinitionDetailProps { definitionId: string } export const DefinitionDetail: FC<DefinitionDetailProps> = ({ definitionId, }) => { const { t } = useTranslationFrontend() const [{ data, fetching }] = useTypedQuery({ query: GetDefinition, variables: { definitionId }, }) const timestampCreatedFormatted = useFormatTimestamp({ timestamp: data?.definition.timestampCreated || null, inExerciseTime: null, }) if (fetching || !data?.definition) { return <CenteredSpinner /> } const definition = data.definition const { id, name, description, version, roles, channels, uploadedBy, prerequisites, targetAudience, config, } = definition return ( <> <table className={fullWidthTable(14)}> <tbody> <tr> <th>ID:</th> <td>{id}</td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.name')}:</th> <td>{name}</td> </tr> {description && ( <tr> <th>{t('exercisePanel.definitionManager.info.description')}:</th> <td>{description}</td> </tr> )} <tr> <th>{t('exercisePanel.definitionManager.info.version')}:</th> <td>{version}</td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.roles')}:</th> <td> {/* TODO: add Description */} {roles.length ? ( roles.map((role, index) => ( <div key={role.id} >{`${role.displayName}${index < roles.length - 1 ? ',' : ''}`}</div> )) ) : ( <Cross color={Colors.RED3} /> )} </td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.channels')}:</th> <td> {/* TODO: add Description */} {channels.map((channel, index) => ( <div key={channel.id} title={channel.description || channel.displayName} >{`${channel.displayName} (${channel.channelType})${index < channels.length - 1 ? ',' : ''}`}</div> ))} </td> </tr> {prerequisites && ( <tr> <th> {t('exercisePanel.definitionManager.info.prerequisites')}: </th> <td> <ul> {prerequisites.map(prerequisite => ( <li key={prerequisite}>{prerequisite}</li> ))} </ul> </td> </tr> )} {targetAudience && ( <tr> <th> {t('exercisePanel.definitionManager.info.targetAudience')}: </th> <td>{targetAudience}</td> </tr> )} <tr> <th>{t('exercisePanel.definitionManager.info.uploadedBy')}:</th> <td> {uploadedBy ? <UserLabel user={uploadedBy} /> : 'a deleted user'} </td> </tr> <tr> <th>{t('exercisePanel.definitionManager.info.uploadedAt')}:</th> <td>{timestampCreatedFormatted}</td> </tr> </tbody> </table> <ConfigInfo config={config} className={configClass} tableClassName={fullWidthTable(25)} /> </> ) }