diff --git a/src/components/Groups/GroupExamStatus/GroupExamStatus.js b/src/components/Groups/GroupExamStatus/GroupExamStatus.js
new file mode 100644
index 000000000..ff6ef5b7a
--- /dev/null
+++ b/src/components/Groups/GroupExamStatus/GroupExamStatus.js
@@ -0,0 +1,338 @@
+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 Callout from '../../widgets/Callout';
+import Icon, { BanIcon, ClockIcon, EditIcon, GroupExamsIcon, LoadingIcon } from '../../icons';
+import DateTime from '../../widgets/DateTime';
+import Explanation from '../../widgets/Explanation';
+import { getErrorMessage } from '../../../locales/apiErrorMessages';
+
+import { hasPermissions } from '../../../helpers/common';
+
+const REFRESH_INTERVAL = 1; // [s]
+
+class GroupExamStatus extends Component {
+ state = { examModal: false };
+ intervalHandler = null;
+
+ static getDerivedStateFromProps({ group }, state) {
+ const now = Date.now() / 1000;
+ const hasExam = group.privateData.examBegin && group.privateData.examEnd && group.privateData.examEnd > now;
+ const examInProgress = hasExam && group.privateData.examBegin <= now;
+ const examEndsIn24 = hasExam && group.privateData.examEnd < now + 86400;
+ const nextChange = examInProgress
+ ? group.privateData.examEnd - now
+ : hasExam
+ ? group.privateData.examBegin - now
+ : null;
+ const changeImminent = nextChange && nextChange <= 5; // s
+ const examModal = (state.examModal && !changeImminent) || false;
+ return { hasExam, examInProgress, changeImminent, examEndsIn24, examModal };
+ }
+
+ examModalOpen = () => {
+ this.setState({ examModal: true });
+ };
+
+ examModalClose = () => {
+ this.setState({ examModal: false });
+ };
+
+ examFormSubmit = data => {
+ const { begin, end, strict } = transformExamData(data);
+ const { examInProgress } = this.state;
+ return this.props.setExamPeriod(examInProgress ? null : begin, end, examInProgress ? null : strict).then(res => {
+ this.examModalClose();
+ return Promise.resolve(res);
+ });
+ };
+
+ removeExam = () => {
+ const {
+ removeExamPeriod,
+ addNotification,
+ intl: { formatMessage },
+ } = this.props;
+ addNotification('kuk');
+ removeExamPeriod().catch(err => {
+ addNotification(getErrorMessage(formatMessage)(err), false);
+ });
+ };
+
+ startNow = () => {
+ const {
+ group,
+ setExamPeriod,
+ addNotification,
+ intl: { formatMessage },
+ } = this.props;
+ setExamPeriod(Math.round(Date.now() / 1000), group.privateData.examEnd).catch(err => {
+ addNotification(getErrorMessage(formatMessage)(err), false);
+ });
+ };
+
+ terminateNow = () => {
+ const {
+ setExamPeriod,
+ addNotification,
+ intl: { formatMessage },
+ } = this.props;
+ setExamPeriod(null, Math.round(Date.now() / 1000)).catch(err => {
+ addNotification(getErrorMessage(formatMessage)(err), false);
+ });
+ };
+
+ periodicRefresh = () => {
+ this.setState(GroupExamStatus.getDerivedStateFromProps(this.props, this.state));
+ // console.log(this.state);
+ };
+
+ componentDidMount() {
+ if (window && 'setInterval' in window) {
+ if (this.intervalHandler) {
+ window.clearInterval(this.intervalHandler);
+ }
+ this.intervalHandler = window.setInterval(this.periodicRefresh, REFRESH_INTERVAL * 1000);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.intervalHandler) {
+ window.clearInterval(this.intervalHandler);
+ this.intervalHandler = null;
+ }
+ }
+
+ render() {
+ const { group, examBeginImmediately, examEndRelative, pending, removeExamPeriod } = this.props;
+
+ return (
+ <>
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+ {group.privateData.examLockStrict ? (
+ |
+