Skip to content

Commit

Permalink
Fixing bugs, improving error reporting.
Browse files Browse the repository at this point in the history
  • Loading branch information
krulis-martin committed May 24, 2024
1 parent 08fdca5 commit c6ba2cd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 16 deletions.
6 changes: 5 additions & 1 deletion src/helpers/groups.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const isExam = ({ privateData: { examBegin, examEnd } }) => {
import { safeGet } from './common';

export const isExam = group => {
const examBegin = safeGet(group, ['privateData', 'examBegin']);
const examEnd = safeGet(group, ['privateData', 'examEnd']);
const now = Date.now() / 1000;
return examBegin && examEnd && examEnd > now && examBegin <= now;
};
43 changes: 38 additions & 5 deletions src/pages/GroupExams/GroupExams.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ import { groupExamLocksSelector } from '../../redux/selectors/groupExamLocks';
import { lockedStudentsOfGroupSelector } from '../../redux/selectors/usersGroups';
import { loggedInUserIdSelector } from '../../redux/selectors/auth';
import { isLoggedAsSuperAdmin, loggedInUserSelector } from '../../redux/selectors/users';
import { getJsData } from '../../redux/helpers/resourceManager';

import withLinks from '../../helpers/withLinks';
import { hasPermissions, safeGet } from '../../helpers/common';
import ResourceRenderer from '../../components/helpers/ResourceRenderer';
import { isExam } from '../../helpers/groups';

const REFRESH_INTERVAL = 1; // [s]

class GroupExams extends Component {
state = { examInProgress: false };
intervalHandler = null;

static loadAsync = ({ groupId }, dispatch) =>
Promise.all([
dispatch(fetchGroupIfNeeded(groupId)).then(({ value: group }) =>
Expand All @@ -41,11 +47,31 @@ class GroupExams extends Component {
),
]);

static getDerivedStateFromProps({ group }) {
const groupJs = getJsData(group);
const examInProgress = Boolean(groupJs && isExam(groupJs));
return { examInProgress };
}

periodicRefresh = () => {
const newState = GroupExams.getDerivedStateFromProps(this.props, this.state);
if (newState.examInProgress !== this.state.examInProgress) {
this.setState(newState);
}
};

componentDidMount() {
this.props.loadAsync();
if (this.props.params.examId) {
this.props.loadGroupExamLocks();
}

if (window && 'setInterval' in window) {
if (this.intervalHandler) {
window.clearInterval(this.intervalHandler);
}
this.intervalHandler = window.setInterval(this.periodicRefresh, REFRESH_INTERVAL * 1000);
}
}

componentDidUpdate(prevProps) {
Expand All @@ -57,6 +83,13 @@ class GroupExams extends Component {
}
}

componentWillUnmount() {
if (this.intervalHandler) {
window.clearInterval(this.intervalHandler);
this.intervalHandler = null;
}
}

linkFactory = id => {
const {
params: { groupId, examId = null },
Expand Down Expand Up @@ -114,19 +147,19 @@ class GroupExams extends Component {
unlimitedHeight>
<GroupExamsTable
exams={group.privateData.exams}
selected={isExam(group) ? null : examId}
linkFactory={isExam(group) ? null : this.linkFactory}
selected={this.state.examInProgress ? null : examId}
linkFactory={this.state.examInProgress ? null : this.linkFactory}
/>
</Box>
</Col>
</Row>

{(examId || isExam(group)) && hasPermissions(group, 'viewStudents', 'setExamPeriod') && (
{(examId || this.state.examInProgress) && hasPermissions(group, 'viewStudents', 'setExamPeriod') && (
<Row>
<Col xs={12}>
<Box
title={
isExam(group) ? (
this.state.examInProgress ? (
<FormattedMessage
id="app.groupExams.studentsBoxTitle"
defaultMessage="Participating students"
Expand All @@ -140,7 +173,7 @@ class GroupExams extends Component {
}
noPadding
unlimitedHeight>
{isExam(group) ? (
{this.state.examInProgress ? (
<LockedStudentsTable
groupId={group.id}
lockedStudents={lockedStudents}
Expand Down
44 changes: 34 additions & 10 deletions src/pages/Login/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import PageContent from '../../components/layout/PageContent';
import LoginForm from '../../components/forms/LoginForm';
import ExternalLoginBox from '../../containers/ExternalLogin';
import Callout from '../../components/widgets/Callout';
import Button from '../../components/widgets/TheButton';
import Icon from '../../components/icons';

import { login } from '../../redux/modules/auth';
import { login, logout } from '../../redux/modules/auth';
import { isLoggedIn, selectedInstanceId } from '../../redux/selectors/auth';
import { loggedInUserSelector } from '../../redux/selectors/users';

import { getError } from '../../redux/helpers/resourceManager';

import { getConfigVar, getConfigVarLocalized } from '../../helpers/config';
import { getErrorMessage } from '../../locales/apiErrorMessages';

Expand Down Expand Up @@ -95,24 +100,41 @@ class Login extends Component {
render() {
const {
isLoggedIn,
loggedInUser,
logout,
params: { redirect = null },
links: { RESET_PASSWORD_URI },
intl: { locale },
intl: { locale, formatMessage },
} = this.props;

const external = EXTERNAL_AUTH_URL && EXTERNAL_AUTH_SERVICE_ID;
const userError = getError(loggedInUser);

return (
<PageContent icon="sign-in-alt" title={<FormattedMessage id="app.login.title" defaultMessage="Sign In" />}>
<>
{isLoggedIn ? (
<Row>
<Col sm={12}>
<Callout variant="success">
<FormattedMessage id="app.login.alreadyLoggedIn" defaultMessage="You are already logged in." />
</Callout>
</Col>
</Row>
userError ? (
<Row>
<Col sm={12}>
<Callout variant="danger">
<p>{getErrorMessage(formatMessage)(userError)}</p>
<Button variant="danger" onClick={logout}>
<Icon icon="sign-out-alt" gapRight />
<FormattedMessage id="app.logout" defaultMessage="Logout" />
</Button>
</Callout>
</Col>
</Row>
) : (
<Row>
<Col sm={12}>
<Callout variant="success">
<FormattedMessage id="app.login.alreadyLoggedIn" defaultMessage="You are already logged in." />
</Callout>
</Col>
</Row>
)
) : (
<>
{redirect && (
Expand Down Expand Up @@ -171,13 +193,14 @@ class Login extends Component {
}

Login.propTypes = {
login: PropTypes.func.isRequired,
params: PropTypes.shape({
redirect: PropTypes.string,
}),
isLoggedIn: PropTypes.bool.isRequired,
instanceId: PropTypes.string,
loggedInUser: ImmutablePropTypes.map,
login: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
links: PropTypes.object.isRequired,
intl: PropTypes.object,
Expand All @@ -193,6 +216,7 @@ export default withLinks(
}),
dispatch => ({
login: ({ email, password }) => dispatch(login(email, password)),
logout: () => dispatch(logout()),
reset: () => {
dispatch(reset('login'));
},
Expand Down

0 comments on commit c6ba2cd

Please sign in to comment.