change(ui): set up msteams for share popup and note creation

This commit is contained in:
sylenien 2022-12-06 16:31:20 +01:00 committed by Delirium
parent 1687b5031a
commit 02027da02b
21 changed files with 328 additions and 116 deletions

View file

@ -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 <Redirect to={CLIENT_PATH} />;
}}

View file

@ -11,6 +11,7 @@ const siteIdRequiredPaths = [
'/metadata',
'/integrations/sentry/events',
'/integrations/slack/notify',
'/integrations/msteams/notify',
'/assignments',
'/integration/sources',
'/issue_types',

View file

@ -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"
/>
<Checkbox
name="msteams"
className="mr-8"
type="checkbox"
checked={instance.msteams}
onClick={onChangeCheck}
label="MS Teams"
/>
<Checkbox
name="email"
type="checkbox"
@ -266,6 +273,20 @@ const AlertForm = (props) => {
</div>
</div>
)}
{instance.msteams && (
<div className="flex items-start my-4">
<label className="w-2/6 flex-shrink-0 font-normal pt-2">{'MS Teams'}</label>
<div className="w-4/6">
<DropdownChips
fluid
selected={instance.msTeamsInput}
options={msTeamsChannels}
placeholder="Select Channel"
onChange={(selected) => props.edit({ msTeamsInput: selected })}
/>
</div>
</div>
)}
{instance.email && (
<div className="flex items-start my-4">

View file

@ -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"
>
>
<AlertTypeLabel type={alert.detectionMethod} />
<div className="capitalize font-medium">{alert.name}</div>
<div className="mt-2 text-sm color-gray-medium">

View file

@ -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<Props> {
}
};
remove = async (id) => {
remove = async (id: string) => {
if (
await confirm({
header: 'Confirm',
@ -41,7 +42,7 @@ class TeamsAddForm extends React.PureComponent<Props> {
}
};
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<Props> {
{errors && (
<div className="my-3">
{errors.map((error) => (
<Message visible={errors} size="mini" error key={error}>
{errors.map((error: any) => (
<Message visible={errors} key={error}>
{error}
</Message>
))}
@ -105,9 +106,9 @@ class TeamsAddForm extends React.PureComponent<Props> {
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);

View file

@ -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) => {
<div className="bg-white h-screen overflow-y-auto flex items-start" style={{ width: active ? '700px' : '350px' }}>
{active && (
<div className="border-r h-full" style={{ width: '350px' }}>
<SlackAddForm onClose={() => setActive(false)} />
<TeamsAddForm onClose={() => setActive(false)} />
</div>
)}
<div className="shrink-0" style={{ width: '350px' }}>

View file

@ -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();
}, []);

View file

@ -6,6 +6,7 @@ interface INotifyHooks {
instance: Alert;
onChangeCheck: (e: React.ChangeEvent<HTMLInputElement>) => void;
slackChannels: Array<any>;
msTeamsChannels: Array<any>;
validateEmail: (value: string) => boolean;
edit: (data: any) => void;
hooks: Array<any>;
@ -16,6 +17,7 @@ function NotifyHooks({
onChangeCheck,
slackChannels,
validateEmail,
msTeamsChannels,
hooks,
edit,
}: INotifyHooks) {
@ -49,7 +51,7 @@ function NotifyHooks({
{instance.slack && (
<div className="flex items-start my-4">
<label className="w-1/6 flex-shrink-0 font-normal pt-2">{'Slack'}</label>
<label className="w-1/6 flex-shrink-0 font-normal pt-2">Slack</label>
<div className="w-2/6">
<DropdownChips
fluid
@ -63,9 +65,25 @@ function NotifyHooks({
</div>
)}
{instance.msteams && (
<div className="flex items-start my-4">
<label className="w-1/6 flex-shrink-0 font-normal pt-2">MS Teams</label>
<div className="w-2/6">
<DropdownChips
fluid
selected={instance.msteamsInput}
options={msTeamsChannels}
placeholder="Select Channel"
// @ts-ignore
onChange={(selected) => edit({ msteamsInput: selected })}
/>
</div>
</div>
)}
{instance.email && (
<div className="flex items-start my-4">
<label className="w-1/6 flex-shrink-0 font-normal pt-2">{'Email'}</label>
<label className="w-1/6 flex-shrink-0 font-normal pt-2">Email</label>
<div className="w-2/6">
<DropdownChips
textFiled
@ -81,7 +99,7 @@ function NotifyHooks({
{instance.webhook && (
<div className="flex items-start my-4">
<label className="w-1/6 flex-shrink-0 font-normal pt-2">{'Webhook'}</label>
<label className="w-1/6 flex-shrink-0 font-normal pt-2">Webhook</label>
<div className="w-2/6">
<DropdownChips
fluid

View file

@ -41,6 +41,9 @@ const getNotifyChannel = (alert: Record<string, any>, webhooks: Array<any>) => {
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(', ') + ')' : '';

View file

@ -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<Record<string, any>>;
teamsChannels: List<Record<string, any>>;
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<iTag>(TAGS[0]);
const [useTimestamp, setUseTs] = React.useState(true);
const [useSlack, setSlack] = React.useState(false);
const [useTeams, setTeams] = React.useState(false);
const inputRef = React.createRef<HTMLTextAreaElement>();
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<string, string>; name: string }) => {
setChannel(value.value);
const changeSlackChannel = ({ value, name }: { value: Record<string, string>; name: string }) => {
setSlackChannel(value.value);
};
const changeTeamsChannel = ({ value, name }: { value: Record<string, string>; name: string }) => {
setTeamsChannel(value.value);
};
return (
<div
className={stl.noteTooltip}
style={{
top: slackChannelsOptions.length > 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({
</div>
{slackChannelsOptions.length > 0 ? (
<div>
<Select
options={slackChannelsOptions}
// @ts-ignore
defaultValue
// @ts-ignore
onChange={changeChannel}
className="mr-4"
/>
<div className="flex flex-col">
<div className="flex items-center cursor-pointer" onClick={() => setSlack(!useSlack)}>
<Checkbox checked={useSlack} />
<span className="ml-1 mr-3"> Send to slack? </span>
</div>
{useSlack && (
<div>
<Select
options={slackChannelsOptions}
// @ts-ignore
defaultValue
// @ts-ignore
onChange={changeSlackChannel}
/>
</div>
)}
</div>
) : null}
{teamsChannelsOptions.length > 0 ? (
<div className="flex flex-col">
<div className="flex items-center cursor-pointer" onClick={() => setTeams(!useTeams)}>
<Checkbox checked={useTeams} />
<span className="ml-1 mr-3"> Send to teams? </span>
</div>
{useTeams && (
<div>
<Select
options={teamsChannelsOptions}
// @ts-ignore
defaultValue
// @ts-ignore
onChange={changeTeamsChannel}
/>
</div>
)}
</div>
) : null}
@ -232,19 +289,17 @@ function CreateNote({
}
export default connect(
(state) => {
(state: any) => {
const {
isVisible,
time = 0,
isEdit,
note: editNote,
// @ts-ignore
} = state.getIn(['sessions', 'createNoteTooltip']);
// @ts-ignore
const slackChannels = state.getIn(['slack', 'list']);
// @ts-ignore
const teamsChannels = state.getIn(['teams', 'list']);
const sessionId = state.getIn(['sessions', 'current', 'sessionId']);
return { isVisible, time, sessionId, isEdit, editNote, slackChannels };
return { isVisible, time, sessionId, isEdit, editNote, slackChannels, teamsChannels };
},
{ setCreateNoteTooltip, addNote, updateNote, fetchSlack }
{ setCreateNoteTooltip, addNote, updateNote, fetchSlack, fetchTeams }
)(CreateNote);

View file

@ -25,14 +25,13 @@
}
.noteTooltip {
position: absolute;
position: fixed;
padding: 1rem;
border-radius: 0.25rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
background: #F5F5F5;
top: -35px;
color: black;
cursor: default;
box-shadow: 0 4px 20px 4px rgb(0 20 60 / 10%), 0 4px 80px -8px rgb(0 20 60 / 20%);

View file

@ -1,26 +1,29 @@
import React from 'react'
import { connect } from 'react-redux'
import { IconButton } from 'UI'
import { Button, Icon } from 'UI'
import { CLIENT_TABS, client as clientRoute } from 'App/routes';
import { withRouter } from 'react-router-dom';
function IntegrateSlackButton({ history, tenantId }) {
function IntegrateSlackTeamsButton({ history, tenantId }) {
const gotoPreferencesIntegrations = () => {
history.push(clientRoute(CLIENT_TABS.INTEGRATIONS));
}
return (
<div>
<IconButton
className="my-auto mt-2 mb-2"
icon="integrations/slack"
label="Integrate Slack"
<Button
className="my-auto mt-2 mb-2 flex items-center gap-2"
onClick={gotoPreferencesIntegrations}
/>
>
<Icon name="integrations/slack" size={16} />
<Icon name="integrations/teams" size={18} className="mr-2" />
<span>Integrate Slack/MS Teams</span>
</Button>
</div>
)
}
export default withRouter(connect(state => ({
tenantId: state.getIn([ 'user', 'account', 'tenantId' ]),
}))(IntegrateSlackButton))
}))(IntegrateSlackTeamsButton))

View file

@ -1,45 +1,68 @@
import React from 'react';
import { connect } from 'react-redux';
import { toast } from 'react-toastify';
import withRequest from 'HOCs/withRequest';
import { Icon, Button, Popover } from 'UI';
import styles from './sharePopup.module.css';
import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton';
import SessionCopyLink from './SessionCopyLink';
import Select from 'Shared/Select';
import cn from 'classnames';
import { fetchList } from 'Duck/integrations/slack';
import { fetchList as fetchSlack, sendSlackMsg } from 'Duck/integrations/slack';
import { fetchList as fetchTeams, sendMsTeamsMsg } from 'Duck/integrations/teams';
@connect(
(state) => ({
sessionId: state.getIn(['sessions', 'current', 'sessionId']),
channels: state.getIn(['slack', 'list']),
msTeamsChannels: state.getIn(['teams', 'list']),
tenantId: state.getIn(['user', 'account', 'tenantId']),
}),
{ fetchList }
{ fetchSlack, fetchTeams, sendSlackMsg, sendMsTeamsMsg }
)
@withRequest({
endpoint: ({ id, entity }, integrationId) =>
`/integrations/slack/notify/${integrationId}/${entity}/${id}`,
method: 'POST',
})
export default class SharePopup extends React.PureComponent {
state = {
comment: '',
isOpen: false,
channelId: this.props.channels.getIn([0, 'webhookId']),
teamsChannel: this.props.msTeamsChannels.getIn([0, 'webhookId']),
loading: false,
};
componentDidMount() {
if (this.props.channels.size === 0) {
this.props.fetchList();
this.props.fetchSlack();
}
if (this.props.msTeamsChannels.size === 0) {
this.props.fetchTeams();
}
}
editMessage = (e) => this.setState({ comment: e.target.value });
share = () =>
this.props
.request({ comment: this.state.comment }, this.state.channelId)
.then(this.handleSuccess);
shareToSlack = () => {
this.setState({ loading: true }, () => {
this.props
.sendSlackMsg({
integrationId: this.state.channelId,
entity: 'sessions',
entityId: this.props.sessionId,
data: { comment: this.state.comment },
})
.then(() => this.handleSuccess('Slack'));
});
};
shareToMSTeams = () => {
this.setState({ loading: true }, () => {
this.props
.sendMsTeamsMsg({
integrationId: this.state.teamsChannel,
entity: 'sessions',
entityId: this.props.sessionId,
data: { comment: this.state.comment },
})
.then(() => this.handleSuccess('MS Teams'));
});
};
handleOpen = () => {
setTimeout(function () {
@ -51,22 +74,28 @@ export default class SharePopup extends React.PureComponent {
this.setState({ comment: '' });
};
handleSuccess = () => {
this.setState({ isOpen: false, comment: '' });
toast.success('Sent to Slack.');
handleSuccess = (endpoint) => {
this.setState({ isOpen: false, comment: '', loading: false });
toast.success(`Sent to ${endpoint}.`);
};
changeChannel = ({ value }) => this.setState({ channelId: value.value });
changeSlackChannel = ({ value }) => this.setState({ channelId: value.value });
changeTeamsChannel = ({ value }) => this.setState({ teamsChannel: value.value });
onClickHandler = () => {
this.setState({ isOpen: true });
};
render() {
const { trigger, loading, channels, showCopyLink = false } = this.props;
const { comment, channelId, isOpen } = this.state;
const { trigger, channels, msTeamsChannels, showCopyLink = false } = this.props;
const { comment, channelId, teamsChannel, loading } = this.state;
const options = channels
const slackOptions = channels
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
.toJS();
const msTeamsOptions = msTeamsChannels
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
.toJS();
@ -75,20 +104,11 @@ export default class SharePopup extends React.PureComponent {
render={() => (
<div className={styles.wrapper}>
<div className={styles.header}>
<div className={cn(styles.title, 'text-lg')}>Share this session link to Slack</div>
<div className={cn(styles.title, 'text-lg')}>
Share this session link to Slack/MS Teams
</div>
</div>
{options.length === 0 ? (
<>
<div className={styles.body}>
<IntegrateSlackButton />
</div>
{showCopyLink && (
<div className={styles.footer}>
<SessionCopyLink />
</div>
)}
</>
) : (
{slackOptions.length > 0 || msTeamsOptions.length > 0 ? (
<div>
<div className={styles.body}>
<textarea
@ -100,30 +120,72 @@ export default class SharePopup extends React.PureComponent {
onChange={this.editMessage}
value={comment}
placeholder="Add Message (Optional)"
className="p-4"
className="p-4 text-figmaColors-text-primary text-base"
/>
<div className="flex items-center justify-between">
<Select
options={options}
defaultValue={channelId}
onChange={this.changeChannel}
className="mr-4"
/>
<div>
<Button onClick={this.share} variant="primary">
<div className="flex items-center">
<Icon name="integrations/slack-bw" size="18" marginRight="10" />
{loading ? 'Sending...' : 'Send'}
</div>
</Button>
</div>
</div>
{slackOptions.length > 0 && (
<>
<span>Share to slack</span>
<div className="flex items-center justify-between mb-2">
<Select
options={slackOptions}
defaultValue={channelId}
onChange={this.changeSlackChannel}
className="mr-4"
/>
{this.state.channelId && (
<Button onClick={this.shareToSlack} variant="primary">
<div className="flex items-center">
<Icon name="integrations/slack-bw" color="white" size="18" marginRight="10" />
{loading ? 'Sending...' : 'Send'}
</div>
</Button>
)}
</div>
</>
)}
{msTeamsOptions.length > 0 && (
<>
<span>Share to MS Teams</span>
<div className="flex items-center justify-between">
<Select
options={msTeamsOptions}
defaultValue={teamsChannel}
onChange={this.changeTeamsChannel}
className="mr-4"
/>
{this.state.teamsChannel && (
<Button onClick={this.shareToMSTeams} variant="primary">
<div className="flex items-center">
<Icon
name="integrations/teams-white"
color="white"
size="18"
marginRight="10"
/>
{loading ? 'Sending...' : 'Send'}
</div>
</Button>
)}
</div>
</>
)}
</div>
<div className={styles.footer}>
<SessionCopyLink />
</div>
</div>
) : (
<>
<div className={styles.body}>
<IntegrateSlackButton />
</div>
{showCopyLink && (
<div className={styles.footer}>
<SessionCopyLink />
</div>
)}
</>
)}
</div>
)}

View file

@ -39,14 +39,9 @@
}
.footer {
/* display: flex; */
/* align-items: center; */
/* justify-content: space-between; */
/* padding: 10px 0; */
border-top: solid thin $gray-light;
margin: 0 -14px;
margin: 0 -8px;
padding: 0 14px;
/* border-bottom: solid thin $gray-light; */
}
textarea {

View file

@ -12,7 +12,7 @@ const Message = ({
inline = false,
success = false,
info = true,
text,
text = undefined,
}) =>
visible || !hidden ? (
<div className={cn(styles.message, 'flex items-center')} data-inline={inline}>

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,7 @@ const SAVE = new RequestTypes('slack/SAVE');
const UPDATE = new RequestTypes('slack/UPDATE');
const REMOVE = new RequestTypes('slack/REMOVE');
const FETCH_LIST = new RequestTypes('slack/FETCH_LIST');
const SEND_MSG = new RequestTypes('slack/SEND_MSG');
const EDIT = 'slack/EDIT';
const INIT = 'slack/INIT';
const idKey = 'webhookId';
@ -61,7 +62,7 @@ export function save(instance) {
export function update(instance) {
return {
types: UPDATE.toArray(),
call: (client) => client.put(`/integrations/slack/${instance.webhookId}`, instance.toData()),
call: (client) => client.post(`/integrations/slack/${instance.webhookId}`, instance.toData()),
};
}
@ -86,3 +87,12 @@ export function remove(id) {
id,
};
}
// https://api.openreplay.com/5587/integrations/slack/notify/315/sessions/7856803626558104
//
export function sendSlackMsg({ integrationId, entity, entityId, data }) {
return {
types: SEND_MSG.toArray(),
call: (client) => client.post(`/integrations/slack/notify/${integrationId}/${entity}/${entityId}`, data)
}
}

View file

@ -7,6 +7,8 @@ const SAVE = new RequestTypes('msteams/SAVE');
const UPDATE = new RequestTypes('msteams/UPDATE');
const REMOVE = new RequestTypes('msteams/REMOVE');
const FETCH_LIST = new RequestTypes('msteams/FETCH_LIST');
const SEND_MSG = new RequestTypes('msteams/SEND_MSG');
const EDIT = 'msteams/EDIT';
const INIT = 'msteams/INIT';
const idKey = 'webhookId';
@ -86,3 +88,12 @@ export function remove(id) {
id,
};
}
// https://api.openreplay.com/5587/integrations/msteams/notify/315/sessions/7856803626558104
//
export function sendMsTeamsMsg({ integrationId, entity, entityId, data }) {
return {
types: SEND_MSG.toArray(),
call: (client) => client.post(`/integrations/msteams/notify/${integrationId}/${entity}/${entityId}`, data)
}
}

View file

@ -130,4 +130,13 @@ export default class NotesStore {
console.error(e)
}
}
async sendMsTeamsNotification(noteId: string, webhook: string) {
try {
const resp = await notesService.sendMsTeamsNotification(noteId, webhook)
return resp
} catch (e) {
console.error(e)
}
}
}

View file

@ -119,4 +119,15 @@ export default class NotesService {
}
})
}
sendMsTeamsNotification(noteId: string, webhook: string) {
return this.client.get(`/notes/${noteId}/msteams/${webhook}`)
.then(r => {
if (r.ok) {
return r.json().then(r => r.data)
} else {
throw new Error('Error sending slack notif: ' + r.status)
}
})
}
}

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M9.186 4.797a2.42 2.42 0 1 0-2.86-2.448h1.178c.929 0 1.682.753 1.682 1.682v.766Zm-4.295 7.738h2.613c.929 0 1.682-.753 1.682-1.682V5.58h2.783a.7.7 0 0 1 .682.716v4.294a4.197 4.197 0 0 1-4.093 4.293c-1.618-.04-3-.99-3.667-2.35Zm10.737-9.372a1.674 1.674 0 1 1-3.349 0 1.674 1.674 0 0 1 3.349 0Zm-2.238 9.488c-.04 0-.08 0-.12-.002a5.19 5.19 0 0 0 .381-2.07V6.306a1.692 1.692 0 0 0-.15-.725h1.792c.39 0 .707.317.707.707v3.765a2.598 2.598 0 0 1-2.598 2.598h-.013Z"/>
<path d="M.682 3.349h6.822c.377 0 .682.305.682.682v6.822a.682.682 0 0 1-.682.682H.682A.682.682 0 0 1 0 10.853V4.03c0-.377.305-.682.682-.682Zm5.206 2.596v-.72h-3.59v.72h1.357V9.66h.87V5.945h1.363Z"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B