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

Schedule two-weeks and monthly view #4318

Merged
merged 35 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
732d5b6
remove schedule tutorial code
maskin25 May 7, 2024
0394559
update layers visualization
maskin25 May 7, 2024
ddeef32
add superbutton to the schedule page
maskin25 May 8, 2024
d8e18fa
add 2 weeks view
maskin25 May 9, 2024
e6f9919
visual minor fixes
maskin25 May 13, 2024
f70c896
add monthly view
maskin25 May 16, 2024
becc75c
make css and scss modules classnames human readable
maskin25 May 20, 2024
e8abe08
polish rotations css
maskin25 May 20, 2024
595ceb2
polixh schedules styling
maskin25 May 21, 2024
01c3eac
Merge branch 'dev' into maxim/2667-schedules-v3-pt1-update-layers-vis…
maskin25 May 22, 2024
7b3e232
minor polishing
maskin25 May 22, 2024
b1483e2
display pages/Schedules in 7 days mode always
maskin25 May 23, 2024
49551c8
request one week events from schedules list
maskin25 May 23, 2024
1a1681f
change scheduleView to one week by default
maskin25 May 23, 2024
23b674a
add Date picker to the schedule page
maskin25 May 23, 2024
dac0e44
sligthly update layers colors
maskin25 May 24, 2024
1a2b954
change shift swaps appearance
maskin25 May 24, 2024
c32e340
change shift swaps appearance 2
maskin25 May 24, 2024
e86bd69
minor UI polishing
maskin25 May 24, 2024
5e81779
Merge branch 'dev' into maxim/2667-schedules-v3-pt1-update-layers-vis…
maskin25 May 24, 2024
2dc4c62
Merge branch 'dev' into maxim/2667-schedules-v3-pt1-update-layers-vis…
maskin25 May 28, 2024
e19ccb0
fix merging schedules and emotion
maskin25 May 28, 2024
8c50713
Merge branch 'maxim/2667-schedules-v3-pt1-update-layers-visualization…
maskin25 May 28, 2024
0da6a78
Update grafana-plugin/src/pages/schedule/Schedule.tsx
maskin25 May 28, 2024
665730e
add HTML_ID const to locate html elements
maskin25 May 28, 2024
90d45d1
Merge branch 'maxim/2667-schedules-v3-pt1-update-layers-visualization…
maskin25 May 28, 2024
892b940
fix shift swaps styling
maskin25 May 29, 2024
640c6c9
add e2e test
maskin25 May 29, 2024
962c3e3
add unit tests
maskin25 May 29, 2024
f0104b5
polish tests, remove Schedule.module.css
maskin25 May 30, 2024
2bebddc
Merge branch 'dev' into maxim/2667-schedules-v3-pt1-update-layers-vis…
maskin25 May 30, 2024
caec3c0
update personal events on schedule deletion
maskin25 May 30, 2024
a0eca7d
Merge branch 'maxim/2667-schedules-v3-pt1-update-layers-visualization…
maskin25 May 30, 2024
dd4a38b
fix typo
maskin25 May 30, 2024
6c4a022
disable scheduleView.test.ts
maskin25 May 30, 2024
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
8 changes: 0 additions & 8 deletions grafana-plugin/.config/webpack/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,6 @@ const config = async (env): Promise<Configuration> => {
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
brojd marked this conversation as resolved.
Show resolved Hide resolved
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource',
Expand Down
31 changes: 31 additions & 0 deletions grafana-plugin/e2e-tests/schedules/scheduleView.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { scheduleViewToDaysInOneRow } from 'models/schedule/schedule.helpers';
import { ScheduleView } from 'models/schedule/schedule.types';
import { HTML_ID } from 'utils/DOM';

import { expect, test } from '../fixtures';
import { generateRandomValue } from '../utils/forms';
import { createOnCallScheduleWithRotation } from '../utils/schedule';

test.skip('schedule view (week/2 weeks/month) toggler works', async ({ adminRolePage }) => {
const { page, userName } = adminRolePage;

const onCallScheduleName = generateRandomValue();
await createOnCallScheduleWithRotation(page, onCallScheduleName, userName);

// ScheduleView.OneWeek is selected by default
expect(await page.getByLabel(ScheduleView.OneWeek, { exact: true }).isChecked()).toBe(true);

expect(await page.locator(`#${HTML_ID.SCHEDULE_FINAL} .TEST_weekday`).count()).toStrictEqual(
scheduleViewToDaysInOneRow[ScheduleView.OneWeek]
);

await page.getByLabel(ScheduleView.TwoWeeks, { exact: true }).click();
expect(await page.getByLabel(ScheduleView.TwoWeeks, { exact: true }).isChecked()).toBe(true);
expect(await page.locator(`#${HTML_ID.SCHEDULE_FINAL} .TEST_weekday`).count()).toStrictEqual(
scheduleViewToDaysInOneRow[ScheduleView.TwoWeeks]
);

await page.getByLabel(ScheduleView.OneMonth, { exact: true }).click();
expect(await page.getByLabel(ScheduleView.OneMonth, { exact: true }).isChecked()).toBe(true);
expect(await page.locator(`#${HTML_ID.SCHEDULE_FINAL} .TEST_weekday`).count()).toBeGreaterThanOrEqual(28);
});
2 changes: 2 additions & 0 deletions grafana-plugin/src/assets/style/vars.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
--hover-selected: #f4f5f5;
--background-canvas: #f4f5f5;
--background-secondary: #f4f5f5;
--background-disabled: rgba(204, 204, 220, 0.11);
--border-medium-color: rgba(36, 41, 46, 0.3);
--border-medium: 1px solid rgba(36, 41, 46, 0.3);
--border-strong: 1px solid rgba(36, 41, 46, 0.4);
Expand Down Expand Up @@ -61,6 +62,7 @@
--oncall-icon-stroke-color: #181b1f;
--background-canvas: #111217;
--background-secondary: #22252b;
--background-disabled: rgba(204, 204, 220, 0.04);
--border-medium-color: rgba(204, 204, 220, 0.15);
--border-medium: 1px solid rgba(204, 204, 220, 0.15);
--border-strong: 1px solid rgba(204, 204, 220, 0.25);
Expand Down
25 changes: 0 additions & 25 deletions grafana-plugin/src/containers/Rotation/Rotation.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
position: relative;
}

.root:last-child .timeline {
padding-bottom: 0;
}

.slots {
width: 100%;
display: flex;
Expand All @@ -39,10 +35,6 @@
opacity: 0.5;
}

.slots__animate {
transition: transform 500ms ease;
}

.current-time {
position: absolute;
left: 450px;
Expand All @@ -64,11 +56,6 @@
margin: 0 2px;
}

.slots--tutorial {
position: absolute;
background: rgba(61, 113, 217, 0.15);
}

.pointer {
position: absolute;
top: -9px;
Expand All @@ -82,15 +69,3 @@
transform: scale(1);
opacity: 1;
}

.tutorial-slot {
width: 175px;
height: 28px;
border-radius: 2px;
margin: 0 1px;
padding: 4px;
}

.tutorial-slot--active {
box-shadow: var(--shadows-z3);
}
34 changes: 19 additions & 15 deletions grafana-plugin/src/containers/Rotation/Rotation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useMemo, useState } from 'react';
import React, { FC, useMemo } from 'react';

import { HorizontalGroup, LoadingPlaceholder } from '@grafana/ui';
import cn from 'classnames/bind';
Expand All @@ -9,11 +9,10 @@ import hash from 'object-hash';
import { ScheduleFiltersType } from 'components/ScheduleFilters/ScheduleFilters.types';
import { Text } from 'components/Text/Text';
import { ScheduleSlot } from 'containers/ScheduleSlot/ScheduleSlot';
import { Event, RotationFormLiveParams, ShiftSwap } from 'models/schedule/schedule.types';
import { scheduleViewToDaysInOneRow } from 'models/schedule/schedule.helpers';
import { Event, ScheduleView, ShiftSwap } from 'models/schedule/schedule.types';
import { useStore } from 'state/useStore';

import { RotationTutorial } from './RotationTutorial';

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

const cx = cn.bind(styles);
Expand All @@ -28,27 +27,26 @@ interface RotationProps {
handleAddShiftSwap?: (id: 'new', params: Partial<ShiftSwap>) => void;
handleOpenSchedule?: (event: Event) => void;
onShiftSwapClick?: (swapId: ShiftSwap['id']) => void;
days?: number;
transparent?: boolean;
tutorialParams?: RotationFormLiveParams;
simplified?: boolean;
filters?: ScheduleFiltersType;
getColor?: (event: Event) => string;
onSlotClick?: (event: Event) => void;
emptyText?: string;
showScheduleNameAsSlotTitle?: boolean;
startDate?: dayjs.Dayjs;
scheduleView?: ScheduleView;
}
brojd marked this conversation as resolved.
Show resolved Hide resolved

export const Rotation: FC<RotationProps> = observer((props) => {
const {
timezoneStore: { calendarStartDate, getDateInSelectedTimezone },
scheduleStore: { scheduleView: storeScheduleView },
} = useStore();
const {
events,
color: propsColor,
days = 7,
transparent = false,
tutorialParams,
onClick,
handleAddOverride,
handleAddShiftSwap,
Expand All @@ -60,18 +58,24 @@ export const Rotation: FC<RotationProps> = observer((props) => {
onSlotClick,
emptyText,
showScheduleNameAsSlotTitle,
startDate: propsStartDate,
scheduleView: propsScheduleView,
} = props;

const [animate, _setAnimate] = useState<boolean>(true);
const scheduleView = propsScheduleView || storeScheduleView;

const startDate = propsStartDate || calendarStartDate;

const days = scheduleViewToDaysInOneRow[scheduleView];

const handleRotationClick = (event: React.MouseEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect();
const x = event.clientX - rect.left; //x position within the element.
const width = event.currentTarget.offsetWidth;

const dayOffset = Math.floor((x / width) * 7);
const dayOffset = Math.floor((x / width) * scheduleViewToDaysInOneRow[scheduleView]);

const shiftStart = calendarStartDate.add(dayOffset, 'day');
const shiftStart = startDate.add(dayOffset, 'day');
const shiftEnd = shiftStart.add(1, 'day');

onClick(shiftStart, shiftEnd);
Expand Down Expand Up @@ -134,27 +138,27 @@ export const Rotation: FC<RotationProps> = observer((props) => {

const firstShift = events[0];
const firstShiftOffset = getDateInSelectedTimezone(firstShift.start).diff(
getDateInSelectedTimezone(calendarStartDate),
getDateInSelectedTimezone(startDate),
'seconds'
);
const base = 60 * 60 * 24 * days;

return firstShiftOffset / base;
}, [events, calendarStartDate]);
}, [events, startDate]);

return (
<div className={cx('root')} onClick={onClick && handleRotationClick}>
<div className={cx('timeline')}>
{tutorialParams && <RotationTutorial {...tutorialParams} />}
{events ? (
events.length ? (
<div
className={cx('slots', { slots__animate: animate, slots__transparent: transparent })}
className={cx('slots', { slots__transparent: transparent })}
style={{ transform: `translate(${x * 100}%, 0)` }}
>
{events.map((event) => {
return (
<ScheduleSlot
scheduleView={scheduleView}
key={hash(event)}
event={event}
color={propsColor || getColor(event)}
Expand Down
117 changes: 0 additions & 117 deletions grafana-plugin/src/containers/Rotation/RotationTutorial.tsx

This file was deleted.

7 changes: 5 additions & 2 deletions grafana-plugin/src/containers/RotationForm/RotationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const RotationForm = observer((props: RotationFormProps) => {

const [rotationName, setRotationName] = useState<string>(`[L${layerPriority}] Rotation`);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [offsetTop, setOffsetTop] = useState<number>(0);
const [offsetTop, setOffsetTop] = useState<number>(GRAFANA_HEADER_HEIGHT + 10);
const [draggablePosition, setDraggablePosition] = useState<{ x: number; y: number }>(undefined);

const [shiftStart, setShiftStart] = useState<dayjs.Dayjs>(propsShiftStart);
Expand Down Expand Up @@ -198,7 +198,10 @@ export const RotationForm = observer((props: RotationFormProps) => {
} catch (err) {
onError(err);
} finally {
setIsOpen(true);
// wait until a scroll to the "Rotations" happened
setTimeout(() => {
setIsOpen(true);
}, 100);
maskin25 marked this conversation as resolved.
Show resolved Hide resolved
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Schedule, Shift } from 'models/schedule/schedule.types';
import { ApiSchemas } from 'network/oncall-api/api.types';
import { getDateTime, getUTCString } from 'pages/schedule/Schedule.helpers';
import { useStore } from 'state/useStore';
import { getCoords, waitForElement } from 'utils/DOM';
import { HTML_ID, getCoords, waitForElement } from 'utils/DOM';
import { GRAFANA_HEADER_HEIGHT } from 'utils/consts';
import { useDebouncedCallback } from 'utils/hooks';

Expand Down Expand Up @@ -79,7 +79,7 @@ export const ScheduleOverrideForm: FC<RotationFormProps> = (props) => {
useEffect(() => {
(async () => {
if (isOpen) {
const elm = await waitForElement('#overrides-list');
const elm = await waitForElement(`#${HTML_ID.SCHEDULE_OVERRIDES_AND_SWAPS}`);
const modal = document.querySelector(`.${cx('draggable')}`) as HTMLDivElement;
const coords = getCoords(elm);
const offsetTop = Math.min(
Expand Down Expand Up @@ -171,7 +171,10 @@ export const ScheduleOverrideForm: FC<RotationFormProps> = (props) => {
} catch (err) {
onError(err);
} finally {
setIsOpen(true);
// wait until a scroll to the "Overrides and swaps" happened
setTimeout(() => {
setIsOpen(true);
}, 100);
}
};

Expand Down
Loading
Loading