Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix integrations and escalations autoselect, improve GList #1601

Merged
merged 3 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions grafana-plugin/src/components/GList/GList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ const GList = <T extends WithId>(props: GListProps<T>) => {
};
}, []);

const selectedRef = useRef<HTMLDivElement>();
const itemsRef = useRef(null);

useEffect(() => {
if (autoScroll && selectedRef.current) {
const selectedElement = selectedRef.current;
if (autoScroll && selectedId) {
const map = getMap();
const selectedElement = map.get(selectedId);

if (!selectedElement) {
return;
}

const divToScroll = selectedElement.parentElement.parentElement;

const maxScroll = Math.max(0, selectedElement.parentElement.offsetHeight - divToScroll.offsetHeight);
Expand All @@ -49,16 +55,26 @@ const GList = <T extends WithId>(props: GListProps<T>) => {
behavior: 'smooth',
});
}
}, [autoScroll, selectedRef.current]);
}, [selectedId, autoScroll]);

function getMap() {
if (!itemsRef.current) {
itemsRef.current = new Map();
}
return itemsRef.current;
}

return (
<div className={cx('root')}>
{items ? (
items.map((item) => (
<div
ref={(node) => {
if (item.id === selectedId) {
selectedRef.current = node;
const map = getMap();
if (node) {
map.set(item.id, node);
} else {
map.delete(item.id);
}
}}
key={item.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const ManualAlertGroup: FC<ManualAlertGroupProps> = (props) => {
const [userResponders, setUserResponders] = useState([]);
const [scheduleResponders, setScheduleResponders] = useState([]);
const { onHide, onCreate } = props;
const data = {};
const data = { team: store.userStore.currentUser?.current_team };

const handleFormSubmit = async (data) => {
store.directPagingStore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ const AlertReceiveChannelCard = observer((props: AlertReceiveChannelCardProps) =
</PluginLink>
)}
</HorizontalGroup>
<HorizontalGroup>
<HorizontalGroup spacing="xs">
<IntegrationLogo scale={0.08} integration={integration} />
<Text type="secondary" size="small">
{integration?.display_name}
</Text>
</HorizontalGroup>
<TeamName team={grafanaTeamStore.items[alertReceiveChannel.team]} />
<TeamName team={grafanaTeamStore.items[alertReceiveChannel.team]} size="small" />
</VerticalGroup>
</HorizontalGroup>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const EscalationChainCard = observer((props: AlertReceiveChannelCardProps) => {
}
/>
</HorizontalGroup>
<TeamName team={grafanaTeamStore.items[escalationChain.team]} />
<TeamName team={grafanaTeamStore.items[escalationChain.team]} size="small" />
</VerticalGroup>
</HorizontalGroup>
</div>
Expand Down
3 changes: 3 additions & 0 deletions grafana-plugin/src/containers/TeamName/TeamName.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.avatar {
margin-right: 4px;
}
11 changes: 8 additions & 3 deletions grafana-plugin/src/containers/TeamName/TeamName.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import React from 'react';

import { Badge, Tooltip } from '@grafana/ui';
import cn from 'classnames/bind';
import { observer } from 'mobx-react';

import Avatar from 'components/Avatar/Avatar';
import Text from 'components/Text/Text';
import { GrafanaTeam } from 'models/grafana_team/grafana_team.types';

import styles from './TeamName.module.css';

const cx = cn.bind(styles);

interface TeamNameProps {
team: GrafanaTeam;
size?: 'small' | 'medium' | 'large';
Expand All @@ -15,17 +20,17 @@ interface TeamNameProps {
const TeamName = observer((props: TeamNameProps) => {
const { team, size } = props;
if (!team) {
return <></>;
return null;
}
if (team.id === 'null') {
return <Badge text={team.name} color={'blue'} tooltip={'Resource is not assigned to any team (ex General team)'} />;
}
return (
<Text type="secondary" size={size ? size : 'medium'}>
<Avatar size="small" src={team.avatar_url} />{' '}
<Avatar size="small" src={team.avatar_url} className={cx('avatar')} />
<Tooltip placement="top" content={'Resource is assigned to ' + team.name}>
<span>{team.name}</span>
</Tooltip>{' '}
</Tooltip>
</Text>
);
});
Expand Down
33 changes: 19 additions & 14 deletions grafana-plugin/src/containers/TeamsList/TeamsList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useState } from 'react';

import { Badge, Button, Field, HorizontalGroup, Modal, RadioButtonGroup, Tooltip, VerticalGroup } from '@grafana/ui';
import { Badge, Button, Field, HorizontalGroup, Modal, RadioButtonList, Tooltip, VerticalGroup } from '@grafana/ui';
import { observer } from 'mobx-react';

import Avatar from 'components/Avatar/Avatar';
Expand Down Expand Up @@ -146,12 +146,14 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
const { grafanaTeamStore } = store;
const team = grafanaTeamStore.items[teamId];

const [shareResourceToAll, setShareResourceToAll] = useState<boolean>(team.is_sharing_resources_to_all);
const [shareResourceToAll, setShareResourceToAll] = useState<string>(
String(Number(team.is_sharing_resources_to_all))
);

const handleSubmit = useCallback(() => {
Promise.all([grafanaTeamStore.updateTeam(teamId, { is_sharing_resources_to_all: shareResourceToAll })]).then(
onHide
);
Promise.all([
grafanaTeamStore.updateTeam(teamId, { is_sharing_resources_to_all: Boolean(Number(shareResourceToAll)) }),
]).then(onHide);
}, [shareResourceToAll]);

return (
Expand All @@ -167,14 +169,17 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
<WithPermissionControlTooltip userAction={UserActions.AlertGroupsWrite}>
<VerticalGroup>
<Field label="Who can see the team name and access the team resources">
<RadioButtonGroup
options={[
{ label: 'All Users', value: true },
{ label: 'Team members and admins', value: false },
]}
value={shareResourceToAll}
onChange={setShareResourceToAll}
/>
<div style={{ marginTop: '8px' }}>
<RadioButtonList
name="shareResourceToAll"
options={[
{ label: 'All Users', value: '1' },
{ label: 'Team members and admins', value: '0' },
]}
value={shareResourceToAll}
onChange={setShareResourceToAll}
/>
</div>
</Field>
</VerticalGroup>
</WithPermissionControlTooltip>
Expand All @@ -184,7 +189,7 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
Cancel
</Button>
<Button onClick={handleSubmit} variant="primary">
Submit
Save
</Button>
</HorizontalGroup>
</Modal>
Expand Down
42 changes: 27 additions & 15 deletions grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es

const { escalationChainStore } = store;

const searchResult = escalationChainStore.getSearchResult();

let selectedEscalationChain: EscalationChain['id'];
if (id) {
let escalationChain = await escalationChainStore
Expand All @@ -87,22 +85,25 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
}
}

if (!selectedEscalationChain) {
selectedEscalationChain = searchResult[0]?.id;
}

if (selectedEscalationChain) {
this.enrichExtraEscalationChainsAndSelect(selectedEscalationChain);
} else {
this.setState({ selectedEscalationChain: undefined });
}
};

handleEsclalationSelect = (id: EscalationChain['id']) => {
const { history } = this.props;

history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
};

setSelectedEscalationChain = async (escalationChainId: EscalationChain['id']) => {
const { store, history } = this.props;
const { store } = this.props;

const { escalationChainStore } = store;

this.setState({ selectedEscalationChain: escalationChainId }, () => {
history.push(`${PLUGIN_ROOT}/escalations/${escalationChainId || ''}${window.location.search}`);
if (escalationChainId) {
escalationChainStore.updateEscalationChainDetails(escalationChainId);
}
Expand Down Expand Up @@ -167,7 +168,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
selectedId={selectedEscalationChain}
items={data}
itemKey="id"
onSelect={this.setSelectedEscalationChain}
onSelect={this.handleEsclalationSelect}
>
{(item) => <EscalationChainCard id={item.id} />}
</GList>
Expand Down Expand Up @@ -234,8 +235,14 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
}

handleFiltersChange = (filters: FiltersValues, isOnMount = false) => {
const {
match: {
params: { id },
},
} = this.props;

this.setState({ escalationChainsFilters: filters, extraEscalationChains: undefined }, () => {
if (isOnMount) {
if (isOnMount && id) {
this.applyFilters().then(this.parseQueryParams);
} else {
this.applyFilters().then(this.autoSelectEscalationChain);
Expand All @@ -244,14 +251,15 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
};

autoSelectEscalationChain = () => {
const { store } = this.props;
const { store, history } = this.props;
const { selectedEscalationChain } = this.state;
const { escalationChainStore } = store;

const searchResult = escalationChainStore.getSearchResult();

if (!searchResult.find((escalationChain: EscalationChain) => escalationChain.id === selectedEscalationChain)) {
this.setSelectedEscalationChain(searchResult[0]?.id);
const id = searchResult[0]?.id;
history.push(`${PLUGIN_ROOT}/escalations/${id || ''}${window.location.search}`);
}
};

Expand Down Expand Up @@ -358,7 +366,11 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
};

handleEscalationChainCreate = async (id: EscalationChain['id']) => {
this.enrichExtraEscalationChainsAndSelect(id);
const { history } = this.props;

await this.applyFilters();

history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
};

enrichExtraEscalationChainsAndSelect = async (id: EscalationChain['id']) => {
Expand Down Expand Up @@ -389,7 +401,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
};

handleDeleteEscalationChain = () => {
const { store } = this.props;
const { store, history } = this.props;
const { escalationChainStore } = store;
const { selectedEscalationChain, extraEscalationChains } = this.state;

Expand All @@ -413,7 +425,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es

const newSelected = escalationChains[index - 1] || escalationChains[0];

this.setSelectedEscalationChain(newSelected?.id);
history.push(`${PLUGIN_ROOT}/escalations/${newSelected?.id || ''}${window.location.search}`);
});
};

Expand Down
43 changes: 23 additions & 20 deletions grafana-plugin/src/pages/integrations/Integrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
} = this.props;
const { alertReceiveChannelStore } = store;

let selectedAlertReceiveChannel = store.selectedAlertReceiveChannel;
let selectedAlertReceiveChannel = undefined;

if (id) {
let alertReceiveChannel = await alertReceiveChannelStore
Expand All @@ -110,6 +110,8 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>

if (selectedAlertReceiveChannel) {
this.enrichAlertReceiveChannelsAndSelect(selectedAlertReceiveChannel);
} else {
store.selectedAlertReceiveChannel = undefined;
}
};

Expand Down Expand Up @@ -309,28 +311,29 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
delete alertReceiveChanneltoPoll[alertReceiveChannelId];
}

alertReceiveChannelStore.deleteAlertReceiveChannel(alertReceiveChannelId).then(async () => {
await this.applyFilters();
alertReceiveChannelStore
.deleteAlertReceiveChannel(alertReceiveChannelId)
.then(this.applyFilters)
.then(() => {
if (alertReceiveChannelId === store.selectedAlertReceiveChannel) {
if (extraAlertReceiveChannels) {
const newExtraAlertReceiveChannels = extraAlertReceiveChannels.filter(
(alertReceiveChannel) => alertReceiveChannel.id !== alertReceiveChannelId
);

this.setState({ extraAlertReceiveChannels: newExtraAlertReceiveChannels });
}

const searchResult = alertReceiveChannelStore.getSearchResult();

if (alertReceiveChannelId === store.selectedAlertReceiveChannel) {
if (extraAlertReceiveChannels) {
const newExtraAlertReceiveChannels = extraAlertReceiveChannels.filter(
(alertReceiveChannel) => alertReceiveChannel.id !== alertReceiveChannelId
const index = searchResult.findIndex(
(alertReceiveChannel: AlertReceiveChannel) => alertReceiveChannel.id === store.selectedAlertReceiveChannel
);
const newSelected = searchResult[index - 1] || searchResult[0];

this.setState({ extraAlertReceiveChannels: newExtraAlertReceiveChannels });
history.push(`${PLUGIN_ROOT}/integrations/${newSelected?.id || ''}${window.location.search}`);
}

const searchResult = alertReceiveChannelStore.getSearchResult();

const index = searchResult.findIndex(
(alertReceiveChannel: AlertReceiveChannel) => alertReceiveChannel.id === store.selectedAlertReceiveChannel
);
const newSelected = searchResult[index - 1] || searchResult[0];

history.push(`${PLUGIN_ROOT}/integrations/${newSelected?.id || ''}${window.location.search}`);
}
});
});
};

applyFilters = () => {
Expand Down Expand Up @@ -363,7 +366,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
},
} = this.props;

this.setState({ integrationsFilters }, () => {
this.setState({ integrationsFilters, extraAlertReceiveChannels: undefined }, () => {
this.applyFilters().then(() => {
if (isOnMount && id) {
this.parseQueryParams();
Expand Down