Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/sidebar/components/time_tracking')
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/constants.js1
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue227
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql17
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue40
7 files changed, 260 insertions, 40 deletions
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/constants.js b/app/assets/javascripts/sidebar/components/time_tracking/constants.js
new file mode 100644
index 00000000000..56e986e3b27
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/constants.js
@@ -0,0 +1 @@
+export const CREATE_TIMELOG_MODAL_ID = 'create-timelog-modal';
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue b/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue
new file mode 100644
index 00000000000..ec8e1ee9952
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue
@@ -0,0 +1,227 @@
+<script>
+import {
+ GlFormGroup,
+ GlFormInput,
+ GlDatepicker,
+ GlFormTextarea,
+ GlModal,
+ GlAlert,
+ GlLink,
+ GlSprintf,
+} from '@gitlab/ui';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { formatDate } from '~/lib/utils/datetime_utility';
+import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
+import { joinPaths } from '~/lib/utils/url_utility';
+import { s__ } from '~/locale';
+import createTimelogMutation from '../../queries/create_timelog.mutation.graphql';
+import { CREATE_TIMELOG_MODAL_ID } from './constants';
+
+export default {
+ components: {
+ GlDatepicker,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlModal,
+ GlAlert,
+ GlLink,
+ GlSprintf,
+ },
+ inject: ['issuableType'],
+ props: {
+ issuableId: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ timeSpent: '',
+ spentAt: null,
+ summary: '',
+ isLoading: false,
+ saveError: '',
+ };
+ },
+ computed: {
+ submitDisabled() {
+ return this.isLoading || this.timeSpent.length === 0;
+ },
+ primaryProps() {
+ return {
+ text: s__('CreateTimelogForm|Save'),
+ attributes: [
+ {
+ variant: 'confirm',
+ disabled: this.submitDisabled,
+ loading: this.isLoading,
+ },
+ ],
+ };
+ },
+ cancelProps() {
+ return {
+ text: s__('CreateTimelogForm|Cancel'),
+ };
+ },
+ timeTrackingDocsPath() {
+ return joinPaths(gon.relative_url_root || '', '/help/user/project/time_tracking.md');
+ },
+ issuableTypeName() {
+ return this.isIssue()
+ ? s__('CreateTimelogForm|issue')
+ : s__('CreateTimelogForm|merge request');
+ },
+ },
+ methods: {
+ resetModal() {
+ this.isLoading = false;
+ this.timeSpent = '';
+ this.spentAt = null;
+ this.summary = '';
+ this.saveError = '';
+ },
+ close() {
+ this.resetModal();
+ this.$refs.modal.close();
+ },
+ registerTimeSpent(event) {
+ event.preventDefault();
+
+ if (this.timeSpent.length === 0) {
+ return;
+ }
+
+ this.isLoading = true;
+ this.saveError = '';
+
+ this.$apollo
+ .mutate({
+ mutation: createTimelogMutation,
+ variables: {
+ input: {
+ timeSpent: this.timeSpent,
+ spentAt: this.spentAt
+ ? formatDate(this.spentAt, 'isoDateTime')
+ : formatDate(Date.now(), 'isoDateTime'),
+ summary: this.summary,
+ issuableId: this.getIssuableId(),
+ },
+ },
+ })
+ .then(({ data }) => {
+ if (data.timelogCreate?.errors.length) {
+ this.saveError = data.timelogCreate.errors[0].message || data.timelogCreate.errors[0];
+ } else {
+ this.close();
+ }
+ })
+ .catch((error) => {
+ this.saveError =
+ error?.message ||
+ s__('CreateTimelogForm|An error occurred while saving the time entry.');
+ })
+ .finally(() => {
+ this.isLoading = false;
+ });
+ },
+ isIssue() {
+ return this.issuableType === 'issue';
+ },
+ getGraphQLEntityType() {
+ return this.isIssue() ? TYPE_ISSUE : TYPE_MERGE_REQUEST;
+ },
+ updateSpentAtDate(val) {
+ this.spentAt = val;
+ },
+ getIssuableId() {
+ return convertToGraphQLId(this.getGraphQLEntityType(), this.issuableId);
+ },
+ },
+ CREATE_TIMELOG_MODAL_ID,
+};
+</script>
+
+<template>
+ <gl-modal
+ ref="modal"
+ :title="s__('CreateTimelogForm|Add time entry')"
+ :modal-id="$options.CREATE_TIMELOG_MODAL_ID"
+ size="sm"
+ data-testid="create-timelog-modal"
+ :action-primary="primaryProps"
+ :action-cancel="cancelProps"
+ @primary="registerTimeSpent"
+ @cancel="close"
+ @close="close"
+ @hide="close"
+ >
+ <p data-testid="timetracking-docs-link">
+ <gl-sprintf
+ :message="
+ s__(
+ 'CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}',
+ )
+ "
+ >
+ <template #issuableTypeName>{{ issuableTypeName }}</template>
+ <template #timeTrackingDocsLink>
+ <gl-link :href="timeTrackingDocsPath" target="_blank">{{
+ s__('CreateTimelogForm|How do I track and estimate time?')
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ <form
+ class="gl-display-flex gl-flex-direction-column js-quick-submit"
+ @submit.prevent="registerTimeSpent"
+ >
+ <div class="gl-display-flex gl-gap-3">
+ <gl-form-group
+ key="time-spent"
+ label-for="time-spent"
+ :label="s__(`CreateTimelogForm|Time spent`)"
+ :description="s__(`CreateTimelogForm|Example: 1h 30m`)"
+ >
+ <gl-form-input
+ id="time-spent"
+ ref="timeSpent"
+ v-model="timeSpent"
+ class="gl-form-input-sm"
+ autocomplete="off"
+ />
+ </gl-form-group>
+ <gl-form-group
+ key="spent-at"
+ optional
+ label-for="spent-at"
+ :label="s__(`CreateTimelogForm|Spent at`)"
+ >
+ <gl-datepicker
+ :target="null"
+ :value="spentAt"
+ show-clear-button
+ autocomplete="off"
+ size="small"
+ @input="updateSpentAtDate"
+ @clear="updateSpentAtDate(null)"
+ />
+ </gl-form-group>
+ </div>
+ <gl-form-group
+ :label="s__('CreateTimelogForm|Summary')"
+ optional
+ label-for="summary"
+ class="gl-mb-0"
+ >
+ <gl-form-textarea id="summary" v-model="summary" rows="3" :no-resize="true" />
+ </gl-form-group>
+ <gl-alert v-if="saveError" variant="danger" class="gl-mt-5" :dismissible="false">
+ {{ saveError }}
+ </gl-alert>
+ <!-- This is needed to have the quick-submit behaviour (with Ctrl + Enter or Cmd + Enter) -->
+ <input type="submit" hidden />
+ </form>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql b/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
deleted file mode 100644
index 6e916893b5a..00000000000
--- a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
+++ /dev/null
@@ -1,17 +0,0 @@
-#import "~/graphql_shared/fragments/issue_time_tracking.fragment.graphql"
-#import "~/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql"
-
-mutation deleteTimelog($input: TimelogDeleteInput!) {
- timelogDelete(input: $input) {
- errors
- timelog {
- id
- issue {
- ...IssueTimeTrackingFragment
- }
- mergeRequest {
- ...MergeRequestTimeTrackingFragment
- }
- }
- }
-}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index 91c15061fb9..6cd9596e43f 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { joinPaths } from '~/lib/utils/url_utility';
import { sprintf, s__ } from '~/locale';
@@ -9,7 +10,7 @@ export default {
GlButton,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
computed: {
href() {
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index 124464088cf..6f4ced06ddf 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -5,8 +5,8 @@ import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import { __, s__ } from '~/locale';
-import { timelogQueries } from '~/sidebar/constants';
-import deleteTimelogMutation from './graphql/mutations/delete_timelog.mutation.graphql';
+import { timelogQueries } from '../../constants';
+import deleteTimelogMutation from '../../queries/delete_timelog.mutation.graphql';
const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
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 62b05421884..06adc048942 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
@@ -30,6 +30,11 @@ export default {
required: false,
default: false,
},
+ canAddTimeEntries: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
mounted() {
this.listenForQuickActions();
@@ -67,6 +72,7 @@ export default {
:issuable-id="issuableId"
:issuable-iid="issuableIid"
:limit-to-hours="limitToHours"
+ :can-add-time-entries="canAddTimeEntries"
/>
</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 13981c477c6..b32836dc87d 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -9,15 +9,17 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import { IssuableType } from '~/issues/constants';
+import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__, __ } from '~/locale';
-import { HOW_TO_TRACK_TIME, timeTrackingQueries } from '~/sidebar/constants';
+import { HOW_TO_TRACK_TIME, timeTrackingQueries } from '../../constants';
import eventHub from '../../event_hub';
import TimeTrackingCollapsedState from './collapsed_state.vue';
import TimeTrackingComparisonPane from './comparison_pane.vue';
-import TimeTrackingHelpState from './help_state.vue';
import TimeTrackingReport from './report.vue';
import TimeTrackingSpentOnlyPane from './spent_only_pane.vue';
+import { CREATE_TIMELOG_MODAL_ID } from './constants';
+import CreateTimelogForm from './create_timelog_form.vue';
export default {
name: 'IssuableTimeTracker',
@@ -34,8 +36,8 @@ export default {
TimeTrackingCollapsedState,
TimeTrackingSpentOnlyPane,
TimeTrackingComparisonPane,
- TimeTrackingHelpState,
TimeTrackingReport,
+ CreateTimelogForm,
},
directives: {
GlModal: GlModalDirective,
@@ -87,6 +89,11 @@ export default {
default: true,
required: false,
},
+ canAddTimeEntries: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
return {
@@ -192,12 +199,12 @@ export default {
eventHub.$on('timeTracker:refresh', this.refresh);
},
methods: {
- toggleHelpState(show) {
- this.showHelp = show;
- },
refresh() {
this.$apollo.queries.issuableTimeTracking.refetch();
},
+ openRegisterTimeSpentModal() {
+ this.$root.$emit(BV_SHOW_MODAL, CREATE_TIMELOG_MODAL_ID);
+ },
},
};
</script>
@@ -215,24 +222,21 @@ export default {
:time-estimate-human-readable="humanTimeEstimate"
/>
<div
- class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold gl-mr-3"
+ class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold"
>
{{ __('Time tracking') }}
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
<gl-button
- :data-testid="showHelpState ? 'closeHelpButton' : 'helpButton'"
+ v-if="canAddTimeEntries"
+ v-gl-tooltip.left
category="tertiary"
size="small"
- variant="link"
class="gl-ml-auto"
- @click="toggleHelpState(!showHelpState)"
+ data-testid="add-time-entry-button"
+ :title="__('Add time entry')"
+ @click="openRegisterTimeSpentModal()"
>
- <gl-icon
- v-gl-tooltip.left
- :title="timeTrackingIconTitle"
- :name="timeTrackingIconName"
- class="gl-text-gray-900!"
- />
+ <gl-icon name="plus" class="gl-text-gray-900!" />
</gl-button>
</div>
<div v-if="!isTimeTrackingInfoLoading" class="hide-collapsed">
@@ -272,9 +276,7 @@ export default {
<time-tracking-report :limit-to-hours="limitToHours" :issuable-id="issuableId" />
</gl-modal>
</template>
- <transition name="help-state-toggle">
- <time-tracking-help-state v-if="showHelpState" />
- </transition>
+ <create-timelog-form :issuable-id="issuableId" />
</div>
</div>
</template>