Browse Source

Added savings from generated energy data, password strenght check, assets crud, change message on error

Oscar José Nuñez Chávez 6 years ago
parent
commit
b6db552663
30 changed files with 579 additions and 243 deletions
  1. 1 0
      src/app/app.component.scss
  2. 30 22
      src/app/components/assets/assets.component.html
  3. 48 0
      src/app/components/assets/assets.component.scss
  4. 223 164
      src/app/components/assets/assets.component.ts
  5. 1 3
      src/app/components/confirm-account/confirm-account.component.ts
  6. 1 1
      src/app/components/dashboard/dashboard.component.ts
  7. 1 1
      src/app/components/login/login.component.ts
  8. 1 1
      src/app/components/organizations/edit-organization/edit-organization.component.ts
  9. 1 1
      src/app/components/organizations/organization/organization.component.ts
  10. 1 1
      src/app/components/organizations/organizations.component.ts
  11. 1 1
      src/app/components/plants/edit-plant/edit-plant.component.ts
  12. 1 3
      src/app/components/plants/new-plant/new-plant.component.ts
  13. 79 0
      src/app/components/plants/plant/plant.component.html
  14. 0 0
      src/app/components/plants/plant/plant.component.scss
  15. 25 0
      src/app/components/plants/plant/plant.component.spec.ts
  16. 57 0
      src/app/components/plants/plant/plant.component.ts
  17. 4 0
      src/app/components/plants/plants.component.html
  18. 1 1
      src/app/components/plants/plants.component.ts
  19. 0 7
      src/app/components/plugins/validator/validator.component.ts
  20. 5 2
      src/app/components/plugins/weather-card/weather-card.component.scss
  21. 34 13
      src/app/components/plugins/weather-card/weather-card.component.ts
  22. 1 1
      src/app/components/users/new-user/new-user.component.ts
  23. 1 1
      src/app/components/users/users.component.ts
  24. 2 0
      src/app/layouts/admin/admin.module.ts
  25. 10 0
      src/app/layouts/admin/admin.routing.ts
  26. 19 9
      src/app/services/auth2.service.ts
  27. 14 1
      src/app/services/logs.service.ts
  28. 12 7
      src/app/services/plants.service.ts
  29. 0 2
      src/app/services/token.interceptor.ts
  30. 5 1
      src/assets/scss/material-dashboard.scss

+ 1 - 0
src/app/app.component.scss

@@ -0,0 +1 @@
+

+ 30 - 22
src/app/components/assets/assets.component.html

@@ -6,7 +6,7 @@
     </div>
   </div>
   <div class="row align-container" >
-    <div class="col-lg-6 col-sm-4 p-1">
+    <div class="col-lg-6 col-sm-6 p-1">
       <div class="row">
         <div class="col-lg-12 col-sm-12">
           <select class="custom-select" (change)="onChangeObj($event)" name="sel3">
@@ -15,27 +15,36 @@
         
         </div>
       </div>
-      <!--
-      <div class="row">
-        <div class="col-lg-12">
-            <div class="widget environment-meters">
-                <div class="mini-stats">
-                  <span class="green-skin"><i class="fas fa-hand-holding-usd"></i></span>
-                  <h5 *ngIf="eProduced">
-                    300
-                    <small>$</small>
-                  </h5>
-                </div>
-              </div>
+
+    <div class="row">
+      <div class="col-lg-12">
+        <div class="widget environment-meters">
+          <div class="mini-stats">
+            <!--<i class="fas fa-money-check-alt"></i> <i class="fas fa-hand-holding-usd"></i>-->
+            <span class="savings-skin"><i class="fas fa-money-check-alt"></i></span>
+            <div class="savings">
+              <h4>Ahorro total </h4>
+              <h3 *ngIf="eProduced">
+                 ${{total_savings}}
+              </h3>
+            </div>
+            <div class="savings-last-24h">
+              <i class="material-icons">
+                trending_up
+              </i>
+              Ahorro últimas 24hrs. <span>${{last_day_savings}}</span>
+            </div>
           </div>
+        </div>
       </div>
-    -->
+    </div>
+
     </div>
     <!-- Weather card -->
-    <div class="col-lg-6 col-sm-8 p-1">
+    <div class="col-lg-6 col-sm-6 p-1">
       <div class="row">
         <div class="col-lg-12">
-          <app-weather-card></app-weather-card>
+          <app-weather-card [city]="city"></app-weather-card>
         </div>
       </div>
     </div>
@@ -125,12 +134,13 @@
                 <div class=" action-buttons">
                   <button class="btn btn-link" [class.btn-success]='isActive[0]' (click)="getMeasureRangeChart('day')">1D</button>
                   <button class="btn btn-link" [class.btn-success]='isActive[1]' (click)="getMeasureRangeChart('week')">7D</button>
-                  <button class="btn btn-link" [class.btn-success]='isActive[2]' (click)="getMeasureRangeChart('wtd')">WTD</button>
+                  <button class="btn btn-link" [class.btn-success]='isActive[2]' (click)="getMeasureRangeChart('WTD')">WTD</button>
                   <button class="btn btn-link" [class.btn-success]='isActive[3]' (click)="getMeasureRangeChart('month')">1M</button>
-                  <button class="btn btn-link" [class.btn-success]='isActive[4]' (click)="getMeasureRangeChart('mtd')">MTD</button>
+                  <button class="btn btn-link" [class.btn-success]='isActive[4]' (click)="getMeasureRangeChart('MTD')">MTD</button>
                   <button class="btn btn-link" [class.btn-success]='isActive[5]' (click)="getMeasureRangeChart('3m')">3M</button>
-                  <button class="btn btn-link" [class.btn-success]='isActive[6]' (click)="getMeasureRangeChart('year')">YTD</button>
-                  <button class="btn btn-link" [class.btn-success]='isActive[7]' (click)="getMeasureRangeChart('total')">TOTAL</button>
+                  <button class="btn btn-link" [class.btn-success]='isActive[6]' (click)="getMeasureRangeChart('12m')">12M</button>
+                  <button class="btn btn-link" [class.btn-success]='isActive[7]' (click)="getMeasureRangeChart('YTD')">YTD</button>
+                  <button class="btn btn-link" [class.btn-success]='isActive[8]' (click)="getMeasureRangeChart('total')">TOTAL</button>
                
                   
                   <div class="input-box-container">
@@ -266,6 +276,4 @@
 
   </div>
 
-  <br>
-
 </div>

+ 48 - 0
src/app/components/assets/assets.component.scss

@@ -106,6 +106,10 @@ table {
   height: 122px;
 }
 
+.environment-meters {
+  height: 100px;
+}
+
 .enviroment-meter {
   height: 60px;
 }
@@ -156,11 +160,55 @@ table {
       //display: block;
     }
 
+    span.savings-skin {
+      color: #548c2f;
+      background: #fff;
+      border: none;
+      font-size: 48px;
+      line-height: 50px;
+    }
+
     h3 {
       margin: 10px 0 0;
       font-size: 2rem;
       font-weight: 400;
     }
+
+    .savings {
+      width: 100%;
+      margin-bottom: 5px;
+      h4, h3, h2 {
+        display: inline-block;
+        width: auto;
+      }
+      h4 {
+        margin-right: 8px;
+      }
+      h3 {
+        color: #39843c;
+        margin: 0;
+      }
+    }
+
+    .savings-last-24h {
+      font-size: 14px;
+      i {
+        font-size: 16px;
+        color: #196d21;
+      }
+
+      span {
+        border: none;
+        color: #39843c;
+        font-size: 18px;
+        line-height: 0;
+        text-align: center;
+        position: static;
+        display: inline;
+      }
+    }
+
+
   }
 
   .enviroment-stats {

+ 223 - 164
src/app/components/assets/assets.component.ts

@@ -4,8 +4,8 @@ import { Label, BaseChartDirective } from 'ng2-charts';
 import { MeasuresService } from 'src/app/services/measures.service';
 import { PlantsService } from 'src/app/services/plants.service';
 import { LogsService } from 'src/app/services/logs.service';
-
 import { OrganizationsService } from '@app/services/organizations.service';
+
 import { ActivatedRoute } from '@angular/router';
 import { MatPaginator } from '@angular/material/paginator';
 import { MatSort } from '@angular/material/sort';
@@ -51,7 +51,7 @@ export class AssetsComponent implements OnInit {
   environmentCO2:any;
   environmentHouse:any;
   environmentFuel:any;
-  isActive:[boolean,boolean,boolean,boolean,boolean,boolean,boolean,boolean]; //Activated param (chart)
+  isActive:[boolean,boolean,boolean,boolean,boolean,boolean,boolean,boolean,boolean]; //Activated param (chart)
   chartActive:[boolean,boolean,boolean,boolean]
   initialLoad: boolean = true;
   metersInstalled:boolean = true;
@@ -96,6 +96,11 @@ export class AssetsComponent implements OnInit {
   public barChartOptions: ChartOptions;
   public barChartData: ChartDataSets[];
   @ViewChild("baseChart",null) chart: BaseChartDirective;
+  city: any;
+  total_savings: any;
+  last_day_savings: number;
+  savings_logs: any;
+  savings_logs_data = [];
 
   
   // Get selected asset
@@ -136,7 +141,7 @@ export class AssetsComponent implements OnInit {
     }
 
     // Initialize default 'clicked' options for chart button options
-    this.isActive = [false, false, false, false, false, false, false, false];
+    this.isActive = [false, false, false, false, false, false, false, false, false];
     
     this.chartActive = [true, false, false, false];
 
@@ -150,12 +155,21 @@ export class AssetsComponent implements OnInit {
       
       if (this.assetID == undefined){
         this.assetID = this.listAssets[0].id;
+        this.city = this.listAssets[0].city;
+      }
+      else {
+        this.city = this.listAssets.find(object=>object.id == this.assetID).city;
       }
 
       let tempAsset = this.listAssets.find(object=>object.id == this.assetID);
 
       if (tempAsset["meters_installed"].length > 0){
         this.metersInstalled = true;
+
+        
+        // Get savings produced by an asset
+        this.getSavings(this.assetID);
+
         // Get energy produced given an asset 
         this.getEnergyProduced(this.assetID); 
         // Initialize a draw chart according to the default values
@@ -172,7 +186,7 @@ export class AssetsComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 
@@ -181,7 +195,7 @@ export class AssetsComponent implements OnInit {
       Swal.close();
     }, 2700);
   }
- 
+  
   // Default check asset dropdown if a value is equal to an assetID
   isSelected(){
     if(this.assetID!=undefined){
@@ -202,9 +216,11 @@ export class AssetsComponent implements OnInit {
   onChangeObj(event:any) {
     this.assetID = event.target.value;
     let tempAsset = this.listAssets.find(object=>object.id == this.assetID);
+    this.city = tempAsset["city"];
 
     if (tempAsset["meters_installed"].length > 0){
       this.metersInstalled = true;
+      this.getSavings(this.assetID);
       this.getEnergyProduced(this.assetID);
       this.getMeasureRangeChart(this.view, this.initialDate, this.assetID);
       this.getEnvironmentalData(this.assetID);
@@ -223,24 +239,20 @@ export class AssetsComponent implements OnInit {
   onDateChanged(event: IMyDateModel) {
     let endDate = `${event.singleDate.date.year}-${event.singleDate.date.month}-${event.singleDate.date.day}`;
     this.initialDate  = endDate;
-    switch (this.view)
-    { 
-      case "day": 
-        this.getMeasureRangeChart(this.view, endDate);
-        break; 
-      case "week": 
-        this.getMeasureRangeChart(this.view, endDate);
-        break; 
-      case "month": 
-        this.getMeasureRangeChart(this.view, endDate);
-        break; 
-      case "year":
-        this.getMeasureRangeChart(this.view, endDate);
-        break;
-      default: 
-        break;
-    } 
+
+    this.getMeasureRangeChart(this.view, endDate);
+
+    
+  }
+
+  getSavings(assetID: string) {
+    this.plantsService.getAssetSavings(this.assetID).subscribe( res => {
+      this.total_savings = res["data"]["ahorro_total"];
+      this.total_savings = +this.total_savings > 10000 ? (+this.total_savings / 1000).toFixed(3) + "k" : +this.total_savings.toFixed(2) || 0;
+      this.last_day_savings = +res["data"]["ahorro_ultimas_24_horas"].toFixed(2) || 0;
+    })  
   }
+ 
 
   getEnvironmentalData(assetID: string) {
     this.logsService.getAssetEnviromentalStats(assetID).subscribe(resp => {
@@ -312,177 +324,212 @@ export class AssetsComponent implements OnInit {
 
       case "day": 
         interval = "1D";
-        this.isActive = [true, false, false, false, false, false, false, false];
+        this.isActive = [true, false, false, false, false, false, false, false, false];
         break; 
       case "week": 
         interval = "7D";
-        this.isActive = [false, true, false, false, false, false, false, false];
+        this.isActive = [false, true, false, false, false, false, false, false, false];
         break; 
-      case "wtd":
+      case "WTD":
         interval = "WTD"
-        this.isActive = [false, false, true, false, false, false, false, false]
+        this.isActive = [false, false, true, false, false, false, false, false, false];
+        break;
       case "month":
         interval = "1M";
-        this.isActive = [false, false, false, true, false, false, false, false];
+        this.isActive = [false, false, false, true, false, false, false, false, false];
         break; 
-      case "mtd":
+      case "MTD":
         interval = "MTD";
-        this.isActive = [false, false, false, false, true, false, false, false];
+        this.isActive = [false, false, false, false, true, false, false, false, false];
+        break;
       case "3m":
         interval = "3M";
-        this.isActive = [false, false, false, false, false, true, false, false];
+        this.isActive = [false, false, false, false, false, true, false, false, false];
         break;
-      case "year":
+      case "12m":
+        interval = "1Y";
+        this.isActive = [false, false, false, false, false, false, true, false, false];
+        break;
+      case "YTD":
         interval = "YTD";
-        this.isActive = [false, false, false, false, false, false, true, false];
+        this.isActive = [false, false, false, false, false, false, false, true, false];
+        break;
       case "total":
         interval = "TOTAL";
-        this.isActive = [false, false, false, false, false, false, false, true];    
+        this.isActive = [false, false, false, false, false, false, false, false, true];
+        break;
       default: 
     }
 
-    // Get the measures according to the meters, given the params required
-    this.logsService.getEnergyProducedByParams(assetId,interval,dateRange).toPromise()
-    .then((data: any) => {
-      this.metersData = [];      
-      this.metersKeys = Object.keys(data["data"]["dataset"]);
-      
-      // Get all the values according to each index (meter) 
-      this.metersValues = Object.values(data["data"]["dataset"]);
-
-      /// Get the keys of those values
-      let meterKeys2 = Object.keys(this.metersValues);
-      this.displayedColumns= [];
-      this.tableData = [];
-
-      // The chart data object requires certain ordered params, so we itirate the returned object from the API call,
-      // and build the new object with the required/needed values
-      for (let prop in meterKeys2) { 
-        //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
-        let label = this.metersValues[prop]["label"];
-        let measure_values = Object.values(this.metersValues[prop]["data"].map(obj => obj.total_energy_kWh).reverse())
-        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: measure_values, borderColor: this.borderChartColors[prop] });
-        this.tableData.push({headers: label+" [kWh]", dataValues: measure_values})
+    this.logsService.getEnergySavingsProducedByAsset(assetId, interval, dateRange).toPromise()
+    .then((res: any)=>{
+      this.savings_logs = undefined;
+      this.savings_logs_data = [];
+      this.savings_logs = res["data"]["dataset"];
+      let savingsKeys = Object.keys(this.savings_logs);
+      for (let key in savingsKeys) {
+        let savingDate = savingsKeys[key];
+        this.savings_logs_data.push(this.savings_logs[savingDate]["ahorro"]);
       }
 
-      // Initialization of arrays for the table view of the measure values
-      this.array1 = this.array2 = this.array3 = [];
-
-      // Loop to build an object that contains the date and each meter value from a certain asset
-      // This object is required to build the datatable
-      for (let prop in meterKeys2) { 
-        this.array2 = [];
-        //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
-        let quantity = Object.keys(this.metersValues[0]["data"]);
-        let mvalues = Object.values(this.metersValues[prop]["data"].map(obj => obj).reverse())
+      // Get the measures according to the meters, given the params required
+      this.logsService.getEnergyProducedByParams(assetId,interval,dateRange).toPromise()
+      .then((data: any) => {
+        this.metersData = [];      
+        this.metersKeys = Object.keys(data["data"]["dataset"]);
         
-        for (let prop2 in quantity){
-          // Date insertion
-          this.array3 = [];
-          let columnname = `medidor${prop}`;
-         
-          if (+prop == 0 && mvalues[prop2] != undefined){
-            let dateT = this.getDateWithFormat(this.view, mvalues[prop2]["dateMax"])
-            this.array3.push(dateT);
-          }
+        // Get all the values according to each index (meter) 
+        this.metersValues = Object.values(data["data"]["dataset"]);
+
+        /// Get the keys of those values
+        let meterKeys2 = Object.keys(this.metersValues);
+        this.displayedColumns= [];
+        this.tableData = [];
+        this.metersData.push({ "label": "AHORRO [US$]", data: this.savings_logs_data.reverse(), type: "line", backgroundColor: "#e57272", borderColor: "#ce2525", borderWidth: 2, yAxisID: 'y-axis-ahorro', fill: false})
+        // The chart data object requires certain ordered params, so we itirate the returned object from the API call,
+        // and build the new object with the required/needed values
+        for (let prop in meterKeys2) { 
+          //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
+          let label = this.metersValues[prop]["label"]+" [kWh]";
+          let measure_values = Object.values(this.metersValues[prop]["data"].map(obj => obj.total_energy_kWh).reverse());
+          this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: measure_values, borderColor: this.borderChartColors[prop] });
+          this.tableData.push({headers: label+" [kWh]", dataValues: measure_values});
+        }
+        
+        // this.metersData.push({ "label": "Ahorro", data: [10,30,30,40,30,20,15,25,30,35], type: "line", backgroundColor: "#e57272", borderColor: "#ce2525", borderWidth: 2, yAxisID: 'y-axis-ahorro',})
+        // Initialization of arrays for the table view of the measure values
+        this.array1 = this.array2 = this.array3 = [];
+
+        // Loop to build an object that contains the date and each meter value from a certain asset
+        // This object is required to build the datatable
+        for (let prop in meterKeys2) { 
+          this.array2 = [];
+          //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
+          let quantity = Object.keys(this.metersValues[0]["data"]);
+          let mvalues = Object.values(this.metersValues[prop]["data"].map(obj => obj).reverse())
           
-          if (mvalues[prop2] != undefined) { 
-            this.array3.push(mvalues[prop2]["total_energy_kWh"]);
-          }
-          else {
-            this.array3.push(0)
-          }
-
-          if (+prop == 0){
-            this.array2.push(this.array3)
-          }
+          for (let prop2 in quantity){
+            // Date insertion
+            this.array3 = [];
+            let columnname = `medidor${prop}`;
           
-          if (+prop != 0) {
-            this.array1[prop2].push(this.array3[0]);
+            if (+prop == 0 && mvalues[prop2] != undefined){
+              let dateT = this.getDateWithFormat(this.view, mvalues[prop2]["dateMax"])
+              this.array3.push(dateT);
+              this.array3.push(this.savings_logs_data[prop2]);
+            }
+            
+            if (mvalues[prop2] != undefined) { 
+              this.array3.push(mvalues[prop2]["total_energy_kWh"]);
+            }
+            else {
+              this.array3.push(0)
+            }
+
+            if (+prop == 0){
+              this.array2.push(this.array3)
+            }
+            
+            if (+prop != 0) {
+              this.array1[prop2].push(this.array3[0]);
+            }
           }
+          if (+prop == 0){
+            this.array1 = (this.array2)
+            //this.array1 = [].concat.apply([], this.array1)
+          }    
         }
-        if (+prop == 0){
-          this.array1 = (this.array2)
-          //this.array1 = [].concat.apply([], this.array1)
-        }    
-      }
-
-      // According to the selected interval in the option buttons of the chart, the date is given an specific format 
-      switch (view){
-        case "day": 
-          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'HH:mm ','es-Es','-0600')).reverse();
-          break; 
-        case 'week':
-          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'EEEE dd','es-Es','-0600')).reverse();
-          break;
-        case "year":
-          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'MM/yyyy','es-Es','-0600')).reverse();
-          break;
-        default: 
-          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'dd/MM','es-Es','-0600')).reverse(); 
-      }
-
-      // Push the values to the chart object
-      this.tableData.push({headers: "Fecha/Hora", dataValues: this.barChartLabels})
-      this.tableData.reverse()
-      for( let v in Object.keys(this.tableData)){
-        this.displayedColumns.push(this.tableData[v]["headers"]);
-      }
-      this.tableData2 = this.array1;
-
-      this.dataSource.data = this.tableData2;
-      this.dataSource.paginator = this.paginator;
-      this.dataSource.sort = this.sort;
-
-      this.dataSourcePrint.data = this.tableData2;
 
+        // According to the selected interval in the option buttons of the chart, the date is given an specific format 
+        switch (view){
+          case "day": 
+            this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'HH:mm ','es-Es','-0600')).reverse();
+            break; 
+          case 'week':
+            this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'EEEE dd','es-Es','-0600')).reverse();
+            break;
+          case "year":
+            this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'MM/yyyy','es-Es','-0600')).reverse();
+            break;
+          default: 
+            this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'dd/MM','es-Es','-0600')).reverse(); 
+        }
 
-      this.showTable = true;
-      this.chart1 = new Chart('canvas', {
-        type: this.chart1Type,
-        options: {
-          title: {
-            display: true,
-          },
-          tooltips: {
-            mode: 'index',
-            callbacks: {
-              // Get the total of the energy produced in every point and show it in the hover tooltip
-              label: function (tooltipItem, data) { 
-                var label = data.datasets[tooltipItem.datasetIndex].label || '';
-                var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
-                var total = 0;
-
-                for (var i = 0; i < data.datasets.length; i++)
-                  total += +data.datasets[i].data[tooltipItem.index];
-
-                if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
-                  return label + " : " + value;
+        // Push the values to the chart object
+        this.tableData.push({headers: "Ahorro[US$]"});
+        this.tableData.push({headers: "Fecha/Hora", dataValues: this.barChartLabels});
+        this.tableData.reverse()
+        for( let v in Object.keys(this.tableData)){
+          this.displayedColumns.push(this.tableData[v]["headers"]);
+        }
+        this.tableData2 = this.array1;
+
+        this.dataSource.data = this.tableData2;
+        this.dataSource.paginator = this.paginator;
+        this.dataSource.sort = this.sort;
+
+        this.dataSourcePrint.data = this.tableData2;
+
+        this.showTable = true;
+        this.chart1 = new Chart('canvas', {
+          type: this.chart1Type,
+          options: {
+            title: {
+              display: true,
+            },
+            tooltips: {
+              mode: 'index',
+              callbacks: {
+                // Get the total of the energy produced in every point and show it in the hover tooltip
+                label: function (tooltipItem, data) { 
+                  var label = data.datasets[tooltipItem.datasetIndex].label || '';
+                  var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
+                  var total = 0;
+
+                  for (var i = 1; i < data.datasets.length; i++)
+                    total += +data.datasets[i].data[tooltipItem.index];
+
+                  if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
+                    return label + " : " + value;
+                  }
+                  else {
+                    return [label + " : " + value, "TOTAL[kWh] : " + Math.round(total)];
+                  }
                 }
-                else {
-                  return [label + " : " + value, "TOTAL : " + Math.round(total)];
+              }         
+            },
+            responsive: true,
+            maintainAspectRatio: false,
+            scales: {
+              xAxes: [{
+                stacked: true,
+                barPercentage: 0.7
+              }],
+              yAxes: [
+                {
+                  stacked: true,
+                  position: "left"
+                },
+                {
+                  display: true,
+                  stacked: true,
+                  position: 'right',
+                  id: 'y-axis-ahorro',
+                  gridLines: {
+                    drawOnChartArea: false
+                  }
                 }
-              }
-            }         
+              ]
+            }
           },
-          responsive: true,
-          maintainAspectRatio: false,
-          scales: {
-            xAxes: [{
-              stacked: true,
-              barPercentage: 0.7
-            }],
-            yAxes: [{
-              stacked: true
-            }]
+      
+          data: {
+            labels: this.barChartLabels,
+            datasets: this.metersData,
           }
-        },
-        data: {
-          labels: this.barChartLabels,
-          datasets: this.metersData,
-        }, 
+        });
+        this.chartjs = true;
       });
-      this.chartjs = true;
     });
     setTimeout(()=>{
       Swal.close();
@@ -590,9 +637,21 @@ export class AssetsComponent implements OnInit {
             stacked: true,
             barPercentage: 0.7
           }],
-          yAxes: [{
-            stacked: true
-          }]
+          yAxes: [
+            {
+              stacked: true,
+              position: "left"
+            },
+            {
+              display: true,
+              stacked: false,
+              position: 'right',
+              id: 'y-axis-ahorro',
+              gridLines: {
+                drawOnChartArea: false
+              }
+            }
+          ]
         }
       },
       data: {

+ 1 - 3
src/app/components/confirm-account/confirm-account.component.ts

@@ -75,10 +75,9 @@ export class ConfirmAccountComponent implements OnInit {
 
     // stop here if form is invalid
     if (this.activateForm.invalid) {
-      console.log(this.activateForm);
       return;
     }
-    /*
+
     this.userService.activateUser(
       {
         email: this.f.email.value,
@@ -96,7 +95,6 @@ export class ConfirmAccountComponent implements OnInit {
       this.validToken = false;
       this.errorMessage = err.message;
     });
-    */
     //
 
   }

+ 1 - 1
src/app/components/dashboard/dashboard.component.ts

@@ -116,7 +116,7 @@ export class DashboardComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
   

+ 1 - 1
src/app/components/login/login.component.ts

@@ -61,7 +61,7 @@ export class LoginComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
   }

+ 1 - 1
src/app/components/organizations/edit-organization/edit-organization.component.ts

@@ -42,7 +42,7 @@ export class EditOrganizationComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 

+ 1 - 1
src/app/components/organizations/organization/organization.component.ts

@@ -42,7 +42,7 @@ export class OrganizationComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 

+ 1 - 1
src/app/components/organizations/organizations.component.ts

@@ -56,7 +56,7 @@ export class OrganizationsComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
     

+ 1 - 1
src/app/components/plants/edit-plant/edit-plant.component.ts

@@ -44,7 +44,7 @@ export class EditPlantComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 

+ 1 - 3
src/app/components/plants/new-plant/new-plant.component.ts

@@ -38,9 +38,7 @@ export class NewPlantComponent implements OnInit {
     if (this.assetForm.invalid) {
       return;
     }
-    
-    console.log(this.f);
-    
+     
     this.plantService.createPlant(
       {
         name: this.f.name.value,

+ 79 - 0
src/app/components/plants/plant/plant.component.html

@@ -0,0 +1,79 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item"><a [routerLink]="['/plants']">Plantas</a></li>
+              <li class="breadcrumb-item">Detalle de planta</li>
+            </ol>
+          </nav>
+        </div>
+      </div>
+    </div>
+    <br>
+    <div class="row justify-content-center">
+      <div class="col-8">
+        <div class="align-container">
+
+          <div class="card" *ngIf="assetExists">
+            <div class="card-header card-header-icon card-header-rose">
+              <div class="card-icon"><i class="material-icons">map</i></div>
+              <h4 class="card-title">Detalle de planta</h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="assetForm">
+
+                  <div class="form-group">
+                    <label for="name">Nombre de la planta: </label>
+                    <input type="text" readonly formControlName="name" class="form-control" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="country">País: </label>
+                    <input type="text" readonly formControlName="country" class="form-control" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="city">Ciudad: </label>
+                    <input type="text" readonly formControlName="city" class="form-control" />
+                  </div>      
+                  
+                  <div class="form-group">
+                    <label for="address">Dirección: </label>
+                    <input type="text" readonly formControlName="address" class="form-control" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="distribuidora">Distribuidora: </label>
+                    <select class="custom-select" readonly formControlName="distribuidora" >
+                      <option *ngFor="let item of distributor" [value]="item" [selected]="item==listPlant.distribuidora" >{{item}}</option>
+                    </select>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="categoria_tarifaria">Categoría tarifaria: </label>
+                    <input type="text" readonly formControlName="categoria_tarifaria" class="form-control" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="cod_tarifa">Código de tarifa: </label>
+                    <input type="text" readonly formControlName="cod_tarifa" class="form-control" />
+                  </div>
+
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</div>

+ 0 - 0
src/app/components/plants/plant/plant.component.scss


+ 25 - 0
src/app/components/plants/plant/plant.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { OrganizationComponent } from './organization.component';
+
+describe('OrganizationComponent', () => {
+  let component: OrganizationComponent;
+  let fixture: ComponentFixture<OrganizationComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ OrganizationComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(OrganizationComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 57 - 0
src/app/components/plants/plant/plant.component.ts

@@ -0,0 +1,57 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { PlantsService } from '@app/services/plants.service';
+import { ActivatedRoute } from '@angular/router';
+import Swal from 'sweetalert2';
+
+
+@Component({
+  selector: 'app-plant',
+  templateUrl: './plant.component.html',
+  styleUrls: ['./plant.component.scss']
+})
+export class PlantComponent implements OnInit {
+  [x: string]: any;
+  title:string = "Detalle de la planta"; 
+  listPlant: any;
+  organizationExists:boolean;
+  assetForm: FormGroup;
+  assetID:any;
+  role_number: any;
+  assetExists:boolean;
+  distributor = ['CAESS', 'DEL SUR', 'AES-CLESA', 'EEO', 'DEUSEM'];
+
+  constructor(private plantsService: PlantsService, private formBuilder: FormBuilder, private route: ActivatedRoute) {
+    console.log("GET HERE")
+    this.route.params.subscribe(params => {
+      this.assetID = params['id'];
+    });
+
+    this.plantsService.getAssetById(this.assetID).subscribe(res => {
+      this.listPlant = res["data"]["asset"];
+      this.organizationExists = true;
+
+      this.assetForm = this.formBuilder.group({
+        name: [this.listPlant.name],
+        country: [this.listPlant.country],
+        city: [this.listPlant.city],
+        address: [this.listPlant.address],
+        distribuidora: [this.listPlant.distribuidora],
+        categoria_tarifaria: [this.listPlant.categoria_tarifaria],
+        cod_tarifa: [this.listPlant.cod_tarifa],
+      });
+      this.assetExists = true;
+    }, (err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: err.message
+      });
+    });
+
+  }
+
+  ngOnInit() { 
+  }
+
+}

+ 4 - 0
src/app/components/plants/plants.component.html

@@ -53,9 +53,13 @@
                     <td mat-cell *matCellDef="let row">
 
                       <div class="action-buttons">
+                        <a class="btn btn-dark btn-sm" [routerLink]="['/plant', row.id]" *ngIf="allowedUser()" >
+                          Ver detalles
+                        </a>
                         <a class="btn btn-primary btn-sm" [routerLink]="['/plant', row.id, 'edit']" *ngIf="allowedUser()" >
                           Editar
                         </a>
+                        
                         <!--
                         <a class="btn btn-danger btn-sm" (click)="delete_organization(row.id)" *ngIf="allowedUser()" >
                           Eliminar

+ 1 - 1
src/app/components/plants/plants.component.ts

@@ -65,7 +65,7 @@ export class PlantsComponent implements OnInit {
 
   allowedUser(){
     let is_allowed: boolean;
-    if(+this.authService.getUserLevel() < 3){
+    if(+this.authService.getUserLevel() < 2){
       is_allowed = false;
     }
     else {

+ 0 - 7
src/app/components/plugins/validator/validator.component.ts

@@ -21,8 +21,6 @@ export function ValidatorComponent(controlName: string, matchingControlName: str
     }
 }
 
-
-
 export const PasswordStrengthValidator = function (control: AbstractControl): ValidationErrors | null {
 
   let value: string = control.value || '';
@@ -35,7 +33,6 @@ export const PasswordStrengthValidator = function (control: AbstractControl): Va
     return null
   }
 
-
   if (upperCaseCharacters.test(value) === false) {
     return { passwordStrength: `La contraseña debe contener al menos una letra Mayúscula\n` };
   }
@@ -54,8 +51,4 @@ export const PasswordStrengthValidator = function (control: AbstractControl): Va
 
   return null;
 
-  
-  
-
-
 }

+ 5 - 2
src/app/components/plugins/weather-card/weather-card.component.scss

@@ -5,8 +5,9 @@ div.widget
   //margin: 150px auto;
   margin-top: 0;
   background-color: #fff;
-  border-radius: 9px;
+  border-radius: 5px;
   padding: 15px 20px;
+  height: 155px;
 }
 
 div.date
@@ -37,7 +38,9 @@ div.temp
   color: rgba(0,0,0,0.9);
   font-weight: 400;
   display: inline-block;
-
+  text-align: right;
+  width: 100%;
+  
   span,
   img {
     display: inline-block;

+ 34 - 13
src/app/components/plugins/weather-card/weather-card.component.ts

@@ -1,4 +1,4 @@
-import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
+import {Component, EventEmitter, Input, OnDestroy, OnInit, OnChanges, Output} from '@angular/core';
 import {Router} from '@angular/router';
 import {DatePipe} from '@angular/common';
 
@@ -12,7 +12,8 @@ import {first} from 'rxjs/operators';
   styleUrls: ['./weather-card.component.scss']
 })
 
-export class WeatherCardComponent implements OnInit {
+export class WeatherCardComponent implements OnInit, OnChanges {
+  @Input() city: string;
   listData: any;
   citesWeather: Object;
   darkMode: boolean;
@@ -35,17 +36,37 @@ export class WeatherCardComponent implements OnInit {
   }
 
   ngOnInit() {
-    this.weather.getWeather("San%20Salvador")
-      .pipe(first())
-      .subscribe((payload) => {
-        this.cityName = payload.name
-        this.state = payload.weather[0].description;
-        this.temp = Math.ceil(payload.main.temp);
-        this.icon_exists = true;
-        this.icon = payload.weather[0].icon;
-      }, (err) => {
-        this.errorMessage = err.error.message;
-      });
+    if (this.city == undefined){
+      this.city = "San%20Salvador,+SV";
+    }
+    else {
+      this.city = this.city + ",+SV";
+    }
+    this.getWeather(this.city);
+  }
+
+  ngOnChanges() {
+    if (this.city == undefined){
+      this.city = "San%20Salvador,+SV";
+    }
+    else {
+      this.city = this.city + ",+SV";
+    }
+    this.getWeather(this.city);
+  }
+
+  getWeather(city:string){
+    this.weather.getWeather(city)
+    .pipe(first())
+    .subscribe((payload) => {
+      this.cityName = payload.name
+      this.state = payload.weather[0].description;
+      this.temp = Math.ceil(payload.main.temp);
+      this.icon_exists = true;
+      this.icon = payload.weather[0].icon;
+    }, (err) => {
+      this.errorMessage = err.error.message;
+    });
 
   }
 

+ 1 - 1
src/app/components/users/new-user/new-user.component.ts

@@ -63,7 +63,7 @@ export class NewUserComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 

+ 1 - 1
src/app/components/users/users.component.ts

@@ -54,7 +54,7 @@ export class UsersComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informaci&oacute;n"
+        text: err.message
       });
     });
 

+ 2 - 0
src/app/layouts/admin/admin.module.ts

@@ -19,6 +19,7 @@ import { EditOrganizationComponent } from '../../components/organizations/edit-o
 import { PlantsComponent } from '@app/components/plants/plants.component';
 import { EditPlantComponent } from '@app/components/plants/edit-plant/edit-plant.component';
 import { NewPlantComponent } from '@app/components/plants/new-plant/new-plant.component';
+import { PlantComponent } from '@app/components/plants/plant/plant.component';
 import { UsersComponent } from '@app/components/users/users.component';
 import { NewUserComponent } from '@app/components/users/new-user/new-user.component';
 
@@ -81,6 +82,7 @@ import { TermsComponent } from '@app/components/terms/terms.component';
     PlantsComponent,
     EditPlantComponent,
     NewPlantComponent,
+    PlantComponent,
     UsersComponent,
     NewUserComponent,
     TermsComponent

+ 10 - 0
src/app/layouts/admin/admin.routing.ts

@@ -11,6 +11,7 @@ import { NewOrganizationComponent } from '@app/components/organizations/new-orga
 import { EditOrganizationComponent } from '@app/components/organizations/edit-organization/edit-organization.component';
 import { EditPlantComponent } from '@app/components/plants/edit-plant/edit-plant.component';
 import { NewPlantComponent } from '@app/components/plants/new-plant/new-plant.component';
+import { PlantComponent } from '@app/components/plants/plant/plant.component';
 import { UsersComponent } from '@app/components/users/users.component';
 import { NewUserComponent } from '@app/components/users/new-user/new-user.component';
 import { AuthGuard } from '@app/services/auth.guard';
@@ -53,6 +54,15 @@ export const AdminLayoutRoutes: Routes = [
       roles: [3]
     },
   },
+  { path: 'plant/:id', 
+  component: PlantComponent,
+  canActivate: [AuthGuard], 
+  data: {
+    title: "Detalle de planta",
+    breadcrumb: "Detalle de planta",
+    roles: [2,3]
+  },
+},
   { path: 'plant/:id/edit', 
     component: EditPlantComponent,
     canActivate: [AuthGuard], 

+ 19 - 9
src/app/services/auth2.service.ts

@@ -1,6 +1,6 @@
 import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
-import { of, Observable, forkJoin } from 'rxjs';
+import { of, Observable, forkJoin, throwError } from 'rxjs';
 import { catchError, mapTo, tap, map, mergeMap } from 'rxjs/operators';
 import { Token } from '@app/models/token';
 import { environment } from '@environments/environment';
@@ -25,9 +25,8 @@ export class AuthService {
       .pipe(
         tap(tokens => this.doLoginUser(user.email, tokens)),
         mapTo(true),
-        catchError(error => {
-          return of(false);
-        }));
+        catchError(this.errorHandl)
+      );
   }
 
 
@@ -58,10 +57,8 @@ export class AuthService {
       'Authorization': `Bearer ${refreshToken}`
     }).pipe(tap((tokens: Token) => {
       this.storeJwtToken(tokens["data"]["access_token"]);
-    },
-      (error) => {
-        console.log("ERROR REFRESHING TOKEN")
-      }
+      },
+      catchError(this.errorHandl)
     ));
   }
 
@@ -89,7 +86,6 @@ export class AuthService {
 
   private storeTokens(tokens: Token) {
     localStorage.clear();
-    //console.log(CryptoJS.AES.encrypt(this.JWT_TOKEN, tokens["data"]["user"].role.toString(), 'soma-inverlec-2019').toString());
     localStorage.setItem(this.USER_MENU, CryptoJS.AES.encrypt(tokens["data"]["user"].role.toString(), 'soma-inverlec-2019').toString())
     localStorage.setItem(this.JWT_TOKEN, tokens["data"]["user"].token); 
     localStorage.setItem(this.REFRESH_TOKEN, tokens["data"]["user"].refresh);
@@ -100,4 +96,18 @@ export class AuthService {
     localStorage.removeItem(this.JWT_TOKEN);
     localStorage.removeItem(this.REFRESH_TOKEN);
   }
+
+
+  errorHandl(error) {
+    let errorMessage = '';
+    if(error.error) {
+      // Get client-side error
+      errorMessage = error.error;
+    } else {
+      // Get server-side error
+      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;      
+    }
+    return throwError(errorMessage);
+  }
+
 }

+ 14 - 1
src/app/services/logs.service.ts

@@ -13,7 +13,7 @@ import { environment } from '@environments/environment';
 })
 export class LogsService {
 
-  time:number = 10000;
+  time:number = 6000;
 
   constructor(private http: HttpClient) {
   }
@@ -29,6 +29,19 @@ export class LogsService {
     )
   }
 
+
+  getEnergySavingsProducedByAsset(id:string, interval:string, date:string) {
+    return this.http.get(`${environment.productionApiUrl}/asset/${id}/historical-savings/${interval}/${date}`)
+    .pipe(
+      timeout(this.time),
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+  
+
   getEnergySummaryByAsset(id:string){
     return this.http.get(`${environment.productionApiUrl}/asset/${id}/energy-stats`)
     .pipe(

+ 12 - 7
src/app/services/plants.service.ts

@@ -1,13 +1,10 @@
 import { Injectable } from '@angular/core';
 import { Organization } from '../models/organization';
 import { PlantData, Plant } from '../models/plant';
-
 import { of as observableOf, Observable, throwError } from 'rxjs';
 import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { retry, catchError, map, timeout } from 'rxjs/operators';
-
 import { environment } from '@environments/environment';
-
 import { plantsData } from '../data/plants.data';
 
 @Injectable({
@@ -80,6 +77,16 @@ export class PlantsService extends PlantData {
     )
   }
 
+  getAssetSavings(id:string){
+    return this.http.get<any>(`${environment.productionApiUrl}/asset/${id}/savings`)
+    .pipe(
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+
   getPlants() {
     return observableOf(this.listData);
   }
@@ -89,7 +96,7 @@ export class PlantsService extends PlantData {
   }
 
   getOrganizationPlants(id:string)  {
-    return this.http.get<any>(`${environment.apiUrl}/assets/${id}`)
+    return this.http.get<any>(`${environment.productionApiUrl}/assets/${id}`)
     .pipe(
       timeout(this.time),
       map(response =>{
@@ -123,8 +130,6 @@ export class PlantsService extends PlantData {
       )  
   }
   
-  
-
   errorHandl(error) {
     let errorMessage = '';
     if(error.error) {
@@ -135,6 +140,6 @@ export class PlantsService extends PlantData {
       errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;      
     }
     return throwError(errorMessage);
- }
+  }
 
 }

+ 0 - 2
src/app/services/token.interceptor.ts

@@ -62,9 +62,7 @@ export class TokenInterceptor implements HttpInterceptor {
 
   // Cuando el endpoint devuelva 401 debe mandarlo al login y destruir las variables del storage
   private handle4xxError(request: HttpRequest<any>, next: HttpHandler) {
-    console.log("handle error 400");
     if (!this.isRefreshing) {
-      console.log("inside ")
       this.isRefreshing = true;
       this.refreshTokenSubject.next(null);
 

+ 5 - 1
src/assets/scss/material-dashboard.scss

@@ -104,6 +104,9 @@
 .floating-title {
   font-size: 1.2rem;
   margin: 21px 65px 10px;
+  @media screen and (max-width: 960px) {
+    margin: 21px 20px 10px;
+  }
 }
 
 .card-icon {
@@ -198,4 +201,5 @@ table {
   background: black;
   color: black;
   z-index: 1;
-}
+}
+