diff options
Diffstat (limited to 'app/assets/javascripts/sidebar/components/time_tracking')
4 files changed, 136 insertions, 65 deletions
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue index 99302993b9a..3705d725a15 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue @@ -70,7 +70,7 @@ export default { </script> <template> - <div data-testid="timeTrackingComparisonPane"> + <div class="gl-mt-2" data-testid="timeTrackingComparisonPane"> <div v-gl-tooltip data-testid="compareMeter" diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue index 67242b3b5b7..f91a78b7f1d 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue @@ -13,13 +13,17 @@ export default { GlLoadingIcon, GlTable, }, - inject: ['issuableId', 'issuableType'], + inject: ['issuableType'], props: { limitToHours: { type: Boolean, default: false, required: false, }, + issuableId: { + type: String, + required: true, + }, }, data() { return { report: [], isLoading: true }; diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index c70d99ac178..58167b3934a 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -5,19 +5,27 @@ import { intersection } from 'lodash'; import '~/smart_interval'; import eventHub from '../../event_hub'; -import Mediator from '../../sidebar_mediator'; -import Store from '../../stores/sidebar_store'; import IssuableTimeTracker from './time_tracker.vue'; export default { components: { IssuableTimeTracker, }, - data() { - return { - mediator: new Mediator(), - store: new Store(), - }; + props: { + fullPath: { + type: String, + required: false, + default: '', + }, + issuableIid: { + type: String, + required: true, + }, + limitToHours: { + type: Boolean, + required: false, + default: false, + }, }, mounted() { this.listenForQuickActions(); @@ -41,7 +49,7 @@ export default { changedCommands = []; } if (changedCommands && intersection(subscribedCommands, changedCommands).length) { - this.mediator.fetch(); + eventHub.$emit('timeTracker:refresh'); } }, }, @@ -51,11 +59,9 @@ export default { <template> <div class="block"> <issuable-time-tracker - :time-estimate="store.timeEstimate" - :time-spent="store.totalTimeSpent" - :human-time-estimate="store.humanTimeEstimate" - :human-time-spent="store.humanTotalTimeSpent" - :limit-to-hours="store.timeTrackingLimitToHours" + :full-path="fullPath" + :issuable-iid="issuableIid" + :limit-to-hours="limitToHours" /> </div> </template> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue index 64f2ddc1d16..3feff8639a1 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue @@ -1,6 +1,9 @@ <script> -import { GlIcon, GlLink, GlModal, GlModalDirective } from '@gitlab/ui'; +import { GlIcon, GlLink, GlModal, GlModalDirective, GlLoadingIcon } from '@gitlab/ui'; +import { IssuableType } from '~/issue_show/constants'; import { s__, __ } from '~/locale'; +import { timeTrackingQueries } from '~/sidebar/constants'; + import eventHub from '../../event_hub'; import TimeTrackingCollapsedState from './collapsed_state.vue'; import TimeTrackingComparisonPane from './comparison_pane.vue'; @@ -18,6 +21,7 @@ export default { GlIcon, GlLink, GlModal, + GlLoadingIcon, TimeTrackingCollapsedState, TimeTrackingSpentOnlyPane, TimeTrackingComparisonPane, @@ -27,29 +31,27 @@ export default { directives: { GlModal: GlModalDirective, }, + inject: ['issuableType'], props: { - timeEstimate: { - type: Number, - required: true, - }, - timeSpent: { - type: Number, - required: true, + limitToHours: { + type: Boolean, + default: false, + required: false, }, - humanTimeEstimate: { + fullPath: { type: String, required: false, default: '', }, - humanTimeSpent: { + issuableIid: { type: String, required: false, default: '', }, - limitToHours: { - type: Boolean, - default: false, + initialTimeTracking: { + type: Object, required: false, + default: null, }, /* In issue list, "time-tracking-collapsed-state" is always rendered even if the sidebar isn't collapsed. @@ -70,47 +72,103 @@ export default { data() { return { showHelp: false, + timeTracking: { + ...this.initialTimeTracking, + }, }; }, + apollo: { + issuableTimeTracking: { + query() { + return timeTrackingQueries[this.issuableType].query; + }, + skip() { + // We don't fetch info via GraphQL in following cases + // 1. Time tracking info was provided via prop + // 2. issuableIid and fullPath are not provided. + if (!this.initialTimeTracking) { + return false; + } else if (this.issuableIid && this.fullPath) { + return false; + } + return true; + }, + variables() { + return { + iid: this.issuableIid, + fullPath: this.fullPath, + }; + }, + update(data) { + this.timeTracking = { + ...data.workspace?.issuable, + }; + }, + }, + }, computed: { - hasTimeSpent() { - return Boolean(this.timeSpent); + isTimeTrackingInfoLoading() { + return this.$apollo?.queries.issuableTimeTracking.loading ?? false; + }, + timeEstimate() { + return this.timeTracking?.timeEstimate || 0; + }, + totalTimeSpent() { + return this.timeTracking?.totalTimeSpent || 0; + }, + humanTimeEstimate() { + return this.timeTracking?.humanTimeEstimate || ''; + }, + humanTotalTimeSpent() { + return this.timeTracking?.humanTotalTimeSpent || ''; + }, + hasTotalTimeSpent() { + return Boolean(this.totalTimeSpent); }, hasTimeEstimate() { return Boolean(this.timeEstimate); }, showComparisonState() { - return this.hasTimeEstimate && this.hasTimeSpent; + return this.hasTimeEstimate && this.hasTotalTimeSpent; }, showEstimateOnlyState() { - return this.hasTimeEstimate && !this.hasTimeSpent; + return this.hasTimeEstimate && !this.hasTotalTimeSpent; }, showSpentOnlyState() { - return this.hasTimeSpent && !this.hasTimeEstimate; + return this.hasTotalTimeSpent && !this.hasTimeEstimate; }, showNoTimeTrackingState() { - return !this.hasTimeEstimate && !this.hasTimeSpent; + return !this.hasTimeEstimate && !this.hasTotalTimeSpent; }, showHelpState() { return Boolean(this.showHelp); }, + isTimeReportSupported() { + return ( + [IssuableType.Issue, IssuableType.MergeRequest].includes(this.issuableType) && + this.issuableIid + ); + }, + }, + watch: { + /** + * When `initialTimeTracking` is provided via prop, + * we don't query the same via GraphQl and instead + * monitor it for any updates (eg; Epic Swimlanes) + */ + initialTimeTracking(timeTracking) { + this.timeTracking = timeTracking; + }, }, created() { - eventHub.$on('timeTracker:updateData', this.update); + eventHub.$on('timeTracker:refresh', this.refresh); }, methods: { toggleHelpState(show) { this.showHelp = show; }, - update(data) { - const { timeEstimate, timeSpent, humanTimeEstimate, humanTimeSpent } = data; - - /* eslint-disable vue/no-mutating-props */ - this.timeEstimate = timeEstimate; - this.timeSpent = timeSpent; - this.humanTimeEstimate = humanTimeEstimate; - this.humanTimeSpent = humanTimeSpent; - /* eslint-enable vue/no-mutating-props */ + refresh() { + this.$apollo.queries.issuableTimeTracking.refetch(); }, }, }; @@ -125,11 +183,12 @@ export default { :show-help-state="showHelpState" :show-spent-only-state="showSpentOnlyState" :show-estimate-only-state="showEstimateOnlyState" - :time-spent-human-readable="humanTimeSpent" + :time-spent-human-readable="humanTotalTimeSpent" :time-estimate-human-readable="humanTimeEstimate" /> - <div class="title hide-collapsed gl-mb-3"> + <div class="hide-collapsed gl-line-height-20 gl-text-gray-900"> {{ __('Time tracking') }} + <gl-loading-icon v-if="isTimeTrackingInfoLoading" inline /> <div v-if="!showHelpState" data-testid="helpButton" @@ -147,14 +206,14 @@ export default { <gl-icon name="close" /> </div> </div> - <div class="time-tracking-content hide-collapsed"> + <div v-if="!isTimeTrackingInfoLoading" class="hide-collapsed"> <div v-if="showEstimateOnlyState" data-testid="estimateOnlyPane"> <span class="gl-font-weight-bold">{{ $options.i18n.estimatedOnlyText }} </span >{{ humanTimeEstimate }} </div> <time-tracking-spent-only-pane v-if="showSpentOnlyState" - :time-spent-human-readable="humanTimeSpent" + :time-spent-human-readable="humanTotalTimeSpent" /> <div v-if="showNoTimeTrackingState" data-testid="noTrackingPane"> <span class="gl-text-gray-500">{{ $options.i18n.noTimeTrackingText }}</span> @@ -162,26 +221,28 @@ export default { <time-tracking-comparison-pane v-if="showComparisonState" :time-estimate="timeEstimate" - :time-spent="timeSpent" - :time-spent-human-readable="humanTimeSpent" + :time-spent="totalTimeSpent" + :time-spent-human-readable="humanTotalTimeSpent" :time-estimate-human-readable="humanTimeEstimate" :limit-to-hours="limitToHours" /> - <gl-link - v-if="hasTimeSpent" - v-gl-modal="'time-tracking-report'" - data-testid="reportLink" - href="#" - class="btn-link" - >{{ __('Time tracking report') }}</gl-link - > - <gl-modal - modal-id="time-tracking-report" - :title="__('Time tracking report')" - :hide-footer="true" - > - <time-tracking-report :limit-to-hours="limitToHours" /> - </gl-modal> + <template v-if="isTimeReportSupported"> + <gl-link + v-if="hasTotalTimeSpent" + v-gl-modal="'time-tracking-report'" + data-testid="reportLink" + href="#" + > + {{ __('Time tracking report') }} + </gl-link> + <gl-modal + modal-id="time-tracking-report" + :title="__('Time tracking report')" + :hide-footer="true" + > + <time-tracking-report :limit-to-hours="limitToHours" :issuable-iid="issuableIid" /> + </gl-modal> + </template> <transition name="help-state-toggle"> <time-tracking-help-state v-if="showHelpState" /> </transition> |