extract common functionality into create service

pull/6973/head
Jens Ulferts 6 years ago
parent d5d70bfb1a
commit 84e54b7417
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 11
      frontend/src/app/components/wp-copy/wp-copy.controller.ts
  2. 2
      frontend/src/app/components/wp-edit-form/work-package-filter-values.ts
  3. 50
      frontend/src/app/components/wp-inline-create/wp-inline-create.component.ts
  4. 49
      frontend/src/app/components/wp-new/wp-create.controller.ts
  5. 97
      frontend/src/app/components/wp-new/wp-create.service.ts
  6. 3
      frontend/src/app/components/wp-table/embedded/wp-embedded-table.component.ts
  7. 3
      spec/features/work_packages/new/new_work_package_spec.rb
  8. 1
      spec/support/components/work_packages/relations.rb

@ -32,12 +32,15 @@ import {WorkPackageChangeset} from 'core-components/wp-edit-form/work-package-ch
import {WorkPackageCreateController} from 'core-components/wp-new/wp-create.controller'; import {WorkPackageCreateController} from 'core-components/wp-new/wp-create.controller';
import {WorkPackageRelationsService} from "core-components/wp-relations/wp-relations.service"; import {WorkPackageRelationsService} from "core-components/wp-relations/wp-relations.service";
import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; import {untilComponentDestroyed} from "ng2-rx-componentdestroyed";
import {WorkPackageEditingService} from "core-components/wp-edit-form/work-package-editing-service";
import {IWorkPackageEditingServiceToken} from "core-components/wp-edit-form/work-package-editing.service.interface";
export class WorkPackageCopyController extends WorkPackageCreateController { export class WorkPackageCopyController extends WorkPackageCreateController {
private __initialized_at:Number; private __initialized_at:Number;
private copiedWorkPackageId:string; private copiedWorkPackageId:string;
private wpRelations:WorkPackageRelationsService = this.injector.get(WorkPackageRelationsService); private wpRelations:WorkPackageRelationsService = this.injector.get(WorkPackageRelationsService);
protected wpEditing:WorkPackageEditingService = this.injector.get<WorkPackageEditingService>(IWorkPackageEditingServiceToken);
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
@ -53,8 +56,8 @@ export class WorkPackageCopyController extends WorkPackageCreateController {
}); });
} }
protected newWorkPackageFromParams(stateParams:any) { protected createdWorkPackage() {
this.copiedWorkPackageId = stateParams.copiedFromWorkPackageId; this.copiedWorkPackageId = this.stateParams.copiedFromWorkPackageId;
return new Promise<WorkPackageChangeset>((resolve, reject) => { return new Promise<WorkPackageChangeset>((resolve, reject) => {
this.wpCacheService.loadWorkPackage(this.copiedWorkPackageId) this.wpCacheService.loadWorkPackage(this.copiedWorkPackageId)
.values$() .values$()
@ -80,6 +83,10 @@ export class WorkPackageCopyController extends WorkPackageCreateController {
.copyWorkPackage(form, wp.project.identifier) .copyWorkPackage(form, wp.project.identifier)
.then((changeset) => { .then((changeset) => {
this.__initialized_at = changeset.workPackage.__initialized_at; this.__initialized_at = changeset.workPackage.__initialized_at;
this.wpCacheService.updateWorkPackage(changeset.workPackage);
this.wpEditing.updateValue('new', changeset);
return changeset; return changeset;
}) })
); );

@ -32,7 +32,7 @@ export class WorkPackageFilterValues {
} }
// Select the first value // Select the first value
var value = filter.values[0]; let value = filter.values[0];
// Avoid empty values // Avoid empty values
if (value) { if (value) {

@ -41,12 +41,8 @@ import {AuthorisationService} from 'core-app/modules/common/model-auth/model-aut
import {WorkPackageTableFocusService} from 'core-components/wp-fast-table/state/wp-table-focus.service'; import {WorkPackageTableFocusService} from 'core-components/wp-fast-table/state/wp-table-focus.service';
import {filter} from 'rxjs/operators'; import {filter} from 'rxjs/operators';
import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource';
import {WorkPackageCacheService} from '../work-packages/work-package-cache.service';
import {TableRowEditContext} from '../wp-edit-form/table-row-edit-context';
import {WorkPackageChangeset} from '../wp-edit-form/work-package-changeset'; import {WorkPackageChangeset} from '../wp-edit-form/work-package-changeset';
import {WorkPackageEditForm} from '../wp-edit-form/work-package-edit-form'; import {WorkPackageEditForm} from '../wp-edit-form/work-package-edit-form';
import {WorkPackageEditingService} from '../wp-edit-form/work-package-editing-service';
import {WorkPackageFilterValues} from '../wp-edit-form/work-package-filter-values';
import {TimelineRowBuilder} from '../wp-fast-table/builders/timeline/timeline-row-builder'; import {TimelineRowBuilder} from '../wp-fast-table/builders/timeline/timeline-row-builder';
import {onClickOrEnter} from '../wp-fast-table/handlers/click-or-enter-handler'; import {onClickOrEnter} from '../wp-fast-table/handlers/click-or-enter-handler';
import {WorkPackageTableColumnsService} from '../wp-fast-table/state/wp-table-columns.service'; import {WorkPackageTableColumnsService} from '../wp-fast-table/state/wp-table-columns.service';
@ -61,7 +57,6 @@ import {TableState} from 'core-components/wp-table/table-state/table-state';
import {untilComponentDestroyed} from 'ng2-rx-componentdestroyed'; import {untilComponentDestroyed} from 'ng2-rx-componentdestroyed';
import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; import {I18nService} from 'core-app/modules/common/i18n/i18n.service';
import {FocusHelperService} from 'core-app/modules/common/focus/focus-helper'; import {FocusHelperService} from 'core-app/modules/common/focus/focus-helper';
import {IWorkPackageEditingServiceToken} from "../wp-edit-form/work-package-editing.service.interface";
import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface"; import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface";
import {CurrentUserService} from "core-components/user/current-user.service"; import {CurrentUserService} from "core-components/user/current-user.service";
import {WorkPackageInlineCreateService} from "core-components/wp-inline-create/wp-inline-create.service"; import {WorkPackageInlineCreateService} from "core-components/wp-inline-create/wp-inline-create.service";
@ -92,7 +87,7 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
private rowBuilder:InlineCreateRowBuilder; private rowBuilder:InlineCreateRowBuilder;
private timelineBuilder:TimelineRowBuilder; private timelineBuilder:TimelineRowBuilder;
private editingSubscription:Subscription; private editingSubscription:Subscription|undefined;
private $element:JQuery; private $element:JQuery;
@ -101,9 +96,7 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
protected readonly FocusHelper:FocusHelperService, protected readonly FocusHelper:FocusHelperService,
protected readonly I18n:I18nService, protected readonly I18n:I18nService,
protected readonly tableState:TableState, protected readonly tableState:TableState,
protected readonly wpCacheService:WorkPackageCacheService,
protected readonly currentUser:CurrentUserService, protected readonly currentUser:CurrentUserService,
@Inject(IWorkPackageEditingServiceToken) protected readonly wpEditing:WorkPackageEditingService,
@Inject(IWorkPackageCreateServiceToken) protected readonly wpCreate:WorkPackageCreateService, @Inject(IWorkPackageCreateServiceToken) protected readonly wpCreate:WorkPackageCreateService,
protected readonly wpInlineCreate:WorkPackageInlineCreateService, protected readonly wpInlineCreate:WorkPackageInlineCreateService,
protected readonly wpTableColumns:WorkPackageTableColumnsService, protected readonly wpTableColumns:WorkPackageTableColumnsService,
@ -196,7 +189,7 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
this.wpInlineCreate.newInlineWorkPackageCreated.next(wp.id); this.wpInlineCreate.newInlineWorkPackageCreated.next(wp.id);
} else { } else {
// Remove current row // Remove current row
this.table.editing.stopEditing('new'); this.wpCreate.cancelCreation();
this.removeWorkPackageRow(); this.removeWorkPackageRow();
this.showRow(); this.showRow();
} }
@ -223,22 +216,16 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
public addWorkPackageRow() { public addWorkPackageRow() {
this.wpCreate this.wpCreate
.createNewWorkPackage(this.projectIdentifier) .createOrContinueWorkPackage(this.projectIdentifier)
.then((changeset:WorkPackageChangeset) => { .then((changeset:WorkPackageChangeset) => {
if (!changeset) {
throw 'No new work package was created';
}
const wp = this.currentWorkPackage = changeset.workPackage; const wp = this.currentWorkPackage = changeset.workPackage;
this.applyDefaults(changeset, wp);
this.editingSubscription = this this.editingSubscription = this
.wpEditing .wpCreate
.state(wp.id) .changesetUpdates$()
.values$()
.pipe( .pipe(
filter((cs) => !!this.currentWorkPackage && !!cs.form), filter((cs) => !!this.currentWorkPackage && !!cs.form),
//untilComponentDestroyed(this)
).subscribe((form) => { ).subscribe((form) => {
if (!this.isActive) { if (!this.isActive) {
this.insertRow(wp); this.insertRow(wp);
@ -250,25 +237,6 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
}); });
} }
// TODO: Move into Service?
/**
* Apply values to the work package from the current set of filters
*
* @param changeset
* @param wp
*/
private applyDefaults(changeset:WorkPackageChangeset, wp:WorkPackageResource) {
if (this.tableState.query.hasValue()) {
const filter = new WorkPackageFilterValues(this.injector, changeset, this.tableState.query.value!.filters);
filter.applyDefaultsFromFilters();
}
// Update the changeset with any added filtered values
this.wpEditing.updateValue('new', changeset);
this.wpCacheService.updateWorkPackage(this.currentWorkPackage!);
}
private insertRow(wp:WorkPackageResource) { private insertRow(wp:WorkPackageResource) {
// Actually render the row // Actually render the row
const form = this.workPackageEditForm = this.renderInlineCreateRow(wp); const form = this.workPackageEditForm = this.renderInlineCreateRow(wp);
@ -320,11 +288,12 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
} }
public removeWorkPackageRow() { public removeWorkPackageRow() {
this.wpCreate.cancelCreation();
this.currentWorkPackage = null; this.currentWorkPackage = null;
this.table.editing.stopEditing('new');
this.wpCacheService.clearSome('new');
this.$element.find('.wp-row-new').remove(); this.$element.find('.wp-row-new').remove();
this.editingSubscription.unsubscribe(); if (this.editingSubscription) {
this.editingSubscription.unsubscribe();
}
} }
public showRow() { public showRow() {
@ -346,5 +315,4 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
public get canAdd():boolean { public get canAdd():boolean {
return this.wpInlineCreate.canAdd; return this.wpInlineCreate.canAdd;
} }
} }

@ -35,8 +35,6 @@ import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-r
import {RootResource} from 'core-app/modules/hal/resources/root-resource'; import {RootResource} from 'core-app/modules/hal/resources/root-resource';
import {WorkPackageCacheService} from '../work-packages/work-package-cache.service'; import {WorkPackageCacheService} from '../work-packages/work-package-cache.service';
import {WorkPackageChangeset} from '../wp-edit-form/work-package-changeset'; import {WorkPackageChangeset} from '../wp-edit-form/work-package-changeset';
import {WorkPackageEditingService} from '../wp-edit-form/work-package-editing-service';
import {WorkPackageFilterValues} from '../wp-edit-form/work-package-filter-values';
import {WorkPackageNotificationService} from '../wp-edit/wp-notification.service'; import {WorkPackageNotificationService} from '../wp-edit/wp-notification.service';
import {WorkPackageTableFiltersService} from '../wp-fast-table/state/wp-table-filters.service'; import {WorkPackageTableFiltersService} from '../wp-fast-table/state/wp-table-filters.service';
import {WorkPackageCreateService} from './wp-create.service'; import {WorkPackageCreateService} from './wp-create.service';
@ -44,9 +42,6 @@ import {takeUntil} from 'rxjs/operators';
import {RootDmService} from 'core-app/modules/hal/dm-services/root-dm.service'; import {RootDmService} from 'core-app/modules/hal/dm-services/root-dm.service';
import {OpTitleService} from 'core-components/html/op-title.service'; import {OpTitleService} from 'core-components/html/op-title.service';
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; import {I18nService} from "core-app/modules/common/i18n/i18n.service";
import {
IWorkPackageEditingServiceToken
} from "../wp-edit-form/work-package-editing.service.interface";
import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface"; import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface";
import {CurrentUserService} from "core-app/components/user/current-user.service"; import {CurrentUserService} from "core-app/components/user/current-user.service";
@ -57,7 +52,6 @@ export class WorkPackageCreateController implements OnInit, OnDestroy {
public newWorkPackage:WorkPackageResource; public newWorkPackage:WorkPackageResource;
public parentWorkPackage:WorkPackageResource; public parentWorkPackage:WorkPackageResource;
public changeset:WorkPackageChangeset; public changeset:WorkPackageChangeset;
protected wpEditing:WorkPackageEditingService = this.injector.get<WorkPackageEditingService>(IWorkPackageEditingServiceToken);
public stateParams = this.$transition.params('to'); public stateParams = this.$transition.params('to');
public text = { public text = {
@ -81,16 +75,14 @@ export class WorkPackageCreateController implements OnInit, OnDestroy {
} }
public ngOnInit() { public ngOnInit() {
this.newWorkPackageFromParams(this.stateParams) this
.createdWorkPackage()
.then((changeset:WorkPackageChangeset) => { .then((changeset:WorkPackageChangeset) => {
this.changeset = changeset; this.changeset = changeset;
this.newWorkPackage = changeset.workPackage; this.newWorkPackage = changeset.workPackage;
this.setTitle(); this.setTitle();
this.wpCacheService.updateWorkPackage(this.newWorkPackage);
this.wpEditing.updateValue('new', changeset);
if (this.stateParams['parent_id']) { if (this.stateParams['parent_id']) {
this.changeset.setValue( this.changeset.setValue(
'parent', 'parent',
@ -137,36 +129,15 @@ export class WorkPackageCreateController implements OnInit, OnDestroy {
this.titleService.setFirstPart(this.I18n.t('js.work_packages.create.title')); this.titleService.setFirstPart(this.I18n.t('js.work_packages.create.title'));
} }
// TODO: move to service
protected newWorkPackageFromParams(stateParams:any):Promise<WorkPackageChangeset> {
const type = parseInt(stateParams.type);
// If there is an open edit for this type, continue it
const changeset = this.wpEditing.state('new').value;
if (changeset !== undefined) {
const changeType = changeset.workPackage.type;
const hasChanges = !changeset.empty;
const typeEmpty = (!changeType && !type);
const typeMatches = (changeType && changeType.idFromLink === type.toString());
if (hasChanges && (typeEmpty || typeMatches)) {
return Promise.resolve(changeset);
}
}
return this.wpCreate.createNewTypedWorkPackage(stateParams.projectPath, type).then(changeset => {
const filter = new WorkPackageFilterValues(this.injector, changeset, this.wpTableFilters.current, ['type']);
filter.applyDefaultsFromFilters();
return changeset;
});
}
public cancelAndBackToList() { public cancelAndBackToList() {
this.wpEditing.stopEditing(this.newWorkPackage.id); this.wpCreate.cancelCreation();
this.$state.go('work-packages.list', this.$state.params); this.$state.go('work-packages.list', this.$state.params);
} }
protected createdWorkPackage() {
const type = this.stateParams.type ? parseInt(this.stateParams.type) : undefined;
const project = this.stateParams.projectPath;
return this.wpCreate.createOrContinueWorkPackage(project, type);
}
} }

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
// ++ // ++
import {Injectable, Injector} from '@angular/core'; import {Injectable, Injector, Inject} from '@angular/core';
import {ApiWorkPackagesService} from '../api/api-work-packages/api-work-packages.service'; import {ApiWorkPackagesService} from '../api/api-work-packages/api-work-packages.service';
import {HalResource} from 'core-app/modules/hal/resources/hal-resource'; import {HalResource} from 'core-app/modules/hal/resources/hal-resource';
import {WorkPackageCacheService} from '../work-packages/work-package-cache.service'; import {WorkPackageCacheService} from '../work-packages/work-package-cache.service';
@ -36,6 +36,11 @@ import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-r
import {HalResourceService} from 'core-app/modules/hal/services/hal-resource.service'; import {HalResourceService} from 'core-app/modules/hal/services/hal-resource.service';
import {IWorkPackageCreateService} from "core-components/wp-new/wp-create.service.interface"; import {IWorkPackageCreateService} from "core-components/wp-new/wp-create.service.interface";
import {HookService} from 'core-app/modules/plugins/hook-service'; import {HookService} from 'core-app/modules/plugins/hook-service';
import {WorkPackageFilterValues} from "core-components/wp-edit-form/work-package-filter-values";
import {IWorkPackageEditingServiceToken} from "core-components/wp-edit-form/work-package-editing.service.interface";
import {WorkPackageEditingService} from "core-components/wp-edit-form/work-package-editing-service";
import {WorkPackageTableFiltersService} from "core-components/wp-fast-table/state/wp-table-filters.service";
import {TableState} from "core-components/wp-table/table-state/table-state";
@Injectable() @Injectable()
export class WorkPackageCreateService implements IWorkPackageCreateService { export class WorkPackageCreateService implements IWorkPackageCreateService {
@ -48,6 +53,8 @@ export class WorkPackageCreateService implements IWorkPackageCreateService {
protected hooks:HookService, protected hooks:HookService,
protected wpCacheService:WorkPackageCacheService, protected wpCacheService:WorkPackageCacheService,
protected halResourceService:HalResourceService, protected halResourceService:HalResourceService,
@Inject(IWorkPackageEditingServiceToken) protected readonly wpEditing:WorkPackageEditingService,
protected readonly tableState:TableState,
protected apiWorkPackages:ApiWorkPackagesService) { protected apiWorkPackages:ApiWorkPackagesService) {
} }
@ -115,4 +122,92 @@ export class WorkPackageCreateService implements IWorkPackageCreateService {
return this.form; return this.form;
} }
public cancelCreation() {
this.wpEditing.stopEditing('new');
this.wpCacheService.clearSome('new');
}
public changesetUpdates$() {
return this
.wpEditing
.state('new')
.values$();
}
public createOrContinueWorkPackage(projectIdentifier:string, type?:number) {
let changesetPromise = this.continueExistingEdit(type);
if (!changesetPromise) {
changesetPromise = this.createNewWithDefaults(projectIdentifier, type);
}
return changesetPromise.then((changeset) => {
this.wpEditing.updateValue('new', changeset);
this.wpCacheService.updateWorkPackage(changeset.workPackage);
return changeset;
});
}
protected continueExistingEdit(type?:number) {
const changeset = this.wpEditing.state('new').value;
if (changeset !== undefined) {
const changeType = changeset.workPackage.type;
const hasChanges = !changeset.empty;
const typeEmpty = !changeType && !type;
const typeMatches = type && changeType && changeType.idFromLink === type.toString();
if (hasChanges && (typeEmpty || typeMatches)) {
return Promise.resolve(changeset);
}
}
return null;
}
protected createNewWithDefaults(projectIdentifier:string, type?:number) {
let changesetPromise = null;
if (type) {
changesetPromise = this.createNewTypedWorkPackage(projectIdentifier, type);
} else {
changesetPromise = this.createNewWorkPackage(projectIdentifier);
}
return changesetPromise.then((changeset:WorkPackageChangeset) => {
if (!changeset) {
throw 'No new work package was created';
}
let except:string[] = [];
if (type) {
except = ['type'];
}
this.applyDefaults(changeset, changeset.workPackage, except);
return changeset;
});
}
/**
* Apply values to the work package from the current set of filters
*
* @param changeset
* @param wp
* @param except
*/
private applyDefaults(changeset:WorkPackageChangeset, wp:WorkPackageResource, except:string[]) {
// Not using WorkPackageTableFiltersService here as the embedded table does not load the form
// which will result in that service having empty current filters.
let query = this.tableState.query.value;
if (query) {
const filter = new WorkPackageFilterValues(this.injector, changeset, query.filters, except);
filter.applyDefaultsFromFilters();
}
}
} }

@ -29,6 +29,8 @@ import {OpModalService} from 'core-components/op-modals/op-modal.service';
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; import {I18nService} from "core-app/modules/common/i18n/i18n.service";
import {WorkPackageEmbeddedBaseComponent} from "core-components/wp-table/embedded/wp-embedded-base.component"; import {WorkPackageEmbeddedBaseComponent} from "core-components/wp-table/embedded/wp-embedded-base.component";
import {WorkPackageTableHighlightingService} from "core-components/wp-fast-table/state/wp-table-highlighting.service"; import {WorkPackageTableHighlightingService} from "core-components/wp-fast-table/state/wp-table-highlighting.service";
import {WorkPackageCreateService} from "core-components/wp-new/wp-create.service";
import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface";
@Component({ @Component({
selector: 'wp-embedded-table', selector: 'wp-embedded-table',
@ -49,6 +51,7 @@ import {WorkPackageTableHighlightingService} from "core-components/wp-fast-table
WorkPackageTableAdditionalElementsService, WorkPackageTableAdditionalElementsService,
WorkPackageTableRefreshService, WorkPackageTableRefreshService,
WorkPackageTableHighlightingService, WorkPackageTableHighlightingService,
{ provide: IWorkPackageCreateServiceToken, useClass: WorkPackageCreateService },
// Order is important here, to avoid this service // Order is important here, to avoid this service
// getting global injections // getting global injections
WorkPackageStatesInitializationService, WorkPackageStatesInitializationService,

@ -306,6 +306,7 @@ describe 'new work package', js: true do
end end
it 'can create the work package, but not update it after saving' do it 'can create the work package, but not update it after saving' do
type_field.activate!
type_field.set_value type_bug.name type_field.set_value type_bug.name
# wait after the type change # wait after the type change
sleep(0.2) sleep(0.2)
@ -321,7 +322,7 @@ describe 'new work package', js: true do
end end
end end
context 'a anonymous user is prompted to login' do context 'an anonymous user is prompted to login' do
let(:user) { FactoryBot.create(:anonymous) } let(:user) { FactoryBot.create(:anonymous) }
let(:wp_page) { ::Pages::Page.new } let(:wp_page) { ::Pages::Page.new }

@ -153,7 +153,6 @@ module Components
expect(page).to have_no_selector '.wp-breadcrumb-parent', wait: 10 expect(page).to have_no_selector '.wp-breadcrumb-parent', wait: 10
end end
def remove_parent def remove_parent
# Open the parent edit # Open the parent edit
find('.wp-relation--parent-change').click find('.wp-relation--parent-change').click

Loading…
Cancel
Save