Commit d9a35db2 authored by Vladimir Kurganov's avatar Vladimir Kurganov
Browse files

Merge branch '477-localisation-into-czech' into 'main'

Resolve "Localisation into Czech"

See merge request inject/frontend!699
parents 96893c12 e3e75ca2
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ import {
} from '@blueprintjs/core'
import { css, cx } from '@emotion/css'
import type { ActionLogSimple, Channel } from '@inject/graphql'
import { useTranslationFrontend } from '@inject/locale'
import type { NavigateOptions } from '@tanstack/react-router'
import {
  Fragment,
@@ -83,6 +84,7 @@ const ListRenderer: FC<{
  noDataProps,
  getFileLink,
}) => {
  const { t } = useTranslationFrontend()
  const actionLogsLengthPrev = useRef<number>(actionLogs.length)

  const getFirstUnreadId = useCallback(
@@ -156,8 +158,8 @@ const ListRenderer: FC<{
      {actionLogs.length == 0 ? (
        <NonIdealState
          icon='low-voltage-pole'
          title='No injects found'
          description='There are no injects in this channel yet'
          title={t('actionLog.noInjectsTitle')}
          description={t('actionLog.noInjectsDescription')}
          className={css`
            height: 80vh;
            width: 100%;
@@ -176,7 +178,7 @@ const ListRenderer: FC<{
              {actionLog.id === firstUnreadId && (
                <div ref={unreadLogsStartRef} className={unreadLogsDivider}>
                  <Divider className={divider} />
                  <div>Unread:</div>
                  <div>{t('actionLog.unreadDivider')}</div>
                  <Divider className={divider} />
                </div>
              )}
+10 −2
Original line number Diff line number Diff line
import { Button, Colors } from '@blueprintjs/core'
import { Dot } from '@blueprintjs/icons'
import { useTranslationFrontend } from '@inject/locale'
import { useCallback, useEffect, useRef, useState, type FC } from 'react'

const TOLERANCE = 20
@@ -98,6 +99,7 @@ const ScrollDownButton: FC<ScrollDownButtonProps> = ({
    }
  }, [])

  const { t } = useTranslationFrontend()
  return (
    <Button
      icon='arrow-down'
@@ -105,10 +107,16 @@ const ScrollDownButton: FC<ScrollDownButtonProps> = ({
      onClick={handleClick}
      rightIcon={highlighted ? <Dot color={Colors.RED3} /> : undefined}
      disabled={isScrolledToBottom}
      title={isScrolledToBottom ? 'Already scrolled down' : undefined}
      title={
        isScrolledToBottom ? t('scrollDownButton.alreadyScrolled') : undefined
      }
      intent={highlighted ? 'warning' : undefined}
    >
      {highlighted ? <b>Scroll down</b> : 'Scroll down'}
      {highlighted ? (
        <b>{t('scrollDownButton.text')}</b>
      ) : (
        t('scrollDownButton.text')
      )}
    </Button>
  )
}
+8 −2
Original line number Diff line number Diff line
import { Button, Colors } from '@blueprintjs/core'
import { Dot } from '@blueprintjs/icons'
import { useTranslationFrontend } from '@inject/locale'
import type { RefObject } from 'react'
import { useCallback, useEffect, useMemo, useState, type FC } from 'react'

@@ -37,6 +38,7 @@ const SeeAllUnreadButton: FC<SeeAllUnreadButtonProps> = ({
    setHighlighted(false)
  }, [unreadLogsStartRef])

  const { t } = useTranslationFrontend()
  return (
    <Button
      icon='arrow-up'
@@ -44,10 +46,14 @@ const SeeAllUnreadButton: FC<SeeAllUnreadButtonProps> = ({
      onClick={handleClick}
      rightIcon={highlighted ? <Dot color={Colors.RED3} /> : undefined}
      disabled={disabled}
      title={disabled ? 'No unread logs' : undefined}
      title={disabled ? t('seeAllUnreadButton.noUnread') : undefined}
      intent={highlighted ? 'warning' : undefined}
    >
      {highlighted ? <b>See all unread</b> : 'See all unread'}
      {highlighted ? (
        <b>{t('seeAllUnreadButton.text')}</b>
      ) : (
        t('seeAllUnreadButton.text')
      )}
    </Button>
  )
}
+18 −6
Original line number Diff line number Diff line
@@ -3,7 +3,8 @@ import { Button } from '@blueprintjs/core'
import { css } from '@emotion/css'
import type { Confirmation, Content, RestrictedUserF } from '@inject/graphql'
import { Confirm, useBlockableMutation } from '@inject/graphql'
import { Done, timedFormatter } from '@inject/shared'
import { useTranslationFrontend } from '@inject/locale'
import { Done, useTimedFormatter } from '@inject/shared'
import type { NavigateOptions } from '@tanstack/react-router'
import type { FC } from 'react'
import { useMemo } from 'react'
@@ -44,35 +45,46 @@ const InjectContent: FC<InjectContentProps> = ({
  getFileLink,
  onClose,
}) => {
  const { t } = useTranslationFrontend()
  const [running, { fetching: loading }, confirm] = useBlockableMutation(
    Confirm,
    teamId
  )

  const confirmedTime = useTimedFormatter({
    datetime: timestampConfirmed ? new Date(timestampConfirmed) : null,
  })

  const { disabled, title } = useMemo<ButtonProps>(() => {
    if (confirmed) {
      return {
        disabled: true,
        title: `Confirmed${confirmedBy ? ` by ${confirmedBy?.username}` : ''}${timestampConfirmed ? ` ${timedFormatter({ datetime: new Date(timestampConfirmed) })}` : ''}`,
        title: `${
          confirmedBy
            ? t('injectContent.confirmedBy', {
                username: confirmedBy?.username,
              })
            : t('injectContent.confirmed')
        }${confirmedTime ? ` ${confirmedTime}` : ''}`,
      }
    }
    if (inInstructor) {
      return {
        disabled: true,
        title: 'Confirming injects is not allowed in the instructor view',
        title: t('injectContent.confirmingNotAllowed'),
      }
    }
    if (!running) {
      return {
        disabled: true,
        title: 'The is not running',
        title: t('injectContent.exerciseNotRunning'),
      }
    }
    return {
      disabled: false,
      title: undefined,
    }
  }, [confirmed, confirmedBy, inInstructor, running, timestampConfirmed])
  }, [confirmed, confirmedBy, inInstructor, running, t, confirmedTime])

  return (
    <div className={wrapper}>
@@ -84,7 +96,7 @@ const InjectContent: FC<InjectContentProps> = ({
      />
      {(onClose || confirmation) && (
        <div className={alignRight}>
          {onClose && <Button text='Close' onClick={onClose} />}
          {onClose && <Button text={t('close')} onClick={onClose} />}
          {confirmation && (
            <Done done={!!confirmed} title={title}>
              <Button
+34 −15
Original line number Diff line number Diff line
@@ -36,18 +36,23 @@ interface TraineeQuestionnaireContentProps {
  inPopup?: boolean
}

const MultipleUsersWarning = () => (
const MultipleUsersWarning = () => {
  const { t } = useTranslationFrontend()

  return (
    <Callout intent='warning'>
    <b>Answers can be submitted only once per team!</b>
      <b>{t('questionnaire.onlyOnce')}</b>
      <br />
    <span>Make sure all team members agree before submitting</span>
      <span>{t('questionnaire.agreeBeforeSubmit')}</span>
    </Callout>
  )
}

const AnswerState = (
  questions: Question[],
  answers: QuestionnaireAnswer[],
  teamStateId: string
  teamStateId: string,
  t: ReturnType<typeof useTranslationFrontend>['t']
) =>
  createSyncedStore<{
    questions: QuestionAndAnswer[]
@@ -88,7 +93,7 @@ const AnswerState = (
            let error = ''
            if (question.answer.length === 0) {
              result = false
              error = 'Answer this question'
              error = t('questionnaire.answerRequired')
            } else {
              if (
                question.question.details.__typename ===
@@ -106,11 +111,25 @@ const AnswerState = (
                const { min, max } = question.question.details
                if (answer.length < min) {
                  result = false
                  error = `Your answer is too short. The minimum length  is ${min} character${min === 1 ? '' : 's'}.`
                  error = t('questionnaire.answerTooShort', { min })
                  if (min === 1) {
                    error += t('questionnaire.character')
                  } else if (min <= 4) {
                    error += t('questionnaire.characters')
                  } else {
                    error += t('questionnaire.charactersGenitive')
                  }
                }
                if (max != -1 && answer.length > max) {
                  result = false
                  error = `Your answer is too long. The maximum length is ${max} character${max === 1 ? '' : 's'}.`
                  error = t('questionnaire.answerTooLong', { max })
                  if (max === 1) {
                    error += t('questionnaire.character')
                  } else if (max <= 4) {
                    error += t('questionnaire.characters')
                  } else {
                    error += t('questionnaire.charactersGenitive')
                  }
                }
              }
            }
@@ -153,8 +172,8 @@ const TraineeQuestionnaireContent: FC<TraineeQuestionnaireContentProps> = ({
  const { id: questionnaireId, questions, content } = questionnaire

  const state = useMemo(
    () => AnswerState(questions, answers, teamStateId),
    [answers, questions, teamStateId]
    () => AnswerState(questions, answers, teamStateId, t),
    [answers, questions, teamStateId, t]
  )

  const questionsAndAnswers: QuestionAndAnswer[] = useStore(
@@ -172,7 +191,7 @@ const TraineeQuestionnaireContent: FC<TraineeQuestionnaireContentProps> = ({
  const { title, disabled } = useMemo<ButtonProps>(() => {
    switch (status) {
      case 'UNSENT':
        return { title: 'Questionnaire has not been sent yet', disabled: true }
        return { title: t('questionnaire.notSent'), disabled: true }
      case 'SENT':
        if (running) {
          return { title: undefined, disabled: false }
@@ -188,7 +207,7 @@ const TraineeQuestionnaireContent: FC<TraineeQuestionnaireContentProps> = ({
        }
      case 'ANSWERED': // fallthrough
      case 'REVIEWED':
        return { title: 'Answers have already been submitted', disabled: true }
        return { title: t('questionnaire.alreadySubmitted'), disabled: true }
    }
  }, [status, running, postExerciseSubmission, exerciseFinished, t])

Loading