Commit 5b15305a authored by David O'Regan's avatar David O'Regan Committed by Natalia Tepluhina

Update rotation assignee timestamp

parent 72c5f09a
<script> <script>
import { import { GlCard, GlButtonGroup, GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
GlSprintf,
GlCard,
GlButtonGroup,
GlButton,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { import {
...@@ -16,7 +9,7 @@ import { ...@@ -16,7 +9,7 @@ import {
nDaysBefore, nDaysBefore,
nDaysAfter, nDaysAfter,
} from '~/lib/utils/datetime_utility'; } from '~/lib/utils/datetime_utility';
import { s__, __ } from '~/locale'; import { s__ } from '~/locale';
import { addRotationModalId, editRotationModalId, PRESET_TYPES } from '../constants'; import { addRotationModalId, editRotationModalId, PRESET_TYPES } from '../constants';
import getShiftsForRotations from '../graphql/queries/get_oncall_schedules_with_rotations_shifts.query.graphql'; import getShiftsForRotations from '../graphql/queries/get_oncall_schedules_with_rotations_shifts.query.graphql';
import EditScheduleModal from './add_edit_schedule_modal.vue'; import EditScheduleModal from './add_edit_schedule_modal.vue';
...@@ -24,10 +17,9 @@ import DeleteScheduleModal from './delete_schedule_modal.vue'; ...@@ -24,10 +17,9 @@ import DeleteScheduleModal from './delete_schedule_modal.vue';
import AddEditRotationModal from './rotations/components/add_edit_rotation_modal.vue'; import AddEditRotationModal from './rotations/components/add_edit_rotation_modal.vue';
import RotationsListSection from './schedule/components/rotations_list_section.vue'; import RotationsListSection from './schedule/components/rotations_list_section.vue';
import ScheduleTimelineSection from './schedule/components/schedule_timeline_section.vue'; import ScheduleTimelineSection from './schedule/components/schedule_timeline_section.vue';
import { getTimeframeForWeeksView } from './schedule/utils'; import { getTimeframeForWeeksView, selectedTimezoneFormattedOffset } from './schedule/utils';
export const i18n = { export const i18n = {
scheduleForTz: s__('OnCallSchedules|On-call schedule for the %{timezone}'),
editScheduleLabel: s__('OnCallSchedules|Edit schedule'), editScheduleLabel: s__('OnCallSchedules|Edit schedule'),
deleteScheduleLabel: s__('OnCallSchedules|Delete schedule'), deleteScheduleLabel: s__('OnCallSchedules|Delete schedule'),
rotationTitle: s__('OnCallSchedules|Rotations'), rotationTitle: s__('OnCallSchedules|Rotations'),
...@@ -51,7 +43,6 @@ export default { ...@@ -51,7 +43,6 @@ export default {
GlButton, GlButton,
GlButtonGroup, GlButtonGroup,
GlCard, GlCard,
GlSprintf,
AddEditRotationModal, AddEditRotationModal,
DeleteScheduleModal, DeleteScheduleModal,
EditScheduleModal, EditScheduleModal,
...@@ -63,6 +54,11 @@ export default { ...@@ -63,6 +54,11 @@ export default {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
inject: ['projectPath', 'timezones'], inject: ['projectPath', 'timezones'],
provide() {
return {
selectedTimezone: this.selectedTimezone,
};
},
props: { props: {
schedule: { schedule: {
type: Object, type: Object,
...@@ -73,7 +69,7 @@ export default { ...@@ -73,7 +69,7 @@ export default {
rotations: { rotations: {
query: getShiftsForRotations, query: getShiftsForRotations,
variables() { variables() {
this.timeframeStartDate.setHours(1, 0, 0, 0); this.timeframeStartDate.setHours(0, 0, 0, 0);
const startsAt = this.timeframeStartDate; const startsAt = this.timeframeStartDate;
const endsAt = nWeeksAfter(startsAt, 2); const endsAt = nWeeksAfter(startsAt, 2);
...@@ -101,9 +97,11 @@ export default { ...@@ -101,9 +97,11 @@ export default {
}; };
}, },
computed: { computed: {
selectedTimezone() {
return this.timezones.find((tz) => tz.identifier === this.schedule.timezone);
},
offset() { offset() {
const selectedTz = this.timezones.find((tz) => tz.identifier === this.schedule.timezone); return selectedTimezoneFormattedOffset(this.selectedTimezone.formatted_offset);
return __(`(UTC ${selectedTz.formatted_offset})`);
}, },
timeframe() { timeframe() {
return getTimeframeForWeeksView(this.timeframeStartDate); return getTimeframeForWeeksView(this.timeframeStartDate);
...@@ -197,10 +195,7 @@ export default { ...@@ -197,10 +195,7 @@ export default {
</div> </div>
</template> </template>
<p class="gl-text-gray-500 gl-mb-3" data-testid="scheduleBody"> <p class="gl-text-gray-500 gl-mb-3" data-testid="scheduleBody">
<gl-sprintf :message="$options.i18n.scheduleForTz"> {{ schedule.timezone }} | {{ offset }}
<template #timezone>{{ schedule.timezone }}</template>
</gl-sprintf>
| {{ offset }}
</p> </p>
<div class="gl-display-flex gl-justify-content-space-between gl-mb-3"> <div class="gl-display-flex gl-justify-content-space-between gl-mb-3">
<div class="gl-display-flex gl-align-items-center"> <div class="gl-display-flex gl-align-items-center">
......
...@@ -4,6 +4,7 @@ import { uniqueId } from 'lodash'; ...@@ -4,6 +4,7 @@ import { uniqueId } from 'lodash';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { truncate } from '~/lib/utils/text_utility'; import { truncate } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { selectedTimezoneFormattedOffset } from '../../schedule/utils';
export const SHIFT_WIDTHS = { export const SHIFT_WIDTHS = {
md: 140, md: 140,
...@@ -12,6 +13,7 @@ export const SHIFT_WIDTHS = { ...@@ -12,6 +13,7 @@ export const SHIFT_WIDTHS = {
}; };
const ROTATION_CENTER_CLASS = 'gl-display-flex gl-justify-content-center gl-align-items-center'; const ROTATION_CENTER_CLASS = 'gl-display-flex gl-justify-content-center gl-align-items-center';
export const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM';
export default { export default {
ROTATION_CENTER_CLASS, ROTATION_CENTER_CLASS,
...@@ -19,6 +21,7 @@ export default { ...@@ -19,6 +21,7 @@ export default {
GlAvatar, GlAvatar,
GlPopover, GlPopover,
}, },
inject: ['selectedTimezone'],
props: { props: {
assignee: { assignee: {
type: Object, type: Object,
...@@ -54,7 +57,9 @@ export default { ...@@ -54,7 +57,9 @@ export default {
}, },
endsAt() { endsAt() {
return sprintf(__('Ends: %{endsAt}'), { return sprintf(__('Ends: %{endsAt}'), {
endsAt: formatDate(this.rotationAssigneeEndsAt, 'mmmm d, yyyy, h:MMtt Z'), endsAt: `${formatDate(this.rotationAssigneeEndsAt, TIME_DATE_FORMAT)} ${
this.timezoneOffset
}`,
}); });
}, },
rotationAssigneeUniqueID() { rotationAssigneeUniqueID() {
...@@ -65,9 +70,14 @@ export default { ...@@ -65,9 +70,14 @@ export default {
}, },
startsAt() { startsAt() {
return sprintf(__('Starts: %{startsAt}'), { return sprintf(__('Starts: %{startsAt}'), {
startsAt: formatDate(this.rotationAssigneeStartsAt, 'mmmm d, yyyy, h:MMtt Z'), startsAt: `${formatDate(this.rotationAssigneeStartsAt, TIME_DATE_FORMAT)} ${
this.timezoneOffset
}`,
}); });
}, },
timezoneOffset() {
return selectedTimezoneFormattedOffset(this.selectedTimezone.formatted_offset);
},
}, },
}; };
</script> </script>
...@@ -93,8 +103,12 @@ export default { ...@@ -93,8 +103,12 @@ export default {
triggers="hover" triggers="hover"
placement="top" placement="top"
> >
<p class="gl-m-0" data-testid="rotation-assignee-starts-at">{{ startsAt }}</p> <p class="gl-m-0" data-testid="rotation-assignee-starts-at">
<p class="gl-m-0" data-testid="rotation-assignee-ends-at">{{ endsAt }}</p> {{ startsAt }}
</p>
<p class="gl-m-0" data-testid="rotation-assignee-ends-at">
{{ endsAt }}
</p>
</gl-popover> </gl-popover>
</div> </div>
</template> </template>
...@@ -45,7 +45,7 @@ export default { ...@@ -45,7 +45,7 @@ export default {
ref="dailyHourCell" ref="dailyHourCell"
class="sublabel-value" class="sublabel-value"
data-testid="sublabel-value" data-testid="sublabel-value"
>{{ hour }}</span >{{ hour - 1 }}</span
> >
<span <span
v-if="isToday" v-if="isToday"
......
...@@ -40,6 +40,11 @@ export default { ...@@ -40,6 +40,11 @@ export default {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
props: { props: {
loading: {
type: Boolean,
required: false,
default: false,
},
presetType: { presetType: {
type: String, type: String,
required: true, required: true,
...@@ -48,18 +53,13 @@ export default { ...@@ -48,18 +53,13 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
timeframe: {
type: Array,
required: true,
},
scheduleIid: { scheduleIid: {
type: String, type: String,
required: true, required: true,
}, },
loading: { timeframe: {
type: Boolean, type: Array,
required: false, required: true,
default: false,
}, },
}, },
data() { data() {
......
...@@ -11,14 +11,6 @@ export default { ...@@ -11,14 +11,6 @@ export default {
WeeksScheduleShift, WeeksScheduleShift,
}, },
props: { props: {
timeframeItem: {
type: [Date, Object],
required: true,
},
timeframe: {
type: Array,
required: true,
},
presetType: { presetType: {
type: String, type: String,
required: true, required: true,
...@@ -27,6 +19,14 @@ export default { ...@@ -27,6 +19,14 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
timeframeItem: {
type: [Date, Object],
required: true,
},
timeframe: {
type: Array,
required: true,
},
}, },
data() { data() {
return { return {
......
import { newDate } from '~/lib/utils/datetime_utility'; import { newDate } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import { PRESET_DEFAULTS, DAYS_IN_WEEK } from '../../constants'; import { PRESET_DEFAULTS, DAYS_IN_WEEK } from '../../constants';
/** /**
...@@ -32,3 +33,18 @@ export const getTimeframeForWeeksView = (initialDate = new Date()) => { ...@@ -32,3 +33,18 @@ export const getTimeframeForWeeksView = (initialDate = new Date()) => {
return timeframe; return timeframe;
}; };
/**
* This method returns the formatted offset for a
* given timezone against UTC
*
*
* @param {String} offset - the selected timezone offset.
* @returns {String}
*
* @example
* selectedTimezoneFormattedOffset(offset:"-10:00")
* => (UTC -10:00)
*
*/
export const selectedTimezoneFormattedOffset = (offset) => __(`(UTC ${offset})`);
...@@ -4,7 +4,7 @@ import AddEditScheduleForm, { ...@@ -4,7 +4,7 @@ import AddEditScheduleForm, {
i18n, i18n,
} from 'ee/oncall_schedules/components/add_edit_schedule_form.vue'; } from 'ee/oncall_schedules/components/add_edit_schedule_form.vue';
import { getOncallSchedulesQueryResponse } from './mocks/apollo_mock'; import { getOncallSchedulesQueryResponse } from './mocks/apollo_mock';
import mockTimezones from './mocks/mockTimezones.json'; import mockTimezones from './mocks/mock_timezones.json';
describe('AddEditScheduleForm', () => { describe('AddEditScheduleForm', () => {
let wrapper; let wrapper;
......
...@@ -15,7 +15,7 @@ import { ...@@ -15,7 +15,7 @@ import {
updateScheduleResponse, updateScheduleResponse,
updateScheduleResponseWithErrors, updateScheduleResponseWithErrors,
} from './mocks/apollo_mock'; } from './mocks/apollo_mock';
import mockTimezones from './mocks/mockTimezones.json'; import mockTimezones from './mocks/mock_timezones.json';
describe('AddScheduleModal', () => { describe('AddScheduleModal', () => {
let wrapper; let wrapper;
......
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
getFormattedTimezone, getFormattedTimezone,
getParticipantsForSave, getParticipantsForSave,
} from 'ee/oncall_schedules/utils/common_utils'; } from 'ee/oncall_schedules/utils/common_utils';
import mockTimezones from './mocks/mockTimezones.json'; import mockTimezones from './mocks/mock_timezones.json';
describe('getFormattedTimezone', () => { describe('getFormattedTimezone', () => {
it('formats the timezone', () => { it('formats the timezone', () => {
......
import { GlCard, GlSprintf, GlButton } from '@gitlab/ui'; import { GlCard, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import OnCallSchedule, { i18n } from 'ee/oncall_schedules/components/oncall_schedule.vue'; import OnCallSchedule, { i18n } from 'ee/oncall_schedules/components/oncall_schedule.vue';
import RotationsListSection from 'ee/oncall_schedules/components/schedule/components/rotations_list_section.vue'; import RotationsListSection from 'ee/oncall_schedules/components/schedule/components/rotations_list_section.vue';
...@@ -8,7 +8,7 @@ import { PRESET_TYPES } from 'ee/oncall_schedules/constants'; ...@@ -8,7 +8,7 @@ import { PRESET_TYPES } from 'ee/oncall_schedules/constants';
import * as commonUtils from 'ee/oncall_schedules/utils/common_utils'; import * as commonUtils from 'ee/oncall_schedules/utils/common_utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import * as dateTimeUtility from '~/lib/utils/datetime_utility'; import * as dateTimeUtility from '~/lib/utils/datetime_utility';
import mockTimezones from './mocks/mockTimezones.json'; import mockTimezones from './mocks/mock_timezones.json';
describe('On-call schedule', () => { describe('On-call schedule', () => {
let wrapper; let wrapper;
...@@ -56,7 +56,6 @@ describe('On-call schedule', () => { ...@@ -56,7 +56,6 @@ describe('On-call schedule', () => {
}, },
stubs: { stubs: {
GlCard, GlCard,
GlSprintf,
}, },
mocks: { $apollo }, mocks: { $apollo },
}), }),
...@@ -90,7 +89,7 @@ describe('On-call schedule', () => { ...@@ -90,7 +89,7 @@ describe('On-call schedule', () => {
}); });
it('shows timezone info', () => { it('shows timezone info', () => {
const timezone = i18n.scheduleForTz.replace('%{timezone}', lastTz.identifier); const timezone = lastTz.identifier;
const offset = `(UTC ${lastTz.formatted_offset})`; const offset = `(UTC ${lastTz.formatted_offset})`;
const description = findSchedule().text(); const description = findSchedule().text();
expect(description).toContain(timezone); expect(description).toContain(timezone);
......
...@@ -2,11 +2,14 @@ import { GlAvatar, GlPopover } from '@gitlab/ui'; ...@@ -2,11 +2,14 @@ import { GlAvatar, GlPopover } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import RotationAssignee, { import RotationAssignee, {
SHIFT_WIDTHS, SHIFT_WIDTHS,
TIME_DATE_FORMAT,
} from 'ee/oncall_schedules/components/rotations/components/rotation_assignee.vue'; } from 'ee/oncall_schedules/components/rotations/components/rotation_assignee.vue';
import { selectedTimezoneFormattedOffset } from 'ee/oncall_schedules/components/schedule/utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { truncate } from '~/lib/utils/text_utility'; import { truncate } from '~/lib/utils/text_utility';
import mockRotations from '../../mocks/mock_rotation.json'; import mockRotations from '../../mocks/mock_rotation.json';
import mockTimezones from '../../mocks/mock_timezones.json';
jest.mock('lodash/uniqueId', () => (prefix) => `${prefix}fakeUniqueId`); jest.mock('lodash/uniqueId', () => (prefix) => `${prefix}fakeUniqueId`);
...@@ -23,12 +26,17 @@ describe('RotationAssignee', () => { ...@@ -23,12 +26,17 @@ describe('RotationAssignee', () => {
const findName = () => wrapper.findByTestId('rotation-assignee-name'); const findName = () => wrapper.findByTestId('rotation-assignee-name');
const formattedDate = (date) => { const formattedDate = (date) => {
return formatDate(date, 'mmmm d, yyyy, h:MMtt Z'); return formatDate(date, TIME_DATE_FORMAT);
}; };
const selectedTimezone = mockTimezones[0];
function createComponent({ props = {} } = {}) { function createComponent({ props = {} } = {}) {
wrapper = extendedWrapper( wrapper = extendedWrapper(
shallowMount(RotationAssignee, { shallowMount(RotationAssignee, {
provide: {
selectedTimezone,
},
propsData: { propsData: {
assignee: { ...assignee.participant }, assignee: { ...assignee.participant },
rotationAssigneeStartsAt: assignee.startsAt, rotationAssigneeStartsAt: assignee.startsAt,
...@@ -73,9 +81,10 @@ describe('RotationAssignee', () => { ...@@ -73,9 +81,10 @@ describe('RotationAssignee', () => {
}); });
it('should render an assignee schedule and rotation information in a popover', () => { it('should render an assignee schedule and rotation information in a popover', () => {
const timezone = selectedTimezoneFormattedOffset(selectedTimezone.formatted_offset);
expect(findPopOver().attributes('target')).toBe('rotation-assignee-fakeUniqueId'); expect(findPopOver().attributes('target')).toBe('rotation-assignee-fakeUniqueId');
expect(findStartsAt().text()).toContain(formattedDate(assignee.startsAt)); expect(findStartsAt().text()).toContain(`${formattedDate(assignee.startsAt)} ${timezone}`);
expect(findEndsAt().text()).toContain(formattedDate(assignee.endsAt)); expect(findEndsAt().text()).toContain(`${formattedDate(assignee.endsAt)} ${timezone}`);
}); });
}); });
}); });
...@@ -105,14 +105,18 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders ...@@ -105,14 +105,18 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders
class="gl-m-0" class="gl-m-0"
data-testid="rotation-assignee-starts-at" data-testid="rotation-assignee-starts-at"
> >
Starts: January 12, 2021, 10:04am GMT+0000
Starts: January 12, 2021, 10:04 (UTC -12:00)
</p> </p>
<p <p
class="gl-m-0" class="gl-m-0"
data-testid="rotation-assignee-ends-at" data-testid="rotation-assignee-ends-at"
> >
Ends: January 15, 2021, 10:04am GMT+0000
Ends: January 15, 2021, 10:04 (UTC -12:00)
</p> </p>
</div> </div>
</div> </div>
...@@ -145,14 +149,18 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders ...@@ -145,14 +149,18 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders
class="gl-m-0" class="gl-m-0"
data-testid="rotation-assignee-starts-at" data-testid="rotation-assignee-starts-at"
> >
Starts: January 16, 2021, 10:04am GMT+0000
Starts: January 16, 2021, 10:04 (UTC -12:00)
</p> </p>
<p <p
class="gl-m-0" class="gl-m-0"
data-testid="rotation-assignee-ends-at" data-testid="rotation-assignee-ends-at"
> >
Ends: January 18, 2021, 10:04am GMT+0000
Ends: January 18, 2021, 10:04 (UTC -12:00)
</p> </p>
</div> </div>
</div> </div>
......
...@@ -8,6 +8,7 @@ import { PRESET_TYPES } from 'ee/oncall_schedules/constants'; ...@@ -8,6 +8,7 @@ import { PRESET_TYPES } from 'ee/oncall_schedules/constants';
import { useFakeDate } from 'helpers/fake_date'; import { useFakeDate } from 'helpers/fake_date';
import { scheduleIid } from '../../mocks/apollo_mock'; import { scheduleIid } from '../../mocks/apollo_mock';
import mockRotations from '../../mocks/mock_rotation.json'; import mockRotations from '../../mocks/mock_rotation.json';
import mockTimezones from '../../mocks/mock_timezones.json';
describe('RotationsListSectionComponent', () => { describe('RotationsListSectionComponent', () => {
let wrapper; let wrapper;
...@@ -28,6 +29,7 @@ describe('RotationsListSectionComponent', () => { ...@@ -28,6 +29,7 @@ describe('RotationsListSectionComponent', () => {
}, },
provide: { provide: {
projectPath, projectPath,
selectedTimezone: mockTimezones[0],
}, },
stubs: { stubs: {
GlCard, GlCard,
......
...@@ -3,6 +3,7 @@ import RotationsAssignee from 'ee/oncall_schedules/components/rotations/componen ...@@ -3,6 +3,7 @@ import RotationsAssignee from 'ee/oncall_schedules/components/rotations/componen
import DaysScheduleShift from 'ee/oncall_schedules/components/schedule/components/shifts/components/days_schedule_shift.vue'; import DaysScheduleShift from 'ee/oncall_schedules/components/schedule/components/shifts/components/days_schedule_shift.vue';
import { PRESET_TYPES, DAYS_IN_WEEK } from 'ee/oncall_schedules/constants'; import { PRESET_TYPES, DAYS_IN_WEEK } from 'ee/oncall_schedules/constants';
import { nDaysAfter } from '~/lib/utils/datetime_utility'; import { nDaysAfter } from '~/lib/utils/datetime_utility';
import mockTimezones from '../../../../mocks/mock_timezones.json';
const shift = { const shift = {
participant: { participant: {
...@@ -31,6 +32,7 @@ describe('ee/oncall_schedules/components/schedule/components/shifts/components/d ...@@ -31,6 +32,7 @@ describe('ee/oncall_schedules/components/schedule/components/shifts/components/d
timeframe, timeframe,
presetType: PRESET_TYPES.WEEKS, presetType: PRESET_TYPES.WEEKS,
shiftTimeUnitWidth: CELL_WIDTH, shiftTimeUnitWidth: CELL_WIDTH,
selectedTimezone: mockTimezones[0],
...props, ...props,
}, },
}); });
......
...@@ -21069,9 +21069,6 @@ msgstr "" ...@@ -21069,9 +21069,6 @@ msgstr ""
msgid "OnCallSchedules|On-call schedule" msgid "OnCallSchedules|On-call schedule"
msgstr "" msgstr ""
msgid "OnCallSchedules|On-call schedule for the %{timezone}"
msgstr ""
msgid "OnCallSchedules|Please note, rotations with shifts that are less than four hours are currently not supported in the weekly view." msgid "OnCallSchedules|Please note, rotations with shifts that are less than four hours are currently not supported in the weekly view."
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment