src/app/fasterq/fasterq-editor.ts
Compbaser
selector | fasterq-editor |
styles |
.personInLine {
margin: 10px;
padding: 0;
float: left;
width: 40px;
height: 100px;
cursor: pointer;
color: #D0D0D0;
}
.called {
color: #BE6734;
}
.serviced {
color: #ACFD89;
}
|
templateUrl | ./fasterq-editor.html |
constructor(yp: YellowPepperService, rp: RedPepperService, commBroker: CommBroker, el: ElementRef, cd: ChangeDetectorRef)
|
Defined in src/app/fasterq/fasterq-editor.ts:68
|
_openRemoteStatus |
_openRemoteStatus()
|
Defined in src/app/fasterq/fasterq-editor.ts:142
|
Returns :
void
|
_buildURL |
_buildURL()
|
Defined in src/app/fasterq/fasterq-editor.ts:151
|
Create URL string to load customer terminal UI for FasterQ queue generation
Returns :
void
|
_selectFirst |
_selectFirst()
|
Decorators : timeout
|
Defined in src/app/fasterq/fasterq-editor.ts:162
|
Returns :
void
|
_pollServices |
_pollServices()
|
Defined in src/app/fasterq/fasterq-editor.ts:169
|
Returns :
void
|
_updateTotalToBeServiced |
_updateTotalToBeServiced()
|
Defined in src/app/fasterq/fasterq-editor.ts:182
|
Update the total number of queues left to be serviced
Returns :
void
|
_onQueueSelected |
_onQueueSelected(i_queue: FasterqQueueModel)
|
Defined in src/app/fasterq/fasterq-editor.ts:191
|
Returns :
void
|
_getQueueIndexByServiceId |
_getQueueIndexByServiceId()
|
Defined in src/app/fasterq/fasterq-editor.ts:204
|
Returns :
number
|
_scrollTo |
_scrollTo(i_index: any)
|
Defined in src/app/fasterq/fasterq-editor.ts:217
|
Scroll to position of selected queue / UI person
Parameters :
Returns :
void
|
_getQueueFromSelectedIndex |
_getQueueFromSelectedIndex()
|
Defined in src/app/fasterq/fasterq-editor.ts:231
|
Returns :
FasterqQueueModel
|
_selectIfDefault |
_selectIfDefault()
|
Defined in src/app/fasterq/fasterq-editor.ts:237
|
Returns :
void
|
_onCall |
_onCall()
|
Defined in src/app/fasterq/fasterq-editor.ts:246
|
Listen to queue being called, mark on UI and post to server
Returns :
void
|
_onService |
_onService()
|
Defined in src/app/fasterq/fasterq-editor.ts:268
|
Listen to queue being serviced, mark on UI and post to server
Returns :
void
|
_calcAverages |
_calcAverages()
|
Defined in src/app/fasterq/fasterq-editor.ts:294
|
Calculate average respond and service line times
Returns :
void
|
_onPrev |
_onPrev()
|
Defined in src/app/fasterq/fasterq-editor.ts:337
|
Returns :
void
|
_onNext |
_onNext()
|
Defined in src/app/fasterq/fasterq-editor.ts:343
|
Returns :
void
|
_onGoTo |
_onGoTo()
|
Defined in src/app/fasterq/fasterq-editor.ts:351
|
Returns :
void
|
_watchStart |
_watchStart()
|
Defined in src/app/fasterq/fasterq-editor.ts:365
|
Start the stop watch UI
Returns :
void
|
_watchStop |
_watchStop()
|
Defined in src/app/fasterq/fasterq-editor.ts:377
|
Stop the stop watch UI
Returns :
void
|
destroy |
destroy()
|
Defined in src/app/fasterq/fasterq-editor.ts:383
|
Returns :
void
|
appBaseUrlServices |
appBaseUrlServices: |
Defined in src/app/fasterq/fasterq-editor.ts:68
|
m_analytics |
m_analytics: |
Defined in src/app/fasterq/fasterq-editor.ts:59
|
m_avgCalledTimeCalc |
m_avgCalledTimeCalc: |
Default value : 00:00:00
|
Defined in src/app/fasterq/fasterq-editor.ts:62
|
m_avgServiceTimeCalc |
m_avgServiceTimeCalc: |
Default value : 00:00:00
|
Defined in src/app/fasterq/fasterq-editor.ts:61
|
m_fasterqLineModel |
m_fasterqLineModel: |
Defined in src/app/fasterq/fasterq-editor.ts:56
|
m_fqLastServiced |
m_fqLastServiced: |
Defined in src/app/fasterq/fasterq-editor.ts:66
|
m_gotoModel |
m_gotoModel: |
Default value : 0
|
Defined in src/app/fasterq/fasterq-editor.ts:53
|
m_lastCalled |
m_lastCalled: |
Default value : 0
|
Defined in src/app/fasterq/fasterq-editor.ts:63
|
m_liveUpdateHandler |
m_liveUpdateHandler: |
Defined in src/app/fasterq/fasterq-editor.ts:67
|
m_nowServicing |
m_nowServicing: |
Default value : 0
|
Defined in src/app/fasterq/fasterq-editor.ts:64
|
m_offsetPosition |
m_offsetPosition: |
Default value : 0
|
Defined in src/app/fasterq/fasterq-editor.ts:60
|
m_queues |
m_queues: |
Defined in src/app/fasterq/fasterq-editor.ts:58
|
m_selectedServiceId |
m_selectedServiceId: |
Defined in src/app/fasterq/fasterq-editor.ts:57
|
m_stopTimer |
m_stopTimer: |
Default value : 00:00:00
|
Defined in src/app/fasterq/fasterq-editor.ts:55
|
m_stopWatchHandle |
m_stopWatchHandle: |
Defined in src/app/fasterq/fasterq-editor.ts:54
|
m_totalToBeServiced |
m_totalToBeServiced: |
Default value : 0
|
Defined in src/app/fasterq/fasterq-editor.ts:65
|
QUEUE_OFFSET |
QUEUE_OFFSET: |
Default value : 8
|
Defined in src/app/fasterq/fasterq-editor.ts:52
|
import {AfterViewInit, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
import {Compbaser} from "ng-mslib";
import {YellowPepperService} from "../../services/yellowpepper.service";
import {FasterqQueueModel} from "../../models/fasterq-queue-model";
import {FasterqLineModel} from "../../models/fasterq-line-model";
import {FasterqAnalyticsModel} from "../../models/fasterq-analytics";
import {RedPepperService} from "../../services/redpepper.service";
import {EFFECT_LOAD_FASTERQ_ANALYTICS, EFFECT_LOAD_FASTERQ_QUEUES, EFFECT_QUEUE_CALL_SAVE, EFFECT_QUEUE_POLL_SERVICE, EFFECT_QUEUE_SERVICE_SAVE} from "../../store/effects/appdb.effects";
import {CommBroker} from "../../services/CommBroker";
import {FASTERQ_QUEUE_CALL_CANCLED} from "../../interfaces/Consts";
import {IUiState} from "../../store/store.data";
import {ACTION_UISTATE_UPDATE, SideProps} from "../../store/actions/appdb.actions";
import {List} from "immutable";
import {Lib} from "../../Lib";
import * as _ from "lodash";
import {timeout} from "../../decorators/timeout-decorator";
export interface IQueueSave {
queue_id: number;
queue: FasterqQueueModel;
serviced?: string;
called?: string;
called_by?: string;
called_by_override?: boolean;
}
@Component({
selector: 'fasterq-editor',
styles: [`
.personInLine {
margin: 10px;
padding: 0;
float: left;
width: 40px;
height: 100px;
cursor: pointer;
color: #D0D0D0;
}
.called {
color: #BE6734;
}
.serviced {
color: #ACFD89;
}
`],
templateUrl: './fasterq-editor.html'
})
export class FasterqEditor extends Compbaser {
QUEUE_OFFSET = 8;
m_gotoModel = 0;
m_stopWatchHandle: any = new Stopwatch();
m_stopTimer = '00:00:00';
m_fasterqLineModel: FasterqLineModel;
m_selectedServiceId = -1;
m_queues: List<FasterqQueueModel> = List([]);
m_analytics: List<FasterqAnalyticsModel> = List([]);
m_offsetPosition = 0;
m_avgServiceTimeCalc: any = '00:00:00';
m_avgCalledTimeCalc: any = '00:00:00';
m_lastCalled: any = 0;
m_nowServicing: any = 0;
m_totalToBeServiced = 0;
m_fqLastServiced = '';
m_liveUpdateHandler;
appBaseUrlServices;
constructor(private yp: YellowPepperService, private rp: RedPepperService, private commBroker: CommBroker, private el: ElementRef, private cd: ChangeDetectorRef) {
super();
this._pollServices();
this.cancelOnDestroy(
this.yp.ngrxStore.select(store => store.appDb.appBaseUrlServices)
.subscribe((i_appBaseUrlServices) => {
this.appBaseUrlServices = i_appBaseUrlServices;
})
)
this.cancelOnDestroy(
this.commBroker.onEvent(FASTERQ_QUEUE_CALL_CANCLED)
.subscribe((data: any) => {
bootbox.confirm('Customer already called by user' + data.message.called_by + ' <br/><br/>Would you like to call the customer again?', (result) => {
if (result) {
data.message['called_by_override'] = true;
this.yp.ngrxStore.dispatch({type: EFFECT_QUEUE_CALL_SAVE, payload: data.message})
}
});
}, (e) => console.error(e))
)
this.cancelOnDestroy(
this.yp.listenFasterqQueueLastServicedPolled()
.subscribe((i_fasterqNowServicing) => {
this.m_nowServicing = i_fasterqNowServicing;
this.cd.markForCheck();
}, (e) => console.error(e))
)
this.cancelOnDestroy(
this.yp.listenFasterqQueueSelected()
.subscribe((i_serviceId) => {
this.m_selectedServiceId = i_serviceId
this.cd.markForCheck();
}, (e) => console.error(e))
)
this.cancelOnDestroy(
this.yp.listenFasterqLineSelected()
.subscribe((i_fasterqLineModel) => {
this.m_fasterqLineModel = i_fasterqLineModel;
this.cd.markForCheck();
}, (e) => console.error(e))
)
this.cancelOnDestroy(
this.yp.listenFasterqQueues()
.subscribe((i_queues: List<FasterqQueueModel>) => {
this.m_queues = List([]);
for (var i = (0 - this.QUEUE_OFFSET); i < 0; i++) {
i_queues = i_queues.unshift(new FasterqQueueModel({line_id: -1}))
}
this.m_queues = i_queues;
this._selectFirst();
this.cd.markForCheck();
}, (e) => console.error(e))
)
this.cancelOnDestroy(
this.yp.listenFasterqAnalytics()
.subscribe((i_analytics: List<FasterqAnalyticsModel>) => {
this.m_analytics = i_analytics;
this._calcAverages();
this.cd.markForCheck();
}, (e) => console.error(e))
)
this._selectFirst();
}
_openRemoteStatus() {
window.open(this._buildURL(), "_blank", "toolbar=yes, scrollbars=yes, resizable=yes, top=10, left=10, width=400, height=400");
}
/**
Create URL string to load customer terminal UI for FasterQ queue generation
@method _buildURL
@return {String} URL
**/
_buildURL() {
var data = {
line_id: this.m_fasterqLineModel.lineId,
business_id: this.m_fasterqLineModel.businessId,
call_type: 'QR'
};
data = $.base64.encode(JSON.stringify(data));
return `${this.appBaseUrlServices}/studioweb/index.html?mode=remoteStatus¶m=${data}`;
}
@timeout(1000)
_selectFirst() {
if (this.m_queues.size != this.QUEUE_OFFSET + 1)
return;
this.m_selectedServiceId = this.m_queues.get(this.QUEUE_OFFSET).serviceId;
this._onQueueSelected(this.m_queues.get(this.QUEUE_OFFSET));
}
_pollServices() {
this.m_liveUpdateHandler = setInterval(() => {
this.yp.ngrxStore.dispatch({type: EFFECT_QUEUE_POLL_SERVICE, payload: {business_id: this.rp.getUserData().businessID, line_id: this.m_fasterqLineModel.lineId}})
this.yp.ngrxStore.dispatch(({type: EFFECT_LOAD_FASTERQ_QUEUES, payload: {line_id: this.m_fasterqLineModel.lineId}}))
this.yp.ngrxStore.dispatch(({type: EFFECT_LOAD_FASTERQ_ANALYTICS, payload: {line_id: this.m_fasterqLineModel.lineId}}))
this._updateTotalToBeServiced();
}, 5000);
}
/**
Update the total number of queues left to be serviced
@method _updateTotalToBeServiced
**/
_updateTotalToBeServiced() {
var total = 0;
this.m_queues.forEach((i_asterqQueueModel: FasterqQueueModel) => {
if (_.isNull(i_asterqQueueModel.serviced))
total++;
this.m_totalToBeServiced = total;
});
}
_onQueueSelected(i_queue: FasterqQueueModel) {
this.m_selectedServiceId = i_queue.serviceId;
var index = this._getQueueIndexByServiceId();
var uiState: IUiState = {
uiSideProps: SideProps.fasterqQueueProps,
fasterq: {
fasterqQueueSelected: this.m_selectedServiceId
}
}
this.yp.dispatch(({type: ACTION_UISTATE_UPDATE, payload: uiState}))
this._scrollTo(index);
}
_getQueueIndexByServiceId(): number {
if (this.m_selectedServiceId == -1)
return this.m_selectedServiceId;
return this.m_queues.findIndex((i_fasterqQueueModel) => {
return i_fasterqQueueModel.serviceId == this.m_selectedServiceId;
})
}
/**
Scroll to position of selected queue / UI person
@method _scrollTo
@param {Element} i_element
**/
_scrollTo(i_index) {
this._watchStop();
var el = $('#fqLineQueueComponent', this.el.nativeElement).children().eq(i_index);
var scrollXPos = $(el).position().left;
// console.log('current offset ' + scrollXPos + ' ' + 'going to index ' + $(i_element).index() + ' service_id ' + i_serviceId);
this.m_offsetPosition = $('#fqLineQueueComponentContainer', this.el.nativeElement).scrollLeft();
scrollXPos += this.m_offsetPosition;
var final = scrollXPos - 480;
TweenLite.to('#fqLineQueueComponentContainer', 2, {
scrollTo: {x: final, y: 0},
ease: Power4['easeOut']
});
}
_getQueueFromSelectedIndex(): FasterqQueueModel {
return this.m_queues.find(i_fasterqQueueModel => {
return i_fasterqQueueModel.serviceId == this.m_selectedServiceId;
})
}
_selectIfDefault() {
if (this.m_queues.size >= this.QUEUE_OFFSET + 1 && this.m_selectedServiceId == -1)
this.m_selectedServiceId = this.m_queues.get(this.QUEUE_OFFSET).serviceId;
}
/**
Listen to queue being called, mark on UI and post to server
@method _listenCalled
**/
_onCall() {
this._selectIfDefault();
if (this.m_queues.size == this.QUEUE_OFFSET) return;
if (!_.isNull(this._getQueueFromSelectedIndex().serviced))
return bootbox.alert('customer has already been serviced');
this._watchStart();
this.m_lastCalled = this._getQueueFromSelectedIndex().serviceId;
var d = new XDate();
var payload: IQueueSave = {
queue_id: this._getQueueFromSelectedIndex().queueId,
called: d.toString('M/d/yyyy hh:mm:ss TT'),
called_by: this.rp.getUserData().userName,
called_by_override: false,
queue: this._getQueueFromSelectedIndex()
}
this.yp.ngrxStore.dispatch({type: EFFECT_QUEUE_CALL_SAVE, payload: payload})
}
/**
Listen to queue being serviced, mark on UI and post to server
@method _listenServiced
**/
_onService() {
this._selectIfDefault();
if (this.m_queues.size == this.QUEUE_OFFSET) return;
this._watchStop();
if (_.isNull(this._getQueueFromSelectedIndex().called)) {
bootbox.alert('customer has not been called yet');
return;
}
if (!_.isNull(this._getQueueFromSelectedIndex().serviced)) {
bootbox.alert('customer has already been serviced');
return;
}
this.m_fqLastServiced = this._getQueueFromSelectedIndex().serviceId;
var d = new XDate();
var payload: IQueueSave = {
queue_id: this._getQueueFromSelectedIndex().queueId,
serviced: d.toString('M/d/yyyy hh:mm:ss TT'),
queue: this._getQueueFromSelectedIndex()
}
this.yp.ngrxStore.dispatch({type: EFFECT_QUEUE_SERVICE_SAVE, payload: payload})
}
/**
Calculate average respond and service line times
@method _calcAverages
**/
_calcAverages() {
var avgServiceTime = [];
var avgCalledTime = [];
this.m_analytics.forEach((i_model: FasterqAnalyticsModel) => {
var entered = i_model.entered;
var serviced = i_model.serviced;
var called = i_model.called;
if (_.isNull(called)) {
// customer not called, do nothing
} else if (!_.isNull(serviced)) {
// customer called & serviced
var xEntered = new XDate(entered);
var minFromEnteredToCalled = xEntered.diffMinutes(called);
if (minFromEnteredToCalled < 0)
minFromEnteredToCalled = 1;
avgCalledTime.push(minFromEnteredToCalled);
var xCalled = new XDate(called);
var minFromCalledToServiced = xCalled.diffMinutes(serviced);
avgServiceTime.push(minFromCalledToServiced);
} else {
// customer called not serviced
var xEntered = new XDate(entered);
var minFromEnteredToCalled = xEntered.diffMinutes(called);
if (minFromEnteredToCalled < 0)
minFromEnteredToCalled = 1;
avgCalledTime.push(minFromEnteredToCalled);
}
});
this.m_avgServiceTimeCalc = _.reduce(avgServiceTime, function (memo, num) {
return memo + num;
}, 0) / (avgServiceTime.length === 0 ? 1 : avgServiceTime.length);
this.m_avgServiceTimeCalc = Lib.ParseToFloatDouble(this.m_avgServiceTimeCalc);
this.m_avgCalledTimeCalc = _.reduce(avgCalledTime, function (memo, num) {
return memo + num;
}, 0) / (avgCalledTime.length === 0 ? 1 : avgCalledTime.length);
this.m_avgCalledTimeCalc = Lib.ParseToFloatDouble(this.m_avgCalledTimeCalc)
}
_onPrev() {
if (this._getQueueIndexByServiceId() == this.QUEUE_OFFSET)
return;
this._onQueueSelected(this.m_queues.get(this._getQueueIndexByServiceId() - 1));
}
_onNext() {
if (this._getQueueIndexByServiceId() + 1 == this.m_queues.size || this.m_queues.size <= this.QUEUE_OFFSET + 1)
return;
if (this._getQueueIndexByServiceId() == -1)
return this._onQueueSelected(this.m_queues.get(this.QUEUE_OFFSET));
this._onQueueSelected(this.m_queues.get(this._getQueueIndexByServiceId() + 1));
}
_onGoTo() {
var queue = this.m_queues.find((i_fasterqQueueModel: FasterqQueueModel) => {
var serviceId = i_fasterqQueueModel.serviceId;
var selectedId = Lib.PadZeros(this.m_gotoModel, 3, 0);
return serviceId == selectedId;
})
if (queue)
this._onQueueSelected(queue);
}
/**
Start the stop watch UI
@method _watchStart
**/
_watchStart() {
this.m_stopWatchHandle.setListener((e) => {
this.m_stopTimer = this.m_stopWatchHandle.toString();
this.cd.markForCheck();
});
this.m_stopWatchHandle.start();
}
/**
Stop the stop watch UI
@method _watchStop
**/
_watchStop() {
this.m_stopWatchHandle.stop();
this.m_stopWatchHandle.reset();
this.m_stopTimer = '00:00:00';
}
destroy() {
clearInterval(this.m_liveUpdateHandler);
var uiState: IUiState = {
uiSideProps: SideProps.miniDashboard,
fasterq: {
fasterqQueueSelected: -1
}
}
this.yp.dispatch(({type: ACTION_UISTATE_UPDATE, payload: uiState}))
}
}
<small class="debug">{{me}}</small>
<div id="fasterQManagerContainer">
<button (click)="_openRemoteStatus()" i18n style="margin-right: 100px; position: relative; top: -30px" id="fqOpenCustomerRemoteStatus" class="pull-right btn btn-primary">remote status</button>
<h5>Line: {{m_fasterqLineModel?.lineName}}</h5>
<h5>Service id: {{m_selectedServiceId > 0 ? m_selectedServiceId : 'none'}}</h5>
<div>
<br/>
<br/>
<br/>
<div style="margin: 0 20px 0 -9px" class="row fqStats">
<div class="col-xs-6 col-sm-3" style="margin-top: 0; padding: 0;">
<div class="fakeVerticalLineDivL" style="height: 58px; width: 100%; float: left">
<span><strong>called customer: </strong></span><br/>
<span id="fqTimeWithCustomer">{{m_stopTimer}}</span>
</div>
</div>
<div class="col-xs-6 col-sm-3" style="margin-top: 0; padding: 0">
<div class="fakeVerticalLineDivL" style="height: 58px; width: 100%; float: left">
<span><strong>last you serviced</strong></span><br/>
<span id="fqLastServiced">{{m_fqLastServiced}}</span>
</div>
</div>
<div class="col-xs-6 col-sm-3" style="margin-top: 0; padding: 0">
<div class="fakeVerticalLineDivL" style="height: 58px; width: 100%; float: left">
<span><strong>last you called</strong></span><br/>
<span id="fqLastCalled">{{m_lastCalled}}</span>
</div>
</div>
<div class="col-xs-6 col-sm-3" style="margin-top: 0; padding: 0;">
<div class="fakeVerticalLineDivL" style="height: 58px; width: 100%; float: left">
<span><strong>total to be serviced</strong></span><br/>
<span id="fqTotalToBeServiced">{{m_totalToBeServiced}}</span>
</div>
</div>
</div>
<div style="margin: 0 20px 0 -9px" class="row fqStats">
<div class="col-xs-4" style="margin-top: 0; padding: 0;">
<div class="fakeVerticalLineDivL" style="width: 100%; float: left">
<span><strong>avg customer service time (minutes): </strong></span><br/>
<h1 id="fqAvgCustomerService">{{m_avgServiceTimeCalc}}</h1>
</div>
</div>
<div class="col-xs-4" style="margin-top: 0; padding: 0">
<div class="fakeVerticalLineDivL" style="width: 100%; float: left">
<span><strong>avg customer wait time (minutes):</strong></span><br/>
<h1 id="fqAvgCustomerWait">{{m_avgCalledTimeCalc}}</h1>
</div>
</div>
<div class="col-xs-4" style="margin-top: 0; padding: 0;">
<div class="fakeVerticalLineDivL" style="width: 100%; float: left">
<span><strong>now servicing: </strong></span><br/>
<h1 id="fqNowServicing">Latest: {{m_nowServicing}}</h1>
</div>
</div>
</div>
<br/>
<div id="fqLineQueueComponentWrap">
<div id="fqSelectedCustomer"></div>
<div id="fqLineQueueComponentContainer">
<div id="fqLineQueueComponent">
<div *ngFor="let queue of m_queues; let i = index" class="personInLine" (click)="_onQueueSelected(queue)" [ngClass]="{serviced: queue.serviced, called: queue.called}">
<div *ngIf="queue.lineId != -1">
<i style="font-size: 90px" class="fa fa-male"></i>
<h3 style="position: relative; left: 6px">{{queue.serviceId}}</h3>
</div>
</div>
</div>
</div>
</div>
<div style="text-align: center; width: 100%;" class="centerElement">
<button (click)="_onCall()" id="fqLineCompCall" type="button" data-localize-tooltip="back" data-localize="empty" title="back" class="lineComponentButtons btn btn-default btn-sm">
<span class="fa fa-bullhorn"></span> Call
</button>
<button (click)="_onService()" id="fqLineCompServiced" type="button" data-localize-tooltip="back" data-localize="empty" title="back" class="lineComponentButtons btn btn-default btn-sm">
<span class="fa fa-thumbs-up"></span> Serviced
</button>
<button (click)="_onPrev()" id="fqLineCompPrev" type="button" data-localize-tooltip="back" data-localize="empty" title="back" class="lineComponentButtons btn btn-default btn-sm">
<span class="glyphicon glyphicon-chevron-left"></span> Previous
</button>
<span>
<button (click)="_onGoTo()" id="fqLineGoTo" type="button" data-localize-tooltip="goTo" data-localize="empty" title="go to" class="lineComponentButtons btn btn-default btn-sm">
Go to <input [(ngModel)]="m_gotoModel" id="fqGoToLineInput">
</button>
</span>
<button (click)="_onNext()" id="fqLineCompNext" type="button" data-localize-tooltip="back" data-localize="empty" title="back" class="lineComponentButtons btn btn-default btn-sm">
Next
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
</div>
</div>