123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- /*
- * Copyright (C) 2016 - present Instructure, Inc.
- *
- * This file is part of Canvas.
- *
- * Canvas is free software: you can redistribute it and/or modify it under
- * the terms of the GNU Affero General Public License as published by the Free
- * Software Foundation, version 3 of the License.
- *
- * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Affero General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- import React from 'react'
- import PropTypes from 'prop-types'
- import _ from 'underscore'
- import $ from 'jquery'
- import Button from 'instructure-ui/lib/components/Button'
- import I18n from 'i18n!grading_periods'
- import GradingPeriodSet from 'jsx/grading/GradingPeriodSet'
- import SearchGradingPeriodsField from 'jsx/grading/SearchGradingPeriodsField'
- import SearchHelpers from 'jsx/shared/helpers/searchHelpers'
- import DateHelper from 'jsx/shared/helpers/dateHelper'
- import EnrollmentTermsDropdown from 'jsx/grading/EnrollmentTermsDropdown'
- import NewGradingPeriodSetForm from 'jsx/grading/NewGradingPeriodSetForm'
- import EditGradingPeriodSetForm from 'jsx/grading/EditGradingPeriodSetForm'
- import SetsApi from 'compiled/api/gradingPeriodSetsApi'
- import TermsApi from 'compiled/api/enrollmentTermsApi'
- import 'jquery.instructure_misc_plugins'
- const presentEnrollmentTerms = function(enrollmentTerms) {
- return _.map(enrollmentTerms, term => {
- let newTerm = _.extend({}, term);
- if (newTerm.name) {
- newTerm.displayName = newTerm.name;
- } else if (_.isDate(newTerm.startAt)) {
- let started = DateHelper.formatDateForDisplay(newTerm.startAt);
- newTerm.displayName = I18n.t("Term starting ") + started;
- } else {
- let created = DateHelper.formatDateForDisplay(newTerm.createdAt);
- newTerm.displayName = I18n.t("Term created ") + created;
- }
- return newTerm;
- });
- };
- const getEditGradingPeriodSetRef = function(set) {
- return "edit-grading-period-set-" + set.id;
- };
- const { bool, string, shape } = PropTypes;
- let GradingPeriodSetCollection = React.createClass({
- propTypes: {
- readOnly: bool.isRequired,
- urls: shape({
- gradingPeriodSetsURL: string.isRequired,
- gradingPeriodsUpdateURL: string.isRequired,
- enrollmentTermsURL: string.isRequired,
- deleteGradingPeriodURL: string.isRequired
- }).isRequired,
- },
- getInitialState() {
- return {
- enrollmentTerms: [],
- sets: [],
- expandedSetIDs: [],
- showNewSetForm: false,
- searchText: "",
- selectedTermID: "0",
- editSet: {
- id: null,
- saving: false
- }
- };
- },
- componentDidUpdate(prevProps, prevState) {
- if (prevState.editSet.id && (prevState.editSet.id !== this.state.editSet.id)) {
- let set = {id: prevState.editSet.id};
- this.refs[this.getShowGradingPeriodSetRef(set)].refs.editButton.focus();
- }
- },
- addGradingPeriodSet(set, termIDs) {
- this.setState({
- sets: [set].concat(this.state.sets),
- expandedSetIDs: this.state.expandedSetIDs.concat([set.id]),
- enrollmentTerms: this.associateTermsWithSet(set.id, termIDs),
- showNewSetForm: false
- }, () => {
- this.refs.addSetFormButton.focus();
- });
- },
- associateTermsWithSet(setID, termIDs) {
- return _.map(this.state.enrollmentTerms, function(term) {
- if (_.contains(termIDs, term.id)) {
- let newTerm = _.extend({}, term);
- newTerm.gradingPeriodGroupId = setID;
- return newTerm;
- } else {
- return term;
- }
- });
- },
- componentWillMount() {
- this.getSets();
- this.getTerms();
- },
- getSets() {
- SetsApi.list()
- .then((sets) => { this.onSetsLoaded(sets); })
- .catch((_) => {
- $.flashError(I18n.t(
- "An error occured while fetching grading period sets."
- ));
- });
- },
- getTerms() {
- TermsApi.list()
- .then((terms) => { this.onTermsLoaded(terms); })
- .catch((_) => {
- $.flashError(I18n.t(
- "An error occured while fetching enrollment terms."
- ));
- });
- },
- onTermsLoaded(terms) {
- this.setState({ enrollmentTerms: presentEnrollmentTerms(terms) });
- },
- onSetsLoaded(sets) {
- const sortedSets = _.sortBy(sets, "createdAt").reverse();
- this.setState({ sets: sortedSets });
- },
- onSetUpdated(updatedSet) {
- let sets = _.map(this.state.sets, (set) => {
- return (set.id === updatedSet.id) ? _.extend({}, set, updatedSet) : set;
- });
- let terms = _.map(this.state.enrollmentTerms, function(term) {
- if (_.contains(updatedSet.enrollmentTermIDs, term.id)) {
- return _.extend({}, term, { gradingPeriodGroupId: updatedSet.id });
- } else if (term.gradingPeriodGroupId === updatedSet.id) {
- return _.extend({}, term, { gradingPeriodGroupId: null });
- } else {
- return term;
- }
- });
- this.setState({ sets: sets, enrollmentTerms: terms });
- $.flashMessage(I18n.t("The grading period set was updated successfully."));
- },
- setAndGradingPeriodTitles(set) {
- let titles = _.pluck(set.gradingPeriods, 'title');
- titles.unshift(set.title);
- return _.compact(titles);
- },
- searchTextMatchesTitles(titles) {
- return _.any(titles, (title) => {
- return SearchHelpers
- .substringMatchRegex(this.state.searchText).test(title);
- });
- },
- filterSetsBySearchText(sets, searchText) {
- if (searchText === "") return sets;
- return _.filter(sets, (set) => {
- let titles = this.setAndGradingPeriodTitles(set);
- return this.searchTextMatchesTitles(titles);
- });
- },
- changeSearchText(searchText) {
- if (searchText !== this.state.searchText) {
- this.setState({ searchText: searchText });
- }
- },
- filterSetsBySelectedTerm(sets, terms, selectedTermID) {
- if (selectedTermID === "0") return sets;
- const activeTerm = _.findWhere(terms, { id: selectedTermID });
- const setID = activeTerm.gradingPeriodGroupId;
- return _.where(sets, { id: setID });
- },
- changeSelectedEnrollmentTerm(event) {
- this.setState({ selectedTermID: event.target.value });
- },
- alertForMatchingSets(numSets) {
- let msg;
- if (this.state.selectedTermID === "0" && this.state.searchText === "") {
- msg = I18n.t("Showing all sets of grading periods.");
- } else {
- msg = I18n.t({
- one: "1 set of grading periods found.",
- other: "%{count} sets of grading periods found.",
- zero: "No matching sets of grading periods found."
- }, {count: numSets}
- );
- }
- const polite = true;
- $.screenReaderFlashMessageExclusive(msg, polite);
- },
- getVisibleSets() {
- let setsFilteredBySearchText =
- this.filterSetsBySearchText(this.state.sets, this.state.searchText);
- let filterByTermArgs = [
- setsFilteredBySearchText,
- this.state.enrollmentTerms,
- this.state.selectedTermID
- ];
- let visibleSets = this.filterSetsBySelectedTerm(...filterByTermArgs);
- this.alertForMatchingSets(visibleSets.length);
- return visibleSets;
- },
- toggleSetBody(setId) {
- if (_.contains(this.state.expandedSetIDs, setId)) {
- this.setState({ expandedSetIDs: _.without(this.state.expandedSetIDs, setId) });
- } else {
- this.setState({ expandedSetIDs: this.state.expandedSetIDs.concat([setId]) });
- }
- },
- editGradingPeriodSet(set) {
- this.setState({ editSet: {id: set.id, saving: false} });
- },
- nodeToFocusOnAfterSetDeletion(setID) {
- const index = this.state.sets.findIndex(set => set.id === setID);
- if (index < 1) {
- return this.refs.addSetFormButton;
- } else {
- const setRef = this.getShowGradingPeriodSetRef(this.state.sets[index - 1]);
- const setToFocus = this.refs[setRef];
- return setToFocus.refs.editButton;
- }
- },
- removeGradingPeriodSet(setID) {
- let newSets = _.reject(this.state.sets, set => set.id === setID);
- const nodeToFocus = this.nodeToFocusOnAfterSetDeletion(setID);
- this.setState({ sets: newSets }, () => nodeToFocus.focus());
- },
- updateSetPeriods(setID, gradingPeriods) {
- let newSets = _.map(this.state.sets, (set) => {
- if (set.id === setID) {
- return _.extend({}, set, { gradingPeriods: gradingPeriods });
- }
- return set;
- });
- this.setState({ sets: newSets });
- },
- openNewSetForm() {
- this.setState({ showNewSetForm: true });
- },
- closeNewSetForm() {
- this.setState({ showNewSetForm: false }, () => {
- this.refs.addSetFormButton.focus();
- });
- },
- termsBelongingToActiveSets() {
- const setIDs = _.pluck(this.state.sets, "id");
- return _.filter(this.state.enrollmentTerms, function(term) {
- const setID = term.gradingPeriodGroupId;
- return setID && _.contains(setIDs, setID);
- });
- },
- termsNotBelongingToActiveSets() {
- return _.difference(this.state.enrollmentTerms, this.termsBelongingToActiveSets());
- },
- selectableTermsForEditSetForm(setID) {
- const termsBelongingToThisSet = _.where(this.termsBelongingToActiveSets(), { gradingPeriodGroupId: setID });
- return _.union(this.termsNotBelongingToActiveSets(), termsBelongingToThisSet);
- },
- closeEditSetForm(id) {
- this.setState({ editSet: {id: null, saving: false} });
- },
- getShowGradingPeriodSetRef(set) {
- return "show-grading-period-set-" + set.id;
- },
- renderEditGradingPeriodSetForm(set) {
- let cancelCallback = () => {
- this.closeEditSetForm(set.id);
- };
- let saveCallback = (set) => {
- let editSet = _.extend({}, this.state.editSet, {saving: true});
- this.setState({editSet: editSet});
- SetsApi.update(set)
- .then((updated) => {
- this.onSetUpdated(updated);
- this.closeEditSetForm(set.id);
- })
- .catch((_) => {
- $.flashError(I18n.t(
- "An error occured while updating the grading period set."
- ));
- });
- };
- return (
- <EditGradingPeriodSetForm
- key = {set.id}
- ref = {getEditGradingPeriodSetRef(set)}
- set = {set}
- enrollmentTerms = {this.selectableTermsForEditSetForm(set.id)}
- disabled = {this.state.editSet.saving}
- onCancel = {cancelCallback}
- onSave = {saveCallback} />
- );
- },
- renderSets() {
- const urls = {
- batchUpdateURL: this.props.urls.gradingPeriodsUpdateURL,
- gradingPeriodSetsURL: this.props.urls.gradingPeriodSetsURL,
- deleteGradingPeriodURL: this.props.urls.deleteGradingPeriodURL
- };
- return _.map(this.getVisibleSets(), set => {
- if (this.state.editSet.id === set.id) {
- return this.renderEditGradingPeriodSetForm(set);
- } else {
- return (
- <GradingPeriodSet
- key = {set.id}
- ref = {this.getShowGradingPeriodSetRef(set)}
- set = {set}
- gradingPeriods = {set.gradingPeriods}
- urls = {urls}
- actionsDisabled = {!!this.state.editSet.id}
- readOnly = {this.props.readOnly}
- permissions = {set.permissions}
- terms = {this.state.enrollmentTerms}
- expanded = {_.contains(this.state.expandedSetIDs, set.id)}
- onEdit = {this.editGradingPeriodSet}
- onDelete = {this.removeGradingPeriodSet}
- onPeriodsChange = {this.updateSetPeriods}
- onToggleBody = {() => { this.toggleSetBody(set.id) }}
- />
- );
- }
- });
- },
- renderNewGradingPeriodSetForm() {
- if (this.state.showNewSetForm) {
- return (
- <NewGradingPeriodSetForm
- ref = "newSetForm"
- closeForm = {this.closeNewSetForm}
- urls = {this.props.urls}
- enrollmentTerms = {this.termsNotBelongingToActiveSets()}
- readOnly = {this.props.readOnly}
- addGradingPeriodSet = {this.addGradingPeriodSet}
- />
- );
- }
- },
- renderAddSetFormButton() {
- let disable = this.state.showNewSetForm || !!this.state.editSet.id;
- if (!this.props.readOnly) {
- return (
- <Button
- ref = 'addSetFormButton'
- variant = 'primary'
- disabled = {disable}
- onClick = {this.openNewSetForm}
- aria-label = {I18n.t("Add Set of Grading Periods")}
- >
- <i className="icon-plus"/>
-
- <span aria-hidden="true">{I18n.t("Set of Grading Periods")}</span>
- </Button>
- );
- }
- },
- render() {
- return (
- <div>
- <div className="GradingPeriodSets__toolbar header-bar no-line ic-Form-action-box">
- <div className="ic-Form-action-box__Form">
- <div className="ic-Form-control">
- <EnrollmentTermsDropdown
- terms={this.termsBelongingToActiveSets()}
- changeSelectedEnrollmentTerm={this.changeSelectedEnrollmentTerm} />
- </div>
- <SearchGradingPeriodsField changeSearchText={this.changeSearchText} />
- </div>
- <div className="ic-Form-action-box__Actions">
- {this.renderAddSetFormButton()}
- </div>
- </div>
- {this.renderNewGradingPeriodSetForm()}
- <div id="grading-period-sets">
- {this.renderSets()}
- </div>
- </div>
- );
- }
- });
- export default GradingPeriodSetCollection
|