app/assets/javascripts/components/overview/available_actions.jsx
import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import CourseUtils from '../../utils/course_utils.js';
import CourseDateUtils from '../../utils/course_date_utils.js';
import { initiateConfirm } from '../../actions/confirm_actions.js';
import { addNotification } from '../../actions/notification_actions.js';
import SalesforceLink from './salesforce_link.jsx';
import CourseStatsDownloadModal from './course_stats_download_modal.jsx';
import EmbedStatsButton from './embed_stats_button.jsx';
import CloneCourseButton from './clone_course_button.jsx';
import { enableAccountRequests } from '../../actions/new_account_actions.js';
import { needsUpdate, linkToSalesforce, updateSalesforceRecord, deleteCourse } from '../../actions/course_actions';
import { STUDENT_ROLE, ONLINE_VOLUNTEER_ROLE } from '../../constants/user_roles';
import { removeUser } from '../../actions/user_actions';
import NotifyInstructorsButton from './notify_instructors_button.jsx';
const AvailableActions = ({ course, current_user, updateCourse, courseCreationNotice }) => {
const dispatch = useDispatch();
const join = (role = null) => {
const enrollURL = course.enroll_url;
if (course.passcode === '' || role === 'online_volunteer') {
const onConfirm = () => window.location = `${enrollURL}?role=${role}`;
const confirmMessage = CourseUtils.i18n('join_no_passcode');
dispatch(initiateConfirm({ confirmMessage, onConfirm }));
} else {
const onConfirm = (passcode) => {
return window.location = `${enrollURL}${passcode}?role=${role}`;
};
const confirmMessage = I18n.t('courses.passcode_prompt');
const explanation = CourseUtils.i18n('join_details', course.string_prefix);
dispatch(initiateConfirm({ confirmMessage, onConfirm, showInput: true, explanation }));
}
};
// NOTE: This is disabled until we have a better way to prevent manual updates from overloading the system.
// const updateStats = () => {
// const updateUrl = `${window.location.origin}/courses/${course.slug}/manual_update`;
// const onConfirm = () => window.location = updateUrl;
// const confirmMessage = I18n.t('courses.confirm_manual_update');
// dispatch(initiateConfirm({ confirmMessage, onConfirm }));
// };
const leave = () => {
const courseSlug = course.slug;
const role = current_user.isOnlineVolunteer ? ONLINE_VOLUNTEER_ROLE : STUDENT_ROLE;
const userRecord = { user: { user_id: current_user.id, role: role } };
const onConfirm = () => dispatch(removeUser(courseSlug, userRecord));
const confirmMessage = I18n.t('courses.leave_confirmation');
dispatch(initiateConfirm({ confirmMessage, onConfirm }));
};
const deleteCourseFunc = () => {
// The action is only available once a course has been removed from all campaigns.
if (course.published) {
return alert(I18n.t('courses.delete_course_instructions'));
}
const enteredTitle = prompt(I18n.t('courses.confirm_course_deletion', { title: course.title }));
// Check if enteredTitle is not null before calling trim.
if (enteredTitle !== null && enteredTitle.trim() === course.title.trim()) {
return dispatch(deleteCourse(course.slug));
} else if (enteredTitle) {
return alert(I18n.t('courses.confirm_course_deletion_failed', { title: enteredTitle }));
}
};
const needsUpdateFunc = () => {
dispatch(needsUpdate(course.slug));
};
const enableRequests = () => {
const onConfirm = () => {
dispatch(enableAccountRequests(course));
updateCourse(course);
dispatch(addNotification({
message: I18n.t('courses.accounts_generation_enabled'),
closable: true,
type: 'success'
}));
};
const confirmMessage = I18n.t('courses.accounts_generation_confirm_message');
const explanation = I18n.t('courses.accounts_generation_explanation');
dispatch(initiateConfirm({ confirmMessage, onConfirm, showInput: false, explanation }));
};
const controls = [];
const urlParams = new URLSearchParams(window.location.search);
const isEnrollmentURL = urlParams.has('enroll');
const user = current_user;
// If user has a role in the course or is an admin
if ((user.isEnrolled) || user.admin || user.isAdvancedRole) {
// If user is a student, show the 'leave' button.
if (user.isStudent || user.isOnlineVolunteer) {
// 'Leave' is not available if the course is controlled by Event Center.
if (!course.flags.event_sync) {
controls.push((
<div key="leave" className="available-action"><button onClick={leave} className="button">{CourseUtils.i18n('leave_course', course.string_prefix)}</button></div>
));
}
}
// If user is admin, go to list of tickets related to this course
if (user.admin) {
controls.push((<div key="search" className="available-action"><a href={`/tickets/dashboard?search_by_course=${course.slug}`} className="button">{I18n.t('courses.search_all_tickets_for_this_course')}</a></div>));
}
// If course is not published, show the 'delete' button to instructors and admins.
// Show a disabled version of it on P&E Dashboard even if a course is published,
// so that users can see the instructions for how to enable deletion.
if ((user.isAdvancedRole || user.admin) && (!course.published || !Features.wikiEd)) {
controls.push((
<div title={I18n.t('courses.delete_course_instructions')} key="delete" className="available-action">
<button className="button danger" onClick={deleteCourseFunc}>
{CourseUtils.i18n('delete_course', course.string_prefix)}
</button>
</div>
));
}
// If the course is ended, show the 'needs update' button.
if (CourseDateUtils.isEnded(course)) {
controls.push((
<div key="needs_update" className="available-action"><button className="button" onClick={needsUpdateFunc}>{I18n.t('courses.needs_update')}</button></div>
));
}
// If user has no role and is logged in, and if he is not on enrollment page, show 'Join course' button.
// On enrollment page, 'Join course' button is not shown in Actions component to avoid confusion.
// The 'Join course' button is not shown for courses controlled by Wikimedia Event Center.
// The 'Join course' button is not shown on Wiki Education Dashboard.
} else if (!course.ended && !isEnrollmentURL && !course.flags.event_sync && user.id && !Features.wikiEd) {
controls.push(
<div key="join" className="available-action"><button onClick={() => join()} className="button">{CourseUtils.i18n('join_course', course.string_prefix)}</button></div>
);
// On P&E Dashboard, offer option to join as online volunteer
if (!Features.wikiEd && course.online_volunteers_enabled) {
controls.push(
<div key="volunteer" className="available-action"><button onClick={() => join('online_volunteer')} className="button">{CourseUtils.i18n('join_course_as_volunteer', course.string_prefix)}</button></div>
);
}
}
// If the user is enrolled in the course or admin, and the course type is editathon and not finished, show a manual stats update button
// NOTE: This is disabled until we have a better way to prevent manual updates from overloading the system.
// if ((user.isEnrolled || user.isAdmin) && (course.type === 'Editathon' && !course.ended)) {
// controls.push((
// <div key="updateStats" className="available-action"><button className="button" onClick={updateStats}>{I18n.t('courses.update_stats')}</button></div>
// ));
// }
// Requested accounts
// These are enabled for instructors on P&E Dashboard, but only for admins on Wiki Education Dashboard.
if ((user.isAdvancedRole && !Features.wikiEd) || user.admin) {
// Enable account requests if allowed
if (!course.account_requests_enabled) {
controls.push((
<div key="enable_account_requests" className="available-action"><button onClick={enableRequests} className="button">{I18n.t('courses.enable_account_requests')}</button></div>
));
}
}
// If the user is an instructor or admin, and the course is published, show a stats download button
// Always show the stats download for published non-Wiki Ed courses.
if ((user.isAdvancedRole || user.admin || !Features.wikiEd) && course.published) {
controls.push((
<div key="download_course_stats" className="available-action"><CourseStatsDownloadModal course={course} /></div>
));
controls.push((
<div key="embed_course_stats" className="available-action"><EmbedStatsButton title={course.title} /></div>
));
}
if (user.admin) {
controls.push((
<div key="clone_course" className="available-action"><CloneCourseButton courseId={course.id} courseCreationNotice={courseCreationNotice}/></div>
));
}
if (user.admin && Features.wikiEd) {
controls.push((
<div key="notify_instructors" className="available-action"><NotifyInstructorsButton courseId={course.id} courseTitle={course.title} /></div>
));
}
// If no controls are available
if (controls.length === 0) {
controls.push(
<div key="none" className="available-action">{I18n.t('courses.no_available_actions')}</div>
);
}
return (
<div className="module actions">
<div className="section-header">
<h3>{I18n.t('courses.actions')}</h3>
</div>
<div className="module__data">
{controls}
<SalesforceLink course={course} current_user={current_user} linkToSalesforce={linkToSalesforce} updateSalesforceRecord={updateSalesforceRecord} />
</div>
</div>
);
};
AvailableActions.propTypes = {
course: PropTypes.object.isRequired,
current_user: PropTypes.object.isRequired,
courseCreationNotice: PropTypes.string
};
export default AvailableActions;