@selfcommunity/react-ui 0.7.50-event.29 → 0.7.50-events.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/components/Category/Category.d.ts +1 -1
- package/lib/cjs/components/Category/Category.js +1 -1
- package/lib/cjs/components/Event/Event.d.ts +0 -6
- package/lib/cjs/components/Event/Event.js +4 -4
- package/lib/cjs/components/Event/Skeleton.d.ts +16 -2
- package/lib/cjs/components/Event/Skeleton.js +16 -8
- package/lib/cjs/components/Event/index.d.ts +2 -2
- package/lib/cjs/components/EventForm/EventForm.js +30 -17
- package/lib/cjs/components/EventInviteButton/EventInviteButton.d.ts +57 -0
- package/lib/cjs/components/EventInviteButton/EventInviteButton.js +284 -0
- package/lib/cjs/components/EventInviteButton/index.d.ts +3 -0
- package/lib/cjs/components/EventInviteButton/index.js +5 -0
- package/lib/cjs/components/Events/Events.d.ts +64 -0
- package/lib/cjs/components/Events/Events.js +230 -0
- package/lib/cjs/components/Events/Skeleton.d.ts +38 -0
- package/lib/cjs/components/Events/Skeleton.js +45 -0
- package/lib/cjs/components/Events/constants.d.ts +1 -0
- package/lib/cjs/components/Events/constants.js +4 -0
- package/lib/cjs/components/Events/index.d.ts +4 -0
- package/lib/cjs/components/Events/index.js +8 -0
- package/lib/cjs/components/Events/prefetchedEvents.d.ts +271 -0
- package/lib/cjs/components/Events/prefetchedEvents.js +278 -0
- package/lib/cjs/components/FeedObject/FeedObject.d.ts +1 -0
- package/lib/cjs/components/FeedObject/FeedObject.js +16 -4
- package/lib/cjs/constants/PubSub.d.ts +3 -8
- package/lib/cjs/constants/PubSub.js +2 -1
- package/lib/cjs/index.d.ts +4 -2
- package/lib/cjs/index.js +7 -2
- package/lib/esm/components/Category/Category.d.ts +1 -1
- package/lib/esm/components/Category/Category.js +1 -1
- package/lib/esm/components/Event/Event.d.ts +0 -6
- package/lib/esm/components/Event/Event.js +4 -4
- package/lib/esm/components/Event/Skeleton.d.ts +16 -2
- package/lib/esm/components/Event/Skeleton.js +17 -9
- package/lib/esm/components/Event/index.d.ts +2 -2
- package/lib/esm/components/EventForm/EventForm.js +30 -17
- package/lib/esm/components/EventInviteButton/EventInviteButton.d.ts +57 -0
- package/lib/esm/components/EventInviteButton/EventInviteButton.js +281 -0
- package/lib/esm/components/EventInviteButton/index.d.ts +3 -0
- package/lib/esm/components/EventInviteButton/index.js +2 -0
- package/lib/esm/components/Events/Events.d.ts +64 -0
- package/lib/esm/components/Events/Events.js +227 -0
- package/lib/esm/components/Events/Skeleton.d.ts +38 -0
- package/lib/esm/components/Events/Skeleton.js +42 -0
- package/lib/esm/components/Events/constants.d.ts +1 -0
- package/lib/esm/components/Events/constants.js +1 -0
- package/lib/esm/components/Events/index.d.ts +4 -0
- package/lib/esm/components/Events/index.js +4 -0
- package/lib/esm/components/Events/prefetchedEvents.d.ts +271 -0
- package/lib/esm/components/Events/prefetchedEvents.js +275 -0
- package/lib/esm/components/FeedObject/FeedObject.d.ts +1 -0
- package/lib/esm/components/FeedObject/FeedObject.js +16 -4
- package/lib/esm/constants/PubSub.d.ts +3 -8
- package/lib/esm/constants/PubSub.js +2 -1
- package/lib/esm/index.d.ts +4 -2
- package/lib/esm/index.js +3 -1
- package/lib/umd/react-ui.js +1 -1
- package/package.json +6 -6
|
@@ -120,13 +120,13 @@ export default function EventForm(inProps) {
|
|
|
120
120
|
// INTL
|
|
121
121
|
const intl = useIntl();
|
|
122
122
|
const initialFieldState = {
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
imageOriginal: '',
|
|
124
|
+
imageOriginalFile: '',
|
|
125
125
|
startDate: null,
|
|
126
126
|
startTime: null,
|
|
127
127
|
endDate: null,
|
|
128
128
|
endTime: null,
|
|
129
|
-
location:
|
|
129
|
+
location: SCEventLocationType.PERSON,
|
|
130
130
|
geolocation: '',
|
|
131
131
|
lat: null,
|
|
132
132
|
lng: null,
|
|
@@ -144,8 +144,9 @@ export default function EventForm(inProps) {
|
|
|
144
144
|
// PREFERENCES
|
|
145
145
|
const scPreferences = useSCPreferences();
|
|
146
146
|
const privateEnabled = useMemo(() => scPreferences.preferences[SCPreferences.CONFIGURATIONS_EVENTS_PRIVATE_ENABLED].value, [scPreferences.preferences]);
|
|
147
|
-
const
|
|
148
|
-
|
|
147
|
+
const visibilityEnabled = useMemo(() => scPreferences.preferences[SCPreferences.CONFIGURATIONS_EVENTS_VISIBILITY_ENABLED].value, [scPreferences.preferences]);
|
|
148
|
+
const _backgroundCover = Object.assign({}, (field.imageOriginal
|
|
149
|
+
? { background: `url('${field.imageOriginal}') center / cover` }
|
|
149
150
|
: { background: `url('${scPreferences.preferences[SCPreferences.IMAGES_USER_DEFAULT_COVER].value}') center / cover` }));
|
|
150
151
|
const combineDateAndTime = (date, time) => {
|
|
151
152
|
if (date && time) {
|
|
@@ -159,14 +160,14 @@ export default function EventForm(inProps) {
|
|
|
159
160
|
return null;
|
|
160
161
|
};
|
|
161
162
|
function handleChangeCover(cover) {
|
|
162
|
-
setField((prev) => (Object.assign(Object.assign({}, prev), { ['
|
|
163
|
+
setField((prev) => (Object.assign(Object.assign({}, prev), { ['imageOriginalFile']: cover })));
|
|
163
164
|
const reader = new FileReader();
|
|
164
165
|
reader.onloadend = () => {
|
|
165
|
-
setField((prev) => (Object.assign(Object.assign({}, prev), { ['
|
|
166
|
+
setField((prev) => (Object.assign(Object.assign({}, prev), { ['imageOriginal']: reader.result })));
|
|
166
167
|
};
|
|
167
168
|
reader.readAsDataURL(cover);
|
|
168
|
-
if (error.
|
|
169
|
-
delete error.
|
|
169
|
+
if (error.imageOriginalError) {
|
|
170
|
+
delete error.imageOriginalError;
|
|
170
171
|
setError(error);
|
|
171
172
|
}
|
|
172
173
|
}
|
|
@@ -176,8 +177,8 @@ export default function EventForm(inProps) {
|
|
|
176
177
|
const handleSubmit = () => {
|
|
177
178
|
setField((prev) => (Object.assign(Object.assign({}, prev), { ['isSubmitting']: true })));
|
|
178
179
|
const formData = new FormData();
|
|
179
|
-
if (field.
|
|
180
|
-
formData.append('
|
|
180
|
+
if (field.imageOriginalFile) {
|
|
181
|
+
formData.append('image_original', field.imageOriginalFile);
|
|
181
182
|
}
|
|
182
183
|
formData.append('name', field.name);
|
|
183
184
|
formData.append('start_date', combineDateAndTime(field.startDate, field.startTime));
|
|
@@ -197,6 +198,9 @@ export default function EventForm(inProps) {
|
|
|
197
198
|
if (privateEnabled) {
|
|
198
199
|
formData.append('privacy', field.isPublic ? SCEventPrivacyType.PUBLIC : SCEventPrivacyType.PRIVATE);
|
|
199
200
|
}
|
|
201
|
+
if (visibilityEnabled) {
|
|
202
|
+
formData.append('visible', true);
|
|
203
|
+
}
|
|
200
204
|
formData.append('description', field.description);
|
|
201
205
|
EventService.createEvent(formData, { headers: { 'Content-Type': 'multipart/form-data' } })
|
|
202
206
|
.then((data) => {
|
|
@@ -205,7 +209,15 @@ export default function EventForm(inProps) {
|
|
|
205
209
|
setField((prev) => (Object.assign(Object.assign({}, prev), { ['isSubmitting']: false })));
|
|
206
210
|
})
|
|
207
211
|
.catch((e) => {
|
|
208
|
-
|
|
212
|
+
const _error = formatHttpErrorCode(e);
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore,@typescript-eslint/ban-ts-comment
|
|
214
|
+
// @ts-ignore
|
|
215
|
+
if (Object.values(_error)[0].error === 'unique') {
|
|
216
|
+
setError(Object.assign(Object.assign({}, error), { ['nameError']: React.createElement(FormattedMessage, { id: "ui.eventForm.name.error.unique", defaultMessage: "ui.eventForm.name.error.unique" }) }));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
setError(Object.assign(Object.assign({}, error), _error));
|
|
220
|
+
}
|
|
209
221
|
setField((prev) => (Object.assign(Object.assign({}, prev), { ['isSubmitting']: false })));
|
|
210
222
|
Logger.error(SCOPE_SC_UI, e);
|
|
211
223
|
});
|
|
@@ -231,7 +243,8 @@ export default function EventForm(inProps) {
|
|
|
231
243
|
return (React.createElement(Root, Object.assign({ DialogContentProps: { dividers: false }, title: React.createElement(FormattedMessage, { id: "ui.eventForm.title", defaultMessage: "ui.eventForm.title" }), open: open, onClose: onClose, className: classNames(classes.root, className), actions: React.createElement(LoadingButton, { loading: field.isSubmitting, disabled: !field.name ||
|
|
232
244
|
(!field.startDate && !field.startTime) ||
|
|
233
245
|
(field.location === SCEventLocationType.ONLINE && !field.link) ||
|
|
234
|
-
(
|
|
246
|
+
(field.location === SCEventLocationType.PERSON && !field.geolocation) ||
|
|
247
|
+
(field.recurring !== SCEventRecurrenceType.NEVER && !field.endDate && !field.endTime) ||
|
|
235
248
|
Object.keys(error).length !== 0 ||
|
|
236
249
|
field.name.length > EVENT_TITLE_MAX_LENGTH ||
|
|
237
250
|
field.description.length > EVENT_DESCRIPTION_MAX_LENGTH, variant: "contained", onClick: handleSubmit, color: "secondary" }, React.createElement(FormattedMessage, { id: "ui.eventForm.button.create", defaultMessage: "ui.eventForm.button.create" })) }, rest),
|
|
@@ -241,7 +254,7 @@ export default function EventForm(inProps) {
|
|
|
241
254
|
React.createElement(FormGroup, { className: classes.form },
|
|
242
255
|
React.createElement(TextField, { required: true, className: classes.name, placeholder: `${intl.formatMessage(messages.name)}`, margin: "normal", value: field.name, name: "name", onChange: handleChange, InputProps: {
|
|
243
256
|
endAdornment: React.createElement(Typography, { variant: "body2" }, EVENT_TITLE_MAX_LENGTH - field.name.length)
|
|
244
|
-
}, error: Boolean(((_a = field === null || field === void 0 ? void 0 : field.name) === null || _a === void 0 ? void 0 : _a.length) > EVENT_TITLE_MAX_LENGTH), helperText: ((_b = field === null || field === void 0 ? void 0 : field.name) === null || _b === void 0 ? void 0 : _b.length) > EVENT_TITLE_MAX_LENGTH ? (React.createElement(FormattedMessage, { id: "ui.eventForm.name.error.maxLength", defaultMessage: "ui.eventForm.name.error.maxLength" })) : null }),
|
|
257
|
+
}, error: Boolean(((_a = field === null || field === void 0 ? void 0 : field.name) === null || _a === void 0 ? void 0 : _a.length) > EVENT_TITLE_MAX_LENGTH) || Boolean(error[`nameError`]), helperText: ((_b = field === null || field === void 0 ? void 0 : field.name) === null || _b === void 0 ? void 0 : _b.length) > EVENT_TITLE_MAX_LENGTH ? (React.createElement(FormattedMessage, { id: "ui.eventForm.name.error.maxLength", defaultMessage: "ui.eventForm.name.error.maxLength" })) : error[`nameError`] ? (error[`nameError`]) : null }),
|
|
245
258
|
React.createElement(Box, { className: classes.dateTime },
|
|
246
259
|
React.createElement(LocalizationProvider, { dateAdapter: AdapterDateFns, adapterLocale: scContext.settings.locale.default === 'it' ? itLocale : enLocale },
|
|
247
260
|
React.createElement(MobileDatePicker, { className: classes.picker, disablePast: true, label: field.startDate && React.createElement(FormattedMessage, { id: "ui.eventForm.date.placeholder", defaultMessage: "ui.eventForm.date.placeholder" }), value: field.startDate, slots: {
|
|
@@ -255,7 +268,7 @@ export default function EventForm(inProps) {
|
|
|
255
268
|
toolbarTitle: React.createElement(FormattedMessage, { id: "ui.eventForm.date.title", defaultMessage: "ui.eventForm.date.title" })
|
|
256
269
|
}
|
|
257
270
|
}, onChange: (value) => handleChangeDateTime(value, 'startDate') }),
|
|
258
|
-
React.createElement(MobileTimePicker, { className: classes.picker, label: field.startTime && React.createElement(FormattedMessage, { id: "ui.eventForm.time.placeholder", defaultMessage: "ui.eventForm.time.placeholder" }), value: field.startTime, slots: {
|
|
271
|
+
React.createElement(MobileTimePicker, { className: classes.picker, disablePast: true, label: field.startTime && React.createElement(FormattedMessage, { id: "ui.eventForm.time.placeholder", defaultMessage: "ui.eventForm.time.placeholder" }), value: field.startTime, slots: {
|
|
259
272
|
textField: (params) => (React.createElement(TextField, Object.assign({}, params, { InputProps: Object.assign(Object.assign({}, params.InputProps), { placeholder: `${intl.formatMessage(messages.startTime)}`, startAdornment: (React.createElement(InputAdornment, { position: "start" },
|
|
260
273
|
React.createElement(IconButton, null,
|
|
261
274
|
React.createElement(Icon, null, "access_time")))) }) })))
|
|
@@ -284,12 +297,12 @@ export default function EventForm(inProps) {
|
|
|
284
297
|
React.createElement(IconButton, null,
|
|
285
298
|
React.createElement(Icon, null, "calendar_off")))) }) })))
|
|
286
299
|
}, onChange: (value) => handleChangeDateTime(value, 'endDate') }),
|
|
287
|
-
React.createElement(MobileTimePicker, { className: classes.picker, label: field.endTime && React.createElement(FormattedMessage, { id: "ui.eventForm.time.end.placeholder", defaultMessage: "ui.eventForm.time.end.placeholder" }), value: field.endTime, slots: {
|
|
300
|
+
React.createElement(MobileTimePicker, { className: classes.picker, disablePast: true, label: field.endTime && React.createElement(FormattedMessage, { id: "ui.eventForm.time.end.placeholder", defaultMessage: "ui.eventForm.time.end.placeholder" }), value: field.endTime, slots: {
|
|
288
301
|
textField: (params) => (React.createElement(TextField, Object.assign({}, params, { InputProps: Object.assign(Object.assign({}, params.InputProps), { placeholder: `${intl.formatMessage(messages.endTime)}`, startAdornment: (React.createElement(InputAdornment, { position: "start" },
|
|
289
302
|
React.createElement(IconButton, null,
|
|
290
303
|
React.createElement(Icon, null, "access_time")))) }) })))
|
|
291
304
|
}, onChange: (value) => handleChangeDateTime(value, 'endTime') })))),
|
|
292
|
-
React.createElement(Button, { variant: "text", color: "secondary", onClick: () => setField((prev) => (Object.assign(Object.assign({}, prev), { ['showEndDateTime']: !field.showEndDateTime }))) },
|
|
305
|
+
React.createElement(Button, { variant: "text", color: "secondary", onClick: () => setField((prev) => (Object.assign(Object.assign({}, prev), { ['showEndDateTime']: !field.showEndDateTime }))), disabled: field.showEndDateTime && field.recurring !== SCEventRecurrenceType.NEVER },
|
|
293
306
|
React.createElement(FormattedMessage, { id: "ui.eventForm.dateTime.placeholder", defaultMessage: "ui.eventForm.dateTime.placeholder", values: { symbol: field.showEndDateTime ? '-' : '+' } })),
|
|
294
307
|
React.createElement(EventAddress, { forwardGeolocationData: handleGeoData }),
|
|
295
308
|
privateEnabled && (React.createElement(Box, { className: classes.privacySection },
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ButtonProps } from '@mui/material/Button/Button';
|
|
2
|
+
import { SCEventType } from '@selfcommunity/types';
|
|
3
|
+
export interface EventInviteButtonProps extends ButtonProps {
|
|
4
|
+
/**
|
|
5
|
+
* Overrides or extends the styles applied to the component.
|
|
6
|
+
* @default null
|
|
7
|
+
*/
|
|
8
|
+
className?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Event Object
|
|
11
|
+
* @default null
|
|
12
|
+
*/
|
|
13
|
+
event?: SCEventType;
|
|
14
|
+
/**
|
|
15
|
+
* Id of the event
|
|
16
|
+
* @default null
|
|
17
|
+
*/
|
|
18
|
+
eventId?: number | string;
|
|
19
|
+
/**
|
|
20
|
+
* Functions to handle invitations sending in event creation mode
|
|
21
|
+
* @default null
|
|
22
|
+
*/
|
|
23
|
+
handleInvitations?: (data: any) => any;
|
|
24
|
+
/**
|
|
25
|
+
* Any other properties
|
|
26
|
+
*/
|
|
27
|
+
[p: string]: any;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
*> API documentation for the Community-JS Event Invite Button component. Learn about the available props and the CSS API.
|
|
31
|
+
*
|
|
32
|
+
#### Import
|
|
33
|
+
```jsx
|
|
34
|
+
import {SCEventInviteButton} from '@selfcommunity/react-ui';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Component Name
|
|
38
|
+
The name `SCEventInviteButton` can be used when providing style overrides in the theme.
|
|
39
|
+
|
|
40
|
+
#### CSS
|
|
41
|
+
|
|
42
|
+
|Rule Name|Global class|Description|
|
|
43
|
+
|---|---|---|
|
|
44
|
+
|root|.SCEventInviteButton-root|Styles applied to the root element.|
|
|
45
|
+
|dialogRoot|.SCEventInviteButton-dialog-root|Styles applied to the dialog root.|
|
|
46
|
+
|dialogTitle|.SCEventInviteButton-dialog-title|Styles applied to the dialog title element.|
|
|
47
|
+
|dialogContent|.SCEventInviteButton-dialog-content|Styles applied to the dialog content.|
|
|
48
|
+
|autocomplete|.SCEventInviteButton-autocomplete|Styles applied to the autocomplete element.|
|
|
49
|
+
|icon|.SCEventInviteButton-icon|Styles applied to the autocomplete icon element.|
|
|
50
|
+
|input|.SCEventInviteButton-input|Styles applied to the autocomplete input element.|
|
|
51
|
+
|clear|.SCEventInviteButton-clear|Styles applied to the autocomplete clear icon element.|
|
|
52
|
+
|invitedBox|.SCEventInviteButton-invited-box|Styles applied to the invited users box.|
|
|
53
|
+
|suggested|.SCEventInviteButton-suggested|Styles applied to the suggested users box.|
|
|
54
|
+
|
|
55
|
+
* @param inProps
|
|
56
|
+
*/
|
|
57
|
+
export default function EventInviteButton(inProps: EventInviteButtonProps): JSX.Element;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { __rest } from "tslib";
|
|
2
|
+
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useThemeProps } from '@mui/system';
|
|
4
|
+
import { styled } from '@mui/material/styles';
|
|
5
|
+
import { Avatar, Box, Button, Chip, Icon, IconButton, InputAdornment, TextField, Typography } from '@mui/material';
|
|
6
|
+
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
7
|
+
import { SCUserContext, useSCFetchEvent } from '@selfcommunity/react-core';
|
|
8
|
+
import classNames from 'classnames';
|
|
9
|
+
import BaseDialog from '../../shared/BaseDialog';
|
|
10
|
+
import { LoadingButton } from '@mui/lab';
|
|
11
|
+
import { EventService } from '@selfcommunity/api-services';
|
|
12
|
+
import Autocomplete from '@mui/material/Autocomplete';
|
|
13
|
+
import User from '../User';
|
|
14
|
+
import { SCOPE_SC_UI } from '../../constants/Errors';
|
|
15
|
+
import { Logger } from '@selfcommunity/utils';
|
|
16
|
+
import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
|
|
17
|
+
import PubSub from 'pubsub-js';
|
|
18
|
+
const messages = defineMessages({
|
|
19
|
+
placeholder: {
|
|
20
|
+
id: 'ui.eventInviteButton.searchBar.placeholder',
|
|
21
|
+
defaultMessage: 'ui.eventInviteButton.searchBar.placeholder'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const PREFIX = 'SCEventInviteButton';
|
|
25
|
+
const classes = {
|
|
26
|
+
root: `${PREFIX}-root`,
|
|
27
|
+
dialogRoot: `${PREFIX}-dialog-root`,
|
|
28
|
+
dialogTitle: `${PREFIX}-dialog-title`,
|
|
29
|
+
dialogContent: `${PREFIX}-dialog-content`,
|
|
30
|
+
autocomplete: `${PREFIX}-autocomplete`,
|
|
31
|
+
icon: `${PREFIX}-icon`,
|
|
32
|
+
input: `${PREFIX}-input`,
|
|
33
|
+
clear: `${PREFIX}-clear`,
|
|
34
|
+
invitedBox: `${PREFIX}-invited-box`,
|
|
35
|
+
suggested: `${PREFIX}-suggested`
|
|
36
|
+
};
|
|
37
|
+
const Root = styled(Button, {
|
|
38
|
+
name: PREFIX,
|
|
39
|
+
slot: 'Root',
|
|
40
|
+
overridesResolver: (props, styles) => styles.root
|
|
41
|
+
})(({ theme }) => ({}));
|
|
42
|
+
const DialogRoot = styled(BaseDialog, {
|
|
43
|
+
name: PREFIX,
|
|
44
|
+
slot: 'Root',
|
|
45
|
+
overridesResolver: (props, styles) => styles.dialogRoot
|
|
46
|
+
})(({ theme }) => ({}));
|
|
47
|
+
/**
|
|
48
|
+
*> API documentation for the Community-JS Event Invite Button component. Learn about the available props and the CSS API.
|
|
49
|
+
*
|
|
50
|
+
#### Import
|
|
51
|
+
```jsx
|
|
52
|
+
import {SCEventInviteButton} from '@selfcommunity/react-ui';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Component Name
|
|
56
|
+
The name `SCEventInviteButton` can be used when providing style overrides in the theme.
|
|
57
|
+
|
|
58
|
+
#### CSS
|
|
59
|
+
|
|
60
|
+
|Rule Name|Global class|Description|
|
|
61
|
+
|---|---|---|
|
|
62
|
+
|root|.SCEventInviteButton-root|Styles applied to the root element.|
|
|
63
|
+
|dialogRoot|.SCEventInviteButton-dialog-root|Styles applied to the dialog root.|
|
|
64
|
+
|dialogTitle|.SCEventInviteButton-dialog-title|Styles applied to the dialog title element.|
|
|
65
|
+
|dialogContent|.SCEventInviteButton-dialog-content|Styles applied to the dialog content.|
|
|
66
|
+
|autocomplete|.SCEventInviteButton-autocomplete|Styles applied to the autocomplete element.|
|
|
67
|
+
|icon|.SCEventInviteButton-icon|Styles applied to the autocomplete icon element.|
|
|
68
|
+
|input|.SCEventInviteButton-input|Styles applied to the autocomplete input element.|
|
|
69
|
+
|clear|.SCEventInviteButton-clear|Styles applied to the autocomplete clear icon element.|
|
|
70
|
+
|invitedBox|.SCEventInviteButton-invited-box|Styles applied to the invited users box.|
|
|
71
|
+
|suggested|.SCEventInviteButton-suggested|Styles applied to the suggested users box.|
|
|
72
|
+
|
|
73
|
+
* @param inProps
|
|
74
|
+
*/
|
|
75
|
+
export default function EventInviteButton(inProps) {
|
|
76
|
+
var _a;
|
|
77
|
+
//PROPS
|
|
78
|
+
const props = useThemeProps({
|
|
79
|
+
props: inProps,
|
|
80
|
+
name: PREFIX
|
|
81
|
+
});
|
|
82
|
+
const { className, event, eventId, handleInvitations = null } = props, rest = __rest(props, ["className", "event", "eventId", "handleInvitations"]);
|
|
83
|
+
// CONTEXT
|
|
84
|
+
const scUserContext = useContext(SCUserContext);
|
|
85
|
+
// STATE
|
|
86
|
+
const [open, setOpen] = useState(false);
|
|
87
|
+
const [isSending, setIsSending] = useState(false);
|
|
88
|
+
const [value, setValue] = useState('');
|
|
89
|
+
const [suggested, setSuggested] = useState([]);
|
|
90
|
+
const [list, setList] = useState([]);
|
|
91
|
+
const [loading, setLoading] = useState(false);
|
|
92
|
+
const [invited, setInvited] = useState([]);
|
|
93
|
+
/**
|
|
94
|
+
* Notify UI when a member is invited to a event
|
|
95
|
+
* @param event
|
|
96
|
+
* @param usersInvited
|
|
97
|
+
*/
|
|
98
|
+
function notifyChanges(event, usersInvited) {
|
|
99
|
+
if (event && usersInvited) {
|
|
100
|
+
PubSub.publish(`${SCTopicType.EVENT}.${SCGroupEventType.INVITE_MEMBER}`, usersInvited);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function convertToInvitedUsersObject(data) {
|
|
104
|
+
const invite_users = {};
|
|
105
|
+
data.forEach((user, index) => {
|
|
106
|
+
invite_users[`invite_users[${index}]`] = user.id;
|
|
107
|
+
});
|
|
108
|
+
return invite_users;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Memoized users invited ids
|
|
112
|
+
*/
|
|
113
|
+
const ids = useMemo(() => {
|
|
114
|
+
if (invited) {
|
|
115
|
+
return invited.map((u) => {
|
|
116
|
+
return parseInt(u.id, 10);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return [invited];
|
|
120
|
+
}, [invited]);
|
|
121
|
+
// HOOKS
|
|
122
|
+
const { scEvent } = useSCFetchEvent({ id: eventId, event });
|
|
123
|
+
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]);
|
|
124
|
+
// INTL
|
|
125
|
+
const intl = useIntl();
|
|
126
|
+
function fetchResults() {
|
|
127
|
+
setLoading(true);
|
|
128
|
+
EventService.getEventSuggestedUsers(scEvent === null || scEvent === void 0 ? void 0 : scEvent.id, value)
|
|
129
|
+
.then((data) => {
|
|
130
|
+
setLoading(false);
|
|
131
|
+
setSuggested(data.results);
|
|
132
|
+
})
|
|
133
|
+
.catch((error) => {
|
|
134
|
+
setLoading(false);
|
|
135
|
+
Logger.error(SCOPE_SC_UI, error);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function fetchGeneralResults() {
|
|
139
|
+
setLoading(true);
|
|
140
|
+
EventService.getEventsSuggestedUsers(value)
|
|
141
|
+
.then((data) => {
|
|
142
|
+
setLoading(false);
|
|
143
|
+
setSuggested(data.results);
|
|
144
|
+
})
|
|
145
|
+
.catch((error) => {
|
|
146
|
+
setLoading(false);
|
|
147
|
+
Logger.error(SCOPE_SC_UI, error);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
if (scEvent === null || scEvent === void 0 ? void 0 : scEvent.id) {
|
|
152
|
+
EventService.getEventSuggestedUsers(scEvent === null || scEvent === void 0 ? void 0 : scEvent.id, value).then((data) => {
|
|
153
|
+
setLoading(false);
|
|
154
|
+
setList(data.results);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
EventService.getEventsSuggestedUsers(value).then((data) => {
|
|
159
|
+
setLoading(false);
|
|
160
|
+
setList(data.results);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}, [scEvent === null || scEvent === void 0 ? void 0 : scEvent.id]);
|
|
164
|
+
/**
|
|
165
|
+
* If a value is entered in new message field, it fetches user suggested
|
|
166
|
+
*/
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (scEvent) {
|
|
169
|
+
fetchResults();
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
fetchGeneralResults();
|
|
173
|
+
}
|
|
174
|
+
}, [value, scEvent]);
|
|
175
|
+
/**
|
|
176
|
+
* Handles dialog close
|
|
177
|
+
*/
|
|
178
|
+
const handleClose = () => {
|
|
179
|
+
setOpen((p) => !p);
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Handles invitation sending
|
|
183
|
+
*/
|
|
184
|
+
const handleSendInvitations = () => {
|
|
185
|
+
if (handleInvitations) {
|
|
186
|
+
handleInvitations(convertToInvitedUsersObject(invited));
|
|
187
|
+
setOpen(false);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const data = { users: ids };
|
|
191
|
+
setIsSending(true);
|
|
192
|
+
EventService.inviteOrAcceptEventRequest(scEvent.id, data)
|
|
193
|
+
.then(() => {
|
|
194
|
+
setIsSending(false);
|
|
195
|
+
setOpen(false);
|
|
196
|
+
setInvited([]);
|
|
197
|
+
notifyChanges(scEvent, invited);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
setOpen(false);
|
|
201
|
+
setLoading(false);
|
|
202
|
+
Logger.error(SCOPE_SC_UI, error);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
// Autocomplete Handlers
|
|
207
|
+
const handleInputChange = (event, value, reason) => {
|
|
208
|
+
switch (reason) {
|
|
209
|
+
case 'input':
|
|
210
|
+
setValue(value);
|
|
211
|
+
!value && setSuggested([]);
|
|
212
|
+
break;
|
|
213
|
+
case 'reset':
|
|
214
|
+
setValue(value);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const handleChange = (event, value, reason, details) => {
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
event.stopPropagation();
|
|
221
|
+
switch (reason) {
|
|
222
|
+
case 'selectOption':
|
|
223
|
+
setInvited(value);
|
|
224
|
+
setList((prev) => prev.filter((u) => u.id !== details.option.id));
|
|
225
|
+
break;
|
|
226
|
+
case 'removeOption':
|
|
227
|
+
setInvited(value);
|
|
228
|
+
setList((prev) => [...prev, details.option]);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
};
|
|
233
|
+
const handleUserInvite = (user) => {
|
|
234
|
+
setInvited((prev) => [...prev, user]);
|
|
235
|
+
setList((prev) => prev.filter((u) => u.id !== user.id));
|
|
236
|
+
};
|
|
237
|
+
const handleDelete = (option) => {
|
|
238
|
+
setInvited(invited.filter((v) => v !== option));
|
|
239
|
+
setList((prev) => [...prev, option]);
|
|
240
|
+
};
|
|
241
|
+
const filterOptions = (options, { inputValue }) => {
|
|
242
|
+
return options.filter((option) => {
|
|
243
|
+
const usernameMatch = option.username.toLowerCase().includes(inputValue.toLowerCase());
|
|
244
|
+
const nameMatch = option.real_name.toLowerCase().includes(inputValue.toLowerCase());
|
|
245
|
+
return usernameMatch || nameMatch;
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* If in event edit mode and logged-in user is not also the event manager, the component is hidden.
|
|
250
|
+
// */
|
|
251
|
+
if (event && !isEventAdmin) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Renders root object
|
|
256
|
+
*/
|
|
257
|
+
return (React.createElement(React.Fragment, null,
|
|
258
|
+
React.createElement(Root, Object.assign({ className: classNames(classes.root, className), onClick: handleClose, variant: scEvent ? 'contained' : 'outlined', color: scEvent ? 'secondary' : 'inherit', startIcon: React.createElement(Icon, null, "add") }, rest),
|
|
259
|
+
React.createElement(FormattedMessage, { id: "ui.eventInviteButton", defaultMessage: "ui.eventInviteButton" })),
|
|
260
|
+
open && (React.createElement(DialogRoot, { DialogContentProps: { dividers: false }, open: true, className: classes.dialogRoot, title: React.createElement(React.Fragment, null,
|
|
261
|
+
React.createElement(IconButton, { onClick: handleClose },
|
|
262
|
+
React.createElement(Icon, null, "arrow_back")),
|
|
263
|
+
React.createElement(Typography, { className: classes.dialogTitle },
|
|
264
|
+
React.createElement(FormattedMessage, { id: "ui.eventInviteButton.dialog.title", defaultMessage: "ui.eventInviteButton.dialog.title" })),
|
|
265
|
+
React.createElement(LoadingButton, { size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending, disabled: !invited.length },
|
|
266
|
+
React.createElement(FormattedMessage, { id: "ui.eventInviteButton.dialog.button.end", defaultMessage: "ui.eventInviteButton.dialog.button.end" }))) },
|
|
267
|
+
React.createElement(Box, { className: classes.dialogContent },
|
|
268
|
+
React.createElement(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) => (React.createElement(Box, Object.assign({ component: "li" }, props),
|
|
269
|
+
React.createElement(Avatar, { alt: option.username, src: option.avatar }),
|
|
270
|
+
React.createElement(Typography, { ml: 1 }, option.username))), renderInput: (params) => (React.createElement(TextField, Object.assign({}, params, { variant: "outlined", placeholder: `${intl.formatMessage(messages.placeholder)}`, InputProps: Object.assign(Object.assign({}, params.InputProps), { className: classes.input, startAdornment: (React.createElement(React.Fragment, null,
|
|
271
|
+
React.createElement(InputAdornment, { position: "start" },
|
|
272
|
+
React.createElement(Icon, { className: classes.icon }, "search")),
|
|
273
|
+
params.InputProps.startAdornment)) }) }))) }),
|
|
274
|
+
React.createElement(Box, { className: classes.invitedBox }, invited.map((option, index) => (React.createElement(Chip, { key: index, avatar: React.createElement(Avatar, { alt: option.username, src: option.avatar }), label: option.username, onDelete: () => {
|
|
275
|
+
handleDelete(option);
|
|
276
|
+
}, style: { marginRight: 8 } })))),
|
|
277
|
+
React.createElement(Box, { className: classes.suggested },
|
|
278
|
+
list.length !== 0 && (React.createElement(Typography, { variant: "h4", fontWeight: "bold" },
|
|
279
|
+
React.createElement(FormattedMessage, { id: "ui.eventInviteButton.dialog.content.list", defaultMessage: "ui.eventInviteButton.dialog.content.list" }))),
|
|
280
|
+
list.slice(0, 5).map((user, index) => (React.createElement(User, { elevation: 0, actions: React.createElement(React.Fragment, null), user: user, userId: user.id, key: index, buttonProps: { onClick: () => handleUserInvite(user) } })))))))));
|
|
281
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EventProps } from '../Event';
|
|
2
|
+
export interface EventsProps {
|
|
3
|
+
/**
|
|
4
|
+
* Overrides or extends the styles applied to the component.
|
|
5
|
+
* @default null
|
|
6
|
+
*/
|
|
7
|
+
className?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Feed API Query Params
|
|
10
|
+
* @default [{'limit': 20, 'offset': 0}]
|
|
11
|
+
*/
|
|
12
|
+
endpointQueryParams?: Record<string, string | number>;
|
|
13
|
+
/**
|
|
14
|
+
* Props to spread to single event object
|
|
15
|
+
* @default {}
|
|
16
|
+
*/
|
|
17
|
+
EventComponentProps?: EventProps;
|
|
18
|
+
/**
|
|
19
|
+
* Show/Hide filters
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
showFilters?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Filters component
|
|
25
|
+
* @param props
|
|
26
|
+
*/
|
|
27
|
+
filters?: JSX.Element;
|
|
28
|
+
/** If true, it means that the endpoint fetches all events available
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
general?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Other props
|
|
34
|
+
*/
|
|
35
|
+
[p: string]: any;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* > API documentation for the Community-JS Events component. Learn about the available props and the CSS API.
|
|
39
|
+
*
|
|
40
|
+
*
|
|
41
|
+
* The Events component renders the list of all available events.
|
|
42
|
+
* Take a look at our <strong>demo</strong> component [here](/docs/sdk/community-js/react-ui/Components/Events)
|
|
43
|
+
|
|
44
|
+
#### Import
|
|
45
|
+
```jsx
|
|
46
|
+
import {Events} from '@selfcommunity/react-ui';
|
|
47
|
+
```
|
|
48
|
+
#### Component Name
|
|
49
|
+
The name `SCEvents` can be used when providing style overrides in the theme.
|
|
50
|
+
|
|
51
|
+
#### CSS
|
|
52
|
+
|
|
53
|
+
|Rule Name|Global class|Description|
|
|
54
|
+
|---|---|---|
|
|
55
|
+
|root|.SCEvents-root|Styles applied to the root element.|
|
|
56
|
+
|filters|.SCEvents-filters|Styles applied to the title element.|
|
|
57
|
+
|events|.SCEvents-events|Styles applied to the title element.|
|
|
58
|
+
|item|.SCEvents-item|Styles applied to the title element.|
|
|
59
|
+
|noResults|.SCEvents-no-results|Styles applied to no results section.|
|
|
60
|
+
|showMore|.SCEvents-show-more|Styles applied to show more button element.|
|
|
61
|
+
|
|
62
|
+
* @param inProps
|
|
63
|
+
*/
|
|
64
|
+
export default function Events(inProps: EventsProps): JSX.Element;
|