@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
273 lines (268 loc) • 19.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const lab_1 = require("@mui/lab");
const material_1 = require("@mui/material");
const system_1 = require("@mui/system");
const api_services_1 = require("@selfcommunity/api-services");
const react_core_1 = require("@selfcommunity/react-core");
const types_1 = require("@selfcommunity/types");
const utils_1 = require("@selfcommunity/utils");
const classnames_1 = tslib_1.__importDefault(require("classnames"));
const pubsub_js_1 = tslib_1.__importDefault(require("pubsub-js"));
const react_1 = require("react");
const react_intl_1 = require("react-intl");
const Errors_1 = require("../../constants/Errors");
const PubSub_1 = require("../../constants/PubSub");
const constants_1 = require("./constants");
const UploadCourseCover_1 = tslib_1.__importDefault(require("./UploadCourseCover"));
const Course_1 = require("../../constants/Course");
const CategoryAutocomplete_1 = tslib_1.__importDefault(require("../CategoryAutocomplete"));
const Edit_1 = tslib_1.__importDefault(require("./Edit"));
const Dialog_1 = tslib_1.__importDefault(require("./Dialog"));
const messages = (0, react_intl_1.defineMessages)({
name: {
id: 'ui.courseForm.name.placeholder',
defaultMessage: 'ui.courseForm.name.placeholder'
},
description: {
id: 'ui.courseForm.description.placeholder',
defaultMessage: 'ui.courseForm.description.placeholder'
},
categoryEmpty: {
id: 'ui.courseForm.category.placeholder.empty',
defaultMessage: 'ui.courseForm.category.placeholder.empty'
},
category: {
id: 'ui.courseForm.category.placeholder',
defaultMessage: 'ui.courseForm.category.placeholder'
}
});
const classes = {
root: `${constants_1.PREFIX}-root`,
actions: `${constants_1.PREFIX}-actions`,
card: `${constants_1.PREFIX}-card`,
content: `${constants_1.PREFIX}-content`,
cover: `${constants_1.PREFIX}-cover`,
description: `${constants_1.PREFIX}-description`,
error: `${constants_1.PREFIX}-error`,
form: `${constants_1.PREFIX}-form`,
frequency: `${constants_1.PREFIX}-frequency`,
name: `${constants_1.PREFIX}-name`,
privacySection: `${constants_1.PREFIX}-privacy-section`,
privacySectionInfo: `${constants_1.PREFIX}-privacy-section-info`,
selected: `${constants_1.PREFIX}-selected`,
disabled: `${constants_1.PREFIX}-disabled`,
stepOne: `${constants_1.PREFIX}-step-one`,
stepTwo: `${constants_1.PREFIX}-step-two`,
stepCustomization: `${constants_1.PREFIX}-step-customization`,
cardTitle: `${constants_1.PREFIX}-card-title`,
title: `${constants_1.PREFIX}-title`,
contrastColor: `${constants_1.PREFIX}-contrast-color`
};
const Root = (0, material_1.styled)(material_1.Box, {
name: constants_1.PREFIX,
slot: 'Root'
})(() => ({}));
/**
*> API documentation for the Community-JS Course Form component. Learn about the available props and the CSS API.
*
#### Import
```jsx
import {CourseForm} from '@selfcommunity/react-ui';
```
#### Component Name
The name `SCCourseForm` can be used when providing style overrides in the theme.
#### CSS
|Rule Name|Global class|Description|
|---|---|---|
| root | .SCCourseForm-root | Styles applied to the root element. |
| actions | .SCCourseForm-actions | Styles applied to the actions element. |
| card | .SCCourseForm-card | Styles applied to the card element. |
| content | .SCCourseForm-content | Styles applied to the content element. |
| cover | .SCCourseForm-cover | Styles applied to the cover element. |
| description | .SCCourseForm-description | Styles applied to the description element. |
| error | .SCCourseForm-error | Styles applied to the error element. |
| form | .SCCourseForm-form | Styles applied to the form element. |
| frequency | .SCCourseForm-frequency | Styles applied to the frequency element. |
| name | .SCCourseForm-name | Styles applied to the name element. |
| privacySection | .SCCourseForm-privacy-section | Styles applied to the privacy section. |
| privacySectionInfo | .SCCourseForm-privacy-section-info | Styles applied to the privacy section info. |
| selected | .SCCourseForm-selected | Styles applied to the selected element. |
| stepOne | .SCCourseForm-step-one | Styles applied to the step-one element. |
| stepTwo | .SCCourseForm-step-two | Styles applied to the step-two element. |
| title | .SCCourseForm-title | Styles applied to the title element. |
* @param inProps
*/
function CourseForm(inProps) {
var _a, _b, _c;
//PROPS
const props = (0, system_1.useThemeProps)({
props: inProps,
name: constants_1.PREFIX
});
const { className, onSuccess, onError, course = null, step = Course_1.SCCourseFormStepType.GENERAL, onStepChange } = props, rest = tslib_1.__rest(props, ["className", "onSuccess", "onError", "course", "step", "onStepChange"]);
// INTL
const intl = (0, react_intl_1.useIntl)();
const initialFieldState = {
imageOriginal: (course === null || course === void 0 ? void 0 : course.image_bigger) || '',
imageOriginalFile: '',
name: (course === null || course === void 0 ? void 0 : course.name) || '',
type: (course === null || course === void 0 ? void 0 : course.type) || '',
description: course ? course.description : '',
categories: course ? course.categories : [],
privacy: course ? course.privacy : '',
isSubmitting: false
};
// STATE
const [field, setField] = (0, react_1.useState)(initialFieldState);
const [_step, setStep] = (0, react_1.useState)(step);
const [error, setError] = (0, react_1.useState)({});
const [openDialog, setOpenDialog] = (0, react_1.useState)(false);
// PREFERENCES
const { preferences } = (0, react_core_1.useSCPreferences)();
const courseAdvancedEnabled = (0, react_1.useMemo)(() => preferences[react_core_1.SCPreferences.CONFIGURATIONS_COURSES_ADVANCED_ENABLED].value, [preferences]);
const _backgroundCover = Object.assign({}, (field.imageOriginal
? { background: `url('${field.imageOriginal}') center / cover` }
: { background: `url('${preferences[react_core_1.SCPreferences.IMAGES_USER_DEFAULT_COVER].value}') center / cover` }));
const handleChangeCover = (0, react_1.useCallback)((cover) => {
setField((prev) => (Object.assign(Object.assign({}, prev), { ['imageOriginalFile']: cover })));
const reader = new FileReader();
reader.onloadend = () => {
setField((prev) => (Object.assign(Object.assign({}, prev), { ['imageOriginal']: reader.result })));
};
reader.readAsDataURL(cover);
if (error.imageOriginalError) {
delete error.imageOriginalError;
setError(error);
}
}, [error]);
/**
* Handles step change
* @param newStep
*/
const handleChangeStep = (newStep) => {
setStep(newStep);
onStepChange(newStep, field.type);
};
/**
* Formats categories object to a specific format needed in the form body
* @param data
*/
function convertToCategoriesObject(data) {
const categories = {};
data.forEach((category, index) => {
categories[`categories[${index}]`] = category.id;
});
return categories;
}
/**
* Handle change category
* @param categories
*/
const handleOnChangeCategory = (categories) => {
setField((prev) => (Object.assign(Object.assign({}, prev), { ['categories']: convertToCategoriesObject(categories) })));
};
/**
* Notify when a group info changed
* @param data
*/
const notifyChanges = (0, react_1.useCallback)((data) => {
if (course) {
// Edit group
pubsub_js_1.default.publish(`${PubSub_1.SCTopicType.COURSE}.${PubSub_1.SCCourseEventType.EDIT}`, data);
}
else {
// Create group
pubsub_js_1.default.publish(`${PubSub_1.SCTopicType.COURSE}.${PubSub_1.SCCourseEventType.CREATE}`, data);
}
}, [course]);
/**
* Handles the form submission for create/update action
*/
const handleSubmit = (0, react_1.useCallback)(() => {
setField((prev) => (Object.assign(Object.assign({}, prev), { isSubmitting: true })));
const formData = new FormData();
if (field.imageOriginalFile) {
formData.append('image_original', field.imageOriginalFile);
}
formData.append('name', field.name);
formData.append('description', field.description);
formData.append('type', field.type);
if (field.privacy) {
formData.append('privacy', field.privacy);
}
if (field.categories) {
for (const key in field.categories) {
formData.append(key, field.categories[key]);
}
}
let courseService;
if (course) {
courseService = api_services_1.CourseService.patchCourse(course.id, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
}
else {
courseService = api_services_1.CourseService.createCourse(formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
}
courseService
.then((data) => {
notifyChanges(data);
setField((prev) => (Object.assign(Object.assign({}, prev), { isSubmitting: false })));
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(data);
})
.catch((e) => {
const _error = (0, api_services_1.formatHttpErrorCode)(e);
if (Object.values(_error)[0]['error'] === 'unique') {
setError(Object.assign(Object.assign({}, error), { nameError: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.name.error.unique", defaultMessage: "ui.courseForm.name.error.unique" }) }));
}
else {
setError(Object.assign(Object.assign({}, error), _error));
}
setField((prev) => (Object.assign(Object.assign({}, prev), { isSubmitting: false })));
utils_1.Logger.error(Errors_1.SCOPE_SC_UI, e);
onError === null || onError === void 0 ? void 0 : onError(e);
});
}, [course, field, onSuccess, onError, notifyChanges]);
/**
* Handles course fields change
*/
const handleChange = (0, react_1.useCallback)((course) => {
const { name, value } = course.target;
setField((prev) => (Object.assign(Object.assign({}, prev), { [name]: value })));
if (error[`${name}Error`]) {
delete error[`${name}Error`];
setError(error);
}
}, [setField, error]);
/**
* Handles for closing confirm dialog
*/
const handleClose = (0, react_1.useCallback)(() => {
setOpenDialog(false);
}, [setOpenDialog]);
/**
* Renders root object
*/
return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Root, Object.assign({ className: (0, classnames_1.default)(classes.root, className) }, rest, { children: (0, jsx_runtime_1.jsxs)(material_1.Box, Object.assign({ className: _step === Course_1.SCCourseFormStepType.GENERAL ? classes.stepOne : classes.stepTwo }, { children: [_step === Course_1.SCCourseFormStepType.GENERAL && ((0, jsx_runtime_1.jsx)(react_1.Fragment, { children: Object.values(types_1.SCCourseTypologyType).map((option, index) => ((0, jsx_runtime_1.jsx)(material_1.Card, Object.assign({ className: (0, classnames_1.default)(classes.card, { [classes.selected]: option === field.type }, { [classes.disabled]: !courseAdvancedEnabled && option !== types_1.SCCourseTypologyType.SELF }) }, { children: (0, jsx_runtime_1.jsx)(material_1.CardActionArea, Object.assign({ onClick: () => setField((prev) => (Object.assign(Object.assign({}, prev), { ['type']: option }))) }, { children: (0, jsx_runtime_1.jsxs)(material_1.CardContent, { children: [(0, jsx_runtime_1.jsxs)(material_1.Typography, Object.assign({ variant: "subtitle2", className: classes.cardTitle }, { children: [(0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: `ui.courseForm.${option}.title`, defaultMessage: `ui.courseForm.${option}.title` }), !courseAdvancedEnabled && option !== types_1.SCCourseTypologyType.SELF && ((0, jsx_runtime_1.jsx)(material_1.Chip, { variant: "outlined", color: "warning", size: "small", label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.comingSoon.chip", defaultMessage: "ui.courseForm.comingSoon.chip" }) }))] })), (0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2" }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: `ui.courseForm.${option}.info`, defaultMessage: `ui.courseForm.${option}.info` }) }))] }) })) }), index))) })), _step === Course_1.SCCourseFormStepType.CUSTOMIZATION && ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [course && ((0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "h5", className: classes.contrastColor }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.edit.title.general", defaultMessage: "ui.courseForm.edit.title.general" }) }))), (0, jsx_runtime_1.jsxs)(material_1.FormGroup, Object.assign({ className: (0, classnames_1.default)(classes.form, _step === Course_1.SCCourseFormStepType.CUSTOMIZATION && course ? classes.stepCustomization : undefined) }, { children: [(0, jsx_runtime_1.jsx)(material_1.Paper, Object.assign({ style: _backgroundCover, classes: { root: classes.cover } }, { children: (0, jsx_runtime_1.jsx)(UploadCourseCover_1.default, { isUploading: field.isSubmitting, onChange: handleChangeCover }) })), (0, jsx_runtime_1.jsx)(material_1.TextField, { required: true, className: classes.name, placeholder: `${intl.formatMessage(messages.name)}`, margin: "normal", value: field.name, name: "name", onChange: handleChange, InputProps: {
endAdornment: (0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2" }, { children: Course_1.COURSE_TITLE_MAX_LENGTH - field.name.length }))
}, error: Boolean((!!course && !field.name) || field.name.length > Course_1.COURSE_TITLE_MAX_LENGTH) || Boolean(error['nameError']), helperText: !!course && !field.name ? ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.required", defaultMessage: "ui.courseForm.required" })) : field.name.length > Course_1.COURSE_TITLE_MAX_LENGTH ? ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.name.error.maxLength", defaultMessage: "ui.courseForm.name.error.maxLength" })) : error['nameError'] ? (error['nameError']) : null }), (0, jsx_runtime_1.jsx)(material_1.TextField, { multiline: true, className: classes.description, placeholder: `${intl.formatMessage(messages.description)}`, margin: "normal", value: field.description, name: "description", onChange: handleChange, InputProps: {
endAdornment: ((0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2" }, { children: ((_a = field.description) === null || _a === void 0 ? void 0 : _a.length) ? Course_1.COURSE_DESCRIPTION_MAX_LENGTH - field.description.length : Course_1.COURSE_DESCRIPTION_MAX_LENGTH })))
}, error: Boolean((!!field.privacy && !field.description) || ((_b = field.description) === null || _b === void 0 ? void 0 : _b.length) > Course_1.COURSE_DESCRIPTION_MAX_LENGTH), helperText: !!field.privacy && !field.description ? ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.required", defaultMessage: "ui.courseForm.required" })) : (((_c = field.description) === null || _c === void 0 ? void 0 : _c.length) > Course_1.COURSE_DESCRIPTION_MAX_LENGTH && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.description.error.maxLength", defaultMessage: "ui.courseForm.description.error.maxLength" }))) }), (0, jsx_runtime_1.jsx)(CategoryAutocomplete_1.default, { defaultValue: field.categories, TextFieldProps: { label: intl.formatMessage(Object.keys(field.categories).length ? messages.category : messages.categoryEmpty) }, multiple: true, onChange: handleOnChangeCategory }), course && (0, jsx_runtime_1.jsx)(Edit_1.default, { course: course, onPrivacyChange: (privacy) => setField((prev) => (Object.assign(Object.assign({}, prev), { ['privacy']: privacy }))) })] }))] })), (0, jsx_runtime_1.jsx)(material_1.Box, Object.assign({ className: (0, classnames_1.default)(classes.actions, _step === Course_1.SCCourseFormStepType.CUSTOMIZATION && course ? classes.stepCustomization : undefined) }, { children: (0, jsx_runtime_1.jsx)(lab_1.LoadingButton, Object.assign({ size: "small", loading: field.isSubmitting, disabled: _step === Course_1.SCCourseFormStepType.GENERAL
? !field.type || Object.keys(error).length !== 0
: _step === Course_1.SCCourseFormStepType.CUSTOMIZATION &&
(!field.name ||
Object.keys(error).length !== 0 ||
field.name.length > Course_1.COURSE_TITLE_MAX_LENGTH ||
field.description.length > Course_1.COURSE_DESCRIPTION_MAX_LENGTH ||
(!!field.privacy && (!field.description || course.num_sections === 0 || course.num_lessons === 0))), variant: "contained", onClick: _step === Course_1.SCCourseFormStepType.GENERAL
? () => handleChangeStep(Course_1.SCCourseFormStepType.CUSTOMIZATION)
: field.privacy !== types_1.SCCoursePrivacyType.DRAFT && course.privacy === types_1.SCCoursePrivacyType.DRAFT
? () => setOpenDialog(true)
: handleSubmit, color: "primary" }, { children: course ? ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.edit.action.save", defaultMessage: "ui.courseForm.edit.action.save" })) : _step === Course_1.SCCourseFormStepType.GENERAL ? ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.button.next", defaultMessage: "ui.courseForm.button.next" })) : ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.courseForm.button.create", defaultMessage: "ui.courseForm.button.create" })) })) }))] })) })), openDialog && (0, jsx_runtime_1.jsx)(Dialog_1.default, { onSubmit: handleSubmit, onClose: handleClose })] }));
}
exports.default = CourseForm;