diff --git a/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx b/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx index af91a512..c2f56059 100644 --- a/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx +++ b/grafana-plugin/src/containers/Rotations/ScheduleFinal.tsx @@ -64,9 +64,6 @@ class ScheduleFinal extends Component 1; - /* console.log('shifts', toJS(shifts)); - console.log('layers', toJS(layers)); */ - return ( <>
diff --git a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.helpers.ts b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.helpers.ts index 0d6049fb..f66d5306 100644 --- a/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.helpers.ts +++ b/grafana-plugin/src/containers/ScheduleSlot/ScheduleSlot.helpers.ts @@ -20,12 +20,6 @@ export const getRandomUser = () => { export const getTitle = (user: User) => { return user ? user.username.split(' ')[0] : null; - return user - ? user.username - .split(' ') - .map((word) => word.charAt(0).toUpperCase()) - .join('') - : null; }; export const getOuRanges = (shift: Shift, user: User) => {}; diff --git a/grafana-plugin/src/containers/UsersTimezones/ColorfulUserCircle.tsx b/grafana-plugin/src/containers/UsersTimezones/ColorfulUserCircle.tsx index 8662ed0b..12759530 100644 --- a/grafana-plugin/src/containers/UsersTimezones/ColorfulUserCircle.tsx +++ b/grafana-plugin/src/containers/UsersTimezones/ColorfulUserCircle.tsx @@ -1,6 +1,7 @@ -import cn from 'classnames/bind'; import React from 'react'; +import cn from 'classnames/bind'; + import styles from './ColorfulUserCircle.module.scss'; const cx = cn.bind(styles); @@ -18,23 +19,33 @@ export default function ColorfulUserCircle({ renderAvatar: () => JSX.Element; renderIcon: () => JSX.Element; }) { - if (!colors?.length) return null; + return
{colors?.length ? renderSVG() : renderAvatarIcon()}
; - return ( -
- - - {renderColorPaths(colors)} - - -
{renderAvatar()}
-
{renderIcon()}
-
- ); + function renderAvatarIcon() { + return ( + <> +
{renderAvatar()}
+
{renderIcon()}
+ + ); + } + + function renderSVG() { + return ( + <> + + + {renderColorPaths(colors)} + + + {renderAvatarIcon()} + + ); + } function renderColorPaths(colors: string[]) { const colorSchemeList = colors; - if (colors.length === 1) colorSchemeList.push(colors[0]); + if (colors.length === 1) {colorSchemeList.push(colors[0]);} const stepAngle = (2 * Math.PI) / colors.length; const radius = 100; diff --git a/grafana-plugin/src/containers/UsersTimezones/UsersTimezones.tsx b/grafana-plugin/src/containers/UsersTimezones/UsersTimezones.tsx index fd162f1f..479b38a6 100644 --- a/grafana-plugin/src/containers/UsersTimezones/UsersTimezones.tsx +++ b/grafana-plugin/src/containers/UsersTimezones/UsersTimezones.tsx @@ -3,23 +3,36 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { HorizontalGroup, InlineSwitch, Tooltip } from '@grafana/ui'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; +import { toJS } from 'mobx'; +import moment from 'moment'; import Avatar from 'components/Avatar/Avatar'; import ScheduleUserDetails from 'components/ScheduleUserDetails/ScheduleUserDetails'; import Text from 'components/Text/Text'; +import { findColor } from 'containers/Rotations/Rotations.helpers'; import { IsOncallIcon } from 'icons'; +import { getColor, getFromString, getOverrideColor } from 'models/schedule/schedule.helpers'; + +import { Event, Layer } from 'models/schedule/schedule.types'; import { Timezone } from 'models/timezone/timezone.types'; import { User } from 'models/user/user.types'; +import { getStartOfWeek } from 'pages/schedule/Schedule.helpers'; +import { RootStore } from 'state'; import { useStore } from 'state/useStore'; -import styles from './UsersTimezones.module.css'; import ColorfulUserCircle from './ColorfulUserCircle'; +import styles from './UsersTimezones.module.css'; + + + interface UsersTimezonesProps { userIds: Array; tz: Timezone; - onTzChange: (tz: Timezone) => void; onCallNow: Array>; + scheduleId: string; + + onTzChange: (tz: Timezone) => void; } const cx = cn.bind(styles); @@ -29,7 +42,7 @@ const hoursToSplit = 3; const jLimit = 24 / hoursToSplit; const UsersTimezones: FC = (props) => { - const { userIds, tz, onTzChange, onCallNow } = props; + const { userIds, tz, onTzChange, onCallNow, scheduleId } = props; const store = useStore(); @@ -87,7 +100,13 @@ const UsersTimezones: FC = (props) => {
- +
@@ -115,12 +134,13 @@ const UsersTimezones: FC = (props) => { interface UserAvatarsProps { users: User[]; currentMoment: dayjs.Dayjs; + scheduleId: string; onTzChange: (timezone: Timezone) => void; onCallNow: Array>; } const UserAvatars = (props: UserAvatarsProps) => { - const { users, currentMoment, onTzChange, onCallNow } = props; + const { users, currentMoment, onTzChange, onCallNow, scheduleId } = props; const userGroups = useMemo(() => { return users .reduce((memo, user) => { @@ -165,6 +185,7 @@ const UserAvatars = (props: UserAvatarsProps) => { xPos={xPos} users={group.users} currentMoment={currentMoment} + scheduleId={scheduleId} onCallNow={onCallNow} /> ); @@ -178,6 +199,7 @@ interface AvatarGroupProps { xPos: number; currentMoment: dayjs.Dayjs; utcOffset: number; + scheduleId: string; onSetActiveUtcOffset: (utcOffset: number | undefined) => void; activeUtcOffset: number; onTzChange: (timezone: Timezone) => void; @@ -198,8 +220,11 @@ const AvatarGroup = (props: AvatarGroupProps) => { onSetActiveUtcOffset, activeUtcOffset, onCallNow, + scheduleId, } = props; + const store = useStore(); + const active = !isNaN(activeUtcOffset) && activeUtcOffset === utcOffset; const translateLeft = -AVATAR_WIDTH / 2; @@ -226,6 +251,7 @@ const AvatarGroup = (props: AvatarGroupProps) => { }; }, []); + const colorSchemeMapping = getColorSchemeMappingForUsers(store, scheduleId); const width = active ? users.length * AVATAR_WIDTH + (users.length - 1) * AVATAR_GAP : AVATAR_WIDTH; return ( @@ -239,6 +265,7 @@ const AvatarGroup = (props: AvatarGroupProps) => { > {users.map((user, index, array) => { const isOncall = onCallNow.some((onCallUser) => user.pk === onCallUser.pk); + const colorSchemeList = colorSchemeMapping[user.pk] ? Array.from(colorSchemeMapping[user.pk]) : []; return ( { opacity: active ? 1 : Math.max(1 - index * 0.25, 0.25), visibility: !active && index >= LIMIT ? 'hidden' : 'visible', zIndex: array.length - index - 1, - /* opacity: userHour >= 9 && userHour < 18 ? 1 : 0.5,*/ }} onClick={getAvatarClickHandler(user.timezone)} > } @@ -283,4 +309,43 @@ const AvatarGroup = (props: AvatarGroupProps) => { ); }; +function getColorSchemeMappingForUsers(store: RootStore, scheduleId: string): { [userId: string]: Set } { + const startMoment = getStartOfWeek(store.currentTimezone); + + const shifts: Array<{ shiftId: string; events: Event[] }> = false + ? store.scheduleStore.finalPreview + : (store.scheduleStore.events[scheduleId]?.['final']?.[getFromString(startMoment)] as any); + + const layers = store.scheduleStore.rotationPreview + ? store.scheduleStore.rotationPreview + : (store.scheduleStore.events[scheduleId]?.['rotation']?.[getFromString(startMoment)] as Layer[]); + + const overrides = store.scheduleStore.overridePreview + ? store.scheduleStore.overridePreview + : store.scheduleStore.events[scheduleId]?.['override']?.[getFromString(startMoment)]; + + const usersColorSchemeHash: { [userId: string]: Set } = {}; + + if (!shifts?.length || !layers?.length) {return usersColorSchemeHash;} + + shifts.forEach(({ shiftId, events }) => populateUserHashSet(events, shiftId, false)); + shifts.forEach(({ events }, rotationIndex) => populateUserHashSet(events, rotationIndex, true)); + + return usersColorSchemeHash; + + function populateUserHashSet(events: Event[], id: string | number, isOverride: boolean) { + events.forEach((event) => { + event.users.forEach((user) => { + if (!usersColorSchemeHash[user.pk]) { + usersColorSchemeHash[user.pk] = new Set(); + } + + usersColorSchemeHash[user.pk].add( + isOverride ? getOverrideColor(id as number) : findColor(id as string, layers, overrides) + ); + }); + }); + } +} + export default UsersTimezones; diff --git a/grafana-plugin/src/pages/schedule/Schedule.tsx b/grafana-plugin/src/pages/schedule/Schedule.tsx index bff532ae..2ea6f7bf 100644 --- a/grafana-plugin/src/pages/schedule/Schedule.tsx +++ b/grafana-plugin/src/pages/schedule/Schedule.tsx @@ -96,8 +96,7 @@ class SchedulePage extends React.Component render() { const { store } = this.props; - const { startMoment, schedulePeriodType, renderType, shiftIdToShowRotationForm, shiftIdToShowOverridesForm } = - this.state; + const { startMoment, shiftIdToShowRotationForm, shiftIdToShowOverridesForm } = this.state; const { query } = this.props; const { id: scheduleId } = query; @@ -119,26 +118,6 @@ class SchedulePage extends React.Component {schedule?.name} - {/* - Grafana 1 -
- Grafana 2 -
- Grafana 3 - - } - /> - */} {users && ( @@ -147,11 +126,6 @@ class SchedulePage extends React.Component )} - {/**/} - {/* - - - */} @@ -170,6 +144,7 @@ class SchedulePage extends React.Component } tz={currentTimezone} onTzChange={this.handleTimezoneChange} + scheduleId={scheduleId} />
@@ -190,32 +165,6 @@ class SchedulePage extends React.Component {startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')}
- {/* - - - */}
{/*
*/}