Skip to content

Commit

Permalink
Merge pull request #14530 from opf/feature/52076-link-from-work-estim…
Browse files Browse the repository at this point in the history
…ated-work-sum-to-query-view

[52076] Add link from work / estimated work sum to detailed query view
  • Loading branch information
cbliard authored Jan 12, 2024
2 parents 67dcb41 + 50336a5 commit 62c22bc
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class EditCellHandler extends ClickOrEnterHandler implements TableEventHa
super();
}

protected processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):boolean {
protected processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):void {
debugLog('Starting editing on cell: ', evt.target);
evt.preventDefault();

Expand All @@ -46,7 +46,7 @@ export class EditCellHandler extends ClickOrEnterHandler implements TableEventHa

if (!fieldName) {
debugLog('Click handled by cell not a field? ', evt.target);
return true;
return;
}

// Locate the row
Expand All @@ -70,7 +70,5 @@ export class EditCellHandler extends ClickOrEnterHandler implements TableEventHa
handler.focus(positionOffset);
})
.catch(() => target.addClass(readOnlyClassName));

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class RelationsCellHandler extends ClickOrEnterHandler implements TableEv
super();
}

protected processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):boolean {
protected processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):void {
debugLog('Handled click on relation cell %o', evt.target);
evt.preventDefault();

Expand All @@ -46,7 +46,5 @@ export class RelationsCellHandler extends ClickOrEnterHandler implements TableEv
} else {
this.wpTableRelationColumns.setExpandFor(workPackageId, columnId);
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import { WorkPackageTable } from '../wp-fast-table';
export function onClickOrEnter(evt:JQuery.TriggeredEvent, callback:() => void) {
if (evt.type === 'click' || (evt.type === 'keydown' && evt.which === KeyCodes.ENTER)) {
callback();
return false;
}

return true;
}

export abstract class ClickOrEnterHandler {
public handleEvent(view:TableEventComponent, evt:JQuery.TriggeredEvent) {
onClickOrEnter(evt, () => this.processEvent(view.workPackageTable, evt));
}

protected abstract processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):boolean;
protected abstract processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class HierarchyClickHandler extends ClickOrEnterHandler implements TableE
return jQuery(view.workPackageTable.tbody);
}

public processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):boolean {
public processEvent(table:WorkPackageTable, evt:JQuery.TriggeredEvent):void {
const target = jQuery(evt.target);

// Locate the row from event
Expand All @@ -40,6 +40,5 @@ export class HierarchyClickHandler extends ClickOrEnterHandler implements TableE

evt.stopImmediatePropagation();
evt.preventDefault();
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,42 @@ export class WorkPackageStateLinksHandler implements TableEventHandler {
protected workPackage:WorkPackageResource;

public handleEvent(view:TableEventComponent, evt:JQuery.TriggeredEvent) {
// Avoid the state capture when clicking with modifier
evt.stopPropagation();

// Avoid the state capture when clicking with modifier to allow browser opening in new tab
if (evt.shiftKey || evt.ctrlKey || evt.metaKey || evt.altKey) {
return true;
}

// Locate the details link from event
const target = jQuery(evt.target);
const element = target.closest(this.SELECTOR);
const state = element.data('wpState');
const workPackageId = element.data('workPackageId');
// debugger;
const target = evt.target as HTMLElement;
const element = target.closest(this.SELECTOR) as HTMLElement & { dataset:DOMStringMap };
const state = element.dataset.wpState;
const workPackageId = element.dataset.workPackageId;

// Normal link processing if there are no state and work package information
if (!state || !workPackageId) {
return true;
}

// Blur the target to avoid focus being kept there
target.closest('a').blur();
target.closest('a')?.blur();

// The current row is the last selected work package
// not matter what other rows are (de-)selected below.
// Thus save that row for the details view button.
// Locate the row from event
const row = target.closest(`.${tableRowClassName}`);
const classIdentifier = row.data('classIdentifier');
const [index, _] = view.workPackageTable.findRenderedRow(classIdentifier);
const row = target.closest(`.${tableRowClassName}`) as HTMLElement & { dataset:DOMStringMap };
const classIdentifier = row.dataset.classIdentifier as string;
const [index] = view.workPackageTable.findRenderedRow(classIdentifier);

// Update single selection if no modifier present
this.wpTableSelection.setSelection(workPackageId, index);

view.stateLinkClicked.emit({ workPackageId, requestedState: state });

evt.preventDefault();
evt.stopPropagation();
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const cssClassCustomOption = 'custom-option';
export class DisplayField<T extends HalResource = HalResource> extends Field {
public static type:string;

public resource:T;

public mode:string | null = null;

public activeChange:ResourceChangeset<T>|null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,20 @@

import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import * as URI from 'urijs';
import { TimezoneService } from 'core-app/core/datetime/timezone.service';
import { ProjectResource } from 'core-app/features/hal/resources/project-resource';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
import { uiStateLinkClass } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder';

export class EstimatedTimeDisplayField extends DisplayField {
@InjectField() timezoneService:TimezoneService;

@InjectField() PathHelper:PathHelperService;

@InjectField() apiV3Service:ApiV3Service;

private derivedText = this.I18n.t('js.label_value_derived_from_children');

public get valueString():string {
Expand Down Expand Up @@ -100,13 +109,15 @@ export class EstimatedTimeDisplayField extends DisplayField {
}

public renderDerived(element:HTMLElement, displayText:string):void {
const span = document.createElement('span');
const link = document.createElement('a');

span.textContent = ${displayText}`;
span.title = `${this.derivedValueString} ${this.derivedText}`;
span.classList.add('-derived-value');
link.textContent = ${displayText}`;
link.title = `${this.derivedValueString} ${this.derivedText}`;
link.classList.add('-derived-value', uiStateLinkClass);

element.appendChild(span);
this.addURLToViewWorkPackageChildren(link);

element.appendChild(link);
}

public get title():string|null {
Expand All @@ -120,4 +131,29 @@ export class EstimatedTimeDisplayField extends DisplayField {

return !value && !derived;
}

private addURLToViewWorkPackageChildren(link:HTMLAnchorElement):void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (this.resource && this.resource.id && this.resource.project) {
const wpID = this.resource.id.toString();
this
.apiV3Service
.projects
.id(this.resource.project as ProjectResource)
.get()
.subscribe((project:ProjectResource) => {
const props = {
c: ['id', 'subject', 'type', 'status', 'estimatedTime', 'remainingTime'],
hi: true,
is: true,
f: [{ n: 'parent', o: '=', v: [wpID] }],
};
const href = URI(this.PathHelper.projectWorkPackagesPath(project.identifier as string))
.query({ query_props: JSON.stringify(props) })
.toString();

link.href = href;
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ export class WorkPackageSpentTimeDisplayField extends EstimatedTimeDisplayField
link.setAttribute('class', 'time-logging--value');
}

if (this.resource.project) {
if (this.resource.project && this.resource.id) {
const wpID = this.resource.id.toString();
this
.apiV3Service
.projects
.id(this.resource.project)
.id(this.resource.project as ProjectResource)
.get()
.subscribe((project:ProjectResource) => {
// Link to the cost report having the work package filter preselected. No grouping.
const href = URI(this.PathHelper.projectTimeEntriesPath(project.identifier))
const href = URI(this.PathHelper.projectTimeEntriesPath(project.identifier as string))
.search(`fields[]=WorkPackageId&operators[WorkPackageId]=%3D&values[WorkPackageId]=${wpID}&set_filter=1`)
.toString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,6 @@ display-field
padding-right: 0.25rem
text-align: center

.-derived-value
color: var(--color-fg-muted)
font-weight: var(--base-text-weight-bold)

&.spentTime .time-logging--value
padding: 0 2px

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@

let(:wp_table) { Pages::WorkPackagesTable.new project }
let(:editor) { Components::WysiwygEditor.new }
let(:initiator_work_package) { child }

before do
WorkPackages::UpdateAncestorsService
.new(user:, work_package: child)
.new(user:, work_package: initiator_work_package)
.call([:estimated_hours])

login_as(user)
Expand Down Expand Up @@ -142,4 +143,61 @@

include_examples 'estimated time display', expected_text: '-'
end

describe 'link to detailed view' do
let_work_packages(<<~TABLE)
hierarchy | work |
parent | 5h |
child 1 | 0h |
child 2 | 3h |
grand child 21 | 12h |
child 3 | |
other one | 2h |
TABLE

# Run UpdateAncestorsService on the grand child to update the whole hierarchy derived values
let(:initiator_work_package) { grand_child21 }

it 'displays a link to a detailed view explaining work calculation' do
wp_table.visit_query query

# parent
expect(page).to have_content("5 h·Σ 20 h")
expect(page).to have_link("Σ 20 h")
# child 2
expect(page).to have_content("3 h·Σ 15 h")
expect(page).to have_link("Σ 15 h")
end

context 'when clicking the link of a top parent' do
before do
visit work_package_path(parent)
end

it 'shows a work package table with a parent filter to list the direct children' do
click_on("Σ 20 h")

wp_table.expect_work_package_count(4)
wp_table.expect_work_package_listed(parent, child1, child2, child3)
within(:table) do
expect(page).to have_columnheader('Work')
expect(page).to have_columnheader('Remaining work')
end
end
end

context 'when clicking the link of an intermediate parent' do
before do
visit work_package_path(child2)
end

it 'shows also all ancestors in the work package table' do
expect(page).to have_content("Work\n3 h·Σ 15 h")
click_on("Σ 15 h")

wp_table.expect_work_package_count(3)
wp_table.expect_work_package_listed(parent, child2, grand_child21)
end
end
end
end

0 comments on commit 62c22bc

Please sign in to comment.