Skip to content

Commit 08214e4

Browse files
feat(courses): added alert on dashboard for suspended courses
1 parent 772f551 commit 08214e4

13 files changed

Lines changed: 155 additions & 64 deletions

File tree

app/models/components/course/course_ability_component.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def allow_unregistered_users_registering_courses
3535
end
3636

3737
def allow_registered_users_showing_course
38-
can :read, Course, id: course.id unless course_user.is_suspended || course.is_suspended
38+
can :read, Course, id: course.id unless course_user.is_suspended || (course.is_suspended && course_user.student?)
3939
end
4040

4141
def allow_staff_show_course_users

app/views/course/admin/admin/index.json.jbuilder

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ json.defaultTimelineAlgorithm current_course.default_timeline_algorithm
1313
json.timeZone current_course.time_zone
1414
json.advanceStartAtDurationDays current_course.advance_start_at_duration_days
1515
json.canDelete can?(:destroy, current_course)
16-
json.canSuspendCourse can?(:manage, current_course)
1716
json.userSuspensionMessage current_course.user_suspension_message unless current_course.user_suspension_message.blank?
1817
json.isSuspended current_course.is_suspended
1918
json.courseSuspensionMessage current_course.course_suspension_message unless current_course.course_suspension_message.blank?

app/views/course/courses/_course_data.json.jbuilder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ if can?(:manage, current_course) || (current_course.user?(current_user) && !is_s
5757
end
5858

5959
json.isSuspended current_course.is_suspended
60+
json.canSuspendCourse can?(:manage, current_course)
6061
json.isSuspendedUser is_suspended_user
6162
json.courseSuspensionMessage current_course.course_suspension_message if current_course.is_suspended
6263
json.userSuspensionMessage current_course.user_suspension_message if is_suspended_user

client/app/bundles/common/ErrorPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const translations = defineMessages({
6767
},
6868
courseSuspended: {
6969
id: 'app.ErrorPage.courseSuspended',
70-
defaultMessage: 'This course has been suspended.',
70+
defaultMessage: 'This course is suspended.',
7171
},
7272
error: {
7373
id: 'app.ErrorPage.error',

client/app/bundles/course/admin/pages/CourseSettings/CourseSettingsForm.tsx

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Controller } from 'react-hook-form';
33
import { Button, Grid, RadioGroup, Typography } from '@mui/material';
44
import { CourseInfo, TimeOffset, TimeZones } from 'types/course/admin/course';
55

6+
import CourseSuspendedAlert from 'course/courses/components/misc/CourseSuspendedAlert';
67
import { getCourseLogoUrl } from 'course/helper';
78
import AvatarSelector from 'lib/components/core/AvatarSelector';
89
import RadioButton from 'lib/components/core/buttons/RadioButton';
@@ -332,65 +333,62 @@ const CourseSettingsForm = forwardRef<
332333
</Section>
333334

334335
<Section sticksToNavbar title={t(translations.suspension)}>
335-
{props.data.canSuspendCourse && (
336-
<>
337-
<Typography variant="body2">
338-
{t(translations.suspendCourseDescription)}
339-
</Typography>
340-
{!props.data.isSuspended && (
341-
<Button
342-
color="warning"
343-
disabled={props.disabled}
344-
onClick={() => setSuspendingCourse(true)}
345-
variant="outlined"
346-
>
347-
{t(translations.suspendCourse)}
348-
</Button>
349-
)}
350-
<Prompt
351-
onClickPrimary={() => {
352-
props.onSuspendCourse();
353-
setSuspendingCourse(false);
354-
}}
355-
onClose={closeSuspendingCoursePrompt}
356-
open={suspendingCourse}
357-
primaryColor="warning"
358-
primaryLabel={t(translations.suspendCourse)}
359-
>
360-
<PromptText>
361-
{t(translations.suspendCoursePromptText)}
362-
</PromptText>
363-
</Prompt>
364-
{props.data.isSuspended && (
365-
<Button
366-
color="warning"
336+
<Typography variant="body2">
337+
{t(translations.suspendCourseDescription)}
338+
</Typography>
339+
{props.data.isSuspended && (
340+
<CourseSuspendedAlert canSuspendCourse />
341+
)}
342+
{!props.data.isSuspended && (
343+
<Button
344+
color="warning"
345+
disabled={props.disabled}
346+
onClick={() => setSuspendingCourse(true)}
347+
variant="outlined"
348+
>
349+
{t(translations.suspendCourse)}
350+
</Button>
351+
)}
352+
<Prompt
353+
onClickPrimary={() => {
354+
props.onSuspendCourse();
355+
setSuspendingCourse(false);
356+
}}
357+
onClose={closeSuspendingCoursePrompt}
358+
open={suspendingCourse}
359+
primaryColor="warning"
360+
primaryLabel={t(translations.suspendCourse)}
361+
>
362+
<PromptText>{t(translations.suspendCoursePromptText)}</PromptText>
363+
</Prompt>
364+
{props.data.isSuspended && (
365+
<Button
366+
color="warning"
367+
disabled={props.disabled}
368+
onClick={props.onUnsuspendCourse}
369+
variant="outlined"
370+
>
371+
{t(translations.unsuspendCourse)}
372+
</Button>
373+
)}
374+
<Subsection
375+
subtitle={t(translations.courseSuspensionMessageDescription)}
376+
title={t(translations.courseSuspensionMessage)}
377+
>
378+
<Controller
379+
control={control}
380+
name="courseSuspensionMessage"
381+
render={({ field, fieldState }): JSX.Element => (
382+
<FormTextField
367383
disabled={props.disabled}
368-
onClick={props.onUnsuspendCourse}
369-
variant="outlined"
370-
>
371-
{t(translations.unsuspendCourse)}
372-
</Button>
373-
)}
374-
<Subsection
375-
subtitle={t(translations.courseSuspensionMessageDescription)}
376-
title={t(translations.courseSuspensionMessage)}
377-
>
378-
<Controller
379-
control={control}
380-
name="courseSuspensionMessage"
381-
render={({ field, fieldState }): JSX.Element => (
382-
<FormTextField
383-
disabled={props.disabled}
384-
field={field}
385-
fieldState={fieldState}
386-
fullWidth
387-
placeholder={t(courseTranslations.suspendedSubtitle)}
388-
/>
389-
)}
384+
field={field}
385+
fieldState={fieldState}
386+
fullWidth
387+
placeholder={t(courseTranslations.suspendedSubtitle)}
390388
/>
391-
</Subsection>
392-
</>
393-
)}
389+
)}
390+
/>
391+
</Subsection>
394392
<Subsection
395393
subtitle={t(translations.userSuspensionMessageDescription)}
396394
title={t(translations.userSuspensionMessage)}

client/app/bundles/course/admin/pages/CourseSettings/translations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export default defineMessages({
263263
userSuspensionMessageDescription: {
264264
id: 'course.admin.CourseSettings.userSuspensionMessageDescription',
265265
defaultMessage:
266-
'This message will be shown to users whose access to this course has been suspended. Leave blank to show a default message.',
266+
'This message will be shown to individual users whose access to this course has been suspended. Leave blank to show a default message.',
267267
},
268268
deleteCoursePromptAction: {
269269
id: 'course.admin.CourseSettingst.deleteCoursePromptAction',
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { FC } from 'react';
2+
import { defineMessages } from 'react-intl';
3+
import { Alert, Typography } from '@mui/material';
4+
5+
import { getComponentTitle } from 'course/translations';
6+
import Link from 'lib/components/core/Link';
7+
import { getCourseId } from 'lib/helpers/url-helpers';
8+
import useTranslation from 'lib/hooks/useTranslation';
9+
10+
const translations = defineMessages({
11+
header: {
12+
id: 'course.courses.CourseSuspendedAlert.header',
13+
defaultMessage:
14+
'This course is suspended. Instructors can still access it, but students cannot.',
15+
},
16+
canSuspendMessage: {
17+
id: 'course.courses.CourseSuspendedAlert.canSuspendMessage',
18+
defaultMessage: 'You can unsuspend it from the {link} page.',
19+
},
20+
cannotSuspendMessage: {
21+
id: 'course.courses.CourseSuspendedAlert.cannotSuspendMessage',
22+
defaultMessage:
23+
'If you believe this is a mistake, contact a course manager or owner to have them unsuspend the course.',
24+
},
25+
});
26+
27+
const CourseSuspendedAlert: FC<{
28+
canSuspendCourse: boolean;
29+
linkToSettings?: boolean;
30+
}> = ({ canSuspendCourse, linkToSettings }) => {
31+
const { t } = useTranslation();
32+
return (
33+
<Alert severity="warning">
34+
<Typography variant="body2">{t(translations.header)}</Typography>
35+
{canSuspendCourse && linkToSettings && (
36+
<Typography variant="body2">
37+
{t(translations.canSuspendMessage, {
38+
link: (
39+
<Link
40+
opensInNewTab
41+
to={`/courses/${getCourseId()}/admin`}
42+
underline="hover"
43+
>
44+
{getComponentTitle(t, 'admin_settings')}
45+
</Link>
46+
),
47+
})}
48+
</Typography>
49+
)}
50+
{!canSuspendCourse && (
51+
<Typography variant="body2">
52+
{t(translations.cannotSuspendMessage)}
53+
</Typography>
54+
)}
55+
</Alert>
56+
);
57+
};
58+
59+
export default CourseSuspendedAlert;

client/app/bundles/course/courses/pages/CourseShow/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import courseTranslations from 'lib/translations/course';
1616
import CourseAnnouncements from '../../components/misc/CourseAnnouncements';
1717
import CourseEnrolOptions from '../../components/misc/CourseEnrolOptions';
1818
import CourseNotifications from '../../components/misc/CourseNotifications';
19+
import CourseSuspendedAlert from '../../components/misc/CourseSuspendedAlert';
1920
import PendingTodosTable from '../../components/tables/PendingTodosTable';
2021
import { loadCourse } from '../../operations';
2122
import { getCourseEntity } from '../../selectors';
@@ -72,6 +73,12 @@ const CourseShow: FC = () => {
7273

7374
return (
7475
<Page className="space-y-5">
76+
{course.isSuspended && (
77+
<CourseSuspendedAlert
78+
canSuspendCourse={course.canSuspendCourse}
79+
linkToSettings
80+
/>
81+
)}
7582
{!course.permissions.isCurrentCourseUser && (
7683
<>
7784
{getShouldShowEnrolOptions(course) && (

client/app/types/course/admin/course.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export interface CourseInfo {
1313
timeZone: string;
1414
advanceStartAtDurationDays: number;
1515
canDelete: boolean;
16-
canSuspendCourse: boolean;
1716
userSuspensionMessage?: string;
1817
isSuspended: boolean;
1918
courseSuspensionMessage?: string;

client/app/types/course/courses.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export interface CourseEntity extends CourseMiniEntity {
7676
userSuspensionMessage?: string;
7777
isSuspended: boolean;
7878
courseSuspensionMessage?: string;
79+
canSuspendCourse: boolean;
7980
}
8081

8182
export interface NewCourseFormData {

0 commit comments

Comments
 (0)