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 all 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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v1.1.24 (2023-02-06)

### Fixed

- Design polishing ([1290](https://github.com/grafana/oncall/pull/1290))
- Not showing contact details in User tooltip if User does not have edit/admin access

## v1.1.23 (2023-02-06)

### Fixed
Expand Down
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,40 @@
color: #ccccdc;
}

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

.timezone-wrapper {
display: flex;
flex-grow: 1;
}

.timezone-icon {
width: 10%;
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 = Array.from(colorSchemeMapping[user.pk] || []);

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