Verified Commit 66c256e1 authored by Marek Veselý's avatar Marek Veselý
Browse files

optimize exercise list with simple fragments

parent f96c6164
Loading
Loading
Loading
Loading
+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'
@@ -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> = ({
+2 −2
Original line number Diff line number Diff line
@@ -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 {
@@ -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]
+0 −1
Original line number Diff line number Diff line
@@ -124,7 +124,6 @@ export const DUMMY_EXERCISE: Exercise = {
    exerciseDuration: 0,
    updateIntervalS: 0,
    enabledFeatures: [],
    instructorNotes: null,
  },
  states: [],
  onDemand: true,
+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 {
@@ -28,7 +28,7 @@ const buttonGroup = css`
`

interface DefinitionButtonsProps {
  definition: Definition
  definition: DefinitionSimple
}

const DefinitionButtons: FC<DefinitionButtonsProps> = ({ definition }) => {
@@ -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
+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