diff --git a/grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx b/grafana-plugin/src/components/NonExistentUserName/NonExistentUserName.tsx new file mode 100644 index 00000000..6435f20d --- /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 f1972e84..7818de5f 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 7369f56c..6243b495 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 43cffa8a..e5b62775 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 1beaa0b4..ded9afaa 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 3d282894..902d7c1e 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;