diff options
Diffstat (limited to 'app/assets/javascripts/boards/components/board_card_inner.vue')
-rw-r--r-- | app/assets/javascripts/boards/components/board_card_inner.vue | 131 |
1 files changed, 118 insertions, 13 deletions
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index 2f4e9044b9e..05b64ddc773 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -1,5 +1,12 @@ <script> -import { GlLabel, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui'; +import { + GlLabel, + GlTooltip, + GlTooltipDirective, + GlIcon, + GlLoadingIcon, + GlSprintf, +} from '@gitlab/ui'; import { sortBy } from 'lodash'; import { mapActions, mapGetters, mapState } from 'vuex'; import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner'; @@ -16,6 +23,7 @@ import IssueTimeEstimate from './issue_time_estimate.vue'; export default { components: { + GlTooltip, GlLabel, GlLoadingIcon, GlIcon, @@ -25,6 +33,7 @@ export default { IssueTimeEstimate, IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'), BoardBlockedIcon, + GlSprintf, }, directives: { GlTooltip: GlTooltipDirective, @@ -55,7 +64,7 @@ export default { }; }, computed: { - ...mapState(['isShowingLabels', 'issuableType']), + ...mapState(['isShowingLabels', 'issuableType', 'allowSubEpics']), ...mapGetters(['isEpicBoard']), cappedAssignees() { // e.g. maxRender is 4, @@ -99,6 +108,12 @@ export default { } return false; }, + shouldRenderEpicCountables() { + return this.isEpicBoard && this.item.hasIssues; + }, + shouldRenderEpicProgress() { + return this.totalWeight > 0; + }, showLabelFooter() { return this.isShowingLabels && this.item.labels.find(this.showLabel); }, @@ -115,6 +130,20 @@ export default { } return __('Blocked issue'); }, + totalEpicsCount() { + return this.item.descendantCounts.openedEpics + this.item.descendantCounts.closedEpics; + }, + totalIssuesCount() { + return this.item.descendantCounts.openedIssues + this.item.descendantCounts.closedIssues; + }, + totalWeight() { + return ( + this.item.descendantWeightSum.openedIssues + this.item.descendantWeightSum.closedIssues + ); + }, + totalProgress() { + return Math.round((this.item.descendantWeightSum.closedIssues / this.totalWeight) * 100); + }, }, methods: { ...mapActions(['performSearch', 'setError']), @@ -227,17 +256,93 @@ export default { {{ itemId }} </span> <span class="board-info-items gl-mt-3 gl-display-inline-block"> - <issue-due-date - v-if="item.dueDate" - :date="item.dueDate" - :closed="item.closed || Boolean(item.closedAt)" - /> - <issue-time-estimate v-if="item.timeEstimate" :estimate="item.timeEstimate" /> - <issue-card-weight - v-if="validIssueWeight(item)" - :weight="item.weight" - @click="filterByWeight(item.weight)" - /> + <span v-if="shouldRenderEpicCountables" data-testid="epic-countables"> + <gl-tooltip :target="() => $refs.countBadge" data-testid="epic-countables-tooltip"> + <p v-if="allowSubEpics" class="gl-font-weight-bold gl-m-0"> + {{ __('Epics') }} • + <span class="gl-font-weight-normal"> + <gl-sprintf :message="__('%{openedEpics} open, %{closedEpics} closed')"> + <template #openedEpics>{{ item.descendantCounts.openedEpics }}</template> + <template #closedEpics>{{ item.descendantCounts.closedEpics }}</template> + </gl-sprintf> + </span> + </p> + <p class="gl-font-weight-bold gl-m-0"> + {{ __('Issues') }} • + <span class="gl-font-weight-normal"> + <gl-sprintf :message="__('%{openedIssues} open, %{closedIssues} closed')"> + <template #openedIssues>{{ item.descendantCounts.openedIssues }}</template> + <template #closedIssues>{{ item.descendantCounts.closedIssues }}</template> + </gl-sprintf> + </span> + </p> + <p class="gl-font-weight-bold gl-m-0"> + {{ __('Total weight') }} • + <span class="gl-font-weight-normal" data-testid="epic-countables-total-weight"> + {{ totalWeight }} + </span> + </p> + </gl-tooltip> + + <gl-tooltip + v-if="shouldRenderEpicProgress" + :target="() => $refs.progressBadge" + data-testid="epic-progress-tooltip" + > + <p class="gl-font-weight-bold gl-m-0"> + {{ __('Progress') }} • + <span class="gl-font-weight-normal" data-testid="epic-progress-tooltip-content"> + <gl-sprintf + :message="__('%{completedWeight} of %{totalWeight} weight completed')" + > + <template #completedWeight>{{ + item.descendantWeightSum.closedIssues + }}</template> + <template #totalWeight>{{ totalWeight }}</template> + </gl-sprintf> + </span> + </p> + </gl-tooltip> + + <span ref="countBadge" class="issue-count-badge board-card-info gl-mr-0 gl-pr-0"> + <span v-if="allowSubEpics" class="gl-mr-3"> + <gl-icon name="epic" /> + {{ totalEpicsCount }} + </span> + <span class="gl-mr-3" data-testid="epic-countables-counts-issues"> + <gl-icon name="issues" /> + {{ totalIssuesCount }} + </span> + <span class="gl-mr-3" data-testid="epic-countables-weight-issues"> + <gl-icon name="weight" /> + {{ totalWeight }} + </span> + </span> + + <span + v-if="shouldRenderEpicProgress" + ref="progressBadge" + class="issue-count-badge board-card-info gl-pl-0" + > + <span class="gl-mr-3" data-testid="epic-progress"> + <gl-icon name="progress" /> + {{ totalProgress }}% + </span> + </span> + </span> + <span v-if="!isEpicBoard"> + <issue-due-date + v-if="item.dueDate" + :date="item.dueDate" + :closed="item.closed || Boolean(item.closedAt)" + /> + <issue-time-estimate v-if="item.timeEstimate" :estimate="item.timeEstimate" /> + <issue-card-weight + v-if="validIssueWeight(item)" + :weight="item.weight" + @click="filterByWeight(item.weight)" + /> + </span> </span> </div> <div class="board-card-assignee gl-display-flex"> |