From b5d900de962ed66e7597c2db1cc32ecbe996ef93 Mon Sep 17 00:00:00 2001 From: Dominik Broj Date: Tue, 30 Apr 2024 11:00:16 +0200 Subject: [PATCH] Show info about missing users in schedules (#4294) # What this PR does - Don't show global notification if one of the rolling_users is a user that doesn't exist anymore (has been deleted) - In case user present in rolling_users has been either deleted or his/her role has been downgraded to Viewer, show such explicit info on UI ![image](https://github.com/grafana/oncall/assets/12073649/45cec62d-b62b-4085-b536-906ef9dbef1f) ![image](https://github.com/grafana/oncall/assets/12073649/692dea50-10c7-47e9-9371-565e21d80cfe) ## Which issue(s) this PR closes Closes https://github.com/grafana/oncall/issues/4251 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --- .../NonExistentUserName.tsx | 19 ++++++++++ .../RotationForm/parts/UserItem.tsx | 22 ++++++++---- .../ScheduleSlot/ScheduleSlot.module.css | 3 +- .../containers/ScheduleSlot/ScheduleSlot.tsx | 35 +++++++++++++++---- .../src/models/schedule/schedule.ts | 2 +- .../src/models/schedule/schedule.types.ts | 2 +- 6 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx diff --git a/grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx b/grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx new file mode 100644 index 0000000000..6435f20d15 --- /dev/null +++ b/grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx @@ -0,0 +1,19 @@ +import React, { ComponentProps, FC } from 'react'; + +import { HorizontalGroup, Icon, Tooltip } from '@grafana/ui'; + +interface NonExistentUserNameProps { + justify?: ComponentProps['justify']; + userName?: string; +} + +const NonExistentUserName: FC = ({ justify = 'space-between', userName }) => ( + + Missing user + + + + +); + +export default NonExistentUserName; diff --git a/grafana-plugin/src/containers/RotationForm/parts/UserItem.tsx b/grafana-plugin/src/containers/RotationForm/parts/UserItem.tsx index f1972e8448..7818de5fd1 100644 --- a/grafana-plugin/src/containers/RotationForm/parts/UserItem.tsx +++ b/grafana-plugin/src/containers/RotationForm/parts/UserItem.tsx @@ -6,6 +6,7 @@ import cn from 'classnames/bind'; import dayjs from 'dayjs'; import { COLORS } from 'styles/utils.styles'; +import NonExistentUserName from 'components/NonExistentUserName/NonExistentUserName'; import { Text } from 'components/Text/Text'; import { WorkingHours } from 'components/WorkingHours/WorkingHours'; import { ApiSchemas } from 'network/oncall-api/api.types'; @@ -30,18 +31,17 @@ export const UserItem = ({ pk, shiftColor, shiftStart, shiftEnd }: UserItemProps useEffect(() => { if (!userStore.items[pk]) { - userStore.fetchItemById({ userPk: pk, skipIfAlreadyPending: true }); + userStore.fetchItemById({ userPk: pk, skipIfAlreadyPending: true, skipErrorHandling: true }); } }, []); const name = userStore.items[pk]?.username; - const desc = userStore.items[pk]?.timezone; - const workingHours = userStore.items[pk]?.working_hours; const timezone = userStore.items[pk]?.timezone; + const workingHours = userStore.items[pk]?.working_hours; const duration = dayjs(shiftEnd).diff(dayjs(shiftStart), 'seconds'); - return ( -
+ const slotContent = name ? ( + <> {duration <= WEEK_IN_SECONDS && ( )}
- {name} ({desc}) + {name} ({timezone})
+ + ) : ( +
+ +
+ ); + + return ( +
+ {slotContent}
); }; diff --git a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.module.css b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.module.css index 7369f56c5d..6243b495f1 100644 --- a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.module.css +++ b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.module.css @@ -56,9 +56,8 @@ z-index: 1; color: #fff; font-size: 12px; + width: 100%; font-weight: 500; - pointer-events: none; - position: absolute; white-space: nowrap; } diff --git a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx index 43cffa8a73..e5b6277576 100644 --- a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx +++ b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.tsx @@ -6,6 +6,8 @@ import dayjs from 'dayjs'; import { observer } from 'mobx-react'; import { Avatar } from 'components/Avatar/Avatar'; +import NonExistentUserName from 'components/NonExistentUserName/NonExistentUserName'; +import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally'; import { ScheduleFiltersType } from 'components/ScheduleFilters/ScheduleFilters.types'; import { Text } from 'components/Text/Text'; import { WorkingHours } from 'components/WorkingHours/WorkingHours'; @@ -59,7 +61,7 @@ export const ScheduleSlot: FC = observer((props) => { const currentMoment = useMemo(() => dayjs(), []); - const renderEvent = (event): React.ReactElement | React.ReactElement[] => { + const renderEvent = (event: Event): React.ReactElement | React.ReactElement[] => { if (event.shiftSwapId) { return ; } @@ -74,12 +76,31 @@ export const ScheduleSlot: FC = observer((props) => { if (event.is_empty) { return ( -
+ 0} + backupChildren={ +
+ } + > + {event.missing_users.map((name) => ( +
+
+ +
+
+ ))} + ); } diff --git a/grafana-plugin/src/models/schedule/schedule.ts b/grafana-plugin/src/models/schedule/schedule.ts index 1beaa0b453..ded9afaa47 100644 --- a/grafana-plugin/src/models/schedule/schedule.ts +++ b/grafana-plugin/src/models/schedule/schedule.ts @@ -522,7 +522,7 @@ export class ScheduleStore extends BaseStore { ...this.events[scheduleId], [type]: { ...this.events[scheduleId]?.[type], - [fromString]: layers ? layers : shifts, + [fromString]: layers || shifts, }, }, }; diff --git a/grafana-plugin/src/models/schedule/schedule.types.ts b/grafana-plugin/src/models/schedule/schedule.types.ts index 3d282894cb..902d7c1ed7 100644 --- a/grafana-plugin/src/models/schedule/schedule.types.ts +++ b/grafana-plugin/src/models/schedule/schedule.types.ts @@ -92,7 +92,7 @@ export interface Event { end: string; is_empty: boolean; is_gap: boolean; - missing_users: Array<{ display_name: ApiSchemas['User']['username']; pk: ApiSchemas['User']['pk'] }>; + missing_users: Array; priority_level: number; shift: Pick & { pk: string }; source: string;