diff --git a/src/app/organization/organization-routing.module.ts b/src/app/organization/organization-routing.module.ts index f72a63ed7..cc6f06693 100644 --- a/src/app/organization/organization-routing.module.ts +++ b/src/app/organization/organization-routing.module.ts @@ -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, diff --git a/src/app/organization/organization.module.ts b/src/app/organization/organization.module.ts index 923c11ec8..22d036c9b 100644 --- a/src/app/organization/organization.module.ts +++ b/src/app/organization/organization.module.ts @@ -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 { } diff --git a/src/app/organization/organization.service.ts b/src/app/organization/organization.service.ts index 587a681b6..99c2c42a0 100644 --- a/src/app/organization/organization.service.ts +++ b/src/app/organization/organization.service.ts @@ -185,8 +185,31 @@ export class OrganizationService { * @param {string} smsCampaignId SMS Campaign ID of SMS Campaign. * @returns {Observable} SMS Campaign. */ - getSmsCampaign(smsCampaignId: string): Observable { - return this.http.get(`/smscampaigns/${smsCampaignId}`); + getSmsCampaign(campaignId: string): Observable { + return this.http.get(`/smscampaigns/${campaignId}`); + } + + /** + * @param {any} campaign Campaign to be created. + * @returns {Observable} + */ + createSmsCampaign(campaign: any): Observable { + return this.http.post('/smscampaigns', campaign); + } + + /** + * @param {any} campaign Campaign to be updated. + * @returns {Observable} + */ + updateSmsCampaign(campaign: any, campaignId: string): Observable { + return this.http.put(`/smscampaigns/${campaignId}`, campaign); + } + + /** + * @returns {Observable} SMS Campaign template + */ + getSmsCampaignTemplate(): Observable { + return this.http.get('/smscampaigns/template'); } /** diff --git a/src/app/organization/sms-campaigns/common-resolvers/sms-campaign-template.resolver.ts b/src/app/organization/sms-campaigns/common-resolvers/sms-campaign-template.resolver.ts new file mode 100644 index 000000000..527e98f05 --- /dev/null +++ b/src/app/organization/sms-campaigns/common-resolvers/sms-campaign-template.resolver.ts @@ -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 { + + /** + * @param {OrganizationService} organizationService Organization service. + */ + constructor(private organizationService: OrganizationService) {} + + /** + * Returns the SMS Campaign Template. + * @returns {Observable} + */ + resolve(): Observable { + return this.organizationService.getSmsCampaignTemplate(); + } + +} diff --git a/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.html b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.html new file mode 100644 index 000000000..a69edd9f7 --- /dev/null +++ b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.html @@ -0,0 +1,63 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + CAMPAIGN + + + + + + + + + MESSAGE + + + + + + + + + PREVIEW + + + + + + + + +
diff --git a/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.scss b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.scss new file mode 100644 index 000000000..7b2c0eea1 --- /dev/null +++ b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.scss @@ -0,0 +1,3 @@ +.container { + width: 60%;; +} diff --git a/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.spec.ts b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.spec.ts new file mode 100644 index 000000000..823f0ae69 --- /dev/null +++ b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CreateCampaignComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateCampaignComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.ts b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.ts new file mode 100644 index 000000000..50499c7a2 --- /dev/null +++ b/src/app/organization/sms-campaigns/create-campaign/create-campaign.component.ts @@ -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 }); + }); + } + +} diff --git a/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.html b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.html new file mode 100644 index 000000000..d991a023a --- /dev/null +++ b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.html @@ -0,0 +1,65 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + CAMPAIGN + + + + + + + + + MESSAGE + + + + + + + + + PREVIEW + + + + + + + + +
diff --git a/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.scss b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.scss new file mode 100644 index 000000000..7b2c0eea1 --- /dev/null +++ b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.scss @@ -0,0 +1,3 @@ +.container { + width: 60%;; +} diff --git a/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.spec.ts b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.spec.ts new file mode 100644 index 000000000..5de03936c --- /dev/null +++ b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EditCampaignComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditCampaignComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.ts b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.ts new file mode 100644 index 000000000..980cdfc50 --- /dev/null +++ b/src/app/organization/sms-campaigns/edit-campaign/edit-campaign.component.ts @@ -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 }); + }); + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.html new file mode 100644 index 000000000..0e8a5b305 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.html @@ -0,0 +1,28 @@ +
+ + + Campaign Message + + + +

Template Parameters

+ +
+ +
+ +
+ + +
+ +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.scss new file mode 100644 index 000000000..8457f6452 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.scss @@ -0,0 +1,11 @@ +h3 { + font-weight: 500; +} + +.margin-t { + margin-top: 1.75em; +} + +.parameter { + margin: 1%; +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.spec.ts new file mode 100644 index 000000000..423c2ce7e --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CampaignMessageStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CampaignMessageStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.ts new file mode 100644 index 000000000..f37ed7e53 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-message-step/campaign-message-step.component.ts @@ -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); + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.html new file mode 100644 index 000000000..f048a790a --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.html @@ -0,0 +1,37 @@ +
+ + + + + Campaign Name : {{ campaign.campaignName }} + + + + SMS Provider : {{ (campaign.providerId | find:smsProviders:'id':'name') || "Unassigned" }} + + + + Trigger Type : {{ (campaign.editFlag ? campaign.triggerType.id : campaign.triggerType) | find:triggerTypes:'id':'value' }} + + + + Bussiness Rule : {{ campaign.editFlag ? campaign.reportName : campaign.paramValue?.reportName }} + + +
+

Campaign Message :

+ +
+ +
+ +
+ + +
+ +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.scss new file mode 100644 index 000000000..7c23b95f5 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.scss @@ -0,0 +1,11 @@ +.tab-content { + padding: 1%; + margin: 1%; + .template-message { + padding-inline: 1.5%; + margin-top: 1%; + } + .margin-t { + margin-top: 1em; + } +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.spec.ts new file mode 100644 index 000000000..18bc9e30f --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CampaignPreviewStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CampaignPreviewStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.ts new file mode 100644 index 000000000..74fbad435 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/campaign-preview-step/campaign-preview-step.component.ts @@ -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; + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.html new file mode 100644 index 000000000..762ed585c --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.html @@ -0,0 +1,65 @@ +
+ + + +

Business Rule Parameters

+ +
+ +
+ + + + + + + {{param.label}} + + + {{param.label}} is required + + + + + {{param.label}} + + + + + {{param.label}} is required + + + + + {{param.label}} + + + {{option.name}} + + + + {{param.label}} is required + + + + + + + +
+ +
+ +
+ +
+ + +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.scss new file mode 100644 index 000000000..1bd42d6a5 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.scss @@ -0,0 +1,15 @@ +h3 { + font-weight: 500; +} + +mat-divider { + margin: 1em 0 2em 0; +} + +.margin-t { + margin-top: 1em; +} + +.parameter { + margin-inline: 1%; +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.spec.ts new file mode 100644 index 000000000..6deb6e1e5 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EditBusinessRuleParametersComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditBusinessRuleParametersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.ts new file mode 100644 index 000000000..ce3bb4073 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-business-rule-parameters/edit-business-rule-parameters.component.ts @@ -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(); + } + ); + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.html new file mode 100644 index 000000000..3952c5091 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.html @@ -0,0 +1,67 @@ +
+ +
+ + + Campaign Name + + + Campaign Name is required + + + + + SMS Provider + + + {{ provider.value }} + + + + + + Trigger Type + + + {{ triggerType.value }} + + + + Trigger Type is required + + + + + is Notification? + + + + Schedule Date + + + + + Schedule Date is required + + + + + Business Rule + + + {{ rule.reportName }} + + + + Business Rule is required + + + +
+ + + + +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.spec.ts new file mode 100644 index 000000000..822c6f863 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EditSmsCampaignStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditSmsCampaignStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.ts new file mode 100644 index 000000000..6a6150c36 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/edit-sms-campaign-step/edit-sms-campaign-step.component.ts @@ -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))); + } + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.html new file mode 100644 index 000000000..d0f96a359 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.html @@ -0,0 +1,61 @@ +
+ + + +

Business Rule Parameters

+ +
+ +
+ + + + + + + {{param.label}} + + + {{param.label}} is required + + + + + {{param.label}} + + + + + {{param.label}} is required + + + + + {{param.label}} + + + {{option.name}} + + + + {{param.label}} is required + + + + + + + +
+ +
+ +
+ +
+ +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.scss new file mode 100644 index 000000000..1bd42d6a5 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.scss @@ -0,0 +1,15 @@ +h3 { + font-weight: 500; +} + +mat-divider { + margin: 1em 0 2em 0; +} + +.margin-t { + margin-top: 1em; +} + +.parameter { + margin-inline: 1%; +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.spec.ts new file mode 100644 index 000000000..3143697f1 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BusinessRuleParametersComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BusinessRuleParametersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.ts new file mode 100644 index 000000000..0f880e148 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/business-rule-parameters/business-rule-parameters.component.ts @@ -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); + } + ); + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.html b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.html new file mode 100644 index 000000000..00e2a5ca1 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.html @@ -0,0 +1,128 @@ +
+ +
+ + + Campaign Name + + + Campaign Name is required + + + + + SMS Provider + + + {{ provider.value }} + + + + + + Trigger Type + + + {{ triggerType.value }} + + + + Trigger Type is required + + + + + is Notification? + + + + Schedule Date + + + + + Schedule Date is required + + + + + Repeats + + Daily + Weekly + Monthly + Yearly + + + Repetition Frequency is required + + + + + Repetition Interval + + + {{ interval }} + + + + Repetition Interval is required + + + + + Repeats on Day + + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday + + + At least one day must be selected + + + + + Business Rule + + + {{ rule.reportName }} + + + + Business Rule is required + + + +
+ +
+ + +
+ + + + +
diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.scss b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.scss new file mode 100644 index 000000000..aa8d23226 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.scss @@ -0,0 +1,3 @@ +.margin-t { + margin-top: 1em; +} diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.spec.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.spec.ts new file mode 100644 index 000000000..d69d66daf --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SmsCampaignStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SmsCampaignStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.ts b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.ts new file mode 100644 index 000000000..621bd2ce4 --- /dev/null +++ b/src/app/organization/sms-campaigns/sms-campaign-stepper/sms-campaign-step/sms-campaign-step.component.ts @@ -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'); + } + }); + } + +} diff --git a/src/app/organization/sms-campaigns/sms-campaigns.component.html b/src/app/organization/sms-campaigns/sms-campaigns.component.html index d773fe4ec..d5daa0374 100644 --- a/src/app/organization/sms-campaigns/sms-campaigns.component.html +++ b/src/app/organization/sms-campaigns/sms-campaigns.component.html @@ -1,6 +1,6 @@
- diff --git a/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.html b/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.html index b46c835a5..ef5e7485a 100644 --- a/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.html +++ b/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.html @@ -1,125 +1,151 @@
-
- - - - - - - - - - - - - - - -
+
- + + + - + + + - + + + - + + + -
- - - - Campaign Name : {{ smsCampaignData.campaignName }} - - - Report Name : {{ smsCampaignData.reportName }} - - - Status : {{ smsCampaignData.campaignStatus.value }} - - - Trigger Type : {{ smsCampaignData.triggerType.value }} - - - Submitted on : {{ smsCampaignData.smsCampaignTimeLine.submittedOnDate | date}} - -
-

Template Message :

- -
-
+ + + -
+
- + - + -
+ -
-
- - From Date - - - - - - To Date - - - - -
- -
-
-
+ + +
-
- - - - - - - - - - - - - - - - - - - -
Message {{sms.message}} Status {{sms.status.value}} Mobile No. {{sms.mobileNo}} Campaign Name {{sms.campaignName}}
+ + + + Campaign Name : {{ smsCampaignData.campaignName }} + + + + Report Name : {{ smsCampaignData.reportName }} + + + + Status : {{ smsCampaignData.campaignStatus.value }} + + + + Trigger Type : {{ smsCampaignData.triggerType.value }} + + + + Submitted on : {{ smsCampaignData.smsCampaignTimeLine.submittedOnDate | date }} + + + + Recurrence : {{ smsCampaignData.recurrence }} + + +
+

Template Message :

+
-
+ - +
-
+ - + - +
+ +
+ +
+ + + From Date + + + + + + + To Date + + + + + +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Message {{sms.message}} Status {{sms.status.value}} Mobile No. {{sms.mobileNo}} Campaign Name {{sms.campaignName}}
+ +
+ +
+ + + + + +
diff --git a/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.scss b/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.scss index 54a08a1e6..b81392f36 100644 --- a/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.scss +++ b/src/app/organization/sms-campaigns/view-campaign/view-campaign.component.scss @@ -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%;