feat: add create and edit sms-campaign pages (#851)
Fixes: #167 Fixes: #764
This commit is contained in:
@@ -53,6 +53,8 @@ import { EditOfficeComponent } from './offices/edit-office/edit-office.component
|
||||
import { BulkImportComponent } from './bulk-import/bulk-import.component';
|
||||
import { ViewBulkImportComponent } from './bulk-import/view-bulk-import/view-bulk-import.component';
|
||||
import { ViewLoanProvisioningCriteriaComponent } from './loan-provisioning-criteria/view-loan-provisioning-criteria/view-loan-provisioning-criteria.component';
|
||||
import { CreateCampaignComponent } from './sms-campaigns/create-campaign/create-campaign.component';
|
||||
import { EditCampaignComponent } from './sms-campaigns/edit-campaign/edit-campaign.component';
|
||||
|
||||
/** Custom Resolvers */
|
||||
import { LoanProvisioningCriteriaResolver } from './loan-provisioning-criteria/loan-provisioning-criteria.resolver';
|
||||
@@ -87,6 +89,7 @@ import { EditCashierResolver } from './tellers/common-resolvers/edit-cashier.res
|
||||
import { HolidayTemplateResolver } from './holidays/holiday-template.resolver';
|
||||
import { AdhocQueryAndTemplateResolver } from './adhoc-query/common-resolvers/adhoc-query-and-template.resolver';
|
||||
import { BulkImportResolver } from './bulk-import/bulk-import.resolver';
|
||||
import { SmsCampaignTemplateResolver } from './sms-campaigns/common-resolvers/sms-campaign-template.resolver';
|
||||
|
||||
/** Organization Routes */
|
||||
const routes: Routes = [
|
||||
@@ -261,13 +264,35 @@ const routes: Routes = [
|
||||
smsCampaigns: SmsCampaignsResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
data: { title: extract('Create SMS Campaign'), breadcrumb: 'Create Campaign' },
|
||||
component: CreateCampaignComponent,
|
||||
resolve: {
|
||||
smsCampaignTemplate: SmsCampaignTemplateResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: ViewCampaignComponent,
|
||||
data: { title: extract('View SMS Campaign'), routeResolveBreadcrumb: ['smsCampaign', 'campaignName'] },
|
||||
resolve: {
|
||||
smsCampaign: SmsCampaignResolver
|
||||
}
|
||||
},
|
||||
runGuardsAndResolvers: 'always',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: ViewCampaignComponent,
|
||||
},
|
||||
{
|
||||
path: 'edit',
|
||||
component: EditCampaignComponent,
|
||||
data: { title: extract('Edit SMS Campaign'), breadcrumb: 'Edit', routeResolveBreadcrumb: false},
|
||||
resolve: {
|
||||
smsCampaignTemplate: SmsCampaignTemplateResolver,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -564,6 +589,7 @@ const routes: Routes = [
|
||||
CurrenciesResolver,
|
||||
SmsCampaignsResolver,
|
||||
SmsCampaignResolver,
|
||||
SmsCampaignTemplateResolver,
|
||||
AdhocQueriesResolver,
|
||||
AdhocQueryResolver,
|
||||
TellersResolver,
|
||||
|
||||
@@ -54,6 +54,14 @@ import { EditHolidayComponent } from './holidays/edit-holiday/edit-holiday.compo
|
||||
import { EditAdhocQueryComponent } from './adhoc-query/edit-adhoc-query/edit-adhoc-query.component';
|
||||
import { BulkImportComponent } from './/bulk-import/bulk-import.component';
|
||||
import { ViewBulkImportComponent } from './bulk-import/view-bulk-import/view-bulk-import.component';
|
||||
import { CreateCampaignComponent } from './sms-campaigns/create-campaign/create-campaign.component';
|
||||
import { SmsCampaignStepComponent } from './sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component';
|
||||
import { CampaignMessageStepComponent } from './sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component';
|
||||
import { CampaignPreviewStepComponent } from './sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component';
|
||||
import { BusinessRuleParametersComponent } from './sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component';
|
||||
import { EditCampaignComponent } from './sms-campaigns/edit-campaign/edit-campaign.component';
|
||||
import { EditSmsCampaignStepComponent } from './sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component';
|
||||
import { EditBusinessRuleParametersComponent } from './sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component';
|
||||
|
||||
/**
|
||||
* Organization Module
|
||||
@@ -113,7 +121,15 @@ import { ViewBulkImportComponent } from './bulk-import/view-bulk-import/view-bul
|
||||
EditHolidayComponent,
|
||||
EditAdhocQueryComponent,
|
||||
BulkImportComponent,
|
||||
ViewBulkImportComponent
|
||||
ViewBulkImportComponent,
|
||||
CreateCampaignComponent,
|
||||
SmsCampaignStepComponent,
|
||||
CampaignMessageStepComponent,
|
||||
CampaignPreviewStepComponent,
|
||||
BusinessRuleParametersComponent,
|
||||
EditCampaignComponent,
|
||||
EditSmsCampaignStepComponent,
|
||||
EditBusinessRuleParametersComponent
|
||||
]
|
||||
})
|
||||
export class OrganizationModule { }
|
||||
|
||||
@@ -185,8 +185,31 @@ export class OrganizationService {
|
||||
* @param {string} smsCampaignId SMS Campaign ID of SMS Campaign.
|
||||
* @returns {Observable<any>} SMS Campaign.
|
||||
*/
|
||||
getSmsCampaign(smsCampaignId: string): Observable<any> {
|
||||
return this.http.get(`/smscampaigns/${smsCampaignId}`);
|
||||
getSmsCampaign(campaignId: string): Observable<any> {
|
||||
return this.http.get(`/smscampaigns/${campaignId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} campaign Campaign to be created.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
createSmsCampaign(campaign: any): Observable<any> {
|
||||
return this.http.post('/smscampaigns', campaign);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} campaign Campaign to be updated.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
updateSmsCampaign(campaign: any, campaignId: string): Observable<any> {
|
||||
return this.http.put(`/smscampaigns/${campaignId}`, campaign);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Observable<any>} SMS Campaign template
|
||||
*/
|
||||
getSmsCampaignTemplate(): Observable<any> {
|
||||
return this.http.get('/smscampaigns/template');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve } from '@angular/router';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/** Custom Services */
|
||||
import { OrganizationService } from '../../organization.service';
|
||||
|
||||
/**
|
||||
* SMS Campaign Template resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SmsCampaignTemplateResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {OrganizationService} organizationService Organization service.
|
||||
*/
|
||||
constructor(private organizationService: OrganizationService) {}
|
||||
|
||||
/**
|
||||
* Returns the SMS Campaign Template.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(): Observable<any> {
|
||||
return this.organizationService.getSmsCampaignTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<div class="container">
|
||||
|
||||
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #smsCampaignStepper>
|
||||
|
||||
<ng-template matStepperIcon="number">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="edit">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="done">
|
||||
<fa-icon icon="check" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="error">
|
||||
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="preview">
|
||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<mat-step [stepControl]="smsCampaignForm">
|
||||
|
||||
<ng-template matStepLabel>CAMPAIGN</ng-template>
|
||||
|
||||
<mifosx-sms-campaign-step
|
||||
[smsCampaignTemplate]="smsCampaignTemplate"
|
||||
(templateParameters)="setParameters($event)"
|
||||
>
|
||||
</mifosx-sms-campaign-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
<mat-step>
|
||||
|
||||
<ng-template matStepLabel>MESSAGE</ng-template>
|
||||
|
||||
<mifosx-campaign-message-step
|
||||
[templateParameters]="templateParameters"
|
||||
[smsCampaignFormValid]="smsCampaignForm.valid"
|
||||
>
|
||||
</mifosx-campaign-message-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
<mat-step state="preview" *ngIf="smsCampaignForm.valid" completed>
|
||||
|
||||
<ng-template matStepLabel>PREVIEW</ng-template>
|
||||
|
||||
<mifosx-campaign-preview-step
|
||||
[smsCampaignTemplate]="smsCampaignTemplate"
|
||||
[campaign]="smsCampaign" (submit)="submit()"
|
||||
>
|
||||
</mifosx-campaign-preview-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
</mat-horizontal-stepper>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.container {
|
||||
width: 60%;;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CreateCampaignComponent } from './create-campaign.component';
|
||||
|
||||
describe('CreateCampaignComponent', () => {
|
||||
let component: CreateCampaignComponent;
|
||||
let fixture: ComponentFixture<CreateCampaignComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CreateCampaignComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CreateCampaignComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/** Angular Imports */
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
/** Custom Components */
|
||||
import { SmsCampaignStepComponent } from '../sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component';
|
||||
import { CampaignMessageStepComponent } from '../sms-campaign-stepper/campaign-message-step/campaign-message-step.component';
|
||||
|
||||
/** Custom Services */
|
||||
import { OrganizationService } from 'app/organization/organization.service';
|
||||
|
||||
/**
|
||||
* Create SMS Campaign Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-create-campaign',
|
||||
templateUrl: './create-campaign.component.html',
|
||||
styleUrls: ['./create-campaign.component.scss']
|
||||
})
|
||||
export class CreateCampaignComponent {
|
||||
|
||||
/** SMS Campaign Template */
|
||||
smsCampaignTemplate: any;
|
||||
/** Run report headers */
|
||||
templateParameters: any;
|
||||
|
||||
/** SMS Campaign Step */
|
||||
@ViewChild(SmsCampaignStepComponent) smsCampaignStep: SmsCampaignStepComponent;
|
||||
/** Campaign Message Step */
|
||||
@ViewChild(CampaignMessageStepComponent) campaignMessageStep: CampaignMessageStepComponent;
|
||||
|
||||
/**
|
||||
* Fetches campaign template from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {Router} router Router
|
||||
* @param {OrganizationService} organizationService Organization Service
|
||||
* @param {DatePipe} datePipe Date Pipe
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private organizationService: OrganizationService,
|
||||
private datePipe: DatePipe) {
|
||||
this.route.data.subscribe((data: { smsCampaignTemplate: any }) => {
|
||||
this.smsCampaignTemplate = data.smsCampaignTemplate;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves SMS Campaign forms in a single form group to check validity.
|
||||
*/
|
||||
get smsCampaignForm() {
|
||||
return this.smsCampaignStep.smsCampaignFormGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves SMS Campaign object after formatting form values.
|
||||
*/
|
||||
get smsCampaign() {
|
||||
return {
|
||||
...this.smsCampaignStep.smsCampaignFormGroupValue,
|
||||
...this.campaignMessageStep.campaignMessage
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes template parameters to campaign-message-step.
|
||||
* @param {any} $event Template parameters
|
||||
*/
|
||||
setParameters($event: any) {
|
||||
this.templateParameters = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates SMS Campaign.
|
||||
*/
|
||||
submit() {
|
||||
// TODO: Update once language and date settings are setup
|
||||
const locale = 'en';
|
||||
const dateFormat = 'dd MMMM yyyy';
|
||||
const dateTimeFormat = 'dd MMMM yyyy HH:mm:ss';
|
||||
const smsCampaign = {
|
||||
...this.smsCampaign,
|
||||
campaignType: this.smsCampaign.isNotification ? 2 : 1,
|
||||
submittedOnDate: this.datePipe.transform(new Date(), dateFormat),
|
||||
dateTimeFormat,
|
||||
dateFormat,
|
||||
locale
|
||||
};
|
||||
if (this.smsCampaign.triggerType === 2) {
|
||||
const prevRecurrenceDate: Date = smsCampaign.recurrenceStartDate;
|
||||
smsCampaign.recurrenceStartDate = this.datePipe.transform(prevRecurrenceDate, dateTimeFormat);
|
||||
}
|
||||
this.organizationService.createSmsCampaign(smsCampaign).subscribe((response: any) => {
|
||||
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<div class="container">
|
||||
|
||||
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #smsCampaignStepper>
|
||||
|
||||
<ng-template matStepperIcon="number">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="edit">
|
||||
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="done">
|
||||
<fa-icon icon="check" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="error">
|
||||
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<ng-template matStepperIcon="preview">
|
||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<mat-step completed>
|
||||
|
||||
<ng-template matStepLabel>CAMPAIGN</ng-template>
|
||||
|
||||
<mifosx-edit-sms-campaign-step
|
||||
[smsCampaign]="smsCampaign"
|
||||
[smsCampaignTemplate]="smsCampaignTemplate"
|
||||
(templateParameters)="setParameters($event)"
|
||||
>
|
||||
</mifosx-edit-sms-campaign-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
<mat-step>
|
||||
|
||||
<ng-template matStepLabel>MESSAGE</ng-template>
|
||||
|
||||
<mifosx-campaign-message-step
|
||||
[templateParameters]="templateParameters"
|
||||
[editCampaignMessage]="smsCampaign.campaignMessage"
|
||||
>
|
||||
</mifosx-campaign-message-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
<mat-step state="preview" completed>
|
||||
|
||||
<ng-template matStepLabel>PREVIEW</ng-template>
|
||||
|
||||
<mifosx-campaign-preview-step
|
||||
[campaign]="smsCampaign"
|
||||
[editedCampaignMessage]="campaignMessage"
|
||||
[smsCampaignTemplate]="smsCampaignTemplate" (submit)="submit()"
|
||||
>
|
||||
</mifosx-campaign-preview-step>
|
||||
|
||||
</mat-step>
|
||||
|
||||
</mat-horizontal-stepper>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.container {
|
||||
width: 60%;;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditCampaignComponent } from './edit-campaign.component';
|
||||
|
||||
describe('EditCampaignComponent', () => {
|
||||
let component: EditCampaignComponent;
|
||||
let fixture: ComponentFixture<EditCampaignComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditCampaignComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditCampaignComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
/** Angular Imports */
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
/** Custom Services */
|
||||
import { OrganizationService } from 'app/organization/organization.service';
|
||||
|
||||
/** Custom Components */
|
||||
import { CampaignMessageStepComponent } from '../sms-campaign-stepper/campaign-message-step/campaign-message-step.component';
|
||||
|
||||
/**
|
||||
* Edit Campaign Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-edit-campaign',
|
||||
templateUrl: './edit-campaign.component.html',
|
||||
styleUrls: ['./edit-campaign.component.scss']
|
||||
})
|
||||
export class EditCampaignComponent {
|
||||
|
||||
/** smsCampaign */
|
||||
smsCampaign: any;
|
||||
/** SMS Campaign Template */
|
||||
smsCampaignTemplate: any;
|
||||
/** Run report headers */
|
||||
templateParameters: any;
|
||||
|
||||
/** Campaign Message Step */
|
||||
@ViewChild(CampaignMessageStepComponent) campaignMessageStep: CampaignMessageStepComponent;
|
||||
|
||||
/**
|
||||
* Fetches campaign template from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {Router} router Router
|
||||
* @param {DatePipe} datePipe Date Pipe
|
||||
* @param {OrganizationService} organizationService Organiztion Service
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private datePipe: DatePipe,
|
||||
private organizationService: OrganizationService) {
|
||||
this.route.data.subscribe((data: { smsCampaign: any, smsCampaignTemplate: any }) => {
|
||||
this.smsCampaignTemplate = data.smsCampaignTemplate;
|
||||
this.smsCampaign = data.smsCampaign;
|
||||
this.smsCampaign.editFlag = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves edited campaign message.
|
||||
*/
|
||||
get campaignMessage() {
|
||||
return this.campaignMessageStep.campaignMessage.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes template parameters to campaign-message-step.
|
||||
* @param {any} $event Template parameters
|
||||
*/
|
||||
setParameters($event: any) {
|
||||
this.templateParameters = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates SMS Campaign.
|
||||
*/
|
||||
submit() {
|
||||
// TODO: Update once language and date settings are setup
|
||||
const locale = 'en';
|
||||
const dateFormat = 'dd MMMM yyyy';
|
||||
const dateTimeFormat = 'dd MMMM yyyy HH:mm:ss';
|
||||
const smsCampaign: any = {
|
||||
campaignName: this.smsCampaign.campaignName,
|
||||
campaignType: this.smsCampaign.isNotification ? 2 : 1,
|
||||
isNotification: this.smsCampaign.isNotification,
|
||||
triggerType: this.smsCampaign.triggerType.id,
|
||||
providerId: this.smsCampaign.providerId === 0 ? null : this.smsCampaign.providerId,
|
||||
runReportId: this.smsCampaign.runReportId,
|
||||
message: this.campaignMessage,
|
||||
paramValue: JSON.parse(this.smsCampaign.paramValue),
|
||||
dateTimeFormat,
|
||||
dateFormat,
|
||||
locale
|
||||
};
|
||||
if (this.smsCampaign.triggerType.id === 2) {
|
||||
smsCampaign.recurrenceStartDate = this.datePipe.transform(new Date(this.smsCampaign.recurrenceStartDate), dateTimeFormat);
|
||||
}
|
||||
this.organizationService.updateSmsCampaign(smsCampaign, this.smsCampaign.id).subscribe((response: any) => {
|
||||
this.router.navigate(['../'], { relativeTo: this.route });
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field fxFlex="98%">
|
||||
<mat-label>Campaign Message</mat-label>
|
||||
<textarea matInput [formControl]="message"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<h3 fxFlex="98%" class="mat-h3">Template Parameters</h3>
|
||||
|
||||
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutAlign="center">
|
||||
<button mat-stroked-button color="primary" class="parameter"
|
||||
*ngFor="let label of parameterLabels" (click)="addText(label)">
|
||||
{{ label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left"></fa-icon>
|
||||
Previous
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext [disabled]="!smsCampaignFormValid">
|
||||
Next
|
||||
<fa-icon icon="arrow-right"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1.75em;
|
||||
}
|
||||
|
||||
.parameter {
|
||||
margin: 1%;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CampaignMessageStepComponent } from './campaign-message-step.component';
|
||||
|
||||
describe('CampaignMessageStepComponent', () => {
|
||||
let component: CampaignMessageStepComponent;
|
||||
let fixture: ComponentFixture<CampaignMessageStepComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CampaignMessageStepComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CampaignMessageStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Campaign Message Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-campaign-message-step',
|
||||
templateUrl: './campaign-message-step.component.html',
|
||||
styleUrls: ['./campaign-message-step.component.scss']
|
||||
})
|
||||
export class CampaignMessageStepComponent implements OnChanges {
|
||||
|
||||
/** Column headers */
|
||||
@Input() templateParameters: any[];
|
||||
/** Valdity check for sms campaign form */
|
||||
@Input() smsCampaignFormValid: boolean;
|
||||
/** [Optional] SMS Campaign message for edit form */
|
||||
@Input() editCampaignMessage: any;
|
||||
|
||||
/** Camapaign Message */
|
||||
message = new FormControl('');
|
||||
/** Column header names */
|
||||
parameterLabels: any[];
|
||||
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* Sets template parameters once response headers are retrieved.
|
||||
*/
|
||||
ngOnChanges() {
|
||||
this.message.patchValue('');
|
||||
this.parameterLabels = [];
|
||||
if (this.templateParameters) {
|
||||
this.parameterLabels = this.templateParameters.map((entry: any) => {
|
||||
return entry.columnName;
|
||||
});
|
||||
}
|
||||
if (this.editCampaignMessage) {
|
||||
this.message.patchValue(this.editCampaignMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SMS Campaign message.
|
||||
*/
|
||||
get campaignMessage() {
|
||||
return { message: this.message.value };
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds template parameter interpolation to campaign message.
|
||||
* @param {string} label Template parameter label.
|
||||
*/
|
||||
addText(label: string) {
|
||||
const prevText = this.message.value;
|
||||
const newText = prevText + ` {{${label}}} `;
|
||||
this.message.patchValue(newText);
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<div class="tab-content mat-typography">
|
||||
|
||||
<mat-list>
|
||||
|
||||
<mat-list-item>
|
||||
Campaign Name : {{ campaign.campaignName }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
SMS Provider : {{ (campaign.providerId | find:smsProviders:'id':'name') || "Unassigned" }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Trigger Type : {{ (campaign.editFlag ? campaign.triggerType.id : campaign.triggerType) | find:triggerTypes:'id':'value' }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Bussiness Rule : {{ campaign.editFlag ? campaign.reportName : campaign.paramValue?.reportName }}
|
||||
</mat-list-item>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="10px" class="template-message">
|
||||
<h3>Campaign Message :</h3>
|
||||
<textarea matInput disabled>{{ campaign.editFlag ? editedCampaignMessage : campaign.message }}</textarea>
|
||||
</div>
|
||||
|
||||
</mat-list>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button [routerLink]="['../']">
|
||||
Cancel
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="submit.emit()">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
.tab-content {
|
||||
padding: 1%;
|
||||
margin: 1%;
|
||||
.template-message {
|
||||
padding-inline: 1.5%;
|
||||
margin-top: 1%;
|
||||
}
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CampaignPreviewStepComponent } from './campaign-preview-step.component';
|
||||
|
||||
describe('CampaignPreviewStepComponent', () => {
|
||||
let component: CampaignPreviewStepComponent;
|
||||
let fixture: ComponentFixture<CampaignPreviewStepComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CampaignPreviewStepComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CampaignPreviewStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Campaign Preview Step.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-campaign-preview-step',
|
||||
templateUrl: './campaign-preview-step.component.html',
|
||||
styleUrls: ['./campaign-preview-step.component.scss']
|
||||
})
|
||||
export class CampaignPreviewStepComponent implements OnInit {
|
||||
|
||||
/** SMS Campaign */
|
||||
@Input() campaign: any;
|
||||
/** [Optional] SMS Campaign Template for create form */
|
||||
@Input() smsCampaignTemplate: any;
|
||||
/** [Optional] SMS Campaign Message for edit form */
|
||||
@Input() editedCampaignMessage: any;
|
||||
|
||||
/** Trigger types options */
|
||||
triggerTypes: any[];
|
||||
/** SMS providers options */
|
||||
smsProviders: any[];
|
||||
|
||||
/** Emits submit() event */
|
||||
@Output() submit = new EventEmitter;
|
||||
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* Sets SMS providers and trigger types options.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.triggerTypes = this.smsCampaignTemplate.triggerTypeOptions;
|
||||
this.smsProviders = this.smsCampaignTemplate.smsProviderOptions;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-divider fxFlex="98%"></mat-divider>
|
||||
|
||||
<h3 fxFlex="98%" class="mat-h3">Business Rule Parameters</h3>
|
||||
|
||||
<form [formGroup]="ReportForm">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%">
|
||||
|
||||
<ng-container *ngFor="let param of paramData" [ngSwitch]="param.displayType">
|
||||
|
||||
<ng-container *ngIf="ReportForm.controls[param.name]">
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'text'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput required [formControlName]="param.name">
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'date'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="runReportDatePicker" required
|
||||
[formControlName]="param.name">
|
||||
<mat-datepicker-toggle matSuffix [for]="runReportDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #runReportDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'select'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<mat-select required [formControlName]="param.name" [compareWith]="compareOptions">
|
||||
<mat-option *ngFor="let option of param.selectOptions" [value]="option">
|
||||
{{option.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious disabled>
|
||||
<fa-icon icon="arrow-left"></fa-icon>
|
||||
Previous
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
Next
|
||||
<fa-icon icon="arrow-right"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin: 1em 0 2em 0;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.parameter {
|
||||
margin-inline: 1%;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditBusinessRuleParametersComponent } from './edit-business-rule-parameters.component';
|
||||
|
||||
describe('EditBusinessRuleParametersComponent', () => {
|
||||
let component: EditBusinessRuleParametersComponent;
|
||||
let fixture: ComponentFixture<EditBusinessRuleParametersComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditBusinessRuleParametersComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditBusinessRuleParametersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnChanges, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Validators, FormGroup, FormControl } from '@angular/forms';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
/** Rxjs Imports */
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from 'app/reports/reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
|
||||
import { SelectOption } from 'app/reports/common-models/select-option.model';
|
||||
|
||||
/**
|
||||
* Edit Business Rule Parameters.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-edit-business-rule-parameters',
|
||||
templateUrl: './edit-business-rule-parameters.component.html',
|
||||
styleUrls: ['./edit-business-rule-parameters.component.scss']
|
||||
})
|
||||
export class EditBusinessRuleParametersComponent implements OnChanges {
|
||||
|
||||
/** Run Report Parameters Data */
|
||||
@Input() paramData: any;
|
||||
/** SMS Campaign */
|
||||
@Input() smsCampaign: any;
|
||||
/** Template Parameters Event Emitter */
|
||||
@Output() templateParameters = new EventEmitter();
|
||||
|
||||
/** Initializes new form group ReportForm */
|
||||
ReportForm = new FormGroup({});
|
||||
/** Array of all parent parameters */
|
||||
parentParameters: any[] = [];
|
||||
/** Displayed user choices */
|
||||
paramValue: any;
|
||||
|
||||
/**
|
||||
* @param {ReportsService} reportsService Reports Service
|
||||
*/
|
||||
constructor(private reportsService: ReportsService,
|
||||
private datePipe: DatePipe) { }
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.paramData) {
|
||||
this.ReportForm = new FormGroup({});
|
||||
this.paramValue = JSON.parse(this.smsCampaign.paramValue);
|
||||
this.createRunReportForm();
|
||||
this.disableFormWhenValid();
|
||||
this.getResponseHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes form controls for Report Parameter's name attribute,
|
||||
* Fetches dropdown options and builds child dependencies.
|
||||
*/
|
||||
createRunReportForm() {
|
||||
this.paramData.forEach(
|
||||
(param: any) => {
|
||||
if (!param.parentParameterName) { // Non Child Parameter
|
||||
this.ReportForm.addControl(param.name, new FormControl('', Validators.required));
|
||||
const controlValue = this.paramValue[param.variable].toString();
|
||||
switch (param.displayType) {
|
||||
case 'text':
|
||||
this.ReportForm.get(param.name).patchValue(controlValue);
|
||||
break;
|
||||
case 'select':
|
||||
this.fetchSelectOptions(param, param.name);
|
||||
break;
|
||||
case 'date':
|
||||
const dateFormat = 'yyyy-MM-dd';
|
||||
const newControlValue = this.datePipe.transform(controlValue, dateFormat);
|
||||
this.ReportForm.get(param.name).patchValue(newControlValue);
|
||||
break;
|
||||
}
|
||||
} else { // Child Parameter
|
||||
const parent: ReportParameter = this.paramData
|
||||
.find((entry: any) => entry.name === param.parentParameterName);
|
||||
// TODO: Refactor once Report Parameter Model is updated for multi-child support.
|
||||
if (parent.childParameter instanceof Array) {
|
||||
parent.childParameter.push(param);
|
||||
} else {
|
||||
parent.childParameter = [];
|
||||
parent.childParameter.push(param);
|
||||
}
|
||||
const parentNames = this.parentParameters.map(parameter => parameter.name);
|
||||
if (!parentNames.includes(parent.name)) {
|
||||
this.parentParameters.push(parent);
|
||||
} else {
|
||||
const index = parentNames.indexOf(parent.name);
|
||||
this.parentParameters[index] = parent;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setChildControls();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to changes in parent parameters value, builds child parameter vis-a-vis parent's value.
|
||||
*/
|
||||
setChildControls() {
|
||||
this.parentParameters.forEach((param: ReportParameter) => {
|
||||
this.ReportForm.get(param.name).valueChanges.subscribe((option: any) => {
|
||||
param.childParameter.forEach((child: ReportParameter) => {
|
||||
if (child.displayType === 'none') {
|
||||
this.ReportForm.addControl(child.name, new FormControl(child.defaultVal));
|
||||
} else {
|
||||
this.ReportForm.addControl(child.name, new FormControl('', Validators.required));
|
||||
}
|
||||
if (child.displayType === 'select') {
|
||||
const inputstring = `${child.name}?${param.inputName}=${option.id}`;
|
||||
this.fetchSelectOptions(child, inputstring);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Select Dropdown options for param type "Select".
|
||||
* @param {ReportParameter} param Parameter for which dropdown options are required.
|
||||
* @param {string} inputstring url substring for API call.
|
||||
*/
|
||||
fetchSelectOptions(param: ReportParameter, inputstring: string) {
|
||||
this.reportsService.getSelectOptions(inputstring).subscribe((options: SelectOption[]) => {
|
||||
param.selectOptions = options;
|
||||
if (param.selectAll === 'Y') {
|
||||
param.selectOptions.push({id: '-1', name: 'All'});
|
||||
}
|
||||
const optionId = this.paramValue[param.variable].toString();
|
||||
const option = options.find(entry => entry.id === optionId);
|
||||
this.ReportForm.controls[param.name].patchValue({ id: optionId, name: option.name });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for mat-select.
|
||||
* Useful in patching values if value is an object.
|
||||
* @param {any} option1 option 1
|
||||
* @param {any} option2 option 2.
|
||||
*/
|
||||
compareOptions(option1: any, option2: any) {
|
||||
return option1 && option2 && option1.id === option2.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the Report Form once all values are patched.
|
||||
*/
|
||||
disableFormWhenValid() {
|
||||
this.ReportForm.statusChanges.pipe(distinctUntilChanged()).subscribe((status: string) => {
|
||||
if (status === 'VALID') {
|
||||
this.ReportForm.disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats user response and readies it for utilization by run report function.
|
||||
* @param {any} response Object containing formcontrol values.
|
||||
*/
|
||||
formatUserResponse(response: any, forHeaders: boolean) {
|
||||
const formattedResponse: any = {};
|
||||
let newKey: string;
|
||||
for (const [key, value] of Object.entries(response)) {
|
||||
const param: ReportParameter = this.paramData
|
||||
.find((_entry: any) => _entry.variable === key);
|
||||
newKey = forHeaders ? param.inputName : param.variable;
|
||||
formattedResponse[newKey] = value;
|
||||
}
|
||||
return formattedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets run report response headers.
|
||||
* Emits them via template parameters event emitter.
|
||||
* TODO: Replace report object with report name once reports service is refactored.
|
||||
*/
|
||||
getResponseHeaders() {
|
||||
const report = { name: this.paramValue.reportName };
|
||||
delete this.paramValue.reportName;
|
||||
const formattedResponse = this.formatUserResponse(this.paramValue, true);
|
||||
this.reportsService.getRunReportData(report, formattedResponse).subscribe(
|
||||
(response: any) => {
|
||||
this.templateParameters.emit(response.columnHeaders);
|
||||
},
|
||||
(error: any) => {
|
||||
this.templateParameters.emit(null);
|
||||
this.ReportForm.disable();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<form [formGroup]="smsCampaignDetailsForm">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" fxLayoutAlign.gt-sm="start center">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Campaign Name</mat-label>
|
||||
<input matInput formControlName="campaignName" required>
|
||||
<mat-error>
|
||||
Campaign Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="!smsCampaignDetailsForm.controls.isNotification.value">
|
||||
<mat-label>SMS Provider</mat-label>
|
||||
<mat-select formControlName="providerId">
|
||||
<mat-option *ngFor="let provider of smsProviders" [value]="provider.id">
|
||||
{{ provider.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Trigger Type</mat-label>
|
||||
<mat-select formControlName="triggerType" required>
|
||||
<mat-option *ngFor="let triggerType of triggerTypes" [value]="triggerType.id">
|
||||
{{ triggerType.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Trigger Type is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox labelPosition="before" formControlName="isNotification" fxFlex="48%">
|
||||
is Notification?
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="smsCampaignDetailsForm.controls.recurrenceStartDate">
|
||||
<mat-label>Schedule Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="recurrenceStartDatePicker"
|
||||
formControlName="recurrenceStartDate" required>
|
||||
<mat-datepicker-toggle matSuffix [for]="recurrenceStartDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #recurrenceStartDatePicker></mat-datepicker>
|
||||
<mat-error>
|
||||
Schedule Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Business Rule</mat-label>
|
||||
<mat-select formControlName="runReportId" required>
|
||||
<mat-option *ngFor="let rule of businessRules" [value]="rule.reportId">
|
||||
{{ rule.reportName }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Business Rule is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<mifosx-edit-business-rule-parameters [paramData]="paramData" [smsCampaign]="smsCampaign"
|
||||
(templateParameters)="passParameters($event)">
|
||||
</mifosx-edit-business-rule-parameters>
|
||||
|
||||
</form>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditSmsCampaignStepComponent } from './edit-sms-campaign-step.component';
|
||||
|
||||
describe('EditSmsCampaignStepComponent', () => {
|
||||
let component: EditSmsCampaignStepComponent;
|
||||
let fixture: ComponentFixture<EditSmsCampaignStepComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditSmsCampaignStepComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditSmsCampaignStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
|
||||
import { FormGroup, Validators, FormBuilder, FormControl } from '@angular/forms';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from 'app/reports/reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
|
||||
|
||||
/**
|
||||
* Edit SMS Campaign step.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-edit-sms-campaign-step',
|
||||
templateUrl: './edit-sms-campaign-step.component.html',
|
||||
styleUrls: ['./edit-sms-campaign-step.component.scss']
|
||||
})
|
||||
export class EditSmsCampaignStepComponent implements OnInit {
|
||||
|
||||
/** SMS Campaign Template */
|
||||
@Input() smsCampaignTemplate: any;
|
||||
/** SMS Campaign */
|
||||
@Input() smsCampaign: any;
|
||||
|
||||
/** SMS Campaign Form */
|
||||
smsCampaignDetailsForm: FormGroup;
|
||||
/** Data to be passed to sub component */
|
||||
paramData: any;
|
||||
/** Trigger types options */
|
||||
triggerTypes: any[];
|
||||
/** SMS providers options */
|
||||
smsProviders: any[];
|
||||
/** Business Rules options */
|
||||
businessRules: any[];
|
||||
/** Repetition Intervals */
|
||||
repetitionIntervals: any[];
|
||||
|
||||
/** Template Parameters Event Emitter */
|
||||
@Output() templateParameters = new EventEmitter();
|
||||
|
||||
/**
|
||||
* @param {FormBuilder} formBuilder Form Builder
|
||||
* @param {ReportsService} reportService Reports Service
|
||||
*/
|
||||
constructor(private formBuilder: FormBuilder,
|
||||
private reportService: ReportsService) {
|
||||
this.createSMSCampaignDetailsForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the SMS campaign form.
|
||||
*/
|
||||
createSMSCampaignDetailsForm() {
|
||||
this.smsCampaignDetailsForm = this.formBuilder.group({
|
||||
'campaignName': ['', Validators.required],
|
||||
'providerId': [null],
|
||||
'triggerType': ['', Validators.required],
|
||||
'runReportId': ['', Validators.required],
|
||||
'isNotification': [false]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.triggerTypes = this.smsCampaignTemplate.triggerTypeOptions;
|
||||
this.smsProviders = this.smsCampaignTemplate.smsProviderOptions;
|
||||
this.businessRules = this.smsCampaignTemplate.businessRulesOptions;
|
||||
this.setControlValues();
|
||||
this.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes template parameters emitted from child to parent.
|
||||
* @param {any} $event Template Parameters
|
||||
*/
|
||||
passParameters($event: any) {
|
||||
this.templateParameters.emit($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Template parameters and disables the SMS form.
|
||||
*/
|
||||
getParameters() {
|
||||
this.reportService.getReportParams(this.smsCampaign.reportName).subscribe((response: ReportParameter[]) => {
|
||||
this.paramData = response;
|
||||
});
|
||||
this.smsCampaignDetailsForm.disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches all control values as in API response.
|
||||
*/
|
||||
setControlValues() {
|
||||
this.smsCampaignDetailsForm.patchValue({
|
||||
'campaignName': this.smsCampaign.campaignName,
|
||||
'providerId': this.smsCampaign.providerId,
|
||||
'triggerType': this.smsCampaign.triggerType.id,
|
||||
'runReportId': this.smsCampaign.runReportId,
|
||||
'isNotification': this.smsCampaign.isNotification
|
||||
});
|
||||
if (this.smsCampaign.triggerType.value === 'Schedule') {
|
||||
this.smsCampaignDetailsForm.addControl('recurrenceStartDate', new FormControl(new Date(this.smsCampaign.recurrenceStartDate)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-divider fxFlex="98%"></mat-divider>
|
||||
|
||||
<h3 fxFlex="98%" class="mat-h3">Business Rule Parameters</h3>
|
||||
|
||||
<form [formGroup]="ReportForm">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%">
|
||||
|
||||
<ng-container *ngFor="let param of paramData" [ngSwitch]="param.displayType">
|
||||
|
||||
<ng-container *ngIf="ReportForm.contains(param.name)">
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'text'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput required [formControlName]="param.name">
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'date'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="runReportDatePicker" required
|
||||
[formControlName]="param.name">
|
||||
<mat-datepicker-toggle matSuffix [for]="runReportDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #runReportDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngSwitchCase="'select'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<mat-select required [formControlName]="param.name">
|
||||
<mat-option *ngFor="let option of param.selectOptions" [value]="option">
|
||||
{{option.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="margin-t" fxLayoutAlign="center">
|
||||
<button mat-raised-button matStepperNext color="primary" (click)="getResponseHeaders()"
|
||||
[disabled]="!this.ReportForm.valid">
|
||||
Get Parameters
|
||||
</button>
|
||||
</div>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin: 1em 0 2em 0;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.parameter {
|
||||
margin-inline: 1%;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BusinessRuleParametersComponent } from './business-rule-parameters.component';
|
||||
|
||||
describe('BusinessRuleParametersComponent', () => {
|
||||
let component: BusinessRuleParametersComponent;
|
||||
let fixture: ComponentFixture<BusinessRuleParametersComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ BusinessRuleParametersComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BusinessRuleParametersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnChanges, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Validators, FormGroup, FormControl } from '@angular/forms';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from 'app/reports/reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
|
||||
import { SelectOption } from 'app/reports/common-models/select-option.model';
|
||||
|
||||
/**
|
||||
* Business Rule Parameters Component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-business-rule-parameters',
|
||||
templateUrl: './business-rule-parameters.component.html',
|
||||
styleUrls: ['./business-rule-parameters.component.scss']
|
||||
})
|
||||
export class BusinessRuleParametersComponent implements OnChanges {
|
||||
|
||||
/** Run Report Parameters Data */
|
||||
@Input() paramData: any;
|
||||
|
||||
/** Report Name */
|
||||
reportName: string;
|
||||
/** Initializes new form group ReportForm */
|
||||
ReportForm = new FormGroup({});
|
||||
/** Array of all parent parameters */
|
||||
parentParameters: any[] = [];
|
||||
|
||||
/** Template Parameters Event Emitter */
|
||||
@Output() templateParameters = new EventEmitter();
|
||||
|
||||
/**
|
||||
* @param {ReportsService} reportsService Reports Service
|
||||
* @param {DatePipe} datePipe Date Pipe
|
||||
*/
|
||||
constructor(private reportsService: ReportsService,
|
||||
private datePipe: DatePipe) { }
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.paramData) {
|
||||
this.ReportForm = new FormGroup({});
|
||||
this.reportName = this.paramData.reportName;
|
||||
this.paramData = this.paramData.response;
|
||||
this.createRunReportForm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatted form value for API request.
|
||||
*/
|
||||
get businessRuleFormValue() {
|
||||
const formattedresponse = this.formatUserResponse(this.ReportForm.value, false);
|
||||
formattedresponse.reportName = this.reportName;
|
||||
return { paramValue: formattedresponse };
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes form controls for Report Parameter's name attribute,
|
||||
* Fetches dropdown options and builds child dependencies.
|
||||
*/
|
||||
createRunReportForm() {
|
||||
this.paramData.forEach(
|
||||
(param: any) => {
|
||||
if (!param.parentParameterName) { // Non Child Parameter
|
||||
this.ReportForm.addControl(param.name, new FormControl('', Validators.required));
|
||||
if (param.displayType === 'select') {
|
||||
this.fetchSelectOptions(param, param.name);
|
||||
}
|
||||
} else { // Child Parameter
|
||||
const parent: ReportParameter = this.paramData
|
||||
.find((entry: any) => entry.name === param.parentParameterName);
|
||||
// TODO: Refactor once Report Parameter Model is updated for multi-child support.
|
||||
if (parent.childParameter instanceof Array) {
|
||||
parent.childParameter.push(param);
|
||||
} else {
|
||||
parent.childParameter = [];
|
||||
parent.childParameter.push(param);
|
||||
}
|
||||
const parentNames = this.parentParameters.map(parameter => parameter.name);
|
||||
if (!parentNames.includes(parent.name)) {
|
||||
this.parentParameters.push(parent);
|
||||
} else {
|
||||
const index = parentNames.indexOf(parent.name);
|
||||
this.parentParameters[index] = parent;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setChildControls();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to changes in parent parameters value, builds child parameter vis-a-vis parent's value.
|
||||
*/
|
||||
setChildControls() {
|
||||
this.parentParameters.forEach((param: ReportParameter) => {
|
||||
this.ReportForm.get(param.name).valueChanges.subscribe((option: any) => {
|
||||
param.childParameter.forEach((child: ReportParameter) => {
|
||||
if (child.displayType === 'none') {
|
||||
this.ReportForm.addControl(child.name, new FormControl(child.defaultVal));
|
||||
} else {
|
||||
this.ReportForm.addControl(child.name, new FormControl('', Validators.required));
|
||||
}
|
||||
if (child.displayType === 'select') {
|
||||
const inputstring = `${child.name}?${param.inputName}=${option.id}`;
|
||||
this.fetchSelectOptions(child, inputstring);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Select Dropdown options for param type "Select".
|
||||
* @param {ReportParameter} param Parameter for which dropdown options are required.
|
||||
* @param {string} inputstring url substring for API call.
|
||||
*/
|
||||
fetchSelectOptions(param: ReportParameter, inputstring: string) {
|
||||
this.reportsService.getSelectOptions(inputstring).subscribe((options: SelectOption[]) => {
|
||||
param.selectOptions = options;
|
||||
if (param.selectAll === 'Y') {
|
||||
param.selectOptions.push({id: '-1', name: 'All'});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats user response and readies it for utilization by run report function.
|
||||
* @param {any} response Object containing formcontrol values.
|
||||
*/
|
||||
formatUserResponse(response: any, forHeaders: boolean) {
|
||||
const formattedResponse: any = {};
|
||||
let newKey: string;
|
||||
for (const [key, value] of Object.entries(response)) {
|
||||
const param: ReportParameter = this.paramData
|
||||
.find((_entry: any) => _entry.name === key);
|
||||
newKey = forHeaders ? param.inputName : param.variable;
|
||||
switch (param.displayType) {
|
||||
case 'text':
|
||||
formattedResponse[newKey] = value;
|
||||
break;
|
||||
case 'select':
|
||||
formattedResponse[newKey] = value['id'];
|
||||
break;
|
||||
case 'date':
|
||||
const dateFormat = 'yyyy-MM-dd';
|
||||
formattedResponse[newKey] = this.datePipe.transform(value, dateFormat);
|
||||
break;
|
||||
case 'none':
|
||||
formattedResponse[newKey] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return formattedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets run report response headers.
|
||||
* Emits them via template parameters event emitter.
|
||||
* TODO: Replace report object with report name once reports service is refactored.
|
||||
*/
|
||||
getResponseHeaders() {
|
||||
const formattedresponse = this.formatUserResponse(this.ReportForm.value, true);
|
||||
this.reportsService.getRunReportData({ name: this.reportName }, formattedresponse).subscribe(
|
||||
(response: any) => {
|
||||
this.templateParameters.emit(response.columnHeaders);
|
||||
},
|
||||
(error: any) => {
|
||||
this.templateParameters.emit(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<form [formGroup]="smsCampaignDetailsForm">
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" fxLayoutAlign.gt-sm="start center">
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Campaign Name</mat-label>
|
||||
<input matInput formControlName="campaignName" required>
|
||||
<mat-error>
|
||||
Campaign Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%"
|
||||
*ngIf="!smsCampaignDetailsForm.controls.isNotification.value">
|
||||
<mat-label>SMS Provider</mat-label>
|
||||
<mat-select formControlName="providerId">
|
||||
<mat-option *ngFor="let provider of smsProviders" [value]="provider.id">
|
||||
{{ provider.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Trigger Type</mat-label>
|
||||
<mat-select formControlName="triggerType" required>
|
||||
<mat-option *ngFor="let triggerType of triggerTypes" [value]="triggerType.id">
|
||||
{{ triggerType.value }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Trigger Type is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox labelPosition="before" formControlName="isNotification" fxFlex="48%">
|
||||
is Notification?
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-form-field fxFlex="48%"
|
||||
*ngIf="smsCampaignDetailsForm.contains('recurrenceStartDate')">
|
||||
<mat-label>Schedule Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="recurrenceStartDatePicker"
|
||||
formControlName="recurrenceStartDate" required>
|
||||
<mat-datepicker-toggle matSuffix [for]="recurrenceStartDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #recurrenceStartDatePicker></mat-datepicker>
|
||||
<mat-error>
|
||||
Schedule Date is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%"
|
||||
*ngIf="smsCampaignDetailsForm.contains('frequency')">
|
||||
<mat-label>Repeats</mat-label>
|
||||
<mat-select formControlName="frequency" required>
|
||||
<mat-option [value]="1">Daily</mat-option>
|
||||
<mat-option [value]="2">Weekly</mat-option>
|
||||
<mat-option [value]="3">Monthly</mat-option>
|
||||
<mat-option [value]="4">Yearly</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Repetition Frequency is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%"
|
||||
*ngIf="smsCampaignDetailsForm.contains('interval')">
|
||||
<mat-label>Repetition Interval</mat-label>
|
||||
<mat-select formControlName="interval" required>
|
||||
<mat-option *ngFor="let interval of repetitionIntervals" [value]="interval">
|
||||
{{ interval }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Repetition Interval is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%"
|
||||
*ngIf="smsCampaignDetailsForm.contains('repeatsOnDay')">
|
||||
<mat-label>Repeats on Day</mat-label>
|
||||
<mat-select formControlName="repeatsOnDay" required>
|
||||
<mat-option value="1">Monday</mat-option>
|
||||
<mat-option value="2">Tuesday</mat-option>
|
||||
<mat-option value="3">Wednesday</mat-option>
|
||||
<mat-option value="4">Thursday</mat-option>
|
||||
<mat-option value="5">Friday</mat-option>
|
||||
<mat-option value="6">Saturday</mat-option>
|
||||
<mat-option value="7">Sunday</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
At least <strong>one</strong> day must be selected
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>Business Rule</mat-label>
|
||||
<mat-select formControlName="runReportId" required>
|
||||
<mat-option *ngFor="let rule of businessRules" [value]="rule.reportId">
|
||||
{{ rule.reportName }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
Business Rule is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%"
|
||||
*ngIf="!smsCampaignDetailsForm.controls.runReportId.value">
|
||||
<button mat-raised-button matStepperPrevious disabled>
|
||||
<fa-icon icon="arrow-left"></fa-icon>
|
||||
Previous
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
Next
|
||||
<fa-icon icon="arrow-right"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mifosx-business-rule-parameters
|
||||
*ngIf="smsCampaignDetailsForm.controls.runReportId.value"
|
||||
[paramData]="paramData"
|
||||
(templateParameters)="passParameters($event)"
|
||||
>
|
||||
</mifosx-business-rule-parameters>
|
||||
|
||||
</form>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SmsCampaignStepComponent } from './sms-campaign-step.component';
|
||||
|
||||
describe('SmsCampaignStepComponent', () => {
|
||||
let component: SmsCampaignStepComponent;
|
||||
let fixture: ComponentFixture<SmsCampaignStepComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SmsCampaignStepComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SmsCampaignStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, ViewChild, EventEmitter, Output} from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from 'app/reports/reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
|
||||
|
||||
/** Custom Components */
|
||||
import { BusinessRuleParametersComponent } from './business-rule-parameters/business-rule-parameters.component';
|
||||
|
||||
/**
|
||||
* SMS Campaign Step Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-sms-campaign-step',
|
||||
templateUrl: './sms-campaign-step.component.html',
|
||||
styleUrls: ['./sms-campaign-step.component.scss']
|
||||
})
|
||||
export class SmsCampaignStepComponent implements OnInit {
|
||||
|
||||
/** SMS Campaign Template */
|
||||
@Input() smsCampaignTemplate: any;
|
||||
/** Business Rule Parameters Component */
|
||||
@ViewChild(BusinessRuleParametersComponent) businessRuleParametersComponent: BusinessRuleParametersComponent;
|
||||
|
||||
/** Min. Date */
|
||||
minDate = new Date();
|
||||
/** Max. Date */
|
||||
maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
||||
|
||||
/** SMS Campaign Form */
|
||||
smsCampaignDetailsForm: FormGroup;
|
||||
/** Data to be passed to sub component */
|
||||
paramData: any;
|
||||
/** Trigger types options */
|
||||
triggerTypes: any[];
|
||||
/** SMS providers options */
|
||||
smsProviders: any[];
|
||||
/** Business Rules options */
|
||||
businessRules: any[];
|
||||
/** Repetition Intervals */
|
||||
repetitionIntervals: any[];
|
||||
|
||||
/** Template Parameters Event Emitter */
|
||||
@Output() templateParameters = new EventEmitter();
|
||||
|
||||
/**
|
||||
* @param {FormBuilder} formBuilder Form Builder
|
||||
* @param {ReportsService} reportService Reports Service
|
||||
*/
|
||||
constructor(private formBuilder: FormBuilder,
|
||||
private reportService: ReportsService) {
|
||||
this.createSMSCampaignDetailsForm();
|
||||
this.buildDependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets SMS providers and trigger types options.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.triggerTypes = this.smsCampaignTemplate.triggerTypeOptions;
|
||||
this.smsProviders = this.smsCampaignTemplate.smsProviderOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SMS Campaign Details Form if Business Rule is unassigned
|
||||
* Else returns a cumulative form group.
|
||||
*/
|
||||
get smsCampaignFormGroup() {
|
||||
let smsCampaignFormGroup: FormGroup;
|
||||
if (this.businessRuleParametersComponent) {
|
||||
smsCampaignFormGroup = new FormGroup({
|
||||
smsCampaign: this.smsCampaignDetailsForm,
|
||||
businessRule: this.businessRuleParametersComponent.ReportForm
|
||||
});
|
||||
} else {
|
||||
smsCampaignFormGroup = new FormGroup({
|
||||
smsCampaign: this.smsCampaignDetailsForm
|
||||
});
|
||||
}
|
||||
return smsCampaignFormGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SMS Campaign Details Form value if Business Rule is unassigned
|
||||
* Else returns cumulative form group value.
|
||||
*/
|
||||
get smsCampaignFormGroupValue() {
|
||||
if (this.businessRuleParametersComponent) {
|
||||
return {
|
||||
...this.smsCampaignDetailsForm.value,
|
||||
...this.businessRuleParametersComponent.businessRuleFormValue
|
||||
};
|
||||
} else {
|
||||
return this.smsCampaignDetailsForm.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes template parameters emitted from child to parent.
|
||||
* @param {any} $event Template Parameters
|
||||
*/
|
||||
passParameters($event: any) {
|
||||
this.templateParameters.emit($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the SMS campaign form.
|
||||
*/
|
||||
createSMSCampaignDetailsForm() {
|
||||
this.smsCampaignDetailsForm = this.formBuilder.group({
|
||||
'campaignName': ['', Validators.required],
|
||||
'providerId': [null],
|
||||
'triggerType': ['', Validators.required],
|
||||
'runReportId': ['', Validators.required],
|
||||
'isNotification': [false]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets conditional form controls and values.
|
||||
* Gets reports parameters and passes it to subcomponent on business rule value changes.
|
||||
*/
|
||||
buildDependencies() {
|
||||
this.smsCampaignDetailsForm.get('isNotification').valueChanges.subscribe((value: boolean) => {
|
||||
if (!value) {
|
||||
this.smsCampaignDetailsForm.addControl('providerId', new FormControl(null));
|
||||
} else {
|
||||
this.smsCampaignDetailsForm.removeControl('providerId');
|
||||
}
|
||||
});
|
||||
this.smsCampaignDetailsForm.get('runReportId').valueChanges.subscribe((value: number) => {
|
||||
if (value) {
|
||||
const report = this.businessRules.find((rule: any) => rule.reportId === value);
|
||||
this.reportService.getReportParams(report.reportName).subscribe((response: ReportParameter[]) => {
|
||||
this.paramData = { response, reportName: report.reportName };
|
||||
});
|
||||
}
|
||||
});
|
||||
this.smsCampaignDetailsForm.get('triggerType').valueChanges.subscribe((value: number) => {
|
||||
this.templateParameters.emit(null);
|
||||
this.businessRules = this.smsCampaignTemplate.businessRulesOptions;
|
||||
if (this.smsCampaignDetailsForm.controls.runReportId.value) {
|
||||
this.smsCampaignDetailsForm.get('runReportId').patchValue('');
|
||||
}
|
||||
if (value === 3) {
|
||||
this.businessRules = this.businessRules.filter((rule: any) => rule.reportSubType === 'Triggered');
|
||||
} else {
|
||||
this.businessRules = this.businessRules.filter((rule: any) => rule.reportSubType !== 'Triggered');
|
||||
}
|
||||
if (value === 2) {
|
||||
this.smsCampaignDetailsForm.addControl('recurrenceStartDate', new FormControl('', Validators.required));
|
||||
this.smsCampaignDetailsForm.addControl('frequency', new FormControl('', Validators.required));
|
||||
this.smsCampaignDetailsForm.addControl('interval', new FormControl('', Validators.required));
|
||||
this.smsCampaignDetailsForm.get('frequency').valueChanges.subscribe((frequency: number) => {
|
||||
this.smsCampaignDetailsForm.removeControl('repeatsOnDay');
|
||||
switch (frequency) {
|
||||
case 1: // Daily
|
||||
this.repetitionIntervals = ['1', '2', '3'];
|
||||
break;
|
||||
case 2: // Weekly
|
||||
this.repetitionIntervals = ['1', '2', '3'];
|
||||
this.smsCampaignDetailsForm.addControl('repeatsOnDay', new FormControl('', Validators.required));
|
||||
break;
|
||||
case 3: // Monthly
|
||||
this.repetitionIntervals = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
|
||||
break;
|
||||
case 4: // Yearly
|
||||
this.repetitionIntervals = ['1', '2', '3', '4', '5'];
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.smsCampaignDetailsForm.removeControl('recurrenceStartDate');
|
||||
this.smsCampaignDetailsForm.removeControl('frequency');
|
||||
this.smsCampaignDetailsForm.removeControl('interval');
|
||||
this.smsCampaignDetailsForm.removeControl('repeatsOnDay');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
|
||||
|
||||
<button mat-raised-button color="primary">
|
||||
<button mat-raised-button color="primary" [routerLink]="['create']">
|
||||
<fa-icon icon="plus"></fa-icon>
|
||||
Create SMS Campaign
|
||||
</button>
|
||||
|
||||
@@ -1,125 +1,151 @@
|
||||
<div class="container">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="1%" fxLayoutAlign="flex-end" class="action-buttons m-b-20">
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'active'">
|
||||
<button mat-raised-button color="primary" *mifosxHasPermission="'UPDATE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="edit"></fa-icon> Edit
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value === 'Pending'">
|
||||
<button mat-raised-button color="accent" *mifosxHasPermission="'ACTIVATE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="lock-open"></fa-icon> Activate
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'closed'">
|
||||
<button mat-raised-button color="warn" *mifosxHasPermission="'CLOSE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="times"></fa-icon> Close
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'Pending' && smsCampaignData.campaignStatus.value !== 'active'">
|
||||
<button mat-raised-button color="accent" *mifosxHasPermission="'REACTIVATE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="undo"></fa-icon> Reactivate
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value === 'closed'">
|
||||
<button mat-raised-button color="warn" *mifosxHasPermission="'DELETE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="trash"></fa-icon> Delete
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div fxLayout="row" fxLayoutGap="1%" fxLayoutAlign="flex-end" class="action-buttons m-b-20">
|
||||
|
||||
<mat-card class="sms-card">
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'active'">
|
||||
<button mat-raised-button color="primary" *mifosxHasPermission="'UPDATE_SMSCAMPAIGN'" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon> Edit
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<mat-card-content>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value === 'Pending'">
|
||||
<button mat-raised-button color="accent" *mifosxHasPermission="'ACTIVATE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="lock-open"></fa-icon> Activate
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<mat-tab-group (selectedTabChange)="onTabChange($event)">
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'closed'">
|
||||
<button mat-raised-button color="warn" *mifosxHasPermission="'CLOSE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="times"></fa-icon> Close
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<mat-tab label="Campaign">
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value !== 'Pending' && smsCampaignData.campaignStatus.value !== 'active'">
|
||||
<button mat-raised-button color="accent" *mifosxHasPermission="'REACTIVATE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="undo"></fa-icon> Reactivate
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div class="tab-content mat-typography">
|
||||
|
||||
<mat-list>
|
||||
<mat-list-item>
|
||||
Campaign Name : {{ smsCampaignData.campaignName }}
|
||||
</mat-list-item>
|
||||
<mat-list-item>
|
||||
Report Name : {{ smsCampaignData.reportName }}
|
||||
</mat-list-item>
|
||||
<mat-list-item>
|
||||
Status : {{ smsCampaignData.campaignStatus.value }}
|
||||
</mat-list-item>
|
||||
<mat-list-item>
|
||||
Trigger Type : {{ smsCampaignData.triggerType.value }}
|
||||
</mat-list-item>
|
||||
<mat-list-item>
|
||||
Submitted on : {{ smsCampaignData.smsCampaignTimeLine.submittedOnDate | date}}
|
||||
</mat-list-item>
|
||||
<div fxLayout="column" fxLayoutGap="10px" class="template-message">
|
||||
<h3>Template Message :</h3>
|
||||
<textarea matInput disabled>{{ smsCampaignData.campaignMessage}}</textarea>
|
||||
</div>
|
||||
</mat-list>
|
||||
<span *ngIf="smsCampaignData.campaignStatus.value === 'closed'">
|
||||
<button mat-raised-button color="warn" *mifosxHasPermission="'DELETE_SMSCAMPAIGN'">
|
||||
<fa-icon icon="trash"></fa-icon> Delete
|
||||
</button>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-tab>
|
||||
<mat-card class="sms-card">
|
||||
|
||||
<mat-tab *ngFor = "let tab of smsTabs" [label]="tab.label">
|
||||
<mat-card-content>
|
||||
|
||||
<div class="tab-content mat-typography">
|
||||
<mat-tab-group (selectedTabChange)="onTabChange($event)">
|
||||
|
||||
<form [formGroup]="smsForm" (ngSubmit)="search()">
|
||||
<div fxLayout="row" fxLayoutGap="3%" fxLayoutAlign="center">
|
||||
<mat-form-field>
|
||||
<mat-label>From Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="fromDatePicker" formControlName="fromDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #fromDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>To Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="toDatePicker" formControlName="toDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #toDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<div class="search-button" >
|
||||
<button mat-raised-button color="primary">
|
||||
<fa-icon icon="search"></fa-icon> Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<mat-tab label="Campaign">
|
||||
|
||||
<div class="tab-content mat-typography">
|
||||
|
||||
<div class="mat-elevation-z1 m-b-25">
|
||||
<table #messageTable mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="Message">
|
||||
<th mat-header-cell *matHeaderCellDef> Message </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.message}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Status">
|
||||
<th mat-header-cell *matHeaderCellDef> Status </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.status.value}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Mobile No.">
|
||||
<th mat-header-cell *matHeaderCellDef> Mobile No. </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.mobileNo}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Campaign Name">
|
||||
<th mat-header-cell *matHeaderCellDef> Campaign Name </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.campaignName}}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<mat-list>
|
||||
|
||||
<mat-list-item>
|
||||
Campaign Name : {{ smsCampaignData.campaignName }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Report Name : {{ smsCampaignData.reportName }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Status : {{ smsCampaignData.campaignStatus.value }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Trigger Type : {{ smsCampaignData.triggerType.value }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
Submitted on : {{ smsCampaignData.smsCampaignTimeLine.submittedOnDate | date }}
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item *ngIf="smsCampaignData.recurrence">
|
||||
Recurrence : {{ smsCampaignData.recurrence }}
|
||||
</mat-list-item>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="10px" class="template-message">
|
||||
<h3>Template Message :</h3>
|
||||
<textarea matInput disabled>{{ smsCampaignData.campaignMessage}}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</mat-list>
|
||||
|
||||
</mat-tab>
|
||||
</div>
|
||||
|
||||
</mat-tab-group>
|
||||
</mat-tab>
|
||||
|
||||
</mat-card-content>
|
||||
<mat-tab *ngFor = "let tab of smsTabs" [label]="tab.label">
|
||||
|
||||
</mat-card>
|
||||
<div class="tab-content mat-typography">
|
||||
|
||||
<form [formGroup]="smsForm" (ngSubmit)="search()">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="3%" fxLayoutAlign="center">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>From Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="fromDatePicker" formControlName="fromDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #fromDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>To Date</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="toDatePicker" formControlName="toDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #toDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="search-button" >
|
||||
<button mat-raised-button color="primary">
|
||||
<fa-icon icon="search"></fa-icon> Search</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<table #messageTable mat-table [dataSource]="dataSource" class="mat-elevation-z1 m-b-25">
|
||||
|
||||
<ng-container matColumnDef="Message">
|
||||
<th mat-header-cell *matHeaderCellDef> Message </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.message}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Status">
|
||||
<th mat-header-cell *matHeaderCellDef> Status </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.status.value}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Mobile No.">
|
||||
<th mat-header-cell *matHeaderCellDef> Mobile No. </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.mobileNo}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Campaign Name">
|
||||
<th mat-header-cell *matHeaderCellDef> Campaign Name </th>
|
||||
<td mat-cell *matCellDef="let sms">{{sms.campaignName}}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-tab>
|
||||
|
||||
</mat-tab-group>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.action-buttons {
|
||||
width: 88.75%;
|
||||
width: 90%;
|
||||
}
|
||||
.sms-card {
|
||||
margin: 0 auto;
|
||||
width: 77.5%;
|
||||
width: 80%;
|
||||
padding: 0;
|
||||
.tab-content {
|
||||
padding: 1%;
|
||||
|
||||
Reference in New Issue
Block a user