From 02027da02b46ec49e540f2881ae9e0d3ab8af76f Mon Sep 17 00:00:00 2001 From: sylenien Date: Tue, 6 Dec 2022 16:31:20 +0100 Subject: [PATCH] change(ui): set up msteams for share popup and note creation --- frontend/app/Router.js | 6 + frontend/app/api_client.js | 1 + frontend/app/components/Alerts/AlertForm.js | 27 ++- frontend/app/components/Alerts/AlertItem.js | 4 +- .../Integrations/Teams/TeamsAddForm.tsx | 15 +- .../Client/Integrations/Teams/index.tsx | 4 +- .../components/Client/Webhooks/Webhooks.js | 2 +- .../Alerts/AlertForm/NotifyHooks.tsx | 24 ++- .../components/Alerts/AlertListItem.tsx | 3 + .../Player/Controls/components/CreateNote.tsx | 115 +++++++++---- .../Controls/components/styles.module.css | 3 +- .../IntegrateSlackButton.js | 19 +- .../shared/SharePopup/SharePopup.js | 162 ++++++++++++------ .../shared/SharePopup/sharePopup.module.css | 7 +- frontend/app/components/ui/Message/Message.js | 2 +- frontend/app/components/ui/SVG.tsx | 3 +- frontend/app/duck/integrations/slack.js | 12 +- frontend/app/duck/integrations/teams.js | 11 ++ frontend/app/mstore/notesStore.ts | 9 + frontend/app/services/NotesService.ts | 11 ++ .../svg/icons/integrations/teams-white.svg | 4 + 21 files changed, 328 insertions(+), 116 deletions(-) create mode 100644 frontend/app/svg/icons/integrations/teams-white.svg diff --git a/frontend/app/Router.js b/frontend/app/Router.js index d1be5a569..86d878a1c 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -195,6 +195,12 @@ class Router extends React.Component { state: tenantId, }); break; + case '/integrations/msteams': + client.post('integrations/msteams/add', { + code: location.search.split('=')[1], + state: tenantId, + }); + break; } return ; }} diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index 0e4699359..8985d0686 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -11,6 +11,7 @@ const siteIdRequiredPaths = [ '/metadata', '/integrations/sentry/events', '/integrations/slack/notify', + '/integrations/msteams/notify', '/assignments', '/integration/sources', '/issue_types', diff --git a/frontend/app/components/Alerts/AlertForm.js b/frontend/app/components/Alerts/AlertForm.js index 6604574e0..9046b4069 100644 --- a/frontend/app/components/Alerts/AlertForm.js +++ b/frontend/app/components/Alerts/AlertForm.js @@ -1,6 +1,5 @@ import React, { useEffect } from 'react'; -import { Button, Form, Input, SegmentSelection, Checkbox, Message, Link, Icon } from 'UI'; -import { alertMetrics as metrics } from 'App/constants'; +import { Button, Form, Input, SegmentSelection, Checkbox, Icon } from 'UI'; import { alertConditions as conditions } from 'App/constants'; import { client, CLIENT_TABS } from 'App/routes'; import { connect } from 'react-redux'; @@ -47,12 +46,12 @@ const AlertForm = (props) => { const { instance, slackChannels, + msTeamsChannels, webhooks, loading, onDelete, deleting, triggerOptions, - metricId, style = { width: '580px', height: '100vh' }, } = props; const write = ({ target: { value, name } }) => props.edit({ [name]: value }); @@ -241,6 +240,14 @@ const AlertForm = (props) => { onClick={onChangeCheck} label="Slack" /> + { )} + {instance.msteams && ( +
+ +
+ props.edit({ msTeamsInput: selected })} + /> +
+
+ )} {instance.email && (
diff --git a/frontend/app/components/Alerts/AlertItem.js b/frontend/app/components/Alerts/AlertItem.js index 9dbb204b8..76431bf77 100644 --- a/frontend/app/components/Alerts/AlertItem.js +++ b/frontend/app/components/Alerts/AlertItem.js @@ -17,6 +17,8 @@ const AlertItem = props => { const getNotifyChannel = alert => { let str = ''; + if (alert.msteams) + str = 'MS Teams' if (alert.slack) str = 'Slack'; if (alert.email) @@ -36,7 +38,7 @@ const AlertItem = props => { className={cn(stl.wrapper, 'p-4 py-6 relative group cursor-pointer', { [stl.active]: active })} onClick={onEdit} id="alert-item" - > + >
{alert.name}
diff --git a/frontend/app/components/Client/Integrations/Teams/TeamsAddForm.tsx b/frontend/app/components/Client/Integrations/Teams/TeamsAddForm.tsx index 018a5f0ca..04b8e9451 100644 --- a/frontend/app/components/Client/Integrations/Teams/TeamsAddForm.tsx +++ b/frontend/app/components/Client/Integrations/Teams/TeamsAddForm.tsx @@ -10,6 +10,7 @@ interface Props { init: (inst: any) => void; update: (inst: any) => void; remove: (id: string) => void; + onClose: () => void; instance: any; saving: boolean; errors: any; @@ -29,7 +30,7 @@ class TeamsAddForm extends React.PureComponent { } }; - remove = async (id) => { + remove = async (id: string) => { if ( await confirm({ header: 'Confirm', @@ -41,7 +42,7 @@ class TeamsAddForm extends React.PureComponent { } }; - write = ({ target: { name, value } }) => this.props.edit({ [name]: value }); + write = ({ target: { name, value } }: { target: { name: string, value: string }}) => this.props.edit({ [name]: value }); render() { const { instance, saving, errors, onClose } = this.props; @@ -91,8 +92,8 @@ class TeamsAddForm extends React.PureComponent { {errors && (
- {errors.map((error) => ( - + {errors.map((error: any) => ( + {error} ))} @@ -105,9 +106,9 @@ class TeamsAddForm extends React.PureComponent { export default connect( (state: any) => ({ - instance: state.getIn(['slack', 'instance']), - saving: state.getIn(['slack', 'saveRequest', 'loading']), - errors: state.getIn(['slack', 'saveRequest', 'errors']), + instance: state.getIn(['teams', 'instance']), + saving: state.getIn(['teams', 'saveRequest', 'loading']), + errors: state.getIn(['teams', 'saveRequest', 'errors']), }), { edit, save, init, remove, update } )(TeamsAddForm); diff --git a/frontend/app/components/Client/Integrations/Teams/index.tsx b/frontend/app/components/Client/Integrations/Teams/index.tsx index d5fdba6fc..4814697b8 100644 --- a/frontend/app/components/Client/Integrations/Teams/index.tsx +++ b/frontend/app/components/Client/Integrations/Teams/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import TeamsChannelList from './TeamsChannelList'; import { fetchList, init } from 'Duck/integrations/teams'; import { connect } from 'react-redux'; -import SlackAddForm from './SlackAddForm'; +import TeamsAddForm from './TeamsAddForm'; import { Button } from 'UI'; interface Props { @@ -31,7 +31,7 @@ const MSTeams = (props: Props) => {
{active && (
- setActive(false)} /> + setActive(false)} />
)}
diff --git a/frontend/app/components/Client/Webhooks/Webhooks.js b/frontend/app/components/Client/Webhooks/Webhooks.js index e005a893a..a87ac2298 100644 --- a/frontend/app/components/Client/Webhooks/Webhooks.js +++ b/frontend/app/components/Client/Webhooks/Webhooks.js @@ -16,7 +16,7 @@ function Webhooks(props) { const { webhooks, loading } = props; const { showModal, hideModal } = useModal(); - const noSlackWebhooks = webhooks.filter((hook) => hook.type !== 'slack'); + const noSlackWebhooks = webhooks.filter((hook) => hook.type === 'webhook'); useEffect(() => { props.fetchList(); }, []); diff --git a/frontend/app/components/Dashboard/components/Alerts/AlertForm/NotifyHooks.tsx b/frontend/app/components/Dashboard/components/Alerts/AlertForm/NotifyHooks.tsx index 921c7ba9b..76d99dc6b 100644 --- a/frontend/app/components/Dashboard/components/Alerts/AlertForm/NotifyHooks.tsx +++ b/frontend/app/components/Dashboard/components/Alerts/AlertForm/NotifyHooks.tsx @@ -6,6 +6,7 @@ interface INotifyHooks { instance: Alert; onChangeCheck: (e: React.ChangeEvent) => void; slackChannels: Array; + msTeamsChannels: Array; validateEmail: (value: string) => boolean; edit: (data: any) => void; hooks: Array; @@ -16,6 +17,7 @@ function NotifyHooks({ onChangeCheck, slackChannels, validateEmail, + msTeamsChannels, hooks, edit, }: INotifyHooks) { @@ -49,7 +51,7 @@ function NotifyHooks({ {instance.slack && (
- +
)} + {instance.msteams && ( +
+ +
+ edit({ msteamsInput: selected })} + /> +
+
+ )} + {instance.email && (
- +
- +
, webhooks: Array) => { str = 'Slack'; str += alert.slackInput.length > 0 ? getSlackChannels() : ''; } + if (alert.msteams) { + str = 'MS Teams' + } if (alert.email) { str += (str === '' ? '' : ' and ') + (alert.emailInput.length > 1 ? 'Emails' : 'Email'); str += alert.emailInput.length > 0 ? ' (' + alert.emailInput.join(', ') + ')' : ''; diff --git a/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx b/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx index 8ac9d93f4..68f510de6 100644 --- a/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx +++ b/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx @@ -8,8 +8,10 @@ import stl from './styles.module.css'; import { useStore } from 'App/mstore'; import { toast } from 'react-toastify'; import { fetchList as fetchSlack } from 'Duck/integrations/slack'; +import { fetchList as fetchTeams } from 'Duck/integrations/teams'; + import Select from 'Shared/Select'; -import { TeamBadge } from 'Shared/SessionListContainer/components/Notes' +import { TeamBadge } from 'Shared/SessionListContainer/components/Notes'; import { List } from 'immutable'; interface Props { @@ -22,7 +24,9 @@ interface Props { isEdit: string; editNote: WriteNote; slackChannels: List>; + teamsChannels: List>; fetchSlack: () => void; + fetchTeams: () => void; } function CreateNote({ @@ -36,12 +40,18 @@ function CreateNote({ updateNote, slackChannels, fetchSlack, + teamsChannels, + fetchTeams, }: Props) { const [text, setText] = React.useState(''); - const [channel, setChannel] = React.useState(''); + const [slackChannel, setSlackChannel] = React.useState(''); + const [teamsChannel, setTeamsChannel] = React.useState(''); const [isPublic, setPublic] = React.useState(false); const [tag, setTag] = React.useState(TAGS[0]); const [useTimestamp, setUseTs] = React.useState(true); + const [useSlack, setSlack] = React.useState(false); + const [useTeams, setTeams] = React.useState(false); + const inputRef = React.createRef(); const { notesStore } = useStore(); @@ -59,6 +69,7 @@ function CreateNote({ React.useEffect(() => { if (inputRef.current && isVisible) { fetchSlack(); + fetchTeams(); inputRef.current.focus(); } }, [isVisible]); @@ -75,17 +86,20 @@ function CreateNote({ isPublic, }; const onSuccess = (noteId: string) => { - if (channel) { - notesStore.sendSlackNotification(noteId, channel) + if (slackChannel) { + notesStore.sendSlackNotification(noteId, slackChannel); } - } + if (teamsChannel) { + notesStore.sendMsTeamsNotification(noteId, teamsChannel); + } + }; if (isEdit) { return notesStore .updateNote(editNote.noteId, note) .then((r) => { toast.success('Note updated'); notesStore.fetchSessionNotes(sessionId).then((notes) => { - onSuccess(editNote.noteId) + onSuccess(editNote.noteId); updateNote(r); }); }) @@ -103,7 +117,7 @@ function CreateNote({ return notesStore .addNote(sessionId, note) .then((r) => { - onSuccess(r.noteId as unknown as string) + onSuccess(r.noteId as unknown as string); toast.success('Note added'); notesStore.fetchSessionNotes(sessionId).then((notes) => { addNote(r); @@ -130,27 +144,41 @@ function CreateNote({ setTag(tag); }; - const slackChannelsOptions = slackChannels.map(({ webhookId, name }) => ({ - value: webhookId, - label: name, - })).toJS() as unknown as { value: string, label: string }[] + const slackChannelsOptions = slackChannels + .map(({ webhookId, name }) => ({ + value: webhookId, + label: name, + })) + .toJS() as unknown as { value: string; label: string }[]; + const teamsChannelsOptions = teamsChannels + .map(({ webhookId, name }) => ({ + value: webhookId, + label: name, + })) + .toJS() as unknown as { value: string; label: string }[]; - slackChannelsOptions.unshift({ value: null, label: 'Share to slack?' }) + slackChannelsOptions.unshift({ value: null, label: 'Pick a channel' }); + teamsChannelsOptions.unshift({ value: null, label: 'Pick a channel' }); - const changeChannel = ({ value, name }: { value: Record; name: string }) => { - setChannel(value.value); + const changeSlackChannel = ({ value, name }: { value: Record; name: string }) => { + setSlackChannel(value.value); + }; + + const changeTeamsChannel = ({ value, name }: { value: Record; name: string }) => { + setTeamsChannel(value.value); }; return (
0 ? -310 : 255, width: 350, left: 'calc(50% - 175px)', display: isVisible ? 'flex' : 'none', flexDirection: 'column', gap: '1rem', + bottom: '15vh', + zIndex: 110, }} onClick={(e) => e.stopPropagation()} > @@ -206,15 +234,44 @@ function CreateNote({
{slackChannelsOptions.length > 0 ? ( -
- +
+ )} +
+ ) : null} + + {teamsChannelsOptions.length > 0 ? ( +
+
setTeams(!useTeams)}> + + Send to teams? +
+ + {useTeams && ( +
+