/*-
* ============LICENSE_START=======================================================
* SDC
* ================================================================================
* Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*/
///
module Sdc.ViewModels.Wizard {
'use strict';
export class StepNames {
static general = "General";
static icon = "Icon";
static deploymentArtifact = "Deployment Artifact";
static informationArtifact = "Information Artifact";
static properties = "Properties";
static hierarchy = "Hierarchy";
}
class FooterButtons {
static cancel = "Cancel";
static back = "Back";
static next = "Next";
static finish = "Finish";
}
export class WizardCreationTypes {
static importAsset = "importAsset";
static create = "create";
static edit = "edit";
}
export class _CreationStep {
name:string;
url:string;
}
export class CurrentStep {
assetCreationStep:_CreationStep;
reference:IWizardCreationStep;
index:number;
valid: boolean;
}
export interface IWizardCreationStep {
save(callback:Function):void;
back(callback:Function):void;
}
export interface IWizardCreationStepScope extends ng.IScope {
data:any;
getComponent():Sdc.Models.Components.Component;
setComponent(component: Sdc.Models.Components.Component):void;
registerChild(child:IWizardCreationStep):void; // Called from the step
setValidState(valid:boolean):void; // Called from the step
}
export interface IWizardCreationScope extends ng.IScope {
isLoading: boolean;
data:any; // data passed from dashboard (opener), need it on the scope, because general step will use this (extends the scope)
directiveSteps: Array; // Steps for the directive, on the scope (on the scope because need to pass to directive via HTML)
templateUrl: string; // On the scope because need to pass to via HTML
footerButtons: Array; // Wizard footer buttons (on the scope because need to pass to directive via HTML)
assetCreationControl:any; // Link to wizard directive functions.
modalInstance:ng.ui.bootstrap.IModalServiceInstance; // Reference to the modal, so we can close it (on the scope because need to pass to directive via HTML)
assetName:string;
assetType:string;
modalTitle:string;
getComponent():Sdc.Models.Components.Component;
setComponent(component: Sdc.Models.Components.Component):void;
registerChild(child:IWizardCreationStep):void; // Called from the step
setValidState(valid:boolean):void; // Called from the step
}
export class WizardCreationBaseViewModel {
component: Sdc.Models.Components.Component;
protected assetCreationSteps: Array<_CreationStep>; // Contains URL and name so we can replace them
currentStep:CurrentStep;
protected type:string;
constructor(protected $scope:IWizardCreationScope,
protected data:any,
protected ComponentFactory: Utils.ComponentFactory,
protected $modalInstance: ng.ui.bootstrap.IModalServiceInstance
) {
this.$scope.data = data;
this.currentStep = new CurrentStep();
this.currentStep.valid=false;
this.$scope.modalInstance = this.$modalInstance;
this.initScope();
this.noBackspaceNav();
// In case the modal was opened with filled resource (edit mode).
if (data.component){
this.$scope.setComponent(data.component);
data.componentType = this.$scope.getComponent().componentType;
window.setTimeout(()=>{
this.safeApply(this.setCurrentStepByIndex(0, false));
},100);
} else {
// Default step to start with
window.setTimeout(()=>{
this.safeApply(this.setCurrentStepByIndex(0, false));
},100);
}
}
private safeApply = (fn:any) => {
let phase = this.$scope.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if (fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$scope.$apply(fn);
}
};
private initScope = ():void => {
// Control to call functions on wizard step directive
this.$scope.assetCreationControl = {};
// Footer buttons definitions for the modal directive.
this.$scope.footerButtons = [
{"name":FooterButtons.cancel, "css":'white cancel',"callback": ()=>{this.btnCancelClicked();}},
{"name":FooterButtons.back, "disabled":true, "css":'white back',"callback": ()=>{this.btnBackClicked();}},
{"name":FooterButtons.next, "disabled":true, "css":'blue next',"callback": ()=>{this.btnNextClicked();}},
{"name":FooterButtons.finish, "disabled":true, "css":'white finish',"callback": ()=>{this.btnFinishedClicked();}}
];
// Will be called from step constructor to register him.
// So the current step will be the reference.
this.$scope.registerChild=(child:IWizardCreationStep):void => {
this.currentStep.reference=child;
};
// Will be called from each step to notify if the step is valid
// The wizard will set the "Next", "Finish" buttons accordingly.
this.$scope.setValidState = (valid:boolean):void => {
this.currentStep.valid=valid;
let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index];
this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, valid);
this.footerButtonsStateMachine();
this.wizardButtonsIconsStateMachine();
};
/**
* Will be called from each step to get current entity.
* This will return copy of the entity (not reference), because I do not want that the step will change entity parameters.
* If the step need to update the entity it can call setComponent function.
* @returns {Sdc.Models.IEntity}
*/
this.$scope.getComponent = ():Sdc.Models.Components.Component => {
return this.component;
};
// Will be called from each step after save to update the resource.
this.$scope.setComponent = (component:Sdc.Models.Components.Component):void => {
this.component = component;
};
};
protected setCurrentStepByName = (stepName:string):boolean => {
let stepIndex:number = this.getStepIndex(stepName);
return this.setCurrentStepByIndex(stepIndex);
};
// Set the current step, change the URL in ng-include.
protected setCurrentStepByIndex = (index:number, doSave:boolean=true):boolean => {
let result:boolean = false;
if (this.currentStep.index!==index) { // Check that not pressing on same step, also the first time currentStepIndex=undefined
if (doSave===true) {
this.callStepSave(() => {
// This section will be executed only if success save.
// Set current step in the left wizard directive = valid
let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index];
this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, true);
// Move to next step
let step:_CreationStep = this.assetCreationSteps[index];
this.currentStep.index = index;
this.currentStep.assetCreationStep = step;
this.$scope.templateUrl = step.url;
// Update the next/back buttons and steps buttons.
this.footerButtonsStateMachine();
this.wizardButtonsIconsStateMachine();
// Can not perform step click without enabling the step
this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it.
this.$scope.assetCreationControl.stepClicked(step.name);
// After saving the asset name and type will be shown in the top right of the screen.
this.fillAssetNameAndType();
result=true;
});
} else {
// For the first time
let step:_CreationStep = this.assetCreationSteps[index];
this.currentStep.index = index;
this.currentStep.assetCreationStep=step;
this.$scope.templateUrl = step.url;
this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it.
this.$scope.assetCreationControl.stepClicked(step.name);
result=true;
}
//this.updateFooterButtonsStates();
} else {
result=true;
}
return result;
};
// Save the current step
private callStepSave = (successCallback:Function):void => {
this.$scope.isLoading = true;
this.currentStep.reference.save((result:boolean)=>{
this.$scope.isLoading = false;
if (result===true){
successCallback();
} else {
// Set the next and finish button enabled.
//this.updateFooterButtonsStates(true);
}
});
};
// Save the current step
private callStepBack = (successCallback:Function):void => {
this.$scope.isLoading = true;
this.currentStep.reference.back((result:boolean)=>{
this.$scope.isLoading = false;
if (result===true){
successCallback();
} else {
// Set the next and finish button enabled.
//this.updateFooterButtonsStates(true);
}
});
};
private getStepIndex = (stepName:string):number => {
let index:number=-1;
let tmp = _.find(this.assetCreationSteps, function (item, indx) {
index = indx;
return item.name === stepName;
});
return index;
};
private btnNextClicked = ():void => {
if (this.hasNext()===true) {
let tmp = this.currentStep.index+1;
this.setCurrentStepByIndex(tmp);
}
};
private btnBackClicked = ():void => {
if (this.hasBack()===true) {
this.callStepBack(() => {
let tmp = this.currentStep.index-1;
this.setCurrentStepByIndex(tmp, false);
});
}
};
private btnCancelClicked = ():void => {
this.$modalInstance.dismiss(this.$scope.getComponent());
};
private btnFinishedClicked = ():void => {
this.callStepSave(() => {
this.$modalInstance.close(this.$scope.getComponent());
});
};
// Check if we can move next
private hasNext = ():boolean => {
if (this.assetCreationSteps.length-1>this.currentStep.index){
return true;
} else {
return false;
}
};
// Check if we can move back
private hasBack = ():boolean => {
if (this.currentStep.index===0){
return false;
} else {
return true;
}
};
private fillAssetNameAndType=():void => {
this.$scope.assetName = this.$scope.getComponent().name;
this.$scope.assetType = this.$scope.getComponent().getComponentSubType();
};
protected enableAllWizardSteps=():void => {
this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => {
step.enabled=true;
});
};
protected disableAllWizardSteps=():void => {
this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => {
step.enabled=false;
});
};
private footerButtonsStateMachine = ():void => {
//console.log("footerButtonsStateMachine, current step validity: " + this.currentStep.valid);
let stepIndex:number = this.currentStep.index;
let cancelButton = this.$scope.footerButtons[0];
let backButton = this.$scope.footerButtons[1];
let nextButton = this.$scope.footerButtons[2];
let finishButton = this.$scope.footerButtons[3];
// NEXT button
// Disable next button if it is the last step, and if not check the validity of the step.
if (this.hasNext()){
nextButton.disabled = !this.currentStep.valid;
} else {
nextButton.disabled = true;
}
// BACK button
backButton.disabled = !this.hasBack();
// FINISH button
// If step 2 is valid show the finish button.
if (stepIndex>=1 && this.currentStep.valid===true) {
finishButton.disabled = false;
}
if (this.currentStep.valid===false){
finishButton.disabled = true;
}
// EDIT
if (this.type===WizardCreationTypes.edit && this.currentStep.valid===true){
finishButton.disabled = false;
}
};
private wizardButtonsIconsStateMachine = ():void => {
// Enable or disable wizard directive next step, in case the current step form is valid or not.
let stepIndex:number = this.currentStep.index;
if (this.$scope.directiveSteps[stepIndex + 1]) {
this.$scope.directiveSteps[stepIndex + 1].enabled = this.currentStep.valid;
}
// In case step 1 and 2 are valid, we can open all other steps.
if (this.$scope.directiveSteps[0].valid===true && this.$scope.directiveSteps[1].valid===true){
// Enable all wizard directive steps
this.enableAllWizardSteps();
} else if (this.currentStep.valid===false) {
// Disable all steps
this.disableAllWizardSteps();
}
};
private noBackspaceNav:Function = ():void => {
this.$scope.$on('$locationChangeStart', (event, newUrl, oldUrl):void =>{
event.preventDefault();
})
};
}
}