diff options
Diffstat (limited to 'app/assets/javascripts/work_items/components/work_item_detail.vue')
-rw-r--r-- | app/assets/javascripts/work_items/components/work_item_detail.vue | 403 |
1 files changed, 174 insertions, 229 deletions
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 1ac40fe7dcb..1402b313cee 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -9,6 +9,7 @@ import { GlButton, GlTooltipDirective, GlEmptyState, + GlIntersectionObserver, } from '@gitlab/ui'; import noAccessSvg from '@gitlab/svgs/dist/illustrations/analytics/no-access.svg?raw'; import { s__ } from '~/locale'; @@ -22,27 +23,17 @@ import { sprintfWorkItem, i18n, WIDGET_TYPE_ASSIGNEES, - WIDGET_TYPE_LABELS, WIDGET_TYPE_NOTIFICATIONS, WIDGET_TYPE_CURRENT_USER_TODOS, WIDGET_TYPE_DESCRIPTION, WIDGET_TYPE_AWARD_EMOJI, - WIDGET_TYPE_START_AND_DUE_DATE, - WIDGET_TYPE_WEIGHT, - WIDGET_TYPE_PROGRESS, WIDGET_TYPE_HIERARCHY, - WIDGET_TYPE_MILESTONE, - WIDGET_TYPE_ITERATION, - WIDGET_TYPE_HEALTH_STATUS, WORK_ITEM_TYPE_VALUE_ISSUE, WORK_ITEM_TYPE_VALUE_OBJECTIVE, WIDGET_TYPE_NOTES, } from '../constants'; -import workItemDatesSubscription from '../../graphql_shared/subscriptions/work_item_dates.subscription.graphql'; -import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql'; -import workItemAssigneesSubscription from '../graphql/work_item_assignees.subscription.graphql'; -import workItemMilestoneSubscription from '../graphql/work_item_milestone.subscription.graphql'; +import workItemUpdatedSubscription from '../graphql/work_item_updated.subscription.graphql'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; @@ -51,17 +42,13 @@ import { findHierarchyWidgetChildren } from '../utils'; import WorkItemTree from './work_item_links/work_item_tree.vue'; import WorkItemActions from './work_item_actions.vue'; import WorkItemTodos from './work_item_todos.vue'; -import WorkItemState from './work_item_state.vue'; import WorkItemTitle from './work_item_title.vue'; +import WorkItemAttributesWrapper from './work_item_attributes_wrapper.vue'; import WorkItemCreatedUpdated from './work_item_created_updated.vue'; import WorkItemDescription from './work_item_description.vue'; -import WorkItemAwardEmoji from './work_item_award_emoji.vue'; -import WorkItemDueDate from './work_item_due_date.vue'; -import WorkItemAssignees from './work_item_assignees.vue'; -import WorkItemLabels from './work_item_labels.vue'; -import WorkItemMilestone from './work_item_milestone.vue'; import WorkItemNotes from './work_item_notes.vue'; import WorkItemDetailModal from './work_item_detail_modal.vue'; +import WorkItemAwardEmoji from './work_item_award_emoji.vue'; export default { i18n, @@ -77,27 +64,19 @@ export default { GlSkeletonLoader, GlIcon, GlEmptyState, - WorkItemAssignees, WorkItemActions, WorkItemTodos, WorkItemCreatedUpdated, WorkItemDescription, WorkItemAwardEmoji, - WorkItemDueDate, - WorkItemLabels, WorkItemTitle, - WorkItemState, - WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'), - WorkItemProgress: () => import('ee_component/work_items/components/work_item_progress.vue'), + WorkItemAttributesWrapper, WorkItemTypeIcon, - WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'), - WorkItemHealthStatus: () => - import('ee_component/work_items/components/work_item_health_status.vue'), - WorkItemMilestone, WorkItemTree, WorkItemNotes, WorkItemDetailModal, AbuseCategorySelector, + GlIntersectionObserver, }, mixins: [glFeatureFlagMixin()], inject: ['fullPath', 'reportAbusePath'], @@ -129,6 +108,7 @@ export default { isReportDrawerOpen: false, reportedUrl: '', reportedUserId: 0, + isStickyHeaderShowing: false, }; }, apollo: { @@ -165,52 +145,17 @@ export default { document.title = `${this.workItem.title} ยท ${this.workItem?.workItemType?.name}${path}`; } }, - subscribeToMore: [ - { - document: workItemTitleSubscription, - variables() { - return { - issuableId: this.workItem.id, - }; - }, - skip() { - return !this.workItem?.id; - }, - }, - { - document: workItemDatesSubscription, - variables() { - return { - issuableId: this.workItem.id, - }; - }, - skip() { - return !this.isWidgetPresent(WIDGET_TYPE_START_AND_DUE_DATE) || !this.workItem?.id; - }, - }, - { - document: workItemAssigneesSubscription, - variables() { - return { - issuableId: this.workItem.id, - }; - }, - skip() { - return !this.isWidgetPresent(WIDGET_TYPE_ASSIGNEES) || !this.workItem?.id; - }, + subscribeToMore: { + document: workItemUpdatedSubscription, + variables() { + return { + id: this.workItem.id, + }; }, - { - document: workItemMilestoneSubscription, - variables() { - return { - issuableId: this.workItem.id, - }; - }, - skip() { - return !this.isWidgetPresent(WIDGET_TYPE_MILESTONE) || !this.workItem?.id; - }, + skip() { + return !this.workItem?.id; }, - ], + }, }, }, computed: { @@ -289,38 +234,17 @@ export default { return this.$options.isLoggedIn && this.workItemCurrentUserTodos; }, currentUserTodos() { - return this.workItemCurrentUserTodos?.currentUserTodos?.edges; + return this.workItemCurrentUserTodos?.currentUserTodos?.nodes; }, workItemAssignees() { return this.isWidgetPresent(WIDGET_TYPE_ASSIGNEES); }, - workItemLabels() { - return this.isWidgetPresent(WIDGET_TYPE_LABELS); - }, - workItemDueDate() { - return this.isWidgetPresent(WIDGET_TYPE_START_AND_DUE_DATE); - }, - workItemWeight() { - return this.isWidgetPresent(WIDGET_TYPE_WEIGHT); - }, - workItemProgress() { - return this.isWidgetPresent(WIDGET_TYPE_PROGRESS); - }, workItemAwardEmoji() { return this.isWidgetPresent(WIDGET_TYPE_AWARD_EMOJI); }, workItemHierarchy() { return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY); }, - workItemIteration() { - return this.isWidgetPresent(WIDGET_TYPE_ITERATION); - }, - workItemHealthStatus() { - return this.isWidgetPresent(WIDGET_TYPE_HEALTH_STATUS); - }, - workItemMilestone() { - return this.isWidgetPresent(WIDGET_TYPE_MILESTONE); - }, workItemNotes() { return this.isWidgetPresent(WIDGET_TYPE_NOTES); }, @@ -332,6 +256,9 @@ export default { 'gl-pt-5': !this.updateError && !this.isModal, }; }, + showIntersectionObserver() { + return !this.isModal && this.workItemsMvc2Enabled; + }, }, mounted() { if (this.modalWorkItemIid) { @@ -437,6 +364,15 @@ export default { this.reportedUrl = reply.url || {}; this.reportedUserId = reply.author ? getIdFromGraphQLId(reply.author.id) : 0; }, + hideStickyHeader() { + this.isStickyHeaderShowing = false; + }, + showStickyHeader() { + // only if scrolled under the work item's title + if (this.$refs?.title?.$el.offsetTop < window.pageYOffset) { + this.isStickyHeaderShowing = true; + } + }, }, WORK_ITEM_TYPE_VALUE_OBJECTIVE, @@ -510,7 +446,9 @@ export default { > <work-item-todos v-if="showWorkItemCurrentUserTodos" - :work-item="workItem" + :work-item-id="workItem.id" + :work-item-iid="workItemIid" + :work-item-fullpath="workItem.project.fullPath" :current-user-todos="currentUserTodos" @error="updateError = $event" /> @@ -539,141 +477,148 @@ export default { @click="$emit('close')" /> </div> - <work-item-title - v-if="workItem.title" - :work-item-id="workItem.id" - :work-item-title="workItem.title" - :work-item-type="workItemType" - :work-item-parent-id="workItemParentId" - :can-update="canUpdate" - @error="updateError = $event" - /> - <work-item-created-updated :work-item-iid="workItemIid" /> - <work-item-state - :work-item="workItem" - :work-item-parent-id="workItemParentId" - :can-update="canUpdate" - @error="updateError = $event" - /> - <work-item-assignees - v-if="workItemAssignees" - :can-update="canUpdate" - :work-item-id="workItem.id" - :assignees="workItemAssignees.assignees.nodes" - :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees" - :work-item-type="workItemType" - :can-invite-members="workItemAssignees.canInviteMembers" - @error="updateError = $event" - /> - <work-item-labels - v-if="workItemLabels" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - @error="updateError = $event" - /> - <work-item-due-date - v-if="workItemDueDate" - :can-update="canUpdate" - :due-date="workItemDueDate.dueDate" - :start-date="workItemDueDate.startDate" - :work-item-id="workItem.id" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-milestone - v-if="workItemMilestone" - :work-item-id="workItem.id" - :work-item-milestone="workItemMilestone.milestone" - :work-item-type="workItemType" - :can-update="canUpdate" - @error="updateError = $event" - /> - <work-item-weight - v-if="workItemWeight" - class="gl-mb-5" - :can-update="canUpdate" - :weight="workItemWeight.weight" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-progress - v-if="workItemProgress" - class="gl-mb-5" - :can-update="canUpdate" - :progress="workItemProgress.progress" - :work-item-id="workItem.id" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-iteration - v-if="workItemIteration" - class="gl-mb-5" - :iteration="workItemIteration.iteration" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-health-status - v-if="workItemHealthStatus" - class="gl-mb-5" - :health-status="workItemHealthStatus.healthStatus" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-description - v-if="hasDescriptionWidget" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - class="gl-pt-5" - @error="updateError = $event" - /> - <work-item-award-emoji - v-if="workItemAwardEmoji" - :work-item-id="workItem.id" - :work-item-fullpath="workItem.project.fullPath" - :award-emoji="workItemAwardEmoji.awardEmoji" - :work-item-iid="workItemIid" - @error="updateError = $event" - /> - <work-item-tree - v-if="workItemType === $options.WORK_ITEM_TYPE_VALUE_OBJECTIVE" - :work-item-type="workItemType" - :parent-work-item-type="workItem.workItemType.name" - :work-item-id="workItem.id" - :work-item-iid="workItemIid" - :children="children" - :can-update="canUpdate" - :confidential="workItem.confidential" - @show-modal="openInModal" - /> - <work-item-notes - v-if="workItemNotes" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - :is-modal="isModal" - :assignees="workItemAssignees && workItemAssignees.assignees.nodes" - :can-set-work-item-metadata="canAssignUnassignUser" - :report-abuse-path="reportAbusePath" - class="gl-pt-5" - @error="updateError = $event" - @has-notes="updateHasNotes" - @openReportAbuse="openReportAbuseDrawer" - /> - <gl-empty-state - v-if="error" - :title="$options.i18n.fetchErrorTitle" - :description="error" - :svg-path="noAccessSvgPath" - /> + <div> + <work-item-title + v-if="workItem.title" + ref="title" + :work-item-id="workItem.id" + :work-item-title="workItem.title" + :work-item-type="workItemType" + :work-item-parent-id="workItemParentId" + :can-update="canUpdate" + @error="updateError = $event" + /> + <work-item-created-updated :work-item-iid="workItemIid" /> + </div> + <gl-intersection-observer + v-if="showIntersectionObserver" + @appear="hideStickyHeader" + @disappear="showStickyHeader" + > + <transition name="issuable-header-slide"> + <div + v-if="isStickyHeaderShowing" + class="issue-sticky-header gl-fixed gl-bg-white gl-border-b gl-z-index-3 gl-py-2" + data-testid="work-item-sticky-header" + > + <div + class="gl-align-items-center gl-mx-auto gl-px-5 gl-display-flex gl-max-w-container-xl" + > + <span class="gl-text-truncate gl-font-weight-bold gl-pr-3 gl-mr-auto"> + {{ workItem.title }} + </span> + <gl-loading-icon v-if="updateInProgress" class="gl-mr-3" /> + <gl-badge + v-if="workItem.confidential" + v-gl-tooltip.bottom + :title="confidentialTooltip" + variant="warning" + icon="eye-slash" + class="gl-mr-3 gl-cursor-help" + >{{ __('Confidential') }}</gl-badge + > + <work-item-todos + v-if="showWorkItemCurrentUserTodos" + :work-item-id="workItem.id" + :work-item-iid="workItemIid" + :work-item-fullpath="workItem.project.fullPath" + :current-user-todos="currentUserTodos" + @error="updateError = $event" + /> + <work-item-actions + :work-item-id="workItem.id" + :subscribed-to-notifications="workItemNotificationsSubscribed" + :work-item-type="workItemType" + :work-item-type-id="workItemTypeId" + :can-delete="canDelete" + :can-update="canUpdate" + :is-confidential="workItem.confidential" + :is-parent-confidential="parentWorkItemConfidentiality" + :work-item-reference="workItem.reference" + :work-item-create-note-email="workItem.createNoteEmail" + :is-modal="isModal" + @deleteWorkItem=" + $emit('deleteWorkItem', { workItemType, workItemId: workItem.id }) + " + @toggleWorkItemConfidentiality="toggleConfidentiality" + @error="updateError = $event" + /> + </div> + </div> + </transition> + </gl-intersection-observer> + <div + data-testid="work-item-overview" + :class="{ 'work-item-overview': workItemsMvc2Enabled }" + > + <section> + <work-item-attributes-wrapper + :class="{ 'gl-md-display-none!': workItemsMvc2Enabled }" + class="gl-border-b" + :work-item="workItem" + :work-item-parent-id="workItemParentId" + @error="updateError = $event" + /> + <work-item-description + v-if="hasDescriptionWidget" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + class="gl-pt-5" + @error="updateError = $event" + /> + <work-item-award-emoji + v-if="workItemAwardEmoji" + :work-item-id="workItem.id" + :work-item-fullpath="workItem.project.fullPath" + :award-emoji="workItemAwardEmoji.awardEmoji" + :work-item-iid="workItemIid" + @error="updateError = $event" + /> + <work-item-tree + v-if="workItemType === $options.WORK_ITEM_TYPE_VALUE_OBJECTIVE" + :work-item-type="workItemType" + :parent-work-item-type="workItem.workItemType.name" + :work-item-id="workItem.id" + :work-item-iid="workItemIid" + :children="children" + :can-update="canUpdate" + :confidential="workItem.confidential" + @show-modal="openInModal" + /> + <work-item-notes + v-if="workItemNotes" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + :work-item-type="workItemType" + :is-modal="isModal" + :assignees="workItemAssignees && workItemAssignees.assignees.nodes" + :can-set-work-item-metadata="canAssignUnassignUser" + :report-abuse-path="reportAbusePath" + class="gl-pt-5" + @error="updateError = $event" + @has-notes="updateHasNotes" + @openReportAbuse="openReportAbuseDrawer" + /> + <gl-empty-state + v-if="error" + :title="$options.i18n.fetchErrorTitle" + :description="error" + :svg-path="noAccessSvgPath" + /> + </section> + <aside + v-if="workItemsMvc2Enabled" + data-testid="work-item-overview-right-sidebar" + class="work-item-overview-right-sidebar gl-display-none gl-md-display-block" + :class="{ 'is-modal': isModal }" + > + <work-item-attributes-wrapper + :work-item="workItem" + :work-item-parent-id="workItemParentId" + @error="updateError = $event" + /> + </aside> + </div> </template> <work-item-detail-modal v-if="!isModal" |