WikiEducationFoundation/WikiEduDashboard

View on GitHub
app/assets/javascripts/actions/wizard_actions.js

Summary

Maintainability
C
1 day
Test Coverage
C
78%
import { find } from 'lodash-es';
import {
  ASSIGNMENTS_PANEL_INDEX,
  RECEIVE_WIZARD_ASSIGNMENT_OPTIONS,
  SELECT_WIZARD_OPTION,
  SELECT_WIZARD_ASSIGNMENT,
  WIZARD_ADVANCE,
  WIZARD_REWIND,
  WIZARD_GOTO,
  WIZARD_ENABLE_SUMMARY_MODE,
  WIZARD_DISABLE_SUMMARY_MODE,
  RECEIVE_WIZARD_PANELS,
  API_FAIL
} from '../constants';
import { fetchCourse } from './course_actions';
import { fetchTimeline } from './timeline_actions';
import request from '../utils/request';

import logErrorMessage from '../utils/log_error_message';

const fetchWizardIndexPromise = async () => {
  const response = await request('/wizards.json');
  if (!response.ok) {
    logErrorMessage(response);
    const data = await response.text();
    response.responseText = data;
    throw response;
  }
  return response.json();
};

export const fetchWizardIndex = () => (dispatch) => {
  return fetchWizardIndexPromise()
    .then((data) => {
      dispatch({ type: RECEIVE_WIZARD_ASSIGNMENT_OPTIONS, assignmentOptions: data });
    })
    .catch(data => dispatch({ type: API_FAIL, data }));
};


const fetchWizardPanelsPromise = async (wizardId) => {
  const response = await request(`/wizards/${wizardId}.json`);
  if (!response.ok) {
    logErrorMessage(response);
    const data = await response.text();
    response.responseText = data;
    throw response;
  }
  return response.json();
};

export const fetchWizardPanels = wizardId => (dispatch, getState) => {
  const state = getState();
  const course = state.course;

  return fetchWizardPanelsPromise(wizardId)
    .then((data) => {
      dispatch({ type: RECEIVE_WIZARD_PANELS, extraPanels: data, course: course });
      // Using a 0 timeout here gives the browser a chance
      // to re-render the new off-screen panels before the
      // advance to the next slide and helps ensure a smooth
      // panel transition.
      setTimeout(dispatch, 0, { type: WIZARD_ADVANCE });
    })
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const advanceWizard = () => (dispatch, getState) => {
  const state = getState();
  // If we're advancing from the Assignments panel,
  // we need to fetch the specific wizard panel for the selected
  // assignment option.
  if (state.wizard.activeIndex === ASSIGNMENTS_PANEL_INDEX) {
    const wizardKey = getWizardKey(state.wizard);
    fetchWizardPanels(wizardKey)(dispatch, getState);
  // If we're advancing from the second-to-last panel to the final summary panel,
  // enable summary mode.
  } else if (state.wizard.activeIndex === state.wizard.panels.length - 2) {
    dispatch({ type: WIZARD_ENABLE_SUMMARY_MODE });
    dispatch({ type: WIZARD_ADVANCE });
  } else {
    dispatch({ type: WIZARD_ADVANCE });
  }
};

export const selectWizardOption = (panelIndex, optionIndex) => (dispatch) => {
  dispatch({ type: SELECT_WIZARD_OPTION, panelIndex, optionIndex });
  // If an assignment selection is made, disable summary mode.
  // Changing the selected assignment clears the wizard, so you can't
  // return directly to the summary.
  if (panelIndex === ASSIGNMENTS_PANEL_INDEX) {
    dispatch({ type: SELECT_WIZARD_ASSIGNMENT, optionIndex });
    dispatch({ type: WIZARD_DISABLE_SUMMARY_MODE });
  }
};

export const rewindWizard = () => {
  return { type: WIZARD_REWIND };
};

export const goToWizard = (toPanelIndex) => {
  return { type: WIZARD_GOTO, toPanelIndex };
};

export const enableSummaryMode = () => {
  return { type: WIZARD_ENABLE_SUMMARY_MODE };
};

export const disableSummaryMode = () => {
  return { type: WIZARD_DISABLE_SUMMARY_MODE };
};

const submitWizardPromise = async (courseSlug, wizardId, wizardOutput) => {
  const response = await request(`/courses/${courseSlug}/wizard/${wizardId}.json`, {
    method: 'POST',
    body: JSON.stringify({ wizard_output: wizardOutput })
  });
  if (!response.ok) {
    logErrorMessage(response);
    const data = await response.text();
    response.responseText = data;
    throw response;
  }
  return response.json();
};

const getWizardKey = (state) => {
  const assignmentOptions = state.assignmentOptions;
  return find(assignmentOptions, option => option.selected).key;
};

const getWizardOutput = (state) => {
  let output = [];
  const logic = [];
  const tags = [];
  state.panels.forEach((panel) => {
    if (Array.isArray(panel.output)) {
      output = output.concat(panel.output);
    } else {
      output.push(panel.output);
    }
    if (panel.options !== undefined && panel.options.length > 0) {
      return panel.options.forEach((option) => {
        if (!option.selected) { return; }
        if (option.output) {
          if (Array.isArray(option.output)) {
            output = output.concat(option.output);
          } else {
            output.push(option.output);
          }
        }
        if (option.logic) { logic.push(option.logic); }
        if (option.tag) { return tags.push({ key: panel.key, tag: option.tag }); }
      });
    }
  });
  return { output, logic, tags };
};

export const submitWizard = courseSlug => (dispatch, getState) => {
  const wizardState = getState().wizard;
  submitWizardPromise(courseSlug, getWizardKey(wizardState), getWizardOutput(wizardState))
    .then(() => {
      fetchCourse(courseSlug)(dispatch); // Update any flags that changed via the Wizard
      fetchTimeline(courseSlug)(dispatch); // Fetch the newly-generated Timeline
    });
};