Skip to content

Commit

Permalink
Changing view mode selection of assignment solutions from two check b…
Browse files Browse the repository at this point in the history
…oxes to a drop-down button selector.
  • Loading branch information
krulis-martin committed Jul 23, 2023
1 parent de27369 commit 3780147
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 85 deletions.
18 changes: 10 additions & 8 deletions src/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,16 @@
"app.assignment.title": "Podrobnosti zadané úlohy",
"app.assignment.visible": "Viditelná studentům",
"app.assignment.visibleFrom": "Viditelná od",
"app.assignmentStats.allUserSolutions": "všechna řešení uživatele",
"app.assignmentStats.assignmentSolutions": "Řešení zadané úlohy",
"app.assignmentStats.groupByUsersCheckbox": "Seskupit dle uživatelů",
"app.assignmentStats.noSolutions": "Momentálně zde nejsou žádná odevzdaná řešení.",
"app.assignmentStats.onlyBestSolutionsCheckbox": "Pouze nejlepší řešení",
"app.assignmentStats.pendingReviews": "V tuto chvíli {count, plural, one {je otevřena} =2 {jsou otevřeny} =3 {jsou otevřeny} =4 {jsou otevřeny} other {je otevřeno}} {count} {count, plural, one {revize} =2 {revize} =3 {revize} =4 {revize} other {revizí}} ze všech řešení vybrané úlohy. Nezapomeňte, že autoři úloh vidí vaše komentáře zdrojových kódů až poté, co jsou příslušné revize uzavřeny.",
"app.assignmentStats.plagiarismsDetected": "V seznamu {count, plural, one {je zobrazeno} =2 {jsou zobrazena} =3 {jsou zobrazena} =4 {jsou zobrazena} other {je zobrazeno}} {count} řešení, {count, plural, one {ke kterému} other {ke kterým}} byla nelezena podobná řešení. V takových případech se může jednat o plagiáty.",
"app.assignmentStats.title": "Všechna řešení zadané úlohy",
"app.assignmentSolutions.allUserSolutions": "všechna řešení uživatele",
"app.assignmentSolutions.assignmentSolutions": "Řešení zadané úlohy",
"app.assignmentSolutions.noSolutions": "Momentálně zde nejsou žádná odevzdaná řešení.",
"app.assignmentSolutions.pendingReviews": "V tuto chvíli {count, plural, one {je otevřena} =2 {jsou otevřeny} =3 {jsou otevřeny} =4 {jsou otevřeny} other {je otevřeno}} {count} {count, plural, one {revize} =2 {revize} =3 {revize} =4 {revize} other {revizí}} ze všech řešení vybrané úlohy. Nezapomeňte, že autoři úloh vidí vaše komentáře zdrojových kódů až poté, co jsou příslušné revize uzavřeny.",
"app.assignmentSolutions.plagiarismsDetected": "V seznamu {count, plural, one {je zobrazeno} =2 {jsou zobrazena} =3 {jsou zobrazena} =4 {jsou zobrazena} other {je zobrazeno}} {count} řešení, {count, plural, one {ke kterému} other {ke kterým}} byla nelezena podobná řešení. V takových případech se může jednat o plagiáty.",
"app.assignmentSolutions.title": "Všechna řešení zadané úlohy",
"app.assignmentSolutions.viewModes.best": "Pouze nejlepší řešení",
"app.assignmentSolutions.viewModes.default": "Všechna řešení (výchozí)",
"app.assignmentSolutions.viewModes.grouped": "Seskupit dle uživatelů",
"app.assignmentSolutions.viewModesTitle": "Vyberte způsob zobrazení řešení",
"app.assignments.deadline": "Termín odevzdání",
"app.assignments.deleteAllButton": "Smazat",
"app.assignments.deleteAllButtonConfirm": "Opravdu si přejete smazat všechny vybrané zadané úlohy?",
Expand Down
18 changes: 10 additions & 8 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,16 @@
"app.assignment.title": "Assignment Detail",
"app.assignment.visible": "Visible to students",
"app.assignment.visibleFrom": "Visible from",
"app.assignmentStats.allUserSolutions": "all user solutions",
"app.assignmentStats.assignmentSolutions": "Assignment Solutions",
"app.assignmentStats.groupByUsersCheckbox": "Group by users",
"app.assignmentStats.noSolutions": "There are currently no submitted solutions.",
"app.assignmentStats.onlyBestSolutionsCheckbox": "Best solutions only",
"app.assignmentStats.pendingReviews": "There {count, plural, one {is} other {are}} {count} pending {count, plural, one {review} other {reviews}} among the solutions of the selected assignment. Remember that the review comments are visible to the author after a review is closed.",
"app.assignmentStats.plagiarismsDetected": "There {count, plural, one {is} other {are}} {count} {count, plural, one {solution} other {solutions}} with detected similarities. Such solutions may be plagiarisms.",
"app.assignmentStats.title": "All Submissions of The Assignment",
"app.assignmentSolutions.allUserSolutions": "all user solutions",
"app.assignmentSolutions.assignmentSolutions": "Assignment Solutions",
"app.assignmentSolutions.noSolutions": "There are currently no submitted solutions.",
"app.assignmentSolutions.pendingReviews": "There {count, plural, one {is} other {are}} {count} pending {count, plural, one {review} other {reviews}} among the solutions of the selected assignment. Remember that the review comments are visible to the author after a review is closed.",
"app.assignmentSolutions.plagiarismsDetected": "There {count, plural, one {is} other {are}} {count} {count, plural, one {solution} other {solutions}} with detected similarities. Such solutions may be plagiarisms.",
"app.assignmentSolutions.title": "All Submissions of The Assignment",
"app.assignmentSolutions.viewModes.best": "Best solutions only",
"app.assignmentSolutions.viewModes.default": "All solutions (default)",
"app.assignmentSolutions.viewModes.grouped": "Grouped by users",
"app.assignmentSolutions.viewModesTitle": "Select solutions view filter",
"app.assignments.deadline": "Deadline",
"app.assignments.deleteAllButton": "Delete",
"app.assignments.deleteAllButtonConfirm": "Do you really wish to remove all selected assignments?",
Expand Down
152 changes: 84 additions & 68 deletions src/pages/AssignmentSolutions/AssignmentSolutions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Row, Col, Modal } from 'react-bootstrap';
import { Row, Col, Modal, DropdownButton, Dropdown } from 'react-bootstrap';
import { connect } from 'react-redux';
import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
import { Link } from 'react-router-dom';
Expand Down Expand Up @@ -30,7 +30,6 @@ import Points from '../../components/Assignments/SolutionsTable/Points';
import SolutionsTable from '../../components/Assignments/SolutionsTable';
import LoadingSolutionsTable from '../../components/Assignments/SolutionsTable/LoadingSolutionsTable';
import FailedLoadingSolutionsTable from '../../components/Assignments/SolutionsTable/FailedLoadingSolutionsTable';
import OnOffCheckbox from '../../components/forms/OnOffCheckbox';
import Box from '../../components/widgets/Box';
import Button, { TheButtonGroup } from '../../components/widgets/TheButton';
import DateTime from '../../components/widgets/DateTime';
Expand Down Expand Up @@ -70,7 +69,22 @@ import { storageGetItem, storageSetItem } from '../../helpers/localStorage';
import withLinks from '../../helpers/withLinks';
import { safeGet, identity, arrayToObject, toPlainAscii, hasPermissions } from '../../helpers/common';

const prepareTableColumnDescriptors = defaultMemoize((loggedUserId, assignmentId, groupId, onlyBest, locale, links) => {
const VIEW_MODE_DEFAULT = 'default';
const VIEW_MODE_GROUPED = 'grouped';
const VIEW_MODE_BEST = 'best';
const viewModes = {
[VIEW_MODE_DEFAULT]: (
<FormattedMessage id="app.assignmentSolutions.viewModes.default" defaultMessage="All solutions (default)" />
),
[VIEW_MODE_GROUPED]: (
<FormattedMessage id="app.assignmentSolutions.viewModes.grouped" defaultMessage="Grouped by users" />
),
[VIEW_MODE_BEST]: (
<FormattedMessage id="app.assignmentSolutions.viewModes.best" defaultMessage="Best solutions only" />
),
};

const prepareTableColumnDescriptors = defaultMemoize((loggedUserId, assignmentId, groupId, viewMode, locale, links) => {
const { SOLUTION_DETAIL_URI_FACTORY, SOLUTION_SOURCE_CODES_URI_FACTORY, GROUP_USER_SOLUTIONS_URI_FACTORY } = links;
const nameComparator = createUserNameComparator(locale);

Expand Down Expand Up @@ -110,13 +124,13 @@ const prepareTableColumnDescriptors = defaultMemoize((loggedUserId, assignmentId
<small
className={
'text-nowrap' +
(onlyBest && lastAttemptIndex && lastAttemptIndex !== attemptIndex
(viewMode === VIEW_MODE_BEST && lastAttemptIndex && lastAttemptIndex !== attemptIndex
? ' text-bold text-warning'
: ' text-muted')
}>
{!lastAttemptIndex ? (
<LoadingIcon />
) : onlyBest && lastAttemptIndex && lastAttemptIndex === attemptIndex ? (
) : viewMode === VIEW_MODE_BEST && lastAttemptIndex && lastAttemptIndex === attemptIndex ? (
attemptIndex
) : (
<FormattedMessage
Expand Down Expand Up @@ -241,14 +255,14 @@ const prepareTableColumnDescriptors = defaultMemoize((loggedUserId, assignmentId
});

const prepareTableData = defaultMemoize(
(assignmentSolutions, users, assignmentSolvers, runtimeEnvironments, onlyBestSolutionsCheckbox) => {
(assignmentSolutions, users, assignmentSolvers, runtimeEnvironments, viewMode) => {
const solvers = (assignmentSolvers && assignmentSolvers.toJS()) || {};
const usersIndex = arrayToObject(users);
return assignmentSolutions
.toArray()
.map(getJsData)
.filter(solution => solution && usersIndex[solution.authorId])
.filter(onlyBestSolutionsCheckbox ? solution => solution && solution.isBestSolution : identity)
.filter(viewMode === VIEW_MODE_BEST ? solution => solution && solution.isBestSolution : identity)
.map(
({
id,
Expand Down Expand Up @@ -319,21 +333,15 @@ class AssignmentSolutions extends Component {
]);

state = {
groupByUsersCheckbox: true,
onlyBestSolutionsCheckbox: false,
viewMode: VIEW_MODE_DEFAULT,
assignmentDialogOpen: false,
closingReviews: false,
closingReviewsFailed: false,
};

checkboxClickHandler = ev => {
this.setState({ [ev.target.name]: !this.state[ev.target.name] }, () => {
// callback after the state is updated
storageSetItem(localStorageStateKey, {
groupByUsersCheckbox: this.state.groupByUsersCheckbox,
onlyBestSolutionsCheckbox: this.state.onlyBestSolutionsCheckbox,
});
});
viewModeSelectHandler = viewMode => {
this.setState({ viewMode });
storageSetItem(localStorageStateKey, { viewMode });
};

openDialog = () => this.setState({ assignmentDialogOpen: true });
Expand Down Expand Up @@ -397,7 +405,9 @@ class AssignmentSolutions extends Component {
<Page
resource={assignment}
icon={<ResultsIcon />}
title={<FormattedMessage id="app.assignmentStats.title" defaultMessage="All Submissions of The Assignment" />}>
title={
<FormattedMessage id="app.assignmentSolutions.title" defaultMessage="All Submissions of The Assignment" />
}>
{assignment => (
<div>
<AssignmentNavigation
Expand All @@ -412,7 +422,7 @@ class AssignmentSolutions extends Component {
{plagiarisms && plagiarisms.length > 0 && (
<Callout variant="danger" icon={<PlagiarismIcon />}>
<FormattedMessage
id="app.assignmentStats.plagiarismsDetected"
id="app.assignmentSolutions.plagiarismsDetected"
defaultMessage="There {count, plural, one {is} other {are}} {count} {count, plural, one {solution} other {solutions}} with detected similarities. Such solutions may be plagiarisms."
values={{ count: plagiarisms.length }}
/>
Expand All @@ -424,7 +434,7 @@ class AssignmentSolutions extends Component {
<Row className="align-items-center">
<Col className="pr-3 py-2">
<FormattedMessage
id="app.assignmentStats.pendingReviews"
id="app.assignmentSolutions.pendingReviews"
defaultMessage="There {count, plural, one {is} other {are}} {count} pending {count, plural, one {review} other {reviews}} among the solutions of the selected assignment. Remember that the review comments are visible to the author after a review is closed."
values={{ count: pendingReviews.length }}
/>
Expand All @@ -446,31 +456,29 @@ class AssignmentSolutions extends Component {
)}

<Row>
<Col md={12} lg={7}>
<div className="mb-3">
<TheButtonGroup>
{hasPermissions(assignment, 'viewAssignmentSolutions') && (
<a href="#" onClick={downloadBestSolutionsArchive(this.getArchiveFileName(assignment))}>
<Button variant="primary">
<DownloadIcon gapRight />
<FormattedMessage
id="app.assignment.downloadBestSolutionsArchive"
defaultMessage="Download Bests"
/>
</Button>
</a>
)}
<Col sm={12} md>
<TheButtonGroup className="mb-3 text-nowrap">
{hasPermissions(assignment, 'viewAssignmentSolutions') && (
<a href="#" onClick={downloadBestSolutionsArchive(this.getArchiveFileName(assignment))}>
<Button variant="primary">
<DownloadIcon gapRight />
<FormattedMessage
id="app.assignment.downloadBestSolutionsArchive"
defaultMessage="Download Bests"
/>
</Button>
</a>
)}

{hasPermissions(assignment, 'resubmitSubmissions') && (
<ResubmitAllSolutionsContainer assignmentId={assignment.id} />
)}
{hasPermissions(assignment, 'resubmitSubmissions') && (
<ResubmitAllSolutionsContainer assignmentId={assignment.id} />
)}

<Button variant="info" onClick={this.openDialog}>
<ChatIcon gapRight />
<FormattedMessage id="generic.discussion" defaultMessage="Discussion" />
</Button>
</TheButtonGroup>
</div>
<Button variant="info" onClick={this.openDialog}>
<ChatIcon gapRight />
<FormattedMessage id="generic.discussion" defaultMessage="Discussion" />
</Button>
</TheButtonGroup>

<Modal show={this.state.assignmentDialogOpen} backdrop="static" onHide={this.closeDialog} size="xl">
<CommentThreadContainer
Expand All @@ -495,25 +503,33 @@ class AssignmentSolutions extends Component {
</Modal>
</Col>

<Col md={12} lg={5} className="text-right text-nowrap pt-2">
<OnOffCheckbox
className="text-left mr-3"
checked={this.state.groupByUsersCheckbox}
disabled={this.state.onlyBestSolutionsCheckbox}
name="groupByUsersCheckbox"
onChange={this.checkboxClickHandler}>
<FormattedMessage id="app.assignmentStats.groupByUsersCheckbox" defaultMessage="Group by users" />
</OnOffCheckbox>
<OnOffCheckbox
className="text-left mr-3"
checked={this.state.onlyBestSolutionsCheckbox}
name="onlyBestSolutionsCheckbox"
onChange={this.checkboxClickHandler}>
<FormattedMessage
id="app.assignmentStats.onlyBestSolutionsCheckbox"
defaultMessage="Best solutions only"
/>
</OnOffCheckbox>
<Col sm={false} md="auto" className="text-nowrap pt-2 mb-3">
<DropdownButton
menuAlign="right"
title={
<>
<Icon icon="binoculars" gapRight />
{viewModes[this.state.viewMode] || ''}
</>
}
id="dropdown-menu-align-right">
<Dropdown.Header>
<FormattedMessage
id="app.assignmentSolutions.viewModesTitle"
defaultMessage="Select solutions view filter"
/>
</Dropdown.Header>
<Dropdown.Divider />
{Object.keys(viewModes).map(viewMode => (
<Dropdown.Item
key={viewMode}
eventKey={viewMode}
active={viewMode === this.state.viewMode}
onSelect={this.viewModeSelectHandler}>
{viewModes[viewMode]}
</Dropdown.Item>
))}
</DropdownButton>
</Col>
</Row>

Expand All @@ -524,7 +540,7 @@ class AssignmentSolutions extends Component {
loading={<LoadingSolutionsTable />}
failed={<FailedLoadingSolutionsTable />}>
{() =>
this.state.groupByUsersCheckbox && !this.state.onlyBestSolutionsCheckbox ? (
this.state.viewMode === VIEW_MODE_GROUPED ? (
<div>
{getStudents(group.id)
.sort(
Expand All @@ -544,7 +560,7 @@ class AssignmentSolutions extends Component {
(
<Link to={links.GROUP_USER_SOLUTIONS_URI_FACTORY(group.id, user.id)}>
<FormattedMessage
id="app.assignmentStats.allUserSolutions"
id="app.assignmentSolutions.allUserSolutions"
defaultMessage="all user solutions"
/>
</Link>
Expand Down Expand Up @@ -574,7 +590,7 @@ class AssignmentSolutions extends Component {
<Box
title={
<FormattedMessage
id="app.assignmentStats.assignmentSolutions"
id="app.assignmentSolutions.assignmentSolutions"
defaultMessage="Assignment Solutions"
/>
}
Expand All @@ -586,7 +602,7 @@ class AssignmentSolutions extends Component {
loggedUserId,
assignmentId,
group.id,
this.state.onlyBestSolutionsCheckbox,
this.state.viewMode,
locale,
links
)}
Expand All @@ -596,12 +612,12 @@ class AssignmentSolutions extends Component {
getStudents(group.id),
assignmentSolversLoading ? null : assignmentSolvers,
runtimes,
this.state.onlyBestSolutionsCheckbox
this.state.viewMode
)}
empty={
<div className="text-center text-muted">
<FormattedMessage
id="app.assignmentStats.noSolutions"
id="app.assignmentSolutions.noSolutions"
defaultMessage="There are currently no submitted solutions."
/>
</div>
Expand Down

0 comments on commit 3780147

Please sign in to comment.