Skip to content

Commit

Permalink
Implementing a pending exam callout (info panel) with locking button …
Browse files Browse the repository at this point in the history
…for students.
  • Loading branch information
krulis-martin committed Apr 4, 2024
1 parent 7497ab5 commit 1fa02a0
Show file tree
Hide file tree
Showing 19 changed files with 601 additions and 200 deletions.
4 changes: 2 additions & 2 deletions recodex-web.spec
@@ -1,8 +1,8 @@
%define name recodex-web
%define short_name web-app
%define version 2.10.0
%define unmangled_version 137ce75c61aec51b5c721de71173ae6f67266283
%define release 2
%define unmangled_version 68e609c525256e3def51c63e6e449849c8794ddf
%define release 3

Summary: ReCodEx web-app component
Name: %{name}
Expand Down
180 changes: 145 additions & 35 deletions src/components/Groups/GroupExamPending/GroupExamPending.js
@@ -1,20 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Modal } from 'react-bootstrap';

import ExamForm, {
prepareInitValues as prepareExamInitValues,
transformSubmittedData as transformExamData,
} from '../../forms/ExamForm';
import Button, { TheButtonGroup } from '../../widgets/TheButton';
import { FormattedMessage } from 'react-intl';

import ExamLockButtonContainer from '../../../containers/ExamLockButtonContainer';
import Callout from '../../widgets/Callout';
import Icon, { BanIcon, ClockIcon, EditIcon, GroupExamsIcon, LoadingIcon } from '../../icons';
import { InfoIcon, GroupExamsIcon } from '../../icons';
import DateTime from '../../widgets/DateTime';
import Explanation from '../../widgets/Explanation';
import { getErrorMessage } from '../../../locales/apiErrorMessages';

import { hasPermissions } from '../../../helpers/common';
import { isStudentRole } from '../../helpers/usersRoles';

const REFRESH_INTERVAL = 1; // [s]

Expand Down Expand Up @@ -54,47 +48,157 @@ class GroupExamPending extends Component {

render() {
const {
id,
privateData: { examBegin, examEnd, examLockStrict },
currentUser,
currentUser: {
privateData: { groupLock, isGroupLockStrict, ipLock, role },
},
} = this.props;
const isStudent = isStudentRole(role);

//console.log(currentUser);
return (
this.state.isExam && (
<Callout
variant={this.state.examInProgress ? 'danger' : this.state.hasExam ? 'warning' : 'secondary'}
icon={
this.state.examInProgress ? (
<GroupExamsIcon className="fa-beat" />
) : this.state.hasExam ? (
<ClockIcon />
) : null
}>
TODO
{this.state.hasExam && (
<table>
<Callout variant={isStudent && !groupLock ? 'danger' : 'warning'} icon={<GroupExamsIcon />}>
<h4>
{isStudent && groupLock ? (
<FormattedMessage
id="app.groupExams.pending.studentLockedTitle"
defaultMessage="You are locked in for an exam"
/>
) : (
<FormattedMessage
id="app.groupExams.inProgress"
defaultMessage="Exam in progress, the group is in secured mode"
/>
)}
</h4>

{isStudent ? (
<>
{groupLock ? (
<>
<div className="mb-1">
<strong className="mr-2">
<FormattedMessage id="app.groupExams.endAtLong" defaultMessage="Exam ends at" />:
</strong>
<DateTime unixts={examEnd} showRelative />
</div>

<div className="text-muted mb-1">
<FormattedMessage
id="app.groupExams.lockedStudentInfo"
defaultMessage="You may now see and submit solutions to exam assignments."
/>{' '}
{isGroupLockStrict ? (
<FormattedMessage
id="app.groupExams.lockedStudentInfoStrict"
defaultMessage="You may not access any other groups until the exam lock expires."
/>
) : (
<FormattedMessage
id="app.groupExams.lockedStudentInfoRegular"
defaultMessage="You may access other groups in read-only mode until the exam lock expires."
/>
)}
</div>

{ipLock && (
<div className="text-muted mb-1">
<FormattedMessage
id="app.groupExams.ipLockInfo"
defaultMessage="Your actions are restricted to IP address [{ipLock}] until the exam lock expires. Contact your exam supervisor if you require relocation."
values={{ ipLock: <code>{ipLock}</code> }}
/>
</div>
)}
</>
) : (
<table>
<tbody>
<tr>
<td className="pr-5">
<ExamLockButtonContainer groupId={id} size="lg" />
</td>
<td>
<div className="mb-1">
<strong className="mr-2">
<FormattedMessage id="app.groupExams.endAtLong" defaultMessage="Exam ends at" />:
</strong>
<DateTime unixts={examEnd} showRelative />
</div>

<p className="text-muted small">
<InfoIcon gapRight />
<FormattedMessage
id="app.groupExams.studentInfo"
defaultMessage="You need to lock yourself in to see the exam assignments. When locked, your actions will be restricted to your current IP address."
/>{' '}
{examLockStrict ? (
<FormattedMessage
id="app.groupExams.studentInfoStrict"
defaultMessage="Furthermore, you will not be able to access other groups until the exam lock expires."
/>
) : (
<FormattedMessage
id="app.groupExams.studentInfoRegular"
defaultMessage="Furthermore, you will be able to access other groups in a read-only mode until the exam lock expires."
/>
)}
</p>
</td>
</tr>
</tbody>
</table>
)}

<hr className="mb-1" />
<div className="text-muted small">
<FormattedMessage
id="app.groupExams.timeAccuracyWarning"
defaultMessage="Your local system clock should be sufficiently synchronized or this component may not work properly."
/>
</div>
</>
) : (
<table className="w-100">
<tbody>
<tr>
<td className="text-bold p-2">
<td className="text-bold text-nowrap">
<FormattedMessage id="app.groupExams.beginAt" defaultMessage="Begins at" />:
</td>
<td>
<td className="px-2 py-1 text-nowrap">
<DateTime unixts={examBegin} showRelative />
</td>
<td rowSpan={3} className="w-100 p-2 pl-5 text-muted">
<p>
<InfoIcon gapRight />
<FormattedMessage
id="app.groupExams.pending.teacherInfo"
defaultMessage="The exam assignments are currently visible only to students who have lock themselves in the group. "
/>
</p>
<p>
<InfoIcon gapRight />
<FormattedMessage
id="app.groupExams.timeAccuracyWarning"
defaultMessage="Your local system clock should be sufficiently synchronized or this component may not work properly."
/>
</p>
</td>
</tr>
<tr>
<td className="text-bold p-2">
<td className="text-bold text-nowrap">
<FormattedMessage id="app.groupExams.endAt" defaultMessage="Ends at" />:
</td>
<td>
<td className="px-2 py-1 text-nowrap">
<DateTime unixts={examEnd} showRelative />
</td>
</tr>
<tr>
<td className="text-bold p-2">
<td className="text-bold text-nowrap">
<FormattedMessage id="app.groupExams.locking" defaultMessage="Lock type" />:
</td>
<td>
<td className="px-2 py-1 text-nowrap">
<em>
{examLockStrict ? (
<FormattedMessage id="app.groupExams.lockStrict" defaultMessage="strict" />
Expand Down Expand Up @@ -135,15 +239,21 @@ class GroupExamPending extends Component {
}

GroupExamPending.propTypes = {
id: PropTypes.string.isRequired,
privateData: PropTypes.shape({
examBegin: PropTypes.number,
examEnd: PropTypes.number,
examLockStrict: PropTypes.bool,
}).isRequired,
archived: PropTypes.bool,
currentUser: PropTypes.object,
//addNotification: PropTypes.func.isRequired,
intl: PropTypes.object,
currentUser: PropTypes.shape({
privateData: PropTypes.shape({
ipLock: PropTypes.string,
groupLock: PropTypes.string,
isGroupLockStrict: PropTypes.bool,
role: PropTypes.string,
}).isRequired,
}),
};

export default injectIntl(GroupExamPending);
export default GroupExamPending;

0 comments on commit 1fa02a0

Please sign in to comment.