import { Component , OnInit , ViewChild } from '@angular/core' ; import { POIDetails , OperatorInfo , ConnectionType , Country , ConnectionInfo , StatusType , ExtendedPOIDetails , ExtendedAddressInfo , UsageType } from '../../model/CoreDataModel' ; import { AppManager } from '../../services/AppManager' ; import { ModalController , LoadingController , AlertController } from '@ionic/angular' ; import { GeoLatLng , POISearchParams } from '../../model/AppModels' ; import { Utils } from '../../core/Utils' ; import { POIManager } from '../../services/POIManager' ; import { Mapping } from '../../services/mapping/Mapping' ; import { PoiLocationEditorComponent } from '../../components/poi-location-editor/poi-location-editor' ; import { PoiEquipmentEditorComponent } from '../../components/poi-equipment-editor/poi-equipment-editor' ; import { StandardStatusTypes , StandardOperators } from '../../model/StandardEnumTypes' ; interface ValidationResult { isValid : boolean ; msg : string ; } @ Component ( { selector : 'app-poi-editor' , templateUrl : './poi-editor.page.html' , styleUrls : [ './poi-editor.page.scss' ] , } ) export class PoiEditorPage implements OnInit { id : number ; item : POIDetails ; step : 'location' | 'poi-nearby' | 'copy-equipment' | 'edit-equipment' | 'info' ; /**
     * Defines the overall UI grouping for our current step
     */ selectedTab : 'location' | 'equipment' | 'info' ; startPos : GeoLatLng ; useFilteredConnectionTypes : boolean = true ; templateSites : Array < ExtendedPOIDetails > = [ ] ; nearbySites : Array < ExtendedPOIDetails > = [ ] ; selectedTemplatePOI : ExtendedPOIDetails = null ; suggestedAddress : ExtendedAddressInfo = null ; suggestedAddressAttribution : string = null ; isNonDuplicateConfirmed : boolean = false ; skipPOICopy : boolean = false ; validationMsg : string = null ; loading : any ; @ ViewChild ( PoiLocationEditorComponent , { static : false } ) private editorMap : PoiLocationEditorComponent ; get countries ( ) : Array < Country > { return this . appManager . referenceDataManager . getCountries ( ) ; } get usageTypes ( ) : Array < UsageType > { return this . appManager . referenceDataManager . getUsageTypes ( ) ; } get statusTypes ( ) : Array < StatusType > { return this . appManager . referenceDataManager . getStatusTypes ( ) . filter ( s => { return s . IsUserSelectable == true ; } ) ; } get isAddMode ( ) : boolean { if ( this . item . ID <= 0 ) { return true ; } else { return false ; } } constructor ( private appManager , private modalController , private poiManager , public mapping , public loadingController , private alertController ) { this . initNewItem ( ) ; } initNewItem ( ) { this . item = { ID : - 1 , DataProviderID : 1 , DataProvidersReference : null , OperatorsReference : null , OperatorID : 1 , UsageCost : null , UsageTypeID : 4 , NumberOfPoints : 1 , GeneralComments : null , DatePlanned : null , StatusTypeID : 50 , SubmissionStatusTypeID : null , Connections : [ ] , MetadataValues : [ ] , AddressInfo : { ID : - 1 , CountryID : 18 , Title : '' , AddressLine1 : '' , Latitude : 0 , Longitude : 0 } } ; this . step = 'location' ; this . selectedTab = 'location' ; } get isReadyToSubmit ( ) : boolean { return this . validate ( ) . isValid && this . step == 'info' ; } ngOnInit ( ) { } async presentLoadingUI ( ) { this . loading = await this . loadingController . create ( { message : 'Please Wait..' } ) ; await this . loading . present ( ) ; } async dismissLoadingUI ( ) { if ( this . loading ) { await this . loading . dismiss ( ) ; } } ionViewDidEnter ( ) { if ( this . id != null ) { this . editExistingPOI ( this . id ) ; } else { let lastOperatorId = localStorage . getItem ( "_editor-operatorid" ) ; if ( lastOperatorId ) { this . item . OperatorID = parseInt ( lastOperatorId , 10 ) ; } if ( this . startPos ) { this . item . AddressInfo . Latitude = this . startPos . latitude ; this . item . AddressInfo . Longitude = this . startPos . longitude ; } else { if ( this . mapping ) { this . mapping . getMapCenter ( ) . subscribe ( p => { if ( p ) { this . item . AddressInfo . Latitude = p . coords . latitude ; this . item . AddressInfo . Longitude = p . coords . longitude ; } } ) ; } } } this . refreshFilteredReferenceData ( ) ; } async previous ( ) { switch ( this . step ) { case 'info' : this . step = 'edit-equipment' ; break ; case 'edit-equipment' : this . step = this . isAddMode ? 'copy-equipment' : 'location' ; break ; case 'copy-equipment' : this . step = 'location' ; break ; case 'poi-nearby' : this . step = 'location' ; break ; } await this . initCurrentStep ( false ) ; } async next ( ) { if ( this . step == 'poi-nearby' && this . nearbySites . length > 0 && ! this . isNonDuplicateConfirmed ) { const alert = await this . alertController . create ( { header : 'Confirm' , message : 'Please confirm you are not adding a duplicate site.' , buttons : [ { text : 'Cancel' , role : 'cancel' , cssClass : 'secondary' , handler : ( blah ) => { } } , { text : 'Confirm' , handler : ( ) => { this . isNonDuplicateConfirmed = true ; this . next ( ) ; } } ] } ) ; alert . present ( ) ; return ; } const validation = this . validate ( this . step ) ; if ( validation . isValid ) { switch ( this . step ) { case 'location' : this . step = this . isAddMode ? 'poi-nearby' : 'edit-equipment' ; break ; case 'poi-nearby' : this . step = this . isAddMode ? 'copy-equipment' : 'edit-equipment' ; break ; case 'copy-equipment' : this . step = 'edit-equipment' ; break ; case 'edit-equipment' : this . step = 'info' ; break ; } await this . initCurrentStep ( true ) ; } else { const a = await this . alertController . create ( { message : validation . msg } ) ; a . present ( ) ; } } async initCurrentStep ( isNextDirection ) { if ( this . step == 'poi-nearby' ) { this . selectedTab = 'location' ; const siteNum = await this . refreshNearbySites ( ) ; if ( siteNum == 0 ) { isNextDirection ? await this . next ( ) : await this . previous ( ) ; } } else if ( this . step == 'location' ) { this . selectedTab = 'location' ; } else if ( this . step == 'copy-equipment' ) { this . selectedTab = 'equipment' ; await this . refreshTemplateSites ( ) ; } else if ( this . step == 'edit-equipment' ) { this . selectedTab = 'equipment' ; } else if ( this . step == 'info' ) { this . selectedTab = 'info' ; } } async onCountryChange ( ) { this . refreshFilteredReferenceData ( ) ; } async onOperatorChange ( operatorInfo = null ) { if ( operatorInfo != null ) { if ( this . item . OperatorID != operatorInfo . ID ) { this . item . OperatorID = operatorInfo . ID ; localStorage . setItem ( "_editor-operatorid" , this . item . OperatorID . toString ( ) ) ; await this . refreshTemplateSites ( ) ; } } } useSuggestedAddress ( $evt = null ) { if ( $evt ) { this . suggestedAddress = $evt . suggestedAddress ; this . suggestedAddressAttribution = $evt . suggestedAddressAttribution ; } Object . assign ( this . item . AddressInfo , this . suggestedAddress ) ; if ( this . suggestedAddressAttribution ) { if ( ! this . item . MetadataValues . find ( a => a . MetadataFieldID == 4 ) ) { this . item . MetadataValues . push ( { ID : - 1 , MetadataFieldID : 4 , ItemValue : this . suggestedAddressAttribution , MetadataFieldOptionID : null , MetadataFieldOption : null } ) ; } } } async editConnection ( c ) { const source = this . item . Connections . find ( f => f . ID == c . ID ) ; const item = Object . assign ( { } , source ) ; const modal = await this . modalController . create ( { component : PoiEquipmentEditorComponent , componentProps : { conn : item } } ) ; modal . onWillDismiss ( ) . then ( ( result ) => { if ( result && result . data && result . data . item ) { this . updateConnection ( result . data . item ) ; } } ) ; return await modal . present ( ) ; } async deleteConnection ( c ) { const source = this . item . Connections . find ( f => f . ID == c . ID ) ; const alert = await this . alertController . create ( { header : 'Confirm Delete' , message : 'Are you sure you want to delete this connection information?' , buttons : [ { text : 'No' , role : 'cancel' , cssClass : 'secondary' , handler : ( ) => { } } , { text : 'Yes' , handler : ( ) => { this . item . Connections = this . item . Connections . filter ( f => f . ID != source . ID ) ; this . appManager . referenceDataManager . hydrateCompactPOI ( this . item , true ) ; } } ] } ) ; alert . present ( ) ; } async addConnection ( ) { const item = { ID : - Utils . getRandomInt ( 10000 ) , ConnectionTypeID : null , StatusTypeID : 50 , PowerKW : null , Quantity : 1 } ; const modal = await this . modalController . create ( { component : PoiEquipmentEditorComponent , componentProps : { conn : item } } ) ; modal . onWillDismiss ( ) . then ( ( result ) => { if ( result && result . data && result . data . item ) { this . updateConnection ( result . data . item ) ; } } ) ; return await modal . present ( ) ; } refreshFilteredReferenceData ( ) { this . appManager . referenceDataManager . refreshFilteredReferenceData ( this . appManager . api , { CountryIds : [ this . item . AddressInfo . CountryID ] } ) ; } updateConnection ( conn ) { if ( conn ) { let update = this . item . Connections . find ( f => f . ID == conn . ID ) ; if ( update ) { Object . assign ( update , conn ) ; } else { this . item . Connections . push ( conn ) ; } } this . appManager . referenceDataManager . hydrateCompactPOI ( this . item , true ) ; } validate ( step = 'all' ) { let validationMsg = null ; if ( step == 'all' || step == 'location' ) { if ( ! this . item . AddressInfo . Latitude || ! this . item . AddressInfo . Longitude ) { validationMsg = "Location is required" ; } if ( this . item . AddressInfo . Title == '' ) { validationMsg = "A location title is required" ; } if ( ! this . item . AddressInfo . CountryID ) { validationMsg = "A country selection is required" ; } if ( ! this . item . AddressInfo . Latitude || ! this . item . AddressInfo . Longitude ) { validationMsg = "A location latitude and longitude is required" ; } } if ( step == 'all' || step == 'poi-nearby' ) { if ( this . nearbySites . length > 0 && ! this . isNonDuplicateConfirmed ) { validationMsg = "Please confirm that the site is not a duplicate" ; } } if ( step == 'all' || step == 'equipment' ) { if ( ! this . item . OperatorID ) { validationMsg = "Please confirm the charging network or equipment operator." ; } if ( this . item . Connections . length == 0 ) { validationMsg = "Equipment information is required" ; } } if ( validationMsg ) { this . validationMsg = validationMsg ; return { isValid : false , msg : validationMsg } ; } else { this . validationMsg = null ; return { isValid : true , msg : null } ; } } async save ( ) { let validationResult = this . validate ( ) ; if ( ! validationResult . isValid ) { alert ( validationResult . msg ) ; return ; } await this . presentLoadingUI ( ) ; try { await this . appManager . submitPOI ( this . item ) ; await this . dismissLoadingUI ( ) ; this . appManager . showToastNotification ( "You submission will be reviewed (if required) and published shortly." ) ; this . modalController . dismiss ( ) ; } catch ( err ) { await this . dismissLoadingUI ( ) ; if ( err . error ) { alert ( "Sorry, your submission could not be completed at this time. Please try again later." ) ; } } } async cancel ( ) { this . modalController . dismiss ( ) ; } /**
     * For the current selected country and network operator, fetch a list of recently added sites to use as a template
     */ async refreshTemplateSites ( ) { if ( this . item . AddressInfo . CountryID && this . item . OperatorID ) { await this . presentLoadingUI ( ) ; this . templateSites = [ ] ; let filter = new POISearchParams ( ) ; filter . countryIdList = [ this . item . AddressInfo . CountryID ] ; filter . minPowerKW = 1 ; filter . operatorIdList = [ this . item . OperatorID ] ; filter . maxResults = 10 ; let results = await this . poiManager . fetchPOIList ( filter ) ; results . sort ( ( a , b ) => { return new Date ( a . DateCreated ) . getTime ( ) - new Date ( b . DateCreated ) . getTime ( ) ; } ) ; this . templateSites = results ; this . dismissLoadingUI ( ) ; } } /**
     * Fetch list of POis dear the currently set location, return numbers of results
     * */ async refreshNearbySites ( ) { if ( this . item . AddressInfo . Latitude && this . item . AddressInfo . Longitude ) { await this . presentLoadingUI ( ) ; this . nearbySites = [ ] ; let filter = new POISearchParams ( ) ; filter . latitude = this . item . AddressInfo . Latitude ; filter . longitude = this . item . AddressInfo . Longitude ; filter . distance = 5 ; filter . distanceUnit = 'km' ; filter . maxResults = 10 ; let results = await this . poiManager . fetchPOIList ( filter ) ; results . sort ( ( a , b ) => { return a . Distance - b . Distance ; } ) ; this . nearbySites = results ; this . dismissLoadingUI ( ) ; return results . length ; } else { return 0 ; } } confirmNonDuplicate ( ) { this . isNonDuplicateConfirmed = true ; this . next ( ) ; } changeTemplatePOI ( ) { this . selectedTemplatePOI = null ; } editPOI ( poi ) { this . editExistingPOI ( poi . ID ) ; } async editExistingPOI ( id ) { this . skipPOICopy = true ; this . isNonDuplicateConfirmed = true ; await this . presentLoadingUI ( ) ; this . poiManager . getPOIById ( id , true , true ) . then ( poi => { this . item = Object . assign ( { } , poi ) ; if ( this . item . OperatorID == null ) { this . item . OperatorID = StandardOperators . UnknownOperator ; } this . refreshFilteredReferenceData ( ) ; this . dismissLoadingUI ( ) ; } ) ; } param source useTemplatePOI ( source ) { this . selectedTemplatePOI = source ; Object . assign ( this . item . Connections , source . Connections ) ; for ( let i of this . item . Connections ) { i . ID = - Utils . getRandomInt ( 10000 ) ; i . Reference = null ; i . Comments = null ; i . StatusTypeID = StandardStatusTypes . Operational ; } this . item . UsageCost = source . UsageCost ; this . item . UsageTypeID = source . UsageTypeID ; this . appManager . referenceDataManager . hydrateCompactPOI ( this . item ) ; } skipCopyingPOI ( ) { this . skipPOICopy = true ; } }