Commit 11cd5752 authored by Florie Guibert's avatar Florie Guibert

Display Project and Subgroup milestones on Roadmap

- Include descendants when fetching group epics
- Display project/subgroup/group info in popover
parent 7df008a5
......@@ -35,5 +35,17 @@ module Types
field :updated_at, Types::TimeType, null: false,
description: 'Timestamp of last milestone update'
field :project_milestone, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if milestone is at project level',
method: :project_milestone?
field :group_milestone, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if milestone is at group level',
method: :group_milestone?
field :subgroup_milestone, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if milestone is at subgroup level',
method: :subgroup_milestone?
end
end
......@@ -179,6 +179,10 @@ class Milestone < ApplicationRecord
end
end
def subgroup_milestone?
group_milestone? && parent.subgroup?
end
private
def milestone_format_reference(format = :iid)
......
......@@ -6673,11 +6673,21 @@ type Milestone {
"""
dueDate: Time
"""
Indicates if milestone is at group level
"""
groupMilestone: Boolean!
"""
ID of the milestone
"""
id: ID!
"""
Indicates if milestone is at project level
"""
projectMilestone: Boolean!
"""
Timestamp of the milestone start date
"""
......@@ -6688,6 +6698,11 @@ type Milestone {
"""
state: MilestoneStateEnum!
"""
Indicates if milestone is at subgroup level
"""
subgroupMilestone: Boolean!
"""
Title of the milestone
"""
......
......@@ -18754,6 +18754,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "groupMilestone",
"description": "Indicates if milestone is at group level",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the milestone",
......@@ -18772,6 +18790,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "projectMilestone",
"description": "Indicates if milestone is at project level",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "startDate",
"description": "Timestamp of the milestone start date",
......@@ -18804,6 +18840,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "subgroupMilestone",
"description": "Indicates if milestone is at subgroup level",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "title",
"description": "Title of the milestone",
......@@ -1019,9 +1019,12 @@ Represents a milestone.
| `createdAt` | Time! | Timestamp of milestone creation |
| `description` | String | Description of the milestone |
| `dueDate` | Time | Timestamp of the milestone due date |
| `groupMilestone` | Boolean! | Indicates if milestone is at group level |
| `id` | ID! | ID of the milestone |
| `projectMilestone` | Boolean! | Indicates if milestone is at project level |
| `startDate` | Time | Timestamp of the milestone start date |
| `state` | MilestoneStateEnum! | State of the milestone |
| `subgroupMilestone` | Boolean! | Indicates if milestone is at subgroup level |
| `title` | String! | Title of the milestone |
| `updatedAt` | Time! | Timestamp of last milestone update |
| `webPath` | String! | Web path of the milestone |
......
<script>
import { GlPopover } from '@gitlab/ui';
import { __ } from '~/locale';
import { GlIcon, GlPopover } from '@gitlab/ui';
import CommonMixin from '../mixins/common_mixin';
import QuartersPresetMixin from '../mixins/quarters_preset_mixin';
import MonthsPresetMixin from '../mixins/months_preset_mixin';
......@@ -9,6 +10,7 @@ import { TIMELINE_CELL_MIN_WIDTH, SCROLL_BAR_SIZE } from '../constants';
export default {
cellWidth: TIMELINE_CELL_MIN_WIDTH,
components: {
GlIcon,
GlPopover,
},
mixins: [CommonMixin, QuartersPresetMixin, MonthsPresetMixin, WeeksPresetMixin],
......@@ -97,6 +99,26 @@ export default {
}
return '';
},
milestoneType() {
const { subgroupMilestone, projectMilestone } = this.milestone;
if (projectMilestone) {
return __('Project milestone');
} else if (subgroupMilestone) {
return __('Subgroup milestone');
}
return __('Group milestone');
},
typeIcon() {
const { subgroupMilestone, projectMilestone } = this.milestone;
if (projectMilestone) {
return 'project';
} else if (subgroupMilestone) {
return 'subgroup';
}
return 'group';
},
},
mounted() {
this.$nextTick(() => {
......@@ -144,7 +166,11 @@ export default {
triggers="hover"
:title="milestone.title"
>
{{ timeframeString(milestone) }}
<div class="milestone-item-type gl-line-height-normal">
<gl-icon :name="typeIcon" class="gl-vertical-align-middle" />
<span class="d-inline-block gl-vertical-align-middle">{{ milestoneType }}</span>
</div>
<div class="milestone-item-date">{{ timeframeString(milestone) }}</div>
</gl-popover>
</span>
</div>
......
......@@ -3,6 +3,7 @@ query groupMilestones(
$state: MilestoneStateEnum
$startDate: Time
$dueDate: Time
$includeDescendants: Boolean
) {
group(fullPath: $fullPath) {
id
......@@ -11,6 +12,7 @@ query groupMilestones(
state: $state
startDate: $startDate
endDate: $dueDate
includeDescendants: $includeDescendants
) {
edges {
node {
......@@ -21,6 +23,9 @@ query groupMilestones(
dueDate
startDate
webPath
projectMilestone
groupMilestone
subgroupMilestone
}
}
}
......
......@@ -267,6 +267,7 @@ export const fetchGroupMilestones = (
presetType,
timeframe: defaultTimeframe || timeframe,
}),
includeDescendants: true,
...filterParams,
};
......
......@@ -547,3 +547,14 @@ html.group-epics-roadmap-html {
}
}
}
.milestone-item-type {
.gl-icon {
padding-right: $gl-spacing-scale-1;
}
}
.milestone-item-date {
color: $gray-700;
padding-top: $gl-spacing-scale-1;
}
---
title: Display Project and Subgroup milestones on Roadmap
merge_request: 32496
author:
type: changed
......@@ -608,6 +608,7 @@ describe('Roadmap Vuex Actions', () => {
state: mockState.milestonessState,
startDate: '2017-11-1',
dueDate: '2018-6-30',
includeDescendants: true,
};
});
......
......@@ -10770,6 +10770,9 @@ msgstr ""
msgid "Group members"
msgstr ""
msgid "Group milestone"
msgstr ""
msgid "Group name"
msgstr ""
......@@ -16586,6 +16589,9 @@ msgstr ""
msgid "Project members"
msgstr ""
msgid "Project milestone"
msgstr ""
msgid "Project name"
msgstr ""
......@@ -20727,6 +20733,9 @@ msgstr ""
msgid "StorageSize|Unknown"
msgstr ""
msgid "Subgroup milestone"
msgstr ""
msgid "Subgroup overview"
msgstr ""
......
......@@ -498,4 +498,23 @@ describe Milestone do
end
end
end
describe '#subgroup_milestone' do
context 'parent is subgroup' do
it 'returns true' do
group = create(:group)
subgroup = create(:group, :private, parent: group)
expect(build(:milestone, group: subgroup).subgroup_milestone?).to eq(true)
end
end
context 'parent is not subgroup' do
it 'returns false' do
group = create(:group)
expect(build(:milestone, group: group).subgroup_milestone?).to eq(false)
end
end
end
end
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