# What this PR does ## Which issue(s) this PR fixes ## Checklist - [ ] Tests updated - [ ] Documentation added - [x] `CHANGELOG.md` updated
This commit is contained in:
parent
c0873007b0
commit
bee9943706
13 changed files with 336 additions and 269 deletions
|
|
@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Fixed UI permission related bug where Editors could not export their user iCal link
|
||||
- Fixed error when a shift is created using Etc/UTC as timezone
|
||||
- Fixed issue with refresh ical file task not considering empty string values
|
||||
- Schedules: Long popup does not fit screen & buttons unreachable & objects outside of the popup ([1002](https://github.com/grafana/oncall/issues/1002))
|
||||
- Can't scroll on integration settings page ([415](https://github.com/grafana/oncall/issues/415))
|
||||
- Team change in the Integration page always causes 403 ([1292](https://github.com/grafana/oncall/issues/1292))
|
||||
- Schedules: Permalink doesn't work with multi-teams ([940](https://github.com/grafana/oncall/issues/940))
|
||||
- Schedules list -> expanded schedule blows page width ([1293](https://github.com/grafana/oncall/issues/1293))
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
top: 10%;
|
||||
max-height: 80%;
|
||||
max-height: 90%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-image: initial;
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
box-shadow: var(--shadows-z3);
|
||||
border-radius: 2px;
|
||||
z-index: 10;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
max-height: 300px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.user {
|
||||
|
|
@ -55,7 +57,6 @@
|
|||
border-radius: 2px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.user-buttons {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.title {
|
||||
background: var(--background-primary);
|
||||
top: -15px;
|
||||
position: sticky;
|
||||
margin: -15px -15px 0 -15px;
|
||||
padding: 15px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
top: 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -277,151 +277,155 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
</Draggable>
|
||||
)}
|
||||
>
|
||||
<VerticalGroup>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text size="medium">
|
||||
<HorizontalGroup spacing="sm">
|
||||
<span>[L{shiftId === 'new' ? layerPriority : shift?.priority_level}]</span>
|
||||
{shiftId === 'new' ? 'New Rotation' : 'Update Rotation'}
|
||||
</HorizontalGroup>
|
||||
</Text>
|
||||
<HorizontalGroup>
|
||||
{shiftId !== 'new' && (
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
tooltip="Delete"
|
||||
name="trash-alt"
|
||||
onClick={() => setShowDeleteRotationConfirmation(true)}
|
||||
/>
|
||||
)}
|
||||
<IconButton variant="secondary" className={cx('drag-handler')} name="draggabledots" />
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
<div className={cx('content')}>
|
||||
<VerticalGroup>
|
||||
<div className={cx('two-fields')}>
|
||||
<Field
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Rotation start
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
minMoment={shiftStart}
|
||||
value={rotationStart}
|
||||
onChange={setRotationStart}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('rotationStart')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label={
|
||||
<HorizontalGroup spacing="xs">
|
||||
<Text type="primary" size="small">
|
||||
Rotation end
|
||||
</Text>
|
||||
<InlineSwitch
|
||||
className={cx('inline-switch')}
|
||||
transparent
|
||||
value={!endLess}
|
||||
onChange={handleChangeEndless}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
}
|
||||
>
|
||||
{endLess ? (
|
||||
<div style={{ lineHeight: '32px' }}>
|
||||
<Text type="secondary">Endless</Text>
|
||||
</div>
|
||||
) : (
|
||||
<DateTimePicker value={rotationEnd} onChange={setRotationEnd} timezone={currentTimezone} />
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
<>
|
||||
<div className={cx('title')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text size="medium">
|
||||
<HorizontalGroup spacing="sm">
|
||||
<span>[L{shiftId === 'new' ? layerPriority : shift?.priority_level}]</span>
|
||||
{shiftId === 'new' ? 'New Rotation' : 'Update Rotation'}
|
||||
</HorizontalGroup>
|
||||
</Text>
|
||||
<HorizontalGroup>
|
||||
<Field className={cx('control')} label="Repeat shifts every">
|
||||
<Select
|
||||
maxMenuHeight={120}
|
||||
value={repeatEveryValue}
|
||||
options={repeatShiftsEveryOptions}
|
||||
onChange={handleRepeatEveryValueChange}
|
||||
allowCustomValue
|
||||
{shiftId !== 'new' && (
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
tooltip="Delete"
|
||||
name="trash-alt"
|
||||
onClick={() => setShowDeleteRotationConfirmation(true)}
|
||||
/>
|
||||
</Field>
|
||||
<Field className={cx('control')} label="">
|
||||
<RemoteSelect
|
||||
href="/oncall_shifts/frequency_options/"
|
||||
value={repeatEveryPeriod}
|
||||
onChange={setRepeatEveryPeriod}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
<IconButton variant="secondary" className={cx('drag-handler')} name="draggabledots" />
|
||||
</HorizontalGroup>
|
||||
{(repeatEveryPeriod === 0 || repeatEveryPeriod === 1) && (
|
||||
<Field label="Select days to repeat">
|
||||
<DaysSelector
|
||||
options={store.scheduleStore.byDayOptions}
|
||||
value={selectedDays}
|
||||
onChange={(value) => setSelectedDays(value)}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
<div className={cx('two-fields')}>
|
||||
<Field
|
||||
className={cx('date-time-picker')}
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Parent shift start
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
value={shiftStart}
|
||||
onChange={updateShiftStart}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('shiftStart')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
className={cx('date-time-picker')}
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Parent shift end
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
value={shiftEnd}
|
||||
onChange={setShiftEnd}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('shiftEnd')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
<UserGroups
|
||||
value={userGroups}
|
||||
onChange={setUserGroups}
|
||||
isMultipleGroups={true}
|
||||
renderUser={renderUser}
|
||||
showError={!isFormValid}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text type="secondary">Timezone: {getTzOffsetString(dayjs().tz(currentTimezone))}</Text>
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={onHide}>
|
||||
{shiftId === 'new' ? 'Cancel' : 'Close'}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleCreate} disabled={!isFormValid}>
|
||||
{shiftId === 'new' ? 'Create' : 'Update'}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
<VerticalGroup>
|
||||
<div className={cx('content')}>
|
||||
<VerticalGroup>
|
||||
<div className={cx('two-fields')}>
|
||||
<Field
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Rotation start
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
minMoment={shiftStart}
|
||||
value={rotationStart}
|
||||
onChange={setRotationStart}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('rotationStart')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label={
|
||||
<HorizontalGroup spacing="xs">
|
||||
<Text type="primary" size="small">
|
||||
Rotation end
|
||||
</Text>
|
||||
<InlineSwitch
|
||||
className={cx('inline-switch')}
|
||||
transparent
|
||||
value={!endLess}
|
||||
onChange={handleChangeEndless}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
}
|
||||
>
|
||||
{endLess ? (
|
||||
<div style={{ lineHeight: '32px' }}>
|
||||
<Text type="secondary">Endless</Text>
|
||||
</div>
|
||||
) : (
|
||||
<DateTimePicker value={rotationEnd} onChange={setRotationEnd} timezone={currentTimezone} />
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
<HorizontalGroup>
|
||||
<Field className={cx('control')} label="Repeat shifts every">
|
||||
<Select
|
||||
maxMenuHeight={120}
|
||||
value={repeatEveryValue}
|
||||
options={repeatShiftsEveryOptions}
|
||||
onChange={handleRepeatEveryValueChange}
|
||||
allowCustomValue
|
||||
/>
|
||||
</Field>
|
||||
<Field className={cx('control')} label="">
|
||||
<RemoteSelect
|
||||
href="/oncall_shifts/frequency_options/"
|
||||
value={repeatEveryPeriod}
|
||||
onChange={setRepeatEveryPeriod}
|
||||
/>
|
||||
</Field>
|
||||
</HorizontalGroup>
|
||||
{(repeatEveryPeriod === 0 || repeatEveryPeriod === 1) && (
|
||||
<Field label="Select days to repeat">
|
||||
<DaysSelector
|
||||
options={store.scheduleStore.byDayOptions}
|
||||
value={selectedDays}
|
||||
onChange={(value) => setSelectedDays(value)}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
<div className={cx('two-fields')}>
|
||||
<Field
|
||||
className={cx('date-time-picker')}
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Parent shift start
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
value={shiftStart}
|
||||
onChange={updateShiftStart}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('shiftStart')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
className={cx('date-time-picker')}
|
||||
label={
|
||||
<Text type="primary" size="small">
|
||||
Parent shift end
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<DateTimePicker
|
||||
value={shiftEnd}
|
||||
onChange={setShiftEnd}
|
||||
timezone={currentTimezone}
|
||||
onFocus={getFocusHandler('shiftEnd')}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
<UserGroups
|
||||
value={userGroups}
|
||||
onChange={setUserGroups}
|
||||
isMultipleGroups={true}
|
||||
renderUser={renderUser}
|
||||
showError={!isFormValid}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text type="secondary">Timezone: {getTzOffsetString(dayjs().tz(currentTimezone))}</Text>
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={onHide}>
|
||||
{shiftId === 'new' ? 'Cancel' : 'Close'}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleCreate} disabled={!isFormValid}>
|
||||
{shiftId === 'new' ? 'Create' : 'Update'}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</>
|
||||
{showDeleteRotationConfirmation && (
|
||||
<GrafanaModal
|
||||
isOpen
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@
|
|||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
|
@ -54,7 +56,6 @@
|
|||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
flex-shrink: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.details {
|
||||
|
|
|
|||
|
|
@ -123,12 +123,14 @@ const ScheduleSlot: FC<ScheduleSlotProps> = observer((props) => {
|
|||
duration={duration}
|
||||
/>
|
||||
)}
|
||||
{userIndex === 0 && label && (
|
||||
<div className={cx('label')} style={{ color }}>
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
<div className={cx('title')}>{title}</div>
|
||||
<div className={cx('title')}>
|
||||
{userIndex === 0 && label && (
|
||||
<div className={cx('label')} style={{ color }}>
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
/* Navigation/Layout */
|
||||
|
||||
.drawer-content {
|
||||
overflow: auto !important; /* fix https://github.com/grafana/oncall/issues/415 */
|
||||
}
|
||||
|
||||
.page-body {
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,8 @@ export class ScheduleStore extends BaseStore {
|
|||
...this.items,
|
||||
[item.id]: item,
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,14 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
this.update().then(() => this.parseQueryParams(true));
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<IntegrationsProps>): void {
|
||||
if (prevProps.match.params.id && !this.props.match.params.id) {
|
||||
this.setState({ errorData: initErrorDataState() }, () => {
|
||||
this.parseQueryParams();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedAlertReceiveChannel = (alertReceiveChannelId: AlertReceiveChannel['id'], shouldRedirect = false) => {
|
||||
const { store, history } = this.props;
|
||||
store.selectedAlertReceiveChannel = alertReceiveChannelId;
|
||||
|
|
|
|||
|
|
@ -27,3 +27,8 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
margin: 50px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import dayjs from 'dayjs';
|
|||
import { observer } from 'mobx-react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
|
||||
import PageErrorHandlingWrapper from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper';
|
||||
import PageErrorHandlingWrapper, { PageBaseState } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper';
|
||||
import {
|
||||
getWrongTeamResponseInfo,
|
||||
initErrorDataState,
|
||||
} from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers';
|
||||
import PluginLink from 'components/PluginLink/PluginLink';
|
||||
import ScheduleWarning from 'components/ScheduleWarning/ScheduleWarning';
|
||||
import Text from 'components/Text/Text';
|
||||
|
|
@ -33,7 +37,7 @@ const cx = cn.bind(styles);
|
|||
|
||||
interface SchedulePageProps extends PageProps, WithStoreProps, RouteComponentProps<{ id: string }> {}
|
||||
|
||||
interface SchedulePageState {
|
||||
interface SchedulePageState extends PageBaseState {
|
||||
startMoment: dayjs.Dayjs;
|
||||
schedulePeriodType: string;
|
||||
renderType: string;
|
||||
|
|
@ -59,6 +63,7 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
|
|||
isLoading: true,
|
||||
showEditForm: false,
|
||||
showScheduleICalSettings: false,
|
||||
errorData: initErrorDataState(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -101,8 +106,11 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
|
|||
shiftIdToShowOverridesForm,
|
||||
showEditForm,
|
||||
showScheduleICalSettings,
|
||||
errorData,
|
||||
} = this.state;
|
||||
|
||||
const { isNotFoundError } = errorData;
|
||||
|
||||
const { scheduleStore, currentTimezone } = store;
|
||||
|
||||
const users = store.userStore.getSearchResult().results;
|
||||
|
|
@ -115,131 +123,147 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
|
|||
shiftIdToShowOverridesForm;
|
||||
|
||||
return (
|
||||
<PageErrorHandlingWrapper pageName="schedules">
|
||||
<PageErrorHandlingWrapper errorData={errorData} objectName="schedule" pageName="schedules">
|
||||
{() => (
|
||||
<>
|
||||
<div className={cx('root')}>
|
||||
<VerticalGroup spacing="lg">
|
||||
<div className={cx('header')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<HorizontalGroup>
|
||||
<PluginLink query={{ page: 'schedules' }}>
|
||||
<IconButton style={{ marginTop: '5px' }} name="arrow-left" size="xl" />
|
||||
</PluginLink>
|
||||
<Text.Title
|
||||
editable
|
||||
editModalTitle="Schedule name"
|
||||
level={2}
|
||||
onTextChange={this.handleNameChange}
|
||||
>
|
||||
{schedule?.name}
|
||||
</Text.Title>
|
||||
{schedule && <ScheduleWarning item={schedule} />}
|
||||
</HorizontalGroup>
|
||||
<HorizontalGroup spacing="lg">
|
||||
{users && (
|
||||
<HorizontalGroup>
|
||||
<Text type="secondary">Current timezone:</Text>
|
||||
<UserTimezoneSelect
|
||||
value={currentTimezone}
|
||||
users={users}
|
||||
onChange={this.handleTimezoneChange}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
)}
|
||||
<HorizontalGroup>
|
||||
<HorizontalGroup>
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={this.handleExportClick()}>
|
||||
Export
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
|
||||
{(schedule?.type === ScheduleType.Ical || schedule?.type === ScheduleType.Calendar) && (
|
||||
<Button variant="secondary" onClick={this.handleReloadClick(scheduleId)}>
|
||||
Reload
|
||||
</Button>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
<ToolbarButton
|
||||
icon="cog"
|
||||
tooltip="Settings"
|
||||
onClick={() => {
|
||||
this.setState({ showEditForm: true });
|
||||
}}
|
||||
/>
|
||||
<WithConfirm>
|
||||
<ToolbarButton icon="trash-alt" tooltip="Delete" onClick={this.handleDelete} />
|
||||
</WithConfirm>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
{isNotFoundError ? (
|
||||
<div className={cx('not-found')}>
|
||||
<VerticalGroup spacing="lg" align="center">
|
||||
<Text.Title level={1}>404</Text.Title>
|
||||
<Text.Title level={4}>Schedule not found</Text.Title>
|
||||
<PluginLink query={{ page: 'schedules' }}>
|
||||
<Button variant="secondary" icon="arrow-left" size="md">
|
||||
Go to Schedules page
|
||||
</Button>
|
||||
</PluginLink>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
<div className={cx('users-timezones')}>
|
||||
<UsersTimezones
|
||||
scheduleId={scheduleId}
|
||||
startMoment={startMoment}
|
||||
onCallNow={schedule?.on_call_now || []}
|
||||
userIds={
|
||||
scheduleStore.relatedUsers[scheduleId] ? Object.keys(scheduleStore.relatedUsers[scheduleId]) : []
|
||||
}
|
||||
tz={currentTimezone}
|
||||
onTzChange={this.handleTimezoneChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={cx('rotations')}>
|
||||
<div className={cx('controls')}>
|
||||
) : (
|
||||
<VerticalGroup spacing="lg">
|
||||
<div className={cx('header')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={this.handleTodayClick}>
|
||||
Today
|
||||
</Button>
|
||||
<HorizontalGroup spacing="xs">
|
||||
<Button variant="secondary" onClick={this.handleLeftClick}>
|
||||
<Icon name="angle-left" />
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={this.handleRightClick}>
|
||||
<Icon name="angle-right" />
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<Text.Title style={{ marginLeft: '8px' }} level={4} type="primary">
|
||||
{startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')}
|
||||
<PluginLink query={{ page: 'schedules' }}>
|
||||
<IconButton style={{ marginTop: '5px' }} name="arrow-left" size="xl" />
|
||||
</PluginLink>
|
||||
<Text.Title
|
||||
editable
|
||||
editModalTitle="Schedule name"
|
||||
level={2}
|
||||
onTextChange={this.handleNameChange}
|
||||
>
|
||||
{schedule?.name}
|
||||
</Text.Title>
|
||||
{schedule && <ScheduleWarning item={schedule} />}
|
||||
</HorizontalGroup>
|
||||
<HorizontalGroup spacing="lg">
|
||||
{users && (
|
||||
<HorizontalGroup>
|
||||
<Text type="secondary">Current timezone:</Text>
|
||||
<UserTimezoneSelect
|
||||
value={currentTimezone}
|
||||
users={users}
|
||||
onChange={this.handleTimezoneChange}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
)}
|
||||
<HorizontalGroup>
|
||||
<HorizontalGroup>
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={this.handleExportClick()}>
|
||||
Export
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
|
||||
{(schedule?.type === ScheduleType.Ical || schedule?.type === ScheduleType.Calendar) && (
|
||||
<Button variant="secondary" onClick={this.handleReloadClick(scheduleId)}>
|
||||
Reload
|
||||
</Button>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
<ToolbarButton
|
||||
icon="cog"
|
||||
tooltip="Settings"
|
||||
onClick={() => {
|
||||
this.setState({ showEditForm: true });
|
||||
}}
|
||||
/>
|
||||
<WithConfirm>
|
||||
<ToolbarButton icon="trash-alt" tooltip="Delete" onClick={this.handleDelete} />
|
||||
</WithConfirm>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
<ScheduleFinal
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onClick={this.handleShowForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Rotations
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onCreate={this.handleCreateRotation}
|
||||
onUpdate={this.handleUpdateRotation}
|
||||
onDelete={this.handleDeleteRotation}
|
||||
shiftIdToShowRotationForm={shiftIdToShowRotationForm}
|
||||
onShowRotationForm={this.handleShowRotationForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ScheduleOverrides
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onCreate={this.handleCreateOverride}
|
||||
onUpdate={this.handleUpdateOverride}
|
||||
onDelete={this.handleDeleteOverride}
|
||||
shiftIdToShowRotationForm={shiftIdToShowOverridesForm}
|
||||
onShowRotationForm={this.handleShowOverridesForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</VerticalGroup>
|
||||
<div className={cx('users-timezones')}>
|
||||
<UsersTimezones
|
||||
scheduleId={scheduleId}
|
||||
startMoment={startMoment}
|
||||
onCallNow={schedule?.on_call_now || []}
|
||||
userIds={
|
||||
scheduleStore.relatedUsers[scheduleId]
|
||||
? Object.keys(scheduleStore.relatedUsers[scheduleId])
|
||||
: []
|
||||
}
|
||||
tz={currentTimezone}
|
||||
onTzChange={this.handleTimezoneChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={cx('rotations')}>
|
||||
<div className={cx('controls')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<HorizontalGroup>
|
||||
<Button variant="secondary" onClick={this.handleTodayClick}>
|
||||
Today
|
||||
</Button>
|
||||
<HorizontalGroup spacing="xs">
|
||||
<Button variant="secondary" onClick={this.handleLeftClick}>
|
||||
<Icon name="angle-left" />
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={this.handleRightClick}>
|
||||
<Icon name="angle-right" />
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<Text.Title style={{ marginLeft: '8px' }} level={4} type="primary">
|
||||
{startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')}
|
||||
</Text.Title>
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
<ScheduleFinal
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onClick={this.handleShowForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Rotations
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onCreate={this.handleCreateRotation}
|
||||
onUpdate={this.handleUpdateRotation}
|
||||
onDelete={this.handleDeleteRotation}
|
||||
shiftIdToShowRotationForm={shiftIdToShowRotationForm}
|
||||
onShowRotationForm={this.handleShowRotationForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ScheduleOverrides
|
||||
scheduleId={scheduleId}
|
||||
currentTimezone={currentTimezone}
|
||||
startMoment={startMoment}
|
||||
onCreate={this.handleCreateOverride}
|
||||
onUpdate={this.handleUpdateOverride}
|
||||
onDelete={this.handleDeleteOverride}
|
||||
shiftIdToShowRotationForm={shiftIdToShowOverridesForm}
|
||||
onShowRotationForm={this.handleShowOverridesForm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
</div>
|
||||
{showEditForm && schedule && (
|
||||
<ScheduleForm
|
||||
|
|
@ -325,7 +349,9 @@ class SchedulePage extends React.Component<SchedulePageProps, SchedulePageState>
|
|||
|
||||
const { startMoment } = this.state;
|
||||
|
||||
store.scheduleStore.updateItem(scheduleId); // to refresh current oncall users
|
||||
store.scheduleStore
|
||||
.updateItem(scheduleId) // to refresh current oncall users
|
||||
.catch((error) => this.setState({ errorData: { ...getWrongTeamResponseInfo(error) } }));
|
||||
store.scheduleStore.updateRelatedUsers(scheduleId); // to refresh related users
|
||||
|
||||
return Promise.all([
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
.schedule {
|
||||
position: relative;
|
||||
margin: 20px 0;
|
||||
max-width: calc(100vw - 104px);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue