color scheme for avatar

This commit is contained in:
Rares Mardare 2022-09-27 21:57:02 +03:00
parent 63d2fbf715
commit d7eb7107b7
5 changed files with 99 additions and 83 deletions

View file

@ -64,9 +64,6 @@ class ScheduleFinal extends Component<ScheduleFinalProps, ScheduleOverridesState
: store.scheduleStore.events[scheduleId]?.['override']?.[getFromString(startMoment)];
const currentTimeHidden = currentTimeX < 0 || currentTimeX > 1;
/* console.log('shifts', toJS(shifts));
console.log('layers', toJS(layers)); */
return (
<>
<div className={cx('root')}>

View file

@ -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) => {};

View file

@ -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 <div className={cx('root')}>{colors?.length ? renderSVG() : renderAvatarIcon()}</div>;
return (
<div className={cx('root')}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width={width} height={height} viewBox="-10 -10 220 220">
<g fill="none" stroke-width="15" transform="translate(100,100)">
{renderColorPaths(colors)}
</g>
</svg>
<div className={cx('avatar')}>{renderAvatar()}</div>
<div className={cx('icon')}>{renderIcon()}</div>
</div>
);
function renderAvatarIcon() {
return (
<>
<div className={cx('avatar')}>{renderAvatar()}</div>
<div className={cx('icon')}>{renderIcon()}</div>
</>
);
}
function renderSVG() {
return (
<>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width={width} height={height} viewBox="-10 -10 220 220">
<g fill="none" strokeWidth="15" transform="translate(100,100)">
{renderColorPaths(colors)}
</g>
</svg>
{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;

View file

@ -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<User['pk']>;
tz: Timezone;
onTzChange: (tz: Timezone) => void;
onCallNow: Array<Partial<User>>;
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<UsersTimezonesProps> = (props) => {
const { userIds, tz, onTzChange, onCallNow } = props;
const { userIds, tz, onTzChange, onCallNow, scheduleId } = props;
const store = useStore();
@ -87,7 +100,13 @@ const UsersTimezones: FC<UsersTimezonesProps> = (props) => {
</div>
<div className={cx('users')}>
<div className={cx('current-time')} style={{ left: `${currentTimeX}%` }} />
<UserAvatars users={users} onCallNow={onCallNow} onTzChange={onTzChange} currentMoment={currentMoment} />
<UserAvatars
users={users}
onCallNow={onCallNow}
onTzChange={onTzChange}
currentMoment={currentMoment}
scheduleId={scheduleId}
/>
</div>
<div className={cx('time-stripe')}>
<div className={cx('current-user-stripe')} />
@ -115,12 +134,13 @@ const UsersTimezones: FC<UsersTimezonesProps> = (props) => {
interface UserAvatarsProps {
users: User[];
currentMoment: dayjs.Dayjs;
scheduleId: string;
onTzChange: (timezone: Timezone) => void;
onCallNow: Array<Partial<User>>;
}
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 (
<Tooltip
@ -254,12 +281,11 @@ const AvatarGroup = (props: AvatarGroupProps) => {
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)}
>
<ColorfulUserCircle
colors={['red']}
colors={colorSchemeList}
width={32}
height={32}
renderAvatar={() => <Avatar src={user.avatar} size="large" />}
@ -283,4 +309,43 @@ const AvatarGroup = (props: AvatarGroupProps) => {
);
};
function getColorSchemeMappingForUsers(store: RootStore, scheduleId: string): { [userId: string]: Set<string> } {
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<string> } = {};
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<string>();
}
usersColorSchemeHash[user.pk].add(
isOverride ? getOverrideColor(id as number) : findColor(id as string, layers, overrides)
);
});
});
}
}
export default UsersTimezones;

View file

@ -96,8 +96,7 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
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<SchedulePageProps, SchedulePageState>
<Text.Title editable editModalTitle="Schedule name" level={3} onTextChange={this.handleNameChange}>
{schedule?.name}
</Text.Title>
{/*<ScheduleCounter
type="link"
count={5}
tooltipTitle="Used in escalations"
tooltipContent={
<>
<PluginLink query={{ page: 'integrations', id: 'CXBEG63MBJMDL' }}>Grafana 1</PluginLink>
<br />
<PluginLink query={{ page: 'integrations', id: 'CXBEG63MBJMDL' }}>Grafana 2</PluginLink>
<br />
<PluginLink query={{ page: 'integrations', id: 'CXBEG63MBJMDL' }}>Grafana 3</PluginLink>
</>
}
/>
<ScheduleCounter
type="warning"
count={2}
tooltipTitle="Warnings"
tooltipContent="Schedule has unassigned time periods during next 7 days"
/>*/}
</HorizontalGroup>
<HorizontalGroup>
{users && (
@ -147,11 +126,6 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
<UserTimezoneSelect value={currentTimezone} users={users} onChange={this.handleTimezoneChange} />
</HorizontalGroup>
)}
{/*<ScheduleQuality quality={0.89} />*/}
{/*<ToolbarButton icon="copy" tooltip="Copy" />
<ToolbarButton icon="brackets-curly" tooltip="Code" />
<ToolbarButton icon="share-alt" tooltip="Share" />
<ToolbarButton icon="cog" tooltip="Settings" />*/}
<WithConfirm>
<ToolbarButton icon="trash-alt" tooltip="Delete" onClick={this.handleDelete} />
</WithConfirm>
@ -170,6 +144,7 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
}
tz={currentTimezone}
onTzChange={this.handleTimezoneChange}
scheduleId={scheduleId}
/>
</div>
<div className={cx('controls')}>
@ -190,32 +165,6 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
{startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')}
</div>
</HorizontalGroup>
{/*<HorizontalGroup width="auto">
<RadioButtonGroup
options={[
{ label: 'Day', value: 'day' },
{
label: 'Week',
value: 'week',
},
{ label: 'Month', value: 'month' },
{ label: 'Custom', value: 'custom' },
]}
value={schedulePeriodType}
onChange={this.handleShedulePeriodTypeChange}
/>
<RadioButtonGroup
options={[
{ label: 'Timeline', value: 'timeline' },
{
label: 'Grid',
value: 'grid',
},
]}
value={renderType}
onChange={this.handleRenderTypeChange}
/>
</HorizontalGroup>*/}
</HorizontalGroup>
</div>
{/* <div className={'current-time'} />*/}