WikiEducationFoundation/WikiEduDashboard

View on GitHub
app/assets/javascripts/components/course/course_alerts.jsx

Summary

Maintainability
C
1 day
Test Coverage
B
83%
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import CourseUtils from '../../utils/course_utils';
import CourseAlert from './course_alert';
import OptInNotification from '../common/opt_in_notification';
import { isAfter, startOfDay } from 'date-fns';
import { toDate } from '../../utils/date_utils';

const CourseAlerts = ({
  userRoles,
  course,
  courseAlerts,
  weeks,
  courseLinkParams,
  usersLoaded,
  studentCount,
  updateCourse,
  persistCourse,
  dismissNotification
}) => {
  const [alerts, setAlerts] = useState([]);

  useEffect(() => {
    const updateAlerts = () => {
      const newAlerts = [];

      // //////////////////////////////////
      // Admin / Instructor notifications /
      // //////////////////////////////////
      // For unpublished courses, when viewed by an instructor or admin
      if (userRoles.isAdvancedRole && !course.legacy && !course.published) {
        // If it's an unsubmitted ClassroomProgramCourse
        const isUnsubmittedClassroomProgramCourse = !course.submitted && course.type === 'ClassroomProgramCourse';
        if (isUnsubmittedClassroomProgramCourse) {
          // Show submit button if there is a timeline with trainings, or user is admin.
          if (CourseUtils.hasTrainings(weeks) || userRoles.isAdmin) {
            newAlerts.push(<CourseAlert key="submit" message={I18n.t('courses.review_timeline')} actionMessage={I18n.t('application.submit')} buttonLink="#" onClick={submit} />);
            // Show 'add trainings' message if there is a timeline with no trainings
          } else if (weeks.length) {
            newAlerts.push(<CourseAlert key="submit" message={I18n.t('courses.add_trainings')} actionMessage={I18n.t('courses.timeline_nav')} buttonLink={`${courseLinkParams}/timeline`} />);
            // Show 'create a timeline' message if there is no timeline.
          } else {
            newAlerts.push(<CourseAlert key="submit" message={I18n.t('courses.review_timeline')} actionMessage={I18n.t('courses.launch_wizard')} buttonLink={`${courseLinkParams}/timeline/wizard`} />);
          }
        }
        if (!(course.type === 'ClassroomProgramCourse')) {
          newAlerts.push(<CourseAlert key="noCampaign" message={I18n.t('courses.no_campaign')} />);
        }
        // Show supplementary information if the user is an admin
        const { onboardingAlert } = courseAlerts;
        if (userRoles.isAdmin && onboardingAlert) {
          const message = CourseUtils.formatOnboardingAlertMessage(onboardingAlert.message);
          const url = `/alerts_list/${onboardingAlert.id}`;
          newAlerts.push(<CourseAlert key="supplementary" message={message} buttonLink={url} actionMessage={'Go to Alert'} />);
        }
      // When the course has been submitted
        if (course.submitted) {
          // Show instructors the 'submitted' notice.
          if (!userRoles.isAdmin) {
            newAlerts.push(<CourseAlert key="submit" message={I18n.t('courses.submitted_note')} buttonLink="/training" actionMessage={'View Training Modules'}/>);
            // Instruct admins to approve the course by adding a campaign.
          } else {
            const homeLink = `${courseLinkParams}/home`;
            newAlerts.push(<CourseAlert key="publish" message={I18n.t('courses.submitted_admin')} courseLink={homeLink} actionMessage={I18n.t('courses.overview')} />);
          }
        }
      }
      // Shows an alert for how many accounts have been requested
      if (course.requestedAccounts) {
        if ((!Features.wikiEd && userRoles.isAdvancedRole) || userRoles.isAdmin) {
          const message = I18n.t('courses.requested_accounts_alert', { count: course.requestedAccounts });
          const actionMessage = I18n.t('courses.requested_accounts_alert_view');
          const url = `/requested_accounts/${course.slug}`;
          newAlerts.push(<CourseAlert key="requested_accounts" message={message} href={url} actionMessage={actionMessage} />);
        }
      }
      // For published courses with no students, highlight the enroll link
      const hasNoStudents = usersLoaded && studentCount === 0;
      if (userRoles.isAdvancedRole && course.published && hasNoStudents && !course.legacy) {
        const enrollEquals = '?enroll=';
        const url = window.location.origin + courseLinkParams + enrollEquals + course.passcode;
        newAlerts.push((
          <div className="notification" key="enroll">
            <div className="container">
              <div>
                <p>{CourseUtils.i18n('published', course.string_prefix)}</p>
                <a href={url}>{url}</a>
              </div>
            </div>
          </div>
        ));
      }

      // ////////////////////////
      // Training notifications /
      // ////////////////////////
      if (course.incomplete_assigned_modules && course.incomplete_assigned_modules.length) {
        // `table` key is because it comes back as an openstruct
        const module = course.incomplete_assigned_modules[0].table;
        const messageKey = isAfter(
          startOfDay(new Date()),
          startOfDay(toDate(module.due_date))
        ) ? 'courses.training_overdue' : 'courses.training_due';

        newAlerts.push(<CourseAlert key="upcoming_module" message={I18n.t(messageKey, { title: module.title, date: module.due_date })} buttonLink={module.link} actionClassName="pull-right" actionMessage={I18n.t('courses.training_nav')} />);
      }

      // //////////////////////
      // Survey notifications /
      // //////////////////////
      if (course.survey_notifications && course.survey_notifications.length) {
        course.survey_notifications.forEach((notification) => {
          const dismissOnClick = () => dismissSurvey(notification.id);
          const components = (
            <button
              className="button small pull-right border inverse-border"
              onClick={dismissOnClick}
            >
              {I18n.t('courses.dismiss_survey')}
            </button>
          );

          newAlerts.push(
            <CourseAlert
              key={`survey_notification_${notification.id}`}
              actionClassName="pull-right"
              actionMessage={I18n.t('courses.survey.link')}
              className="notification--survey"
              components={components}
              href={notification.survey_url}
              message={notification.message || I18n.t('courses.survey.notification_message')}
            />
          );
        });
      }
      // ////////////////////////////////
      // Very Long Update notifications /
      // ////////////////////////////////
      if (course.flags && course.flags.very_long_update) {
        newAlerts.push(
          <CourseAlert
            key="updates_paused"
            message="Updates for this program or event have been taking too long and are currently paused. If you need updated data soon, please use the 'Report a problem' link to let us know."
            actionMessage="See details"
            buttonLink="https://phabricator.wikimedia.org/T277651"
          />
        );
      }

      // //////////////////////////
      // Experiment notifications /
      // //////////////////////////
      if (course.experiment_notification) {
        newAlerts.push(<OptInNotification notification={course.experiment_notification} key="opt_in" />);
      }
      setAlerts(newAlerts);
    };

    updateAlerts();
  }, [userRoles, course, courseAlerts, weeks, courseLinkParams, usersLoaded, studentCount]);

  const submit = (e) => {
    e.preventDefault();
    if (!confirm(I18n.t('courses.warn_mirrored'))) { return; }
    updateCourse({ submitted: true });
    return persistCourse(course.slug);
  };

  const dismissSurvey = (surveyNotificationId) => {
    if (confirm(I18n.t('courses.dismiss_survey_confirm'))) {
      return dismissNotification(surveyNotificationId);
    }
  };

  return (
    <div className="course-alerts">
      {alerts}
    </div>
  );
};

CourseAlerts.propTypes = {
  userRoles: PropTypes.object.isRequired,
  course: PropTypes.object.isRequired,
  courseAlerts: PropTypes.object.isRequired,
  weeks: PropTypes.any.isRequired,
  courseLinkParams: PropTypes.string.isRequired,
  usersLoaded: PropTypes.bool.isRequired,
  studentCount: PropTypes.number.isRequired,
  updateCourse: PropTypes.func.isRequired,
  persistCourse: PropTypes.func.isRequired,
  dismissNotification: PropTypes.func.isRequired
};

export default CourseAlerts;