UNPKG

@selfcommunity/react-ui

Version:

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

251 lines (246 loc) • 12.6 kB
import { __rest } from "tslib"; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { LoadingButton } from '@mui/lab'; import { Avatar, Box, Button, Chip, Icon, IconButton, InputAdornment, TextField, Typography } from '@mui/material'; import Autocomplete from '@mui/material/Autocomplete'; import { styled } from '@mui/material/styles'; import { useThemeProps } from '@mui/system'; import { EventService } from '@selfcommunity/api-services'; import { SCUserContext, useSCFetchEvent } from '@selfcommunity/react-core'; import { Logger } from '@selfcommunity/utils'; import classNames from 'classnames'; import PubSub from 'pubsub-js'; import React, { useContext, useEffect, useMemo, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { SCOPE_SC_UI } from '../../constants/Errors'; import { SCGroupEventType, SCTopicType } from '../../constants/PubSub'; import BaseDialog from '../../shared/BaseDialog'; import User from '../User'; const messages = defineMessages({ placeholder: { id: 'ui.eventInviteButton.searchBar.placeholder', defaultMessage: 'ui.eventInviteButton.searchBar.placeholder' } }); const PREFIX = 'SCEventInviteButton'; const classes = { root: `${PREFIX}-root`, dialogRoot: `${PREFIX}-dialog-root`, dialogTitle: `${PREFIX}-dialog-title`, dialogContent: `${PREFIX}-dialog-content`, autocomplete: `${PREFIX}-autocomplete`, icon: `${PREFIX}-icon`, input: `${PREFIX}-input`, clear: `${PREFIX}-clear`, invitedBox: `${PREFIX}-invited-box`, suggested: `${PREFIX}-suggested` }; const Root = styled(Button, { name: PREFIX, slot: 'Root', overridesResolver: (_props, styles) => styles.root })(() => ({})); const DialogRoot = styled(BaseDialog, { name: PREFIX, slot: 'Root', overridesResolver: (_props, styles) => styles.dialogRoot })(() => ({})); /** *> API documentation for the Community-JS Event Invite Button component. Learn about the available props and the CSS API. * #### Import ```jsx import {SCEventInviteButton} from '@selfcommunity/react-ui'; ``` #### Component Name The name `SCEventInviteButton` can be used when providing style overrides in the theme. #### CSS |Rule Name|Global class|Description| |---|---|---| |root|.SCEventInviteButton-root|Styles applied to the root element.| |dialogRoot|.SCEventInviteButton-dialog-root|Styles applied to the dialog root.| |dialogTitle|.SCEventInviteButton-dialog-title|Styles applied to the dialog title element.| |dialogContent|.SCEventInviteButton-dialog-content|Styles applied to the dialog content.| |autocomplete|.SCEventInviteButton-autocomplete|Styles applied to the autocomplete element.| |icon|.SCEventInviteButton-icon|Styles applied to the autocomplete icon element.| |input|.SCEventInviteButton-input|Styles applied to the autocomplete input element.| |clear|.SCEventInviteButton-clear|Styles applied to the autocomplete clear icon element.| |invitedBox|.SCEventInviteButton-invited-box|Styles applied to the invited users box.| |suggested|.SCEventInviteButton-suggested|Styles applied to the suggested users box.| * @param inProps */ export default function EventInviteButton(inProps) { var _a; //PROPS const props = useThemeProps({ props: inProps, name: PREFIX }); const { className, event, eventId, handleInvitations = null } = props, rest = __rest(props, ["className", "event", "eventId", "handleInvitations"]); // CONTEXT const scUserContext = useContext(SCUserContext); // STATE const [open, setOpen] = useState(false); const [isSending, setIsSending] = useState(false); const [value, setValue] = useState(''); const [suggested, setSuggested] = useState([]); const [list, setList] = useState([]); const [loading, setLoading] = useState(false); const [invited, setInvited] = useState([]); /** * Notify UI when a member is invited to a event * @param event * @param usersInvited */ function notifyChanges(event, usersInvited) { if (event && usersInvited) { PubSub.publish(`${SCTopicType.EVENT}.${SCGroupEventType.INVITE_MEMBER}`, usersInvited); } } /** * Memoized users invited ids */ const ids = useMemo(() => { if (invited) { return invited.map((u) => { return parseInt(u.id, 10); }); } return [invited]; }, [invited]); // HOOKS const { scEvent } = useSCFetchEvent({ id: eventId, event }); const isEventAdmin = useMemo(() => { var _a; return scUserContext.user && ((_a = scEvent === null || scEvent === void 0 ? void 0 : scEvent.managed_by) === null || _a === void 0 ? void 0 : _a.id) === scUserContext.user.id; }, [scUserContext.user, (_a = scEvent === null || scEvent === void 0 ? void 0 : scEvent.managed_by) === null || _a === void 0 ? void 0 : _a.id]); // INTL const intl = useIntl(); function fetchResults() { setLoading(true); EventService.getEventSuggestedUsers(scEvent === null || scEvent === void 0 ? void 0 : scEvent.id, value) .then((data) => { setLoading(false); setSuggested(data.results); }) .catch((error) => { setLoading(false); Logger.error(SCOPE_SC_UI, error); }); } function fetchGeneralResults() { setLoading(true); EventService.getEventsSuggestedUsers(value) .then((data) => { setLoading(false); setSuggested(data.results); }) .catch((error) => { setLoading(false); Logger.error(SCOPE_SC_UI, error); }); } useEffect(() => { if (scEvent === null || scEvent === void 0 ? void 0 : scEvent.id) { EventService.getEventSuggestedUsers(scEvent === null || scEvent === void 0 ? void 0 : scEvent.id, value).then((data) => { setLoading(false); setList(data.results); }); } else { EventService.getEventsSuggestedUsers(value).then((data) => { setLoading(false); setList(data.results); }); } }, [scEvent === null || scEvent === void 0 ? void 0 : scEvent.id]); /** * If a value is entered in new message field, it fetches user suggested */ useEffect(() => { if (scEvent) { fetchResults(); } else { fetchGeneralResults(); } }, [value, scEvent]); /** * Handles dialog close */ const handleClose = () => { setOpen((p) => !p); }; /** * Handles invitation sending */ const handleSendInvitations = () => { const data = { users: ids }; setIsSending(true); EventService.inviteOrAcceptEventRequest(scEvent.id, data) .then(() => { setIsSending(false); setOpen(false); setInvited([]); notifyChanges(scEvent, invited); handleInvitations === null || handleInvitations === void 0 ? void 0 : handleInvitations(true); }) .catch((error) => { setOpen(false); setLoading(false); handleInvitations === null || handleInvitations === void 0 ? void 0 : handleInvitations(false); Logger.error(SCOPE_SC_UI, error); }); }; // Autocomplete Handlers const handleInputChange = (_event, value, reason) => { switch (reason) { case 'input': setValue(value); !value && setSuggested([]); break; case 'reset': setValue(value); break; } }; const handleChange = (event, value, reason, details) => { event.preventDefault(); event.stopPropagation(); switch (reason) { case 'selectOption': setInvited(value); setList((prev) => prev.filter((u) => u.id !== details.option.id)); break; case 'removeOption': setInvited(value); setList((prev) => [...prev, details.option]); break; } return false; }; const handleUserInvite = (user) => { setInvited((prev) => [...prev, user]); setList((prev) => prev.filter((u) => u.id !== user.id)); }; const handleDelete = (option) => { setInvited(invited.filter((v) => v !== option)); setList((prev) => [...prev, option]); }; const filterOptions = (options, { inputValue }) => { return options.filter((option) => { const usernameMatch = option.username.toLowerCase().includes(inputValue.toLowerCase()); const nameMatch = option.real_name.toLowerCase().includes(inputValue.toLowerCase()); return usernameMatch || nameMatch; }); }; /** * If in event edit mode and logged-in user is not also the event manager, the component is hidden. // */ if (event && !isEventAdmin) { return null; } /** * Renders root object */ return (_jsxs(React.Fragment, { children: [_jsx(Root, Object.assign({ className: classNames(classes.root, className), onClick: handleClose, variant: scEvent ? 'contained' : 'outlined', color: scEvent ? 'secondary' : 'inherit', startIcon: _jsx(Icon, { children: "add" }) }, rest, { children: _jsx(FormattedMessage, { id: "ui.eventInviteButton", defaultMessage: "ui.eventInviteButton" }) })), open && (_jsx(DialogRoot, Object.assign({ DialogContentProps: { dividers: false }, open: true, className: classes.dialogRoot, title: _jsxs(_Fragment, { children: [_jsx(IconButton, Object.assign({ onClick: handleClose }, { children: _jsx(Icon, { children: "close" }) })), _jsx(Typography, Object.assign({ className: classes.dialogTitle }, { children: _jsx(FormattedMessage, { id: "ui.eventInviteButton.dialog.title", defaultMessage: "ui.eventInviteButton.dialog.title" }) })), _jsx(LoadingButton, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending, disabled: !invited.length }, { children: _jsx(FormattedMessage, { id: "ui.eventInviteButton.dialog.button.end", defaultMessage: "ui.eventInviteButton.dialog.button.end" }) }))] }) }, { children: _jsxs(Box, Object.assign({ className: classes.dialogContent }, { children: [_jsx(Autocomplete, { className: classes.autocomplete, loading: loading, size: "small", multiple: true, freeSolo: true, disableClearable: true, options: suggested, onChange: handleChange, onInputChange: handleInputChange, inputValue: value, filterOptions: filterOptions, value: invited, getOptionLabel: (option) => (option ? option.username : '...'), isOptionEqualToValue: (option, value) => (option ? value.id === option.id : false), renderTags: () => null, renderOption: (props, option) => (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { alt: option.username, src: option.avatar }), _jsx(Typography, Object.assign({ ml: 1 }, { children: option.username }))] }))), renderInput: (params) => (_jsx(TextField, Object.assign({}, params, { variant: "outlined", placeholder: `${intl.formatMessage(messages.placeholder)}`, InputProps: Object.assign(Object.assign({}, params.InputProps), { className: classes.input, startAdornment: (_jsxs(_Fragment, { children: [_jsx(InputAdornment, Object.assign({ position: "start" }, { children: _jsx(Icon, Object.assign({ className: classes.icon }, { children: "search" })) })), params.InputProps.startAdornment] })) }) }))) }), _jsx(Box, Object.assign({ className: classes.invitedBox }, { children: invited.map((option, index) => (_jsx(Chip, { avatar: _jsx(Avatar, { alt: option.username, src: option.avatar }), label: option.username, onDelete: () => { handleDelete(option); }, style: { marginRight: 8 } }, index))) })), _jsxs(Box, Object.assign({ className: classes.suggested }, { children: [list.length !== 0 && (_jsx(Typography, Object.assign({ variant: "h4", fontWeight: "bold" }, { children: _jsx(FormattedMessage, { id: "ui.eventInviteButton.dialog.content.list", defaultMessage: "ui.eventInviteButton.dialog.content.list" }) }))), list.slice(0, 5).map((user, index) => (_jsx(User, { elevation: 0, actions: _jsx(_Fragment, {}), user: user, userId: user.id, buttonProps: { onClick: () => handleUserInvite(user) } }, index)))] }))] })) })))] })); }