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

Polishing design for User tooltip in Schedules #1290

Merged
merged 7 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.root {
width: 220px;
padding: 10px;
width: 210px;
padding: 8px 4px;
}

.oncall-badge {
Expand All @@ -25,7 +25,7 @@

.line-break {
width: 100vw;
margin: -4px -18px -4px -18px;
margin: 8px -14px 8px -14px;
}

.times {
Expand All @@ -37,21 +37,41 @@
color: #ccccdc;
}

.username {
word-break: break-all;
}

.timezone-wrapper {
display: flex;
width: 92%;
}

.timezone-icon {
width: 10%;
width: 8%;
margin-right: 8px;
Ukochka marked this conversation as resolved.
Show resolved Hide resolved
}

.contact-icon {
margin-right: 8px;
}

.timezone-info {
width: 50%;
overflow-wrap: anywhere;
margin-left: 4px;
margin-right: 8px;
}

.contact-details {
display: flex;
}

.contact-details a {
text-decoration-line: none;
word-break: break-all;
}

.user-timezones {
margin-top: 4px;
display: flex;
width: 100%;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,117 +5,142 @@ import cn from 'classnames/bind';
import dayjs from 'dayjs';

import Avatar from 'components/Avatar/Avatar';
import ScheduleBorderedAvatar from 'components/ScheduleBorderedAvatar/ScheduleBorderedAvatar';
import Text from 'components/Text/Text';
import { isInWorkingHours } from 'components/WorkingHours/WorkingHours.helpers';
import { getTzOffsetString } from 'models/timezone/timezone.helpers';
import { User } from 'models/user/user.types';
import { getColorSchemeMappingForUsers } from 'pages/schedule/Schedule.helpers';
import { useStore } from 'state/useStore';
import { isUserActionAllowed, UserActions } from 'utils/authorization';

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

interface ScheduleUserDetailsProps {
currentMoment: dayjs.Dayjs;
user: User;
isOncall: boolean;
scheduleId: string;
startMoment: dayjs.Dayjs;
}

const cx = cn.bind(styles);

const ScheduleUserDetails: FC<ScheduleUserDetailsProps> = (props) => {
const { user, currentMoment, isOncall } = props;
const { user, currentMoment, isOncall, scheduleId, startMoment } = props;
const userMoment = currentMoment.tz(user.timezone);
const userOffsetHoursStr = getTzOffsetString(userMoment);
const isInWH = isInWorkingHours(currentMoment, user.working_hours, user.timezone);

const store = useStore();
const colorSchemeMapping = getColorSchemeMappingForUsers(store, scheduleId, startMoment);
const colorSchemeList = colorSchemeMapping[user.pk] ? Array.from(colorSchemeMapping[user.pk]) : [];
Ukochka marked this conversation as resolved.
Show resolved Hide resolved

const { teamStore } = store;

const slackWorkspaceName = teamStore.currentTeam.slack_team_identity?.cached_name?.replace(/[^0-9a-z]/gi, '') || '';
return (
<div className={cx('root')}>
<VerticalGroup spacing="md">
<HorizontalGroup justify="space-between">
<Avatar src={user.avatar} size="large" />
</HorizontalGroup>
<VerticalGroup spacing="sm">
<Text type="primary">{user.username}</Text>
{isOncall && <Badge text="OnCall now" color="green" />}
{isInWH ? (
<Badge text="Inside working hours" color="blue" />
) : (
<Badge text="Outside working hours" color="orange" />
)}
<HorizontalGroup align="flex-start">
<VerticalGroup spacing="xs">
<ScheduleBorderedAvatar
colors={colorSchemeList}
width={35}
height={35}
renderAvatar={() => <Avatar src={user.avatar} size="large" />}
renderIcon={() => null}
></ScheduleBorderedAvatar>

<VerticalGroup spacing="xs" width="100%">
<div className={cx('username')}>
<Text type="primary">{user.username}</Text>
</div>
<HorizontalGroup spacing="xs">
{isOncall && <Badge text="OnCall" color="green" />}
{isInWH ? (
<Badge text="Inside working hours" color="blue" />
) : (
<Badge text="Outside working hours" color="orange" />
)}
</HorizontalGroup>
<div className={cx('user-timezones')}>
<div className={cx('timezone-icon')}>
<Text type="secondary">
<Icon name="clock-nine" />
</Text>
</div>
<div className={cx('timezone-wrapper')}>
<div className={cx('timezone-info')}>
<VerticalGroup>
<VerticalGroup spacing="none">
<Text type="secondary">Local time</Text>
<Text type="secondary">{currentMoment.tz().format('DD MMM, HH:mm')}</Text>
<Text type="secondary">({getTzOffsetString(currentMoment)})</Text>
</VerticalGroup>
</div>

<div className={cx('timezone-info')}>
<VerticalGroup className={cx('timezone-info')}>
<Text>{user.username}'s time</Text>
<VerticalGroup className={cx('timezone-info')} spacing="none">
<Text>User's time</Text>
<Text>{`${userMoment.tz(user.timezone).format('DD MMM, HH:mm')}`}</Text>
<Text>({userOffsetHoursStr})</Text>
</VerticalGroup>
</div>
</div>
</HorizontalGroup>
</div>
</VerticalGroup>

<hr className={cx('line-break')} />
<VerticalGroup spacing="sm">
<Text>Contacts</Text>
{isUserActionAllowed(UserActions.UserSettingsAdmin) && (
<VerticalGroup spacing="xs">
<hr className={cx('line-break')} />
<VerticalGroup spacing="xs">
<Text>Contacts</Text>

<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="envelope" />{' '}
<a href={`mailto:${user.email}`} target="_blank" rel="noreferrer">
<Text type="link">{user.email}</Text>
</a>{' '}
</Text>
</div>
{user.slack_user_identity && (
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="slack" />{' '}
<a
href={`https://${slackWorkspaceName}.slack.com/team/${user.slack_user_identity.slack_id}`}
target="_blank"
rel="noreferrer"
>
<Text type="link">{user.slack_user_identity.slack_login}</Text>
</a>{' '}
</Text>
</div>
)}
{user.telegram_configuration && (
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="message" />{' '}
<a
href={`https://t.me/${user.telegram_configuration.telegram_nick_name}`}
target="_blank"
rel="noreferrer"
>
<Text type="link">{user.telegram_configuration.telegram_nick_name}</Text>
</a>{' '}
</Text>
</div>
)}
{!user.hide_phone_number && user.verified_phone_number && (
<Text type="secondary">Phone: {user.verified_phone_number}</Text>
)}
</VerticalGroup>
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="envelope" className={cx('contact-icon')} />
</Text>
<a href={`mailto:${user.email}`} target="_blank" rel="noreferrer">
<Text type="link">{user.email}</Text>
</a>
</div>
{user.slack_user_identity && (
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="slack" className={cx('contact-icon')} />
</Text>
<a
href={`https://${slackWorkspaceName}.slack.com/team/${user.slack_user_identity.slack_id}`}
target="_blank"
rel="noreferrer"
>
<Text type="link">{user.slack_user_identity.slack_login}</Text>
</a>
</div>
)}
{user.telegram_configuration && (
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="message" className={cx('contact-icon')} />
</Text>
<a
href={`https://t.me/${user.telegram_configuration.telegram_nick_name}`}
target="_blank"
rel="noreferrer"
>
<Text type="link">{user.telegram_configuration.telegram_nick_name}</Text>
</a>
</div>
)}
{!user.hide_phone_number && user.verified_phone_number && (
<div className={cx('contact-details')}>
<Text type="secondary">
<Icon name="document-info" className={cx('contact-icon')} />
</Text>
<Text type="secondary">{user.verified_phone_number}</Text>
</div>
)}
</VerticalGroup>
</VerticalGroup>
)}
</VerticalGroup>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,15 @@ const AvatarGroup = (props: AvatarGroupProps) => {
placement="top"
interactive
key={index}
content={<ScheduleUserDetails currentMoment={currentMoment} user={user} isOncall={isOncall} />}
content={
<ScheduleUserDetails
currentMoment={currentMoment}
user={user}
isOncall={isOncall}
scheduleId={scheduleId}
startMoment={startMoment}
/>
}
>
<div
className={cx('avatar')}
Expand Down
1 change: 1 addition & 0 deletions grafana-plugin/src/pages/incident/Incident.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,5 @@

.title-icon {
color: var(--secondary-text-color);
margin-left: 4px;
}
22 changes: 11 additions & 11 deletions grafana-plugin/src/pages/incident/Incident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,6 @@ class IncidentPage extends React.Component<IncidentPageProps, IncidentPageState>
</HorizontalGroup>
<HorizontalGroup align="center">
<Text>
<CopyToClipboard
text={window.location.href}
onCopy={() => {
openNotification('Link copied');
}}
>
<IconButton name="code-branch" tooltip="Copy link" className={cx('title-icon')} />
</CopyToClipboard>
<a href={incident.slack_permalink} target="_blank" rel="noreferrer">
<IconButton name="slack" tooltip="View in Slack" className={cx('title-icon')} />
</a>
{showLinkTo && (
<IconButton
name="share-alt"
Expand All @@ -265,6 +254,17 @@ class IncidentPage extends React.Component<IncidentPageProps, IncidentPageState>
className={cx('title-icon')}
/>
)}
<a href={incident.slack_permalink} target="_blank" rel="noreferrer">
<IconButton name="slack" tooltip="View in Slack" className={cx('title-icon')} />
</a>
<CopyToClipboard
text={window.location.href}
onCopy={() => {
openNotification('Link copied');
}}
>
<IconButton name="copy" tooltip="Copy link" className={cx('title-icon')} />
</CopyToClipboard>
</Text>
</HorizontalGroup>
</HorizontalGroup>
Expand Down