@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
202 lines (201 loc) • 15.2 kB
JavaScript
import { __rest } from "tslib";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment, memo, useCallback, useEffect, useMemo, useState } from 'react';
import { PREFIX } from './constants';
import { Avatar, Box, Divider, LinearProgress, Stack, styled, Typography, useThemeProps } from '@mui/material';
import classNames from 'classnames';
import HeaderCourseDashboard from './Header';
import { SCCourseJoinStatusType, SCCourseLessonCompletionStatusType, SCCoursePrivacyType } from '@selfcommunity/types';
import { FormattedMessage, useIntl } from 'react-intl';
import ActionButton from './Student/ActionButton';
import { CLAPPING } from '../../assets/courses/clapping';
import { SCRoutes, useSCFetchCourse, useSCRouting, Link, useSCUser, useSCContext } from '@selfcommunity/react-core';
import AccordionLessons from '../../shared/AccordionLessons';
import { CourseService } from '@selfcommunity/api-services';
import { Logger } from '@selfcommunity/utils';
import { SCOPE_SC_UI } from '../../constants/Errors';
import { useSnackbar } from 'notistack';
import StudentSkeleton from './Student/Skeleton';
import UserAvatar from '../../shared/UserAvatar';
const BUTTON_MESSAGES = {
dashboard: 'ui.course.dashboard.student.button.dashboard',
request: 'ui.course.dashboard.student.button.request',
signUp: 'ui.course.dashboard.student.button.signUp',
review: 'ui.course.dashboard.student.button.review',
cancel: 'ui.course.dashboard.student.button.cancel',
start: 'ui.course.dashboard.student.button.start',
continue: 'ui.course.dashboard.student.button.continue'
};
const SNACKBAR_MESSAGES = {
cancel: 'ui.course.dashboard.student.snackbar.success.cancel',
enroll: 'ui.course.dashboard.student.snackbar.success.enroll',
request: 'ui.course.dashboard.student.snackbar.success.request'
};
const classes = {
root: `${PREFIX}-root`,
studentContainer: `${PREFIX}-student-container`,
userWrapper: `${PREFIX}-user-wrapper`,
actionsWrapper: `${PREFIX}-actions-wrapper`,
user: `${PREFIX}-user`,
avatar: `${PREFIX}-avatar`,
description: `${PREFIX}-description`,
progress: `${PREFIX}-progress`,
lessonsSections: `${PREFIX}-lessons-sections`,
circle: `${PREFIX}-circle`,
accordion: `${PREFIX}-accordion`,
margin: `${PREFIX}-margin`,
box: `${PREFIX}-box`,
percentageWrapper: `${PREFIX}-percentage-wrapper`,
completedWrapper: `${PREFIX}-completed-wrapper`,
contrastColor: `${PREFIX}-contrast-color`
};
function getUrlNextLesson(course) {
var _a, _b, _c;
const data = {
id: course.id,
slug: course.slug
};
if (course.user_completion_rate === 100) {
Object.assign(data, {
section_id: (_a = course.sections) === null || _a === void 0 ? void 0 : _a[0].id,
lesson_id: (_b = course.sections) === null || _b === void 0 ? void 0 : _b[0].lessons[0].id
});
return data;
}
(_c = course.sections) === null || _c === void 0 ? void 0 : _c.some((section) => {
var _a;
const isNextLessonInThisSection = section.num_lessons_completed < section.num_lessons && !section.locked;
if (isNextLessonInThisSection) {
Object.assign(data, {
section_id: section.id,
lesson_id: (_a = section.lessons.find((lesson) => lesson.completion_status === SCCourseLessonCompletionStatusType.UNCOMPLETED)) === null || _a === void 0 ? void 0 : _a.id
});
}
return isNextLessonInThisSection;
});
return data;
}
function getIsNextLessonLocked(course) {
var _a;
if (course.join_status === null) {
return undefined;
}
return (_a = course.sections) === null || _a === void 0 ? void 0 : _a.every((section) => {
var _a, _b;
return (section.num_lessons_completed < section.num_lessons &&
((_b = (_a = section.lessons) === null || _a === void 0 ? void 0 : _a.find((lesson) => lesson.completion_status === SCCourseLessonCompletionStatusType.UNCOMPLETED)) === null || _b === void 0 ? void 0 : _b.locked));
});
}
const Root = styled(Box, {
name: PREFIX,
slot: 'Root',
overridesResolver: (_props, styles) => styles.root
})(() => ({}));
function Student(inProps) {
// PROPS
const props = useThemeProps({
props: inProps,
name: PREFIX
});
const { courseId, course, className } = props, rest = __rest(props, ["courseId", "course", "className"]);
// STATES
const [sentRequest, setSentRequest] = useState(null);
const [loadingRequest, setLoadingRequest] = useState(false);
// CONTEXTS
const scRoutingContext = useSCRouting();
const scContext = useSCContext();
const scUserContext = useSCUser();
// HOOKS
const { scCourse, setSCCourse } = useSCFetchCourse({ id: courseId, course });
const intl = useIntl();
const { enqueueSnackbar } = useSnackbar();
// EFFETCS
useEffect(() => {
if (scCourse) {
setSentRequest(scCourse.join_status === SCCourseJoinStatusType.REQUESTED);
}
}, [scCourse, setSentRequest]);
// HANDLERS
const handleRequest = useCallback(() => {
setLoadingRequest(true);
let request;
if (sentRequest) {
request = CourseService.leaveOrRemoveCourseRequest(scCourse.id);
}
else {
request = CourseService.joinOrAcceptInviteToCourse(scCourse.id);
}
request
.then((data) => {
let updatedCourse;
if (data) {
updatedCourse = data;
}
else {
updatedCourse = Object.assign(Object.assign({}, scCourse), { join_status: null });
}
setSCCourse(updatedCourse);
setSentRequest((prev) => !prev);
setLoadingRequest(false);
enqueueSnackbar(_jsx(FormattedMessage, { id: updatedCourse.join_status === SCCourseJoinStatusType.REQUESTED
? SNACKBAR_MESSAGES.request
: updatedCourse.join_status === SCCourseJoinStatusType.JOINED
? SNACKBAR_MESSAGES.enroll
: SNACKBAR_MESSAGES.cancel, defaultMessage: updatedCourse.join_status === SCCourseJoinStatusType.REQUESTED
? SNACKBAR_MESSAGES.request
: updatedCourse.join_status === SCCourseJoinStatusType.JOINED
? SNACKBAR_MESSAGES.enroll
: SNACKBAR_MESSAGES.cancel }), {
variant: 'success',
autoHideDuration: 3000
});
})
.catch((error) => {
enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.error.action", defaultMessage: "ui.common.error.action" }), {
variant: 'error',
autoHideDuration: 3000
});
Logger.error(SCOPE_SC_UI, error);
});
}, [scCourse, sentRequest, setLoadingRequest]);
const handleAnonymousAction = useCallback(() => {
scContext.settings.handleAnonymousAction();
}, [scContext.settings.handleAnonymousAction]);
// MEMOS
const actionButton = useMemo(() => {
if (!scCourse) {
return _jsx(_Fragment, {});
}
return (_jsxs(Stack, Object.assign({ className: classes.actionsWrapper }, { children: [(scCourse.join_status === SCCourseJoinStatusType.CREATOR || scCourse.join_status === SCCourseJoinStatusType.MANAGER) && (_jsx(ActionButton, { labelId: BUTTON_MESSAGES.dashboard, to: scRoutingContext.url(SCRoutes.COURSE_DASHBOARD_ROUTE_NAME, scCourse), color: "inherit", variant: "outlined" })), (((scCourse.privacy === SCCoursePrivacyType.PRIVATE || scCourse.privacy === SCCoursePrivacyType.SECRET) &&
(scCourse.join_status === SCCourseJoinStatusType.MANAGER || scCourse.join_status === SCCourseJoinStatusType.JOINED)) ||
(scCourse.privacy === SCCoursePrivacyType.OPEN && scCourse.join_status !== SCCourseJoinStatusType.CREATOR)) && (_jsx(ActionButton, { labelId: scCourse.join_status === null
? BUTTON_MESSAGES.signUp
: scCourse.user_completion_rate === 0
? BUTTON_MESSAGES.start
: scCourse.user_completion_rate === 100
? BUTTON_MESSAGES.review
: BUTTON_MESSAGES.continue, to: scCourse.join_status !== null ? scRoutingContext.url(SCRoutes.COURSE_LESSON_ROUTE_NAME, getUrlNextLesson(scCourse)) : undefined, disabled: getIsNextLessonLocked(scCourse), color: scCourse.user_completion_rate === 100 ? 'inherit' : undefined, variant: scCourse.user_completion_rate === 100 ? 'outlined' : undefined, loading: scCourse.join_status === null ? loadingRequest : undefined, onClick: !scUserContext.user ? handleAnonymousAction : scCourse.join_status === null ? handleRequest : undefined })), scCourse.privacy === SCCoursePrivacyType.PRIVATE &&
(scCourse.join_status === null || scCourse.join_status === SCCourseJoinStatusType.REQUESTED) && (_jsx(ActionButton, { labelId: sentRequest ? BUTTON_MESSAGES.cancel : BUTTON_MESSAGES.request, color: "inherit", variant: "outlined", loading: loadingRequest, onClick: handleRequest }))] })));
}, [scCourse, sentRequest, loadingRequest, handleRequest]);
if (!scCourse) {
return _jsx(StudentSkeleton, {});
}
return (_jsxs(Root, Object.assign({ className: classNames(classes.root, classes.studentContainer, className) }, rest, { children: [_jsx(HeaderCourseDashboard, { course: scCourse }), _jsx(Divider, {}), _jsxs(Stack, Object.assign({ className: classes.userWrapper }, { children: [_jsxs(Stack, Object.assign({ className: classes.user }, { children: [_jsx(Link, Object.assign({}, (!scCourse.created_by.deleted && {
to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, scCourse.created_by)
}), { children: _jsx(UserAvatar, Object.assign({ hide: !scCourse.created_by.community_badge, smaller: true }, { children: _jsx(Avatar, { className: classes.avatar, src: scCourse.created_by.avatar, alt: scCourse.created_by.username }) })) })), _jsxs(Box, { children: [_jsx(Link, Object.assign({}, (!scCourse.created_by.deleted && {
to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, scCourse.created_by)
}), { children: _jsx(Typography, Object.assign({ variant: "body1" }, { children: scCourse.created_by.username })) })), _jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.header.user.creator", defaultMessage: "ui.course.dashboard.header.user.creator" }) }))] })] })), actionButton] })), _jsx(Divider, {}), (((scCourse.privacy === SCCoursePrivacyType.PRIVATE || scCourse.privacy === SCCoursePrivacyType.SECRET) &&
(scCourse.join_status === SCCourseJoinStatusType.CREATOR ||
scCourse.join_status === SCCourseJoinStatusType.MANAGER ||
scCourse.join_status === SCCourseJoinStatusType.JOINED)) ||
scCourse.privacy === SCCoursePrivacyType.OPEN ||
scCourse.privacy === SCCoursePrivacyType.DRAFT) &&
scCourse.description && (_jsxs(Fragment, { children: [_jsx(Typography, Object.assign({ variant: "h6", className: classNames(classes.margin, classes.contrastColor) }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.description", defaultMessage: "ui.course.dashboard.student.description" }) })), _jsx(Stack, Object.assign({ className: classes.box }, { children: _jsx(Typography, Object.assign({ variant: "body1", className: classes.description }, { children: scCourse.description })) }))] })), (((scCourse.privacy === SCCoursePrivacyType.PRIVATE || scCourse.privacy === SCCoursePrivacyType.SECRET) &&
(scCourse.join_status === SCCourseJoinStatusType.MANAGER || scCourse.join_status === SCCourseJoinStatusType.JOINED)) ||
(scCourse.privacy === SCCoursePrivacyType.OPEN && scCourse.join_status !== SCCourseJoinStatusType.CREATOR)) && (_jsxs(Fragment, { children: [scCourse.join_status !== null && (_jsxs(Fragment, { children: [_jsx(Typography, Object.assign({ variant: "h6", className: classNames(classes.margin, classes.contrastColor) }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.progress", defaultMessage: "ui.course.dashboard.student.progress" }) })), _jsxs(Stack, Object.assign({ className: classes.box }, { children: [_jsxs(Stack, Object.assign({ className: classes.percentageWrapper }, { children: [_jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.progress.described", defaultMessage: "ui.course.dashboard.student.progress.described", values: { progress: scCourse.num_lessons_completed, end: scCourse.num_lessons } }) })), _jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.progress.percentage", defaultMessage: "ui.course.dashboard.student.progress.percentage", values: { percentage: scCourse.user_completion_rate } }) }))] })), _jsx(LinearProgress, { className: classes.progress, variant: "determinate", value: scCourse.user_completion_rate })] }))] })), scCourse.user_completion_rate === 100 && (_jsxs(Stack, Object.assign({ className: classNames(classes.completedWrapper, classes.margin) }, { children: [_jsx(Typography, Object.assign({ variant: "h3", className: classes.contrastColor }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.completed", defaultMessage: "ui.course.dashboard.student.completed" }) })), _jsx("img", { src: CLAPPING, alt: intl.formatMessage({ id: 'ui.course.dashboard.student.completed', defaultMessage: 'ui.course.dashboard.student.completed' }), width: 32, height: 32 })] }))), _jsx(Typography, Object.assign({ variant: "h6", className: classNames(classes.margin, classes.contrastColor) }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.student.contents", defaultMessage: "ui.course.dashboard.student.contents" }) })), _jsxs(Stack, Object.assign({ className: classes.lessonsSections }, { children: [_jsx(Typography, Object.assign({ variant: "h5" }, { children: _jsx(FormattedMessage, { id: "ui.course.table.sections.title", defaultMessage: "ui.course.table.sections.title", values: {
sectionsNumber: scCourse.num_sections
} }) })), _jsx(Box, { className: classes.circle }), _jsx(Typography, Object.assign({ variant: "h5" }, { children: _jsx(FormattedMessage, { id: "ui.course.table.lessons.title", defaultMessage: "ui.course.table.lessons.title", values: {
lessonsNumber: scCourse.num_lessons
} }) }))] })), _jsx(AccordionLessons, { course: scCourse, className: classes.accordion })] }))] })));
}
export default memo(Student);