UNPKG

@selfcommunity/react-ui

Version:

React UI Components to integrate a Community created with SelfCommunity Platform.

273 lines (268 loc) • 19.4 kB
"use strict"; 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;