Commit 678a662c authored by Coung Ngo's avatar Coung Ngo

Show iteration report in projects

Show report in projects so users can view an iteration report
while in the context of a project. Group-level information is
currently shown which will be corrected in a subsequent MR
parent 8d75358f
......@@ -35,11 +35,11 @@ export default {
IterationReportTabs,
},
apollo: {
group: {
namespace: {
query,
variables() {
return {
groupPath: this.groupPath,
groupPath: this.fullPath,
iid: this.iterationIid,
};
},
......@@ -56,7 +56,7 @@ export default {
},
},
props: {
groupPath: {
fullPath: {
type: String,
required: true,
},
......@@ -74,17 +74,17 @@ export default {
return {
isEditing: false,
error: '',
group: {
namespace: {
iteration: {},
},
};
},
computed: {
iteration() {
return this.group.iteration;
return this.namespace.iteration;
},
hasIteration() {
return !this.$apollo.queries.group.loading && this.iteration?.title;
return !this.$apollo.queries.namespace.loading && this.iteration?.title;
},
status() {
switch (this.iteration.state) {
......@@ -115,7 +115,7 @@ export default {
<gl-alert v-if="error" variant="danger" @dismiss="error = ''">
{{ error }}
</gl-alert>
<gl-loading-icon v-if="$apollo.queries.group.loading" class="gl-py-5" size="lg" />
<gl-loading-icon v-if="$apollo.queries.namespace.loading" class="gl-py-5" size="lg" />
<gl-empty-state
v-else-if="!hasIteration"
:title="__('Could not find iteration')"
......@@ -123,7 +123,7 @@ export default {
/>
<iteration-form
v-else-if="isEditing"
:group-path="groupPath"
:group-path="fullPath"
:is-editing="true"
:iteration="iteration"
@updated="isEditing = false"
......@@ -158,8 +158,8 @@ export default {
</div>
<h3 ref="title" class="page-title">{{ iteration.title }}</h3>
<div ref="description" v-text="iteration.description"></div>
<iteration-report-summary :group-path="groupPath" :iteration-id="iteration.id" />
<iteration-report-tabs :group-path="groupPath" :iteration-id="iteration.id" />
<iteration-report-summary :group-path="fullPath" :iteration-id="iteration.id" />
<iteration-report-tabs :group-path="fullPath" :iteration-id="iteration.id" />
</template>
</div>
</template>
......@@ -51,7 +51,7 @@ export function initIterationForm() {
export function initIterationReport() {
const el = document.querySelector('.js-iteration');
const { groupPath, iterationIid, editIterationPath } = el.dataset;
const { fullPath, iterationIid, editIterationPath } = el.dataset;
const canEdit = parseBoolean(el.dataset.canEdit);
return new Vue({
......@@ -60,7 +60,7 @@ export function initIterationReport() {
render(createElement) {
return createElement(IterationReport, {
props: {
groupPath,
fullPath,
iterationIid,
canEdit,
editIterationPath,
......
import { initIterationReport } from 'ee/iterations';
document.addEventListener('DOMContentLoaded', initIterationReport);
......@@ -14,14 +14,14 @@ class Groups::IterationsController < Groups::ApplicationController
private
def check_iterations_available!
return render_404 unless group.feature_available?(:iterations)
render_404 unless group.feature_available?(:iterations)
end
def authorize_create_iteration!
return render_404 unless can?(current_user, :create_iteration, group)
render_404 unless can?(current_user, :create_iteration, group)
end
def authorize_show_iteration!
return render_404 unless can?(current_user, :read_iteration, group)
render_404 unless can?(current_user, :read_iteration, group)
end
end
......@@ -2,10 +2,12 @@
class Projects::IterationsController < Projects::ApplicationController
before_action :check_iterations_available!
before_action :authorize_show_iteration!, only: [:index]
before_action :authorize_show_iteration!
def index; end
def show; end
private
def check_iterations_available!
......
......@@ -3,4 +3,4 @@
- page_title _("Iterations")
- if Feature.enabled?(:group_iterations, @group, default_enabled: true)
.js-iteration{ data: { group_path: @group.full_path, iteration_iid: params[:id], can_edit: can?(current_user, :admin_iteration, @group).to_s, preview_markdown_path: preview_markdown_path(@group) } }
.js-iteration{ data: { full_path: @group.full_path, iteration_iid: params[:id], can_edit: can?(current_user, :admin_iteration, @group).to_s, preview_markdown_path: preview_markdown_path(@group) } }
- add_to_breadcrumbs _("Iterations"), project_iterations_path(@project)
- breadcrumb_title params[:id]
- page_title _("Iterations")
- if Feature.enabled?(:project_iterations, @project.group)
.js-iteration{ data: { full_path: @project.group.full_path,
can_edit: can?(current_user, :admin_iteration, @project).to_s,
iteration_iid: params[:id],
preview_markdown_path: preview_markdown_path(@project) } }
......@@ -107,7 +107,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
resources :iterations, only: [:index]
resources :iterations, only: [:index, :show], constraints: { id: /\d+/ }
end
# End of the /-/ scope.
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User views iteration' do
let_it_be(:now) { Time.now }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group).user }
let_it_be(:iteration) { create(:iteration, :skip_future_date_validation, iid: 1, id: 2, group: group, title: 'Correct Iteration', start_date: now - 1.day, due_date: now) }
let_it_be(:other_iteration) { create(:iteration, :skip_future_date_validation, iid: 2, id: 1, group: group, title: 'Wrong Iteration', start_date: now - 4.days, due_date: now - 3.days) }
let_it_be(:issue) { create(:issue, project: project, iteration: iteration) }
let_it_be(:assigned_issue) { create(:issue, project: project, iteration: iteration, assignees: [user]) }
let_it_be(:closed_issue) { create(:closed_issue, project: project, iteration: iteration) }
let_it_be(:other_issue) { create(:issue, project: project, iteration: other_iteration) }
context 'with license' do
before do
stub_licensed_features(iterations: true)
sign_in(user)
end
context 'view an iteration', :js do
before do
visit project_iteration_path(project, iteration)
end
it 'shows iteration info and dates' do
expect(page).to have_content(iteration.title)
expect(page).to have_content(iteration.description)
expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y'))
expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y'))
end
it 'shows correct issues for issue' do
expect(page).to have_content(issue.title)
expect(page).to have_content(assigned_issue.title)
expect(page).to have_content(closed_issue.title)
expect(page).not_to have_content(other_issue.title)
end
end
end
end
......@@ -5,7 +5,7 @@ import { GlEmptyState, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
describe('Iterations tabs', () => {
let wrapper;
const defaultProps = {
groupPath: 'gitlab-org',
fullPath: 'gitlab-org',
iterationIid: '3',
};
......@@ -18,7 +18,7 @@ describe('Iterations tabs', () => {
propsData: props,
mocks: {
$apollo: {
queries: { group: { loading } },
queries: { namespace: { loading } },
},
},
stubs: {
......@@ -70,7 +70,7 @@ describe('Iterations tabs', () => {
});
wrapper.setData({
group: {
namespace: {
iteration,
},
});
......@@ -94,7 +94,7 @@ describe('Iterations tabs', () => {
it('escapes html in description', async () => {
wrapper.setData({
group: {
namespace: {
iteration: {
...iteration,
description: `<img src=x onerror=alert(document.domain)>`,
......
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