Forráskód Böngészése

Merge branch 'development' of onunez/soma-frontend into master

Oscar José Nuñez Chávez 6 éve
szülő
commit
f04074d204
53 módosított fájl, 1508 hozzáadás és 431 törlés
  1. 20 10
      package-lock.json
  2. 1 0
      package.json
  3. 1 0
      src/app/app.component.scss
  4. 3 1
      src/app/app.module.ts
  5. 96 61
      src/app/components/assets/assets.component.html
  6. 98 7
      src/app/components/assets/assets.component.scss
  7. 347 185
      src/app/components/assets/assets.component.ts
  8. 19 8
      src/app/components/confirm-account/confirm-account.component.html
  9. 7 0
      src/app/components/confirm-account/confirm-account.component.scss
  10. 5 5
      src/app/components/confirm-account/confirm-account.component.ts
  11. 1 1
      src/app/components/dashboard/dashboard.component.html
  12. 27 41
      src/app/components/dashboard/dashboard.component.ts
  13. 1 1
      src/app/components/login/login.component.ts
  14. 13 31
      src/app/components/organizations/edit-organization/edit-organization.component.ts
  15. 0 5
      src/app/components/organizations/new-organization/new-organization.component.ts
  16. 13 0
      src/app/components/organizations/organization/organization.component.html
  17. 3 0
      src/app/components/organizations/organization/organization.component.scss
  18. 70 1
      src/app/components/organizations/organization/organization.component.ts
  19. 1 1
      src/app/components/organizations/organizations.component.html
  20. 1 1
      src/app/components/organizations/organizations.component.ts
  21. 84 1
      src/app/components/plants/edit-plant/edit-plant.component.html
  22. 87 1
      src/app/components/plants/edit-plant/edit-plant.component.ts
  23. 49 7
      src/app/components/plants/new-plant/new-plant.component.html
  24. 73 1
      src/app/components/plants/new-plant/new-plant.component.ts
  25. 87 0
      src/app/components/plants/plant/plant.component.html
  26. 0 0
      src/app/components/plants/plant/plant.component.scss
  27. 25 0
      src/app/components/plants/plant/plant.component.spec.ts
  28. 74 0
      src/app/components/plants/plant/plant.component.ts
  29. 5 1
      src/app/components/plants/plants.component.html
  30. 1 1
      src/app/components/plants/plants.component.ts
  31. 38 5
      src/app/components/plugins/validator/validator.component.ts
  32. 10 1
      src/app/components/plugins/weather-card/weather-card.component.html
  33. 26 2
      src/app/components/plugins/weather-card/weather-card.component.scss
  34. 40 14
      src/app/components/plugins/weather-card/weather-card.component.ts
  35. 9 1
      src/app/components/shared/footer/footer.component.html
  36. 22 0
      src/app/components/terms/terms.component.html
  37. 4 0
      src/app/components/terms/terms.component.scss
  38. 25 0
      src/app/components/terms/terms.component.spec.ts
  39. 15 0
      src/app/components/terms/terms.component.ts
  40. 1 1
      src/app/components/users/new-user/new-user.component.ts
  41. 1 1
      src/app/components/users/users.component.html
  42. 1 1
      src/app/components/users/users.component.ts
  43. 3 2
      src/app/layouts/admin/admin.component.html
  44. 8 7
      src/app/layouts/admin/admin.module.ts
  45. 15 0
      src/app/layouts/admin/admin.routing.ts
  46. 1 1
      src/app/models/organization.ts
  47. 19 9
      src/app/services/auth2.service.ts
  48. 14 1
      src/app/services/logs.service.ts
  49. 36 9
      src/app/services/plants.service.ts
  50. 0 2
      src/app/services/token.interceptor.ts
  51. 6 2
      src/assets/scss/material-dashboard.scss
  52. 1 0
      src/environments/environment.ts
  53. 1 1
      src/index.html

+ 20 - 10
package-lock.json

@@ -180,6 +180,11 @@
         }
       }
     },
+    "@angular-material-extensions/password-strength": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@angular-material-extensions/password-strength/-/password-strength-4.0.1.tgz",
+      "integrity": "sha512-+UG/9sGKtHxlwAM2eTFQDI8uXkdqFMgfKfl+ZmGvU9JV8f3oP2loCo+mfGy+a07HNo0QnLNCgP2BHzOwdgxeyw=="
+    },
     "@angular/animations": {
       "version": "8.2.3",
       "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.3.tgz",
@@ -606,14 +611,12 @@
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "minipass": {
               "version": "2.3.5",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.2",
                 "yallist": "^3.0.0"
@@ -632,7 +635,6 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -7655,7 +7657,8 @@
             },
             "console-control-strings": {
               "version": "1.1.0",
-              "bundled": true
+              "bundled": true,
+              "optional": true
             },
             "core-util-is": {
               "version": "1.0.2",
@@ -7758,7 +7761,8 @@
             },
             "inherits": {
               "version": "2.0.3",
-              "bundled": true
+              "bundled": true,
+              "optional": true
             },
             "ini": {
               "version": "1.3.5",
@@ -7794,6 +7798,7 @@
             "minipass": {
               "version": "2.3.5",
               "bundled": true,
+              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.2",
                 "yallist": "^3.0.0"
@@ -7810,6 +7815,7 @@
             "mkdirp": {
               "version": "0.5.1",
               "bundled": true,
+              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -7893,6 +7899,7 @@
             "once": {
               "version": "1.4.0",
               "bundled": true,
+              "optional": true,
               "requires": {
                 "wrappy": "1"
               }
@@ -7968,7 +7975,8 @@
             },
             "safe-buffer": {
               "version": "5.1.2",
-              "bundled": true
+              "bundled": true,
+              "optional": true
             },
             "safer-buffer": {
               "version": "2.1.2",
@@ -7998,6 +8006,7 @@
             "string-width": {
               "version": "1.0.2",
               "bundled": true,
+              "optional": true,
               "requires": {
                 "code-point-at": "^1.0.0",
                 "is-fullwidth-code-point": "^1.0.0",
@@ -8054,11 +8063,13 @@
             },
             "wrappy": {
               "version": "1.0.2",
-              "bundled": true
+              "bundled": true,
+              "optional": true
             },
             "yallist": {
               "version": "3.0.3",
-              "bundled": true
+              "bundled": true,
+              "optional": true
             }
           }
         },
@@ -12158,7 +12169,6 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
   "private": true,
   "dependencies": {
     "@agm/core": "^1.0.0-beta.7",
+    "@angular-material-extensions/password-strength": "^4.0.1",
     "@angular/animations": "^8.2.3",
     "@angular/cdk": "^8.1.3",
     "@angular/common": "~8.2.0",

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

@@ -0,0 +1 @@
+

+ 3 - 1
src/app/app.module.ts

@@ -36,6 +36,7 @@ import { LoginComponent } from './components/login/login.component';
 
 import { TokenInterceptor } from '@app/services/token.interceptor';
 import { ConfirmAccountComponent } from './components/confirm-account/confirm-account.component';
+import { MatPasswordStrengthModule } from '@angular-material-extensions/password-strength';
 
 
 @NgModule({
@@ -59,7 +60,8 @@ import { ConfirmAccountComponent } from './components/confirm-account/confirm-ac
     PluginsModule,
     NgxDatatableModule,
     MatSelectModule,
-    AngularMyDatePickerModule
+    AngularMyDatePickerModule,
+    MatPasswordStrengthModule.forRoot(),
   ],
   providers: [
     { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },

+ 96 - 61
src/app/components/assets/assets.component.html

@@ -5,45 +5,66 @@
       <label for="sel3">Plantas</label>
     </div>
   </div>
-  <div class="row align-container" style="padding: 0 25px;">
-    <div class="col-lg-6 col-sm-4 p-1">
+  <div class="row align-container" >
+    <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">
-            <option *ngFor="let item of listAssets" [selected]="item.id===organizationId" [value]="item.id" >{{item.name}}</option>
+            <option *ngFor="let item of listAssets" [selected]="item.id===assetID" [value]="item.id" >{{item.name}}</option>
           </select>
         
         </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>
 
   </div>
 
+  <div class="row align-container" *ngIf="!metersInstalled">
+    <div class="col-12 p-1">
+      <br>
+      <div class="alert alert-danger" role="alert">
+        <h4 class="card-text">
+          <i class="fas fa-exclamation-circle"></i>
+          La planta no tiene medidores asignados
+        </h4>
+      </div>
+    </div>
+  </div>
+
   <!-- Basic stats cards -->
-  <div class="row align-container" style="padding: 0 25px;">
+  <div class="row align-container" *ngIf="metersInstalled">
 
     <div class="col-xl-3 col-lg-6 col-md-6 col-sm-6 p-1">
       <div class="widget energy-stats">
@@ -51,7 +72,7 @@
           <span class="sky-skin"><i class="fa fa-bolt"></i></span>
           <h3 *ngIf="eProduced">
             {{energyDay}}
-            <small>kW</small>
+            <small>kWh</small>
           </h3>
           <p>Ultimo dia</p>
         </div>
@@ -101,35 +122,66 @@
 
   <!-- Chartjs potency generation graphic -->
   <div class="row align-container">
-    <div class="col-lg-12 col-md-12 col-sm-12">
+    <div class="col-lg-12 col-md-12 col-sm-12 p-1">
       <div class="widget">
         <div class="mini-stats">
 
-          <p class="align-right">Seleccione el tipo de visualización</p>
-      
-          <div class=" action-buttons">
-            <button [ngClass]="[chartActive[0]==true ? 'btn  btn-success' : 'btn btn-dark']"  (click)="changeGraphicType('bar')"> 
-              <i class="fa fa-chart-bar"></i>
-              Barra
-            </button>
-            <button [ngClass]="[chartActive[1]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeGraphicType('line')"> 
-              <i class="fa fa-chart-line"></i>
-              Linea
-            </button>
-            <button [ngClass]="[chartActive[2]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeGraphicType('radar')"> 
-              <i class="fa fa-chart-area"></i>
-              Area
-            </button>
-            <button [ngClass]="[chartActive[3]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeToTable()"> 
-              <i class="fa fa-table"></i>
-              Tabla
-            </button>    
+          <div class="row" *ngIf="metersInstalled">
+            <div class="col-12">
+              <p class="align-right">Seleccione un rango y un tipo de visualización</p>
+                
+              <div class="visualization">
+                <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[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[5]' (click)="getMeasureRangeChart('3m')">3M</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">
+                    <div>
+                      <p>
+                        <i class="far fa-calendar" aria-hidden="true"></i>
+                      </p>
+                      <input class="input-box form-control" placeholder="Seleccione una fecha" 
+                        angular-mydatepicker name="mydate" (click)="dp.toggleCalendar()" 
+                        [(ngModel)]="model" [options]="myDpOptions" (dateChanged)="onDateChanged($event)" 
+                        #dp="angular-mydatepicker"/>
+                    </div>
+                  </div>
+                </div>
+                <div class=" action-buttons">
+                  <button title="Barra" [ngClass]="[chartActive[0]==true ? 'btn  btn-success' : 'btn btn-dark']"  (click)="changeGraphicType('bar')"> 
+                    <i class="fa fa-chart-bar"></i>
+                  </button>
+                  <button title="Línea" [ngClass]="[chartActive[1]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeGraphicType('line')"> 
+                    <i class="fa fa-chart-area"></i>
+                  </button>
+                  <!--
+                  <button title="Area" [ngClass]="[chartActive[2]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeGraphicType('radar')"> 
+                    <i class="fa fa-chart-area"></i>
+                  </button>-->
+                  <button title="Tabla" [ngClass]="[chartActive[3]==true ? 'btn  btn-success' : 'btn btn-dark']" (click)="changeToTable()"> 
+                    <i class="fa fa-table"></i>
+                  </button>
+                </div>
+              </div>
+            </div>
           </div>
 
           <div class="chart-container" id="chart-wrapper">
             <canvas id="canvas">{{ chart1 }}</canvas>
           </div>
-          
+          <div class="no-data">
+            <h2>
+              Información no disponible
+            </h2>
+          </div>
           <div id="toogleTable">
             <ng-container class="example-container mat-elevation-z8" *ngIf="showTable" >
               <div class="example-table-container">
@@ -152,10 +204,8 @@
                     IMPRIMIR
                   </button>
                 </div>
-                
 
                 <mat-table  matTableExporter #table [dataSource]="dataSource"  #exporter="matTableExporter" id="measuresTable">
-          
                   <div *ngFor="let disCol of displayedColumns; let colIndex = index" matColumnDef="{{disCol}}">
                     <mat-header-cell *matHeaderCellDef>{{disCol}}</mat-header-cell>
                     <mat-cell *matCellDef="let element"> {{element[colIndex]}}
@@ -167,21 +217,9 @@
                 </mat-table>
               </div>
             </ng-container>
-            <mat-paginator class="hiddenonload" [pageSizeOptions]="[5, 10, 25] " [length]="dataSource.data.length" [pageIndex]="0" [pageSize]="5"></mat-paginator>
+            <mat-paginator class="hiddenonload" [pageSizeOptions]="[5, 10, 25, dataSource.data.length] " [length]="dataSource.data.length" [pageIndex]="0" [pageSize]="10"></mat-paginator>
           </div>
 
-          <p>Seleccione un rango de visualización</p>
-
-          <button class="btn" [class.btn-success]='isActive[0]' (click)="onMeasureClickRange('day')">Día</button>
-          <button class="btn" [class.btn-success]='isActive[1]' (click)="onMeasureClickRange('week')">7 días</button>
-          <button class="btn" [class.btn-success]='isActive[2]' (click)="onMeasureClickRange('month')">30 días</button>
-          <button class="btn" [class.btn-success]='isActive[3]' (click)="onMeasureClickRange('year')">Año</button>
-          <div class="input-box-container">
-            <input class="input-box" placeholder="Click to select a date" 
-              angular-mydatepicker name="mydate" (click)="dp.toggleCalendar()" 
-              [(ngModel)]="model" [options]="myDpOptions" (dateChanged)="onDateChanged($event)" 
-              #dp="angular-mydatepicker"/>
-          </div>
         </div>
       </div>
 
@@ -189,7 +227,7 @@
   </div>
   
   <!-- Enviromental cool stats cards -->
-  <div class="row align-container">
+  <div class="row align-container" *ngIf="metersInstalled">
 
     <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6 p-1">
       <div class="widget">
@@ -199,7 +237,7 @@
           </div>
           <div class="enviromental-text">
             <span>{{environmentFuel}}</span>
-            Barriles de petroleo crudo producidos en 1 año.
+            Barriles de petróleo crudo producidos.
           </div>
         </div>
       </div>
@@ -237,7 +275,4 @@
 
   </div>
 
-  <br>
-  <br>
-
 </div>

+ 98 - 7
src/app/components/assets/assets.component.scss

@@ -1,16 +1,35 @@
+h4.card-text {
+  margin: 0;
+  color: #fff;
+  i {
+    color: inherit;
+  }
+}
+
 .input-box-container {
   display: inline-block;
-  margin-left: 15px;
+  margin: 0 15px;
   position: relative;
   border-collapse: separate;
+  max-width: 130px;
   
   input {
     margin: 5px 0 0;
     font-family: inherit;
-    font-size: inherit;
     line-height: inherit;
-    padding: 6px;
+    padding-left: 25px;
+    max-width: inherit;
+  }
+
+  p{
+    position: absolute;
+    margin-left: 5px;
+    height: 25px;
+    display: flex;
+    align-items: center;
+    left: 5px
   }
+
 }
 
 .savings-wrapper {
@@ -26,12 +45,23 @@
 }
 
 .action-buttons {
+  display: inline-block;
   margin-top: 10px;
   button {
-    margin-right: 7px;
+    margin-right: 5px;
+    padding: 8px 12px;
+  }
+  .btn-success {
+    background: #47a44b;
+    border-color: #39843c;
+    color: #fff;
   }
 }
 
+.visualization {
+  text-align: right;
+}
+
 table {
   width: 100%;
 }
@@ -76,6 +106,10 @@ table {
   height: 122px;
 }
 
+.environment-meters {
+  height: 100px;
+}
+
 .enviroment-meter {
   height: 60px;
 }
@@ -104,7 +138,7 @@ table {
       color: #878888;
       //display: block;
       font-size: 14px;
-      line-height: 35px;
+      line-height: 25px;
       margin: 6px 0 0;
       text-transform: uppercase;
       width: auto;
@@ -126,11 +160,56 @@ table {
       //display: block;
     }
 
+    span.savings-skin {
+      color: #47a44b;
+      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: #47a44b;
+        margin: 0;
+      }
+    }
+
+    .savings-last-24h {
+      font-size: 14px;
+      i {
+        font-size: 16px;
+        color: #47a44b;
+      }
+
+      span {
+        border: none;
+        color: #47a44b;
+        font-size: 20px;
+        line-height: 0;
+        text-align: center;
+        position: static;
+        display: inline;
+        font-weight: bold;
+      }
+    }
+
+
   }
 
   .enviroment-stats {
@@ -160,12 +239,14 @@ table {
         border: none;
         font-size: 1.2rem;
         font-weight: bold;
-        color: #386641;
+        color: #47a44b;
         vertical-align: middle;
       }
     }
 
   }
+
+
 }
 
 .btn {
@@ -176,12 +257,22 @@ table {
   display: block; 
   height:48vh; 
   @media screen and (max-width: 960px) {
-    height:38vh; 
+    height:350px; 
   }
   position: relative;
 }
 
 
+.no-data {
+  text-align: center;
+  z-index: 20;
+  position: absolute;
+  top: 50%;
+  left: 30%;
+  right: 30%;
+  visibility: hidden;
+}
+
 
 @media screen and (min-width: 960px) {
   .mini-stats {

+ 347 - 185
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';
@@ -17,6 +17,7 @@ import { formatDate, DatePipe } from '@angular/common';
 import {AngularMyDatePickerDirective,IAngularMyDpOptions, IMyDateModel} from 'angular-mydatepicker';
 import printJS from 'print-js'
 import { ExportType,ExcelOptions, Options } from 'mat-table-exporter';
+import * as $ from "jquery";
 
 @Component({
   selector: 'app-assets',
@@ -34,7 +35,7 @@ export class AssetsComponent implements OnInit {
   title = "Plantas";
 
   // General var declarations
-  organizationId:string;
+  assetID:string;
   listAssets:any;
   eProduced:any;
   error:boolean;
@@ -51,9 +52,10 @@ export class AssetsComponent implements OnInit {
   environmentCO2:any;
   environmentHouse:any;
   environmentFuel:any;
-  isActive:[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;
 
   // For daterange
   daysLabels:any = {su: 'Dom', mo: 'Lun', tu: 'Mar', we: 'Mie', th: 'Jue', fr: 'Vie', sa: 'Sab'};
@@ -78,7 +80,8 @@ export class AssetsComponent implements OnInit {
   array1: any[];
   array2: any[];
   array3: any[];
-  dataSource = new MatTableDataSource(this.tableData2);  
+  dataSource = new MatTableDataSource(this.tableData2);
+  dataSourcePrint = new MatTableDataSource(this.tableData2);
   @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
   @ViewChild(MatSort, { static: true }) sort: MatSort;
 
@@ -94,6 +97,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
@@ -107,7 +115,7 @@ export class AssetsComponent implements OnInit {
     public datepipe: DatePipe) {
       
       this.route.queryParams.subscribe(params => {
-        this.organizationId = params['id'];
+        this.assetID = params['id'];
       });
       
       Swal.fire({
@@ -134,7 +142,8 @@ export class AssetsComponent implements OnInit {
     }
 
     // Initialize default 'clicked' options for chart button options
-    this.isActive = [false, false, false, false];
+    this.isActive = [false, false, false, false, false, false, false, false, false];
+    
     this.chartActive = [true, false, false, false];
 
     // Get all assets (according to user's assigned organizations)
@@ -143,48 +152,54 @@ export class AssetsComponent implements OnInit {
       
       // Default values for chart 
       this.view = "month";
-      if (this.organizationId == undefined){
-        this.organizationId = this.listAssets[0].id;
-      }
       this.chart1Type = "bar";
+      
+      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);
 
-      // Initialize a draw chart according to the default values
-      this.onMeasureClickRange(this.view, this.initialDate);
-
-      // Api call to get the energy summary stats of all assets in the organization
-      let energy_produced = this.logsService.getEnergySummaryByAsset(this.organizationId).subscribe(resp => {
-        this.eProduced = resp["data"]["energy"];//results[1];
-        this.energyDay = this.eProduced.today.total_energy_kWh || 0;
-        this.energyWeek = this.eProduced.thisWeek.total_energy_kWh > 0 ? (this.eProduced.thisWeek.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisWeek.total_energy_kWh || 0
-        this.energyMonth = this.eProduced.thisMonth.total_energy_kWh > 0 ? (this.eProduced.thisMonth.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisMonth.total_energy_kWh || 0
-        this.energyYear = this.eProduced.lifeTime.total_energy_kWh > 0 ? (this.eProduced.lifeTime.total_energy_kWh/1000).toFixed(2) : this.eProduced.lifeTime.total_energy_kWh || 0
-      });
+      if (tempAsset["meters_installed"].length > 0){
+        this.metersInstalled = true;
 
-      this.logsService.getAssetEnviromentalStats(this.organizationId).subscribe(resp => {
-        this.environment = resp["data"]["environmentals"];
-        this.environmentCO2 = this.environment.avoided_kg_of_co2.toFixed(2); 
-        this.environmentHouse = this.environment.number_of_homes.toFixed(0);
-        this.environmentFuel = this.environment.number_of_crude_barrels.toFixed(2);
-      });
+        
+        // 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
+        this.getMeasureRangeChart(this.view, this.initialDate, this.assetID);
+        // Get environmental data given an asset
+        this.getEnvironmentalData(this.assetID);
+      }
+      else {
+        this.metersInstalled = false;
+      }
 
 
     }, (err) => {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 
     //this.chartjs = true;
     setTimeout(()=>{
       Swal.close();
-    }, 2700);
+    }, 3000);
   }
- 
+  
   // Default check asset dropdown if a value is equal to an assetID
   isSelected(){
-    if(this.organizationId!=undefined){
+    if(this.assetID!=undefined){
       return true;
     }
     else{
@@ -200,36 +215,88 @@ export class AssetsComponent implements OnInit {
 
   // Trigger again the chart with the selected assetID
   onChangeObj(event:any) {
-    this.organizationId = event.target.value;
-    this.onMeasureClickRange(this.view, this.initialDate, this.organizationId);
+    this.assetID = event.target.value;
+    let tempAsset = this.listAssets.find(object=>object.id == this.assetID);
+    this.city = tempAsset["city"];
+    this.total_savings = 0;
+    this.last_day_savings = 0;
+    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);
+    }
+    else { 
+
+      Swal.fire({
+        allowOutsideClick: false,
+        type: 'info',
+        text: 'Espere por favor...'
+      });
+      Swal.showLoading();
+      setTimeout(()=>{
+        Swal.close();
+        this.metersInstalled = false;
+        if (this.chart1 != undefined ){
+          this.chart1.destroy();
+        }
+        this.chart1 = undefined;
+        this.chart1 = new Chart('canvas', {});
+    
+      }, 3000);
+  
+    }
   }
   
   // Change the date range of the chart, according to the selected view (daily, weekly, ...)
   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.onMeasureClickRange(this.view, endDate);
-        break; 
-      case "week": 
-        this.onMeasureClickRange(this.view, endDate);
-        break; 
-      case "month": 
-        this.onMeasureClickRange(this.view, endDate);
-        break; 
-      case "year":
-        this.onMeasureClickRange(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 => {
+      this.environment = resp["data"]["environmentals"];
+      this.environmentCO2 = this.environment.avoided_kg_of_co2.toFixed(2); 
+      this.environmentHouse = this.environment.number_of_homes.toFixed(0);
+      this.environmentFuel = this.environment.number_of_crude_barrels.toFixed(2);
+    });
+  }
+
+  getEnergyProduced(assetID:string){
+    // Api call to get the energy summary stats of all assets in the organization
+    let energy_produced = this.logsService.getEnergySummaryByAsset(assetID).subscribe(resp => {
+      this.eProduced = resp["data"]["energy"];//results[1];
+      this.energyDay = this.eProduced.today.total_energy_kWh || 0;
+      this.energyWeek = this.eProduced.thisWeek.total_energy_kWh > 0 ? (this.eProduced.thisWeek.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisWeek.total_energy_kWh || 0
+      this.energyMonth = this.eProduced.thisMonth.total_energy_kWh > 0 ? (this.eProduced.thisMonth.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisMonth.total_energy_kWh || 0
+      this.energyYear = this.eProduced.lifeTime.total_energy_kWh > 0 ? (this.eProduced.lifeTime.total_energy_kWh/1000).toFixed(2) : this.eProduced.lifeTime.total_energy_kWh || 0
+    }, (err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
   }
 
   // Draw a measure chart using chartjs, given some params, view, for daily, weekly, monthly or yearly measures;
   // measureDate, for a date range given a view and a paramID, for an specific assetID selected in the dropdown  
-  onMeasureClickRange(view:string, measureDate?:string, paramId?:string): void {
+  getMeasureRangeChart(view:string, measureDate?:string, paramId?:string): void {
     if(this.initialLoad == false){
       Swal.fire({
         allowOutsideClick: false,
@@ -268,171 +335,237 @@ export class AssetsComponent implements OnInit {
     let interval:string;
     switch (view)
     { 
+
       case "day": 
         interval = "1D";
-        this.isActive = [true, 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];
+        this.isActive = [false, true, false, false, false, false, false, false, false];
         break; 
+      case "WTD":
+        interval = "WTD"
+        this.isActive = [false, false, true, false, false, false, false, false, false];
+        break;
       case "month":
         interval = "1M";
-        this.isActive = [false, false, true, false];
+        this.isActive = [false, false, false, true, false, false, false, false, false];
         break; 
-      case "mtd": //TO DO
+      case "MTD":
         interval = "MTD";
+        this.isActive = [false, false, false, false, true, false, false, false, false];
         break;
-      case "year":
+      case "3m":
+        interval = "3M";
+        this.isActive = [false, false, false, false, false, true, false, false, false];
+        break;
+      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, true];
+        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, 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 = [];
+      
+      // Get the measures according to the meters, given the params required
+      this.logsService.getEnergyProducedByParams(assetId,interval,dateRange).toPromise()
+      .then((data: any) => {
 
-      // 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())
+        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);
-          }
-          
-          if (mvalues[prop2] != undefined) { 
-            this.array3.push(mvalues[prop2]["total_energy_kWh"]);
-          }
-          else {
-            this.array3.push(0)
-          }
+        // 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});
+        }
 
-          if (+prop == 0){
-            this.array2.push(this.array3)
-          }
+        // 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())
+          
+          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(); 
-      }
+        // 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.dateMin, '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.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;
+        if (this.array1.length == 0){
+          $(".no-data").css('visibility', 'visible');
+          $(".chart-container").css('visibility', 'hidden');
+        }
+        else {
+          $(".no-data").css('visibility', 'hidden');
+          $(".chart-container").css('visibility', 'visible');
+        }
+
+        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",
+                  scaleLabel: {
+                    display: true,
+                    labelString: 'Kilowatts hora [kWh]'
+                  },
+                },
+                {
+                  display: true,
+                  stacked: true,
+                  position: 'right',
+                  id: 'y-axis-ahorro',
+                  scaleLabel: {
+                    display: true,
+                    labelString: 'Ahorro [US$]'
+                  },
+                  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();
-    }, 1500)
+    }, 1800)
   }
 
   getDateWithFormat(view:string, date:string){
@@ -466,6 +599,7 @@ export class AssetsComponent implements OnInit {
   printTable(){
     printJS(
       { printable: 'measuresTable', type: 'html', 
+        //properties: this.displayedColumns.join(","),
         header: '<h3 class="report-header">Datos de la planta </h3>',
         documentTitle: 'DENMARK - Informacion generada',
         style: '.report-header{ color: #075D9D; font-size: 24px; }' }
@@ -484,19 +618,27 @@ export class AssetsComponent implements OnInit {
     { 
       case "bar": 
         this.chartActive = [true, false, false, false];
+        if(this.metersData.find(v => (v.type == "radar"))){
+          this.metersData.find(v => (v.type != "line")).type = "line";
+        }
+
         break; 
       case "line": 
         this.chartActive = [false, true, false, false];
+        if(this.metersData.find(v => (v.type == "radar"))){
+          this.metersData.find(v => (v.type != "line")).type = "line";
+        }
         break; 
       case "radar":
         this.chartActive = [false, false, true, false];
+        this.metersData.find(v => v.type == "line").type = "radar";
         break; 
       default: 
     }  
 
     this.chart1Type = chartType;
 
-    if (this.chart1 != undefined){
+    if (this.chart1 != undefined || this.chart1 == undefined){
       this.chart1.destroy();
     }
     this.chart1 = undefined;
@@ -535,9 +677,29 @@ export class AssetsComponent implements OnInit {
             stacked: true,
             barPercentage: 0.7
           }],
-          yAxes: [{
-            stacked: true
-          }]
+          yAxes: [
+            {
+              stacked: true,
+              position: "left",
+              scaleLabel: {
+                display: true,
+                labelString: 'Kilowatts hora [kWh]'
+              }
+            },
+            {
+              display: true,
+              stacked: false,
+              position: 'right',
+              id: 'y-axis-ahorro',
+              scaleLabel: {
+                display: true,
+                labelString: 'Ahorro [US$]'
+              },
+              gridLines: {
+                drawOnChartArea: false
+              }
+            }
+          ]
         }
       },
       data: {

+ 19 - 8
src/app/components/confirm-account/confirm-account.component.html

@@ -27,23 +27,34 @@
                   <div *ngIf="f.first_name.errors.required">Campo requerido</div>
                 </div>
               </div>
-
               
               <div class="form-group">
-                  <label for="name">Apellido</label>
-                  <input type="text" class="form-control"
-                  formControlName="last_name" [ngClass]="{ 'is-invalid': submitted && f.last_name.errors }"/>
-                  <div *ngIf="submitted && f.last_name.errors" class="invalid-feedback">
-                    <div *ngIf="f.last_name.errors.required">Campo requerido</div>
-                  </div>
+                <label for="name">Apellido</label>
+                <input type="text" class="form-control"
+                formControlName="last_name" [ngClass]="{ 'is-invalid': submitted && f.last_name.errors }"/>
+                <div *ngIf="submitted && f.last_name.errors" class="invalid-feedback">
+                  <div *ngIf="f.last_name.errors.required">Campo requerido</div>
+                  
                 </div>
+              </div>
 
               <div class="form-group">
+                <div class="hint-wrapper">
+                  <small class="hint">
+                    <i class="fas fa-exclamation-circle"></i>
+                    La contraseña debe contener letras mayusculas, minusculas, al menos un número y un símbolo.
+                  </small>
+                </div>
+                
                 <label for="password">Contraseña</label>
                 <input type="password" class="form-control"
                 formControlName="password" [ngClass]="{ 'is-invalid': submitted && f.password.errors }"/>
                 <div *ngIf="submitted && f.password.errors" class="invalid-feedback">
                   <div *ngIf="f.password.errors.required">Campo requerido</div>
+                  <div *ngIf="f.password.errors.minlength">La contraseña debe contener al menos 8 caracteres</div>
+                  <div *ngIf="f.password.hasError('passwordStrength')" style="white-space: pre;">
+                    {{f.password.errors['passwordStrength']}}
+                  </div>
                 </div>
               </div>
 
@@ -56,7 +67,7 @@
                   <div *ngIf="f.confirm_password.errors.mustMatch">Las contraseñas deben coincidir</div>
                 </div>
               </div>
-
+    
               <br>
 
               <div class="div-center">

+ 7 - 0
src/app/components/confirm-account/confirm-account.component.scss

@@ -1,3 +1,10 @@
+.hint-wrapper {
+  background: #eee;
+  border: 1px solid #aaa;
+  padding: 5px;
+  margin-bottom: 5px;
+}
+
 .vertical-align-wrap {
   //position: absolute;
   width: 100%;

+ 5 - 5
src/app/components/confirm-account/confirm-account.component.ts

@@ -1,10 +1,9 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewChild } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { FormGroup, FormBuilder, Validators } from '@angular/forms';
 import { UserService } from '@app/services/user.service';
 import Swal from 'sweetalert2';
-import { ValidatorComponent } from '../plugins/validator/validator.component';
-
+import { ValidatorComponent, PasswordStrengthValidator } from '../plugins/validator/validator.component';
 
 
 @Component({
@@ -22,6 +21,7 @@ export class ConfirmAccountComponent implements OnInit {
   activateMessage: string;
   invalidToken: boolean;
   submitted: boolean = false;
+  showDetails: boolean;
 
   constructor(private formBuilder: FormBuilder, private route: ActivatedRoute, private userService: UserService) {
     this.route.queryParams.subscribe(params => {
@@ -46,9 +46,10 @@ export class ConfirmAccountComponent implements OnInit {
           email: [(userData.email)],
           first_name: [(userData.first_name),Validators.required],
           last_name: [(userData.last_name),Validators.required],
-          password: ['',[Validators.required, Validators.minLength(6)]],
+          password: ['',[Validators.required, Validators.minLength(8), PasswordStrengthValidator]],
           confirm_password: ['',Validators.required]
         },{
+          
             validator: ValidatorComponent('password', 'confirm_password')
         });
       }, (err) => {
@@ -94,7 +95,6 @@ export class ConfirmAccountComponent implements OnInit {
       this.validToken = false;
       this.errorMessage = err.message;
     });
-
     //
 
   }

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

@@ -118,7 +118,7 @@
               <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
               <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
             </table>
-            <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+            <mat-paginator [pageSizeOptions]="[5, 10, 20]" [pageIndex]="0" [pageSize]="5"></mat-paginator>
           </div>
         </div>
       </div>

+ 27 - 41
src/app/components/dashboard/dashboard.component.ts

@@ -100,21 +100,23 @@ export class DashboardComponent implements OnInit {
       this.dataSource.data = this.listAssets;
       this.dataSource.paginator = this.paginator;
       this.dataSource.sort = this.sort;
-
       this.assetKeys = Object.keys(this.listAssets);
       for (let prop in this.assetKeys){
         this.meterKeys2 = Object.keys(this.listAssets.map(item => item["meters_installed"])[prop]);
-        for (let prop2 in this.meterKeys2){
-          this.sumarize += this.listAssets.map(item => item["meters_installed"])[prop].map(response => response["installedCapacity_kW"])[prop2];
-          //this.totalMetersInstalled = this.sumarize.toString();
-          //localStorage.setItem("installedCapacityTotal_kW", this.totalMetersInstalled);
+        if (this.meterKeys2.length > 0){
+          for (let prop2 in this.meterKeys2){
+            this.sumarize += this.listAssets.map(item => item["meters_installed"])[prop].map(response => response["installedCapacity_kW"])[prop2];
+            //this.totalMetersInstalled = this.sumarize.toString();
+            //localStorage.setItem("installedCapacityTotal_kW", this.totalMetersInstalled);
+          }
+  
         }
       }      
     }, (err) => {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
   
@@ -137,6 +139,7 @@ export class DashboardComponent implements OnInit {
     setTimeout(() => {
        if (this.listAssets != undefined) {
         this.addMarkers();
+        this.sendPlantId(this.listAssets[0].id);
       }
       Swal.close();
     }, 1800);
@@ -152,51 +155,34 @@ export class DashboardComponent implements OnInit {
     let lat, long, address, name2;
 
     for (const plant of this.listAssets) {
-      if (localStorage.getItem("email") == "inverlec@grupomerelec.com") {
-        lat = "13.6613819";
-        long = "-89.2514334";
-        address = "Urbanización Madre Selva Calle Llama del Bosque, Edificio Avante, Local 4-5/4-6, Antiguo Cuscatlán";
-        name2 = "Inversiones MERELEC S.A de C.V";
-      }
-      else {
+      if (plant["meters_installed"].length > 0){
         lat = plant["meters_installed"][0].gpsLat;
         long = plant["meters_installed"][0].gpsLong;
         address = plant.address;
         name2 = plant.name;
-      }
-
-      const newMarker = marker(
-        [lat, long],
-        { icon: this.icon })
-        .bindPopup('<b>' + name2 + '</b><br>Dirección: ' + address)
-        .on('click', () => {
-          this.zone.run(() => {
-            this.sendPlantId(plant.id);
+      
+        const newMarker = marker(
+          [lat, long],
+          { icon: this.icon })
+          .bindPopup('<b>' + name2 + '</b><br>Dirección: ' + address)
+          .on('click', () => {
+            this.zone.run(() => {
+              this.sendPlantId(plant.id);
+            });
           });
-        });
-      this.points.push(latLng([lat, long]));
-      this.markers.push(newMarker);
+        this.points.push(latLng([lat, long]));
+        this.markers.push(newMarker);
+      }
     }
   }
 
   sendPlantId(id: string) {
     this.plantId = id;
-    if (localStorage.getItem("email") == "inverlec@grupomerelec.com") {
-      this.selectedPlant = {
-        name: "Inversiones MERELEC S.A de C.V",
-        country: "El Salvador",
-        city: "La Libertad",
-        address: "Urbanización Madre Selva Calle Llama del Bosque, Edificio Avante, Local 4-5/4-6, Antiguo Cuscatlán",
-        installedCapacity_kW: 300
-      }
-    }
-    else {
-      
-      this.selectedPlant = this.listAssets.find(e => e.id === this.plantId);
-      let keys = Object.keys(this.selectedPlant.meters_installed.map(item => item["meters_installed"]));
-      for (let prop2 in keys){
-        this.totalInstalled += this.selectedPlant.meters_installed.map(response => response["installedCapacity_kW"])[prop2];
-      }
+    this.totalInstalled = 0;
+    this.selectedPlant = this.listAssets.find(e => e.id === this.plantId);
+    let keys = Object.keys(this.selectedPlant.meters_installed.map(item => item["meters_installed"]));
+    for (let prop2 in keys){
+      this.totalInstalled += this.selectedPlant.meters_installed.map(response => response["installedCapacity_kW"])[prop2];
     }
   }
 

+ 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
       });
     });
   }

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

@@ -42,48 +42,30 @@ export class EditOrganizationComponent implements OnInit {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
-        text: "No su pudo obtener la informacion"
+        text: err.message
       });
     });
 
   }
 
   ngOnInit() {
-    /*
-    this.organizationForm = this.formBuilder.group({
-      name: [this.listOrganization.name],
-      address: [this.listOrganization.address],
-      city: [this.listOrganization.city],
-      country: [this.listOrganization.country],
-      contactName: [this.listOrganization.contactName],
-      contactNumber: [this.listOrganization.contactNumber],
-    });
-
-    this.organizationForm = this.formBuilder.group({
-      name: [""], //this.listOrganization.name
-      address: [""], //this.listOrganization.address
-      city: [""], //this.listOrganization.city
-      country: [""], //this.listOrganization.country
-      contactName: [""], //this.listOrganization.contactName
-      contactNumber: [""], //this.listOrganization.contactNumber
-    });
-    */
+  
   }
 
   get f() { return this.organizationForm.controls; }
 
   editOrganization() {
-      this.orgService.updateOrganization(this.organizationId, 
-        {
-          name: this.f.name.value,
-          address: this.f.address.value,
-          city: this.f.city.value,
-          country: this.f.country.value,
-          contactName: this.f.contactName.value,
-          contactNumber: this.f.contactNumber.value,
-          assets: [""],
-        }
-      )
+    this.orgService.updateOrganization(this.organizationId, 
+      {
+        name: this.f.name.value,
+        address: this.f.address.value,
+        city: this.f.city.value,
+        country: this.f.country.value,
+        contactName: this.f.contactName.value,
+        contactNumber: this.f.contactNumber.value,
+        assets: [""],
+      }
+    )
     .subscribe(success => {
       if (success) {
         Swal.fire({

+ 0 - 5
src/app/components/organizations/new-organization/new-organization.component.ts

@@ -17,7 +17,6 @@ export class NewOrganizationComponent implements OnInit {
 
 
   constructor(private orgService: OrganizationsService, private formBuilder: FormBuilder) {
-
   }
 
   ngOnInit() {
@@ -66,11 +65,7 @@ export class NewOrganizationComponent implements OnInit {
           }
         });
       }
-      
     });
-    
-
-
   }
 
 

+ 13 - 0
src/app/components/organizations/organization/organization.component.html

@@ -62,6 +62,19 @@
                     <textarea name="address" class="form-control" rows="2" readonly formControlName="address"></textarea>
                   </div>
                   <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
+                  <br>
+                  <div cass="action-buttons">
+                    <a class="btn btn-primary btn-sm" [routerLink]="['/organization', organizationId, 'edit']" >
+                      Editar
+                    </a>
+                    <a class="btn btn-danger btn-sm" (click)="delete_organization2(organizationId)" >
+                      Eliminar
+                    </a>
+                    <!--
+                    <a class="btn btn-danger btn-sm" (click)="delete_organization(row.id)" *ngIf="allowedUser()" >
+                      Eliminar
+                    </a>-->
+                  </div>
                 </form>
               </div>
             </div>

+ 3 - 0
src/app/components/organizations/organization/organization.component.scss

@@ -0,0 +1,3 @@
+.btn.btn-danger.btn-sm {
+  color: #fff;
+}

+ 70 - 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
       });
     });
 
@@ -51,4 +51,73 @@ export class OrganizationComponent implements OnInit {
   ngOnInit() { 
   }
 
+  delete_organization(id: string){
+    Swal.fire({
+      allowOutsideClick: false,
+      type: 'warning',
+      title: '¿Esta seguro?',
+      text: 'No podrá revertir esta acción',
+      showCancelButton: true,
+      confirmButtonColor: '#3085d6',
+      cancelButtonColor: '#d33',
+      confirmButtonText: 'Borrar registro',
+    })
+    .then((result) => {
+        if (result.value) {
+          this.delete_organization2(id);
+        }
+    });
+  }
+
+  delete_organization2(id: string){
+    this.orgService.deleteOrganization(id)
+    .subscribe(
+      data => {
+        if (data["success"] == true) {
+          //document.getElementById(id).remove();
+          Swal.fire({
+            allowOutsideClick: false,
+            type: 'success',
+            text: 'Registro eliminado con exito'
+          }).then((result) => {
+            if (result.value) {
+              this.reloadComponent();
+            }
+          });
+          //window.location.href="#/organizations";
+        }
+        else {
+          Swal.fire({
+            allowOutsideClick: false,
+            type: 'error',
+            text: "No tienes permiso para eliminar el registro"
+          });
+        }  
+      },
+      err => {
+        Swal.fire({
+          type: 'error',
+          title: 'Error en el servidor',
+          text: err.message
+        });
+      });
+  }
+
+  reloadComponent(){
+    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
+    this.router.onSameUrlNavigation = 'reload';
+    this.router.navigate(['/organizations']);
+  }
+
+  allowedUser(){
+    let is_allowed: boolean;
+    if(+this.authService.getUserLevel() < 3){
+      is_allowed = false;
+    }
+    else {
+      is_allowed = true;
+    }
+    return is_allowed
+  }
+
 }

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

@@ -74,7 +74,7 @@
                   <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
                   <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
                 </table>
-                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+                <mat-paginator [pageSizeOptions]="[10, 15, 25]" [pageIndex]="0" [pageSize]="10"></mat-paginator>
               </div>
             </div>
           </div>

+ 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
       });
     });
     

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

@@ -14,9 +14,92 @@
             </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">Editar planta - <small class="category">Complete la información básica</small></h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="assetForm" (ngSubmit)="editAsset()">
 
+                  <div class="form-group">
+                    <label for="name">Nombre de la planta: </label>
+                    <input type="text" formControlName="name" class="form-control" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="country">País: </label>
+                    <input type="text" formControlName="country" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.country.errors }" />
+                    <div *ngIf="submitted && f.country.errors" class="invalid-feedback">
+                      <div *ngIf="f.country.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="city">Ciudad: </label>
+                    <input type="text" formControlName="city" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.city.errors }" />
+                    <div *ngIf="submitted && f.city.errors" class="invalid-feedback">
+                      <div *ngIf="f.city.errors.required">Campo requerido</div>
+                    </div>
+                  </div>      
+                  
+                  <div class="form-group">
+                    <label for="address">Dirección: </label>
+                    <input type="text" formControlName="address" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.address.errors }" />
+                    <div *ngIf="submitted && f.address.errors" class="invalid-feedback">
+                      <div *ngIf="f.address.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="distribuidora">Distribuidora: </label>
+                    <select class="custom-select" formControlName="distribuidora" [ngClass]="{ 'is-invalid': submitted && f.distribuidora.errors }">
+                      <option *ngFor="let item of distributor" [value]="item" [selected]="item==listPlant.distribuidora" >{{item}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.distribuidora.errors" class="invalid-feedback">
+                      <div *ngIf="f.distribuidora.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="categoria_tarifaria">Categoría tarifaria: </label>
+                    <select class="custom-select" formControlName="categoria_tarifaria" [ngClass]="{ 'is-invalid': submitted && f.categoria_tarifaria.errors }">
+                      <option *ngFor="let item of categoria_tarifaria" [value]="item.key" [selected]="item.key==listPlant.categoria_tarifaria" >{{item.value}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.categoria_tarifaria.errors" class="invalid-feedback">
+                      <div *ngIf="f.categoria_tarifaria.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="cod_tarifa">Código de tarifa: </label>
+                    <select class="custom-select" formControlName="cod_tarifa" [ngClass]="{ 'is-invalid': submitted && f.cod_tarifa.errors }">
+                      <option *ngFor="let item of codigo_tarifa" [value]="item.key" [selected]="item.key==listPlant.cod_tarifa" >{{item.value}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.cod_tarifa.errors" class="invalid-feedback">
+                      <div *ngIf="f.cod_tarifa.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+
+                  <br>
+                  <button class="btn btn-primary">
+                    Actualizar planta
+                  </button>
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
       </div>
+    </div>
 
-    </div>          
   </div>
 </div>

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

@@ -1,5 +1,8 @@
 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-edit-plant',
@@ -8,9 +11,92 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 })
 export class EditPlantComponent implements OnInit {
   title:string = "Editar planta"; 
-  constructor() { }
+
+  listPlant: any;
+  organizationExists:boolean;
+  assetForm: FormGroup;
+  assetID:any;
+  role_number: any;
+  assetExists:boolean;
+  distributor = ["EEO","CAESS","DELSUR","CLESA","DEUSEM","ABRUZZO","EDESAL","B&D","DEL SUR"];
+  categoria_tarifaria = [
+    { key: "PD", value: "PD Pequeña Demanda" },
+    { key: "MD", value: "MD Mediana Demanda" },
+    { key: "GD", value: "GD Gran Demanda" }
+  ];
+
+  codigo_tarifa = [
+    { key:"R", value: "R Residencial" },
+    { key:"G", value: "G General" },
+    { key:"AP", value: "AP Alumbrado Público" },
+    { key:"MD CMP - BT" ,value: "MD CMP-BT Mediana Demanda Con Medicion de Potencia Baja Tensión" },
+    { key:"MD CMP - MT" ,value: "MD CMP-MT Mediana Demanda Con Medicion de Potencia  Media Tensión" },
+    { key:"MD CMH - BT" ,value: "MD CMH-BT Mediana Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"MD CMH - MT" ,value: "MD CMH-MT Mediana Demanda Con Medicion Horaria Media Tensión" },
+    { key:"GD CMH - BT", value: "GD CMH-BT Gran Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"GD CMH - MT", value: "GD CMH-MT Gran Demanda Con Medicion Horaria Media Tensión" }
+  ];
+
+  constructor(private plantsService: PlantsService, private formBuilder: FormBuilder, private route: ActivatedRoute) {
+    
+    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() {
+  
   }
 
+  get f() { return this.assetForm.controls; }
+  editAsset() {
+    this.plantsService.updatePlant(this.assetID, 
+      {
+        name: this.f.name.value,
+        country: this.f.country.value,
+        city: this.f.city.value,
+        address: this.f.address.value,
+        distribuidora: this.f.distribuidora.value,
+        categoria_tarifaria: this.f.categoria_tarifaria.value,
+        cod_tarifa: this.f.cod_tarifa.value,
+        meters_installed: this.listPlant.meters_installed
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        Swal.fire({
+          allowOutsideClick: false,
+          type: 'success',
+          text: 'Informacion actualizada con exito'
+        });
+        //window.location.href="#/dashboard";
+
+      }
+      
+    });
+  }
+
+
 }

+ 49 - 7
src/app/components/plants/new-plant/new-plant.component.html

@@ -28,30 +28,72 @@
             </div>
             <div class="card-body">              
               <div class="align-container">
-                <form class="form-auth-small ng-untouched ng-pristine ng-valid" >
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="assetForm" (ngSubmit)="createAsset()" >
                   <div class="form-group">
                     <label for="name">Nombre de la planta: </label>
-                    <input type="text" name="name" class="form-control" required />
+                    <input type="text" formControlName="name" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" />
+                    <div *ngIf="submitted && f.name.errors" class="invalid-feedback">
+                      <div *ngIf="f.name.errors.required">Campo requerido</div>
+                    </div>
                   </div>
-
+                  
                   <div class="form-group">
                     <label for="country">País: </label>
-                    <input type="text" name="country" class="form-control" required />
+                    <input type="text" formControlName="country"class="form-control" [ngClass]="{ 'is-invalid': submitted && f.country.errors }" />
+                    <div *ngIf="submitted && f.country.errors" class="invalid-feedback">
+                      <div *ngIf="f.country.errors.required">Campo requerido</div>
+                    </div>
                   </div>
                   
                   <div class="form-group">
                     <label for="city">Ciudad: </label>
-                    <input type="text" name="city" class="form-control" required />
+                    <input type="text" formControlName="city" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.city.errors }" />
+                    <div *ngIf="submitted && f.city.errors" class="invalid-feedback">
+                      <div *ngIf="f.city.errors.required">Campo requerido</div>
+                    </div>
                   </div>      
                   
                   <div class="form-group">
                     <label for="address">Dirección: </label>
-                    <input type="text" name="address" class="form-control" required />
+                    <input type="text" formControlName="address" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.address.errors }" />
+                    <div *ngIf="submitted && f.address.errors" class="invalid-feedback">
+                      <div *ngIf="f.address.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="distribuidora">Distribuidora: </label>
+                    <select class="custom-select" formControlName="distribuidora" [ngClass]="{ 'is-invalid': submitted && f.distribuidora.errors }">
+                      <option *ngFor="let item of distributor" [value]="item" >{{item}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.distribuidora.errors" class="invalid-feedback">
+                      <div *ngIf="f.distribuidora.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="categoria_tarifaria">Categoría tarifaria: </label>
+                    <select class="custom-select" formControlName="categoria_tarifaria" [ngClass]="{ 'is-invalid': submitted && f.categoria_tarifaria.errors }">
+                      <option *ngFor="let item of categoria_tarifaria" [value]="item.key" >{{item.value}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.categoria_tarifaria.errors" class="invalid-feedback">
+                      <div *ngIf="f.categoria_tarifaria.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="cod_tarifa">Código de tarifa: </label>
+                    <select class="custom-select" formControlName="cod_tarifa" [ngClass]="{ 'is-invalid': submitted && f.cod_tarifa.errors }">
+                      <option *ngFor="let item of codigo_tarifa" [value]="item.key" >{{item.value}}</option>
+                    </select>
+                    <div *ngIf="submitted && f.cod_tarifa.errors" class="invalid-feedback">
+                      <div *ngIf="f.cod_tarifa.errors.required">Campo requerido</div>
+                    </div>
                   </div>
 
                   <br>
                   <button class="btn btn-primary">
-                    Crear organización
+                    Crear planta
                   </button>
                   <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
                 </form>

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

@@ -1,5 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { PlantsService } from '@app/services/plants.service';
+import Swal from 'sweetalert2';
 
 @Component({
   selector: 'app-new-plant',
@@ -8,10 +10,80 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 })
 export class NewPlantComponent implements OnInit {
   title:string = "Nueva planta"; 
+  assetForm: FormGroup;
+  submitted: boolean = false;
+  role_number: any;
+  distributor = ["EEO","CAESS","DELSUR","CLESA","DEUSEM","ABRUZZO","EDESAL","B&D","DEL SUR"];
+  
+  categoria_tarifaria = [
+    { key: "PD", value: "PD Pequeña Demanda" },
+    { key: "MD", value: "MD Mediana Demanda" },
+    { key: "GD", value: "GD Gran Demanda" }
+  ];
 
-  constructor() { }
+  codigo_tarifa = [
+    { key:"R", value: "R Residencial" },
+    { key:"G", value: "G General" },
+    { key:"AP", value: "AP Alumbrado Público" },
+    { key:"MD CMP - BT" ,value: "MD CMP-BT Mediana Demanda Con Medicion de Potencia Baja Tensión" },
+    { key:"MD CMP - MT" ,value: "MD CMP-MT Mediana Demanda Con Medicion de Potencia  Media Tensión" },
+    { key:"MD CMH - BT" ,value: "MD CMH-BT Mediana Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"MD CMH - MT" ,value: "MD CMH-MT Mediana Demanda Con Medicion Horaria Media Tensión" },
+    { key:"GD CMH - BT", value: "GD CMH-BT Gran Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"GD CMH - MT", value: "GD CMH-MT Gran Demanda Con Medicion Horaria Media Tensión" }
+  ];
+  constructor(private plantService: PlantsService, private formBuilder: FormBuilder) { }
 
   ngOnInit() {
+    this.assetForm = this.formBuilder.group({
+      name: ['', Validators.required],
+      country: ['', Validators.required],
+      city: ['', Validators.required],
+      address: ['', Validators.required],
+      distribuidora: ['', Validators.required],
+      categoria_tarifaria: ['', Validators.required],
+      cod_tarifa: ['', Validators.required]
+    });
   }
 
+  get f() { return this.assetForm.controls; }
+
+  createAsset() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.assetForm.invalid) {
+      return;
+    }
+     
+    this.plantService.createPlant(
+      {
+        name: this.f.name.value,
+        country: this.f.country.value,
+        city: this.f.city.value,
+        address: this.f.address.value,
+        distribuidora: this.f.distribuidora.value,
+        categoria_tarifaria: this.f.categoria_tarifaria.value,
+        cod_tarifa: this.f.cod_tarifa.value,
+        meters_installed: []
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        Swal.fire({
+          allowOutsideClick: false,
+          type: 'success',
+          showCancelButton: false,
+          title: 'Exito',
+          confirmButtonText: 'El registro ha sido guardado'
+        }).then((result) => {
+          if (result.value) {
+            window.location.href="#/plants";
+          }
+        });
+      }
+    });
+  }
+
+
 }

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

@@ -0,0 +1,87 @@
+<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>
+                    <input type="text" readonly formControlName="distribuidora" class="form-control" />
+                  </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>
+                  <br>
+                  <div class="action-buttons">
+                    <a class="btn btn-primary btn-sm" [routerLink]="['/plant', assetID, 'edit']" >
+                      Editar
+                    </a>
+                    <!--
+                    <a class="btn btn-danger btn-sm" (click)="delete_organization(row.id)" *ngIf="allowedUser()" >
+                      Eliminar
+                    </a>-->
+                  </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();
+  });
+});

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

@@ -0,0 +1,74 @@
+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'];
+  
+  categoria_tarifaria = [
+    { key: "PD", value: "PD Pequeña Demanda" },
+    { key: "MD", value: "MD Mediana Demanda" },
+    { key: "GD", value: "GD Gran Demanda" }
+  ];
+
+  codigo_tarifa = [
+    { key:"R", value: "R Residencial" },
+    { key:"G", value: "G General" },
+    { key:"AP", value: "AP Alumbrado Público" },
+    { key:"MD CMP - BT" ,value: "MD CMP-BT Mediana Demanda Con Medicion de Potencia Baja Tensión" },
+    { key:"MD CMP - MT" ,value: "MD CMP-MT Mediana Demanda Con Medicion de Potencia  Media Tensión" },
+    { key:"MD CMH - BT" ,value: "MD CMH-BT Mediana Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"MD CMH - MT" ,value: "MD CMH-MT Mediana Demanda Con Medicion Horaria Media Tensión" },
+    { key:"GD CMH - BT", value: "GD CMH-BT Gran Demanda Con Medicion Horaria Baja Tensión" },
+    { key:"GD CMH - MT", value: "GD CMH-MT Gran Demanda Con Medicion Horaria Media Tensión" }
+  ];
+
+  constructor(private plantsService: PlantsService, private formBuilder: FormBuilder, private route: ActivatedRoute) {
+    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() { 
+  }
+
+}

+ 5 - 1
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
@@ -67,7 +71,7 @@
                   <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
                   <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
                 </table>
-                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+                <mat-paginator [pageSizeOptions]="[10, 15, 25]" [pageIndex]="0" [pageSize]="10"></mat-paginator>
               </div>
             </div>
           </div>

+ 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 {

+ 38 - 5
src/app/components/plugins/validator/validator.component.ts

@@ -1,4 +1,5 @@
 import { FormGroup } from '@angular/forms';
+import { AbstractControl, ValidationErrors } from "@angular/forms"
 
 // custom validator to check that two fields match
 export function ValidatorComponent(controlName: string, matchingControlName: string) {
@@ -7,15 +8,47 @@ export function ValidatorComponent(controlName: string, matchingControlName: str
         const matchingControl = formGroup.controls[matchingControlName];
 
         if (matchingControl.errors && !matchingControl.errors.mustMatch) {
-            // return if another validator has already found an error on the matchingControl
-            return;
+          // return if another validator has already found an error on the matchingControl
+          return;
         }
 
         // set error on matchingControl if validation fails
         if (control.value !== matchingControl.value) {
-            matchingControl.setErrors({ mustMatch: true });
-        } else {
-            matchingControl.setErrors(null);
+          matchingControl.setErrors({ mustMatch: true });
+        } {
+          matchingControl.setErrors(null);
         }
     }
 }
+
+export const PasswordStrengthValidator = function (control: AbstractControl): ValidationErrors | null {
+
+  let value: string = control.value || '';
+  let upperCaseCharacters = /[A-Z]+/g
+  let lowerCaseCharacters = /[a-z]+/g
+  let numberCharacters = /[0-9]+/g
+  let specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/
+
+  if (!value) {
+    return null
+  }
+
+  if (upperCaseCharacters.test(value) === false) {
+    return { passwordStrength: `La contraseña debe contener al menos una letra Mayúscula\n` };
+  }
+
+  if (lowerCaseCharacters.test(value) === false) {
+    return { passwordStrength: `La contraseña debe contener al menos una letra minúscula\n` };
+  }
+
+  if (numberCharacters.test(value) === false) {
+    return { passwordStrength: `La contraseña debe contener al menos un número\n` };
+  }
+
+  if (specialCharacters.test(value) === false) {
+    return { passwordStrength: `La contraseña debe contener al menos un símbolo\n` };
+  }
+
+  return null;
+
+}

+ 10 - 1
src/app/components/plugins/weather-card/weather-card.component.html

@@ -1,5 +1,5 @@
 <div class="widget">
-  <div class="row">
+  <div class="row no-gutters">
     <div class="col-7">
       <div class="date">
         {{currentDate}}
@@ -18,4 +18,13 @@
       </div>
     </div>
   </div>
+  <div class="row no-gutters" *ngIf="errorWeather">
+    <div class="error-weather alert" role="alert">
+      <i class="fas fa-exclamation-circle"></i>
+      {{errorMessage}}
+      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+        <span aria-hidden="true">&times;</span>
+      </button>
+    </div>
+  </div>
 </div>

+ 26 - 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;
@@ -68,3 +71,24 @@ div.right-panel
   margin-top: 35px;
   padding-left: 10px;
 }
+
+.error-weather {
+  border: 0;
+  border-radius: 3px;
+  position: relative;
+  padding: 5px;
+  line-height: 10px;
+  font-size: 10px;
+  color:rgba(0, 0, 0, 0.5);
+  margin: 5px 0;
+  background: #FFF3CD;
+  width: 100%;
+
+  .close {
+    font-size: 12px;
+  }
+
+  i {
+    color: inherit;
+  }
+}

+ 40 - 14
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;
@@ -29,23 +30,48 @@ export class WeatherCardComponent implements OnInit {
   pipe = new DatePipe('es-ES')
   newDate = Date.now();
   currentDate = this.pipe.transform(this.newDate, 'fullDate')
-
+  errorWeather: boolean = false;
   constructor(public weather: WeatherService, public router: Router) {
 
   }
 
   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.errorWeather = false;
+      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.errorWeather = true;
+      this.errorMessage = err.error.message;
+      if (this.errorMessage == "city not found"){
+        this.errorMessage = "Datos del clima para el municipio de la planta no disponible.";
+      }
+    });
 
   }
 

+ 9 - 1
src/app/components/shared/footer/footer.component.html

@@ -1 +1,9 @@
-<p>footer works!</p>
+<div class="row align-container">
+  <div class="col-12">
+    <div class="card">
+      <div class="card-body">
+        <p class="card-text">&copy; 2019-2020 - INVERLEC. <a href="#/terms">Términos y condiciones.</a> </p>
+      </div>
+    </div>
+  </div>
+</div>

+ 22 - 0
src/app/components/terms/terms.component.html

@@ -0,0 +1,22 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  
+  <div class="container-fluid">
+
+    <div class="row">
+      <div class="col-lg-12 col-md-12 col-sm-12 p-1">
+        <div class="card card-img-holder">
+          <div class="card-body">
+              The information and data above is confidential data of INVERLEC, intended for the exclusive use of its DENMARK customers and not for distribution 
+              to any third parties for any purpose whatsoever. By accessing and using such information and data, you agree to use it only for your internal analysis and other similar 
+              internal business purposes, not to transfer or disclose such information or data to any person, company or other entity, except to colleagues within your employer who 
+              have a need to know such information or data to carry out those internal business purposes, and to destroy such information and data and any copies or duplicates as soon 
+              as your use thereof for those purposes has concluded. Your foregoing agreement is subject to the terms, conditions and exceptions relating to confidentiality contained 
+              in the Terms of Use of DENMARK, to which have separately agreed.
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 4 - 0
src/app/components/terms/terms.component.scss

@@ -0,0 +1,4 @@
+.card-body {
+  line-height: 2;
+  text-align: justify;
+}

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

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

+ 15 - 0
src/app/components/terms/terms.component.ts

@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-terms',
+  templateUrl: './terms.component.html',
+  styleUrls: ['./terms.component.scss']
+})
+export class TermsComponent implements OnInit {
+  title:string = "Términos y Condiciones"
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 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.html

@@ -67,7 +67,7 @@
                   <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
                   <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
                 </table>
-                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+                <mat-paginator [pageSizeOptions]="[10, 15, 25]" [pageIndex]="0" [pageSize]="10"></mat-paginator>
               </div>
             </div>
           </div>

+ 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
       });
     });
 

+ 3 - 2
src/app/layouts/admin/admin.component.html

@@ -4,8 +4,9 @@
         <div class="sidebar-background" ></div>
     </div>
     <div class="main-panel">
-        <app-navbar></app-navbar>
-        <router-outlet></router-outlet>
+      <app-navbar></app-navbar>
+      <router-outlet></router-outlet>
+      <app-footer></app-footer>
     </div>
     <!--
     <div class="fixed-plugin">

+ 8 - 7
src/app/layouts/admin/admin.module.ts

@@ -4,13 +4,10 @@ import { RouterModule } from '@angular/router';
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { LeafletModule } from "@asymmetrik/ngx-leaflet";
-
 import { ChartsModule } from 'ng2-charts';
-import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
 import { AngularMyDatePickerModule } from 'angular-mydatepicker';
 
-
-
 import { AdminLayoutRoutes } from './admin.routing';
 import { DashboardComponent } from '../../components/dashboard/dashboard.component';
 import { ProfileComponent } from '../../components/profile/profile.component';
@@ -22,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';
 
@@ -29,7 +27,6 @@ import { NewUserComponent } from '@app/components/users/new-user/new-user.compon
 import { PluginsModule } from '../../components/plugins/plugins.module';
 
 import { MatTableExporterModule } from 'mat-table-exporter';
-
 import {
   MatButtonModule,
   MatInputModule,
@@ -44,8 +41,9 @@ import {
   MatSortModule
 
 } from '@angular/material';
-
+import { MatPasswordStrengthModule } from '@angular-material-extensions/password-strength';
 import { BreadcrumbModule, IconsModule } from 'angular-bootstrap-md'
+import { TermsComponent } from '@app/components/terms/terms.component';
 
 
 @NgModule({
@@ -70,7 +68,8 @@ import { BreadcrumbModule, IconsModule } from 'angular-bootstrap-md'
     LeafletModule,
     ChartsModule,
     NgbModule,
-    AngularMyDatePickerModule
+    AngularMyDatePickerModule,
+    MatPasswordStrengthModule
   ],
   declarations: [
     DashboardComponent,
@@ -83,8 +82,10 @@ import { BreadcrumbModule, IconsModule } from 'angular-bootstrap-md'
     PlantsComponent,
     EditPlantComponent,
     NewPlantComponent,
+    PlantComponent,
     UsersComponent,
     NewUserComponent,
+    TermsComponent
   ]
 })
 

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

@@ -11,9 +11,11 @@ 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';
+import { TermsComponent } from '@app/components/terms/terms.component';
 
 
 export const AdminLayoutRoutes: Routes = [
@@ -29,6 +31,10 @@ export const AdminLayoutRoutes: Routes = [
     component: AssetsComponent,
     data: {title: "Listado de plantas"}
   },
+  { path: 'terms', 
+    component: TermsComponent,
+    data: {title: "Términos y condiciones"}
+  },
   // General management
   { path: 'plants', 
     component: PlantsComponent,
@@ -48,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], 

+ 1 - 1
src/app/models/organization.ts

@@ -8,4 +8,4 @@ export interface Organization {
     country: string;
     city: string;
     address: string;
-}
+}

+ 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(

+ 36 - 9
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({
@@ -69,7 +66,6 @@ export class PlantsService extends PlantData {
     //return observableOf(this.listData.find(e => e.id === id));
   //}
 
-  
   getAssetsProducedEnergy() {
     return this.http.get(`${environment.apiUrl}/assets/energyProduced`)
     .pipe(
@@ -81,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,11 +95,8 @@ export class PlantsService extends PlantData {
     return observableOf(this.listData.find(e => e.id === id));
   }
 
- 
-  
-
   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 =>{
@@ -102,6 +105,30 @@ export class PlantsService extends PlantData {
       catchError(this.errorHandl)
     )
   }
+
+  createPlant(asset: { name :string,  country :string, 
+    city :string, address :string, distribuidora :string,
+    categoria_tarifaria: string, cod_tarifa:string, meters_installed?:any}): Observable<boolean> {
+      return this.http.post<any>(`${environment.productionApiUrl}/assets`, asset)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
+
+  updatePlant(id:string, asset: {  name :string,  country :string, 
+    city :string, address :string, distribuidora :string,
+    categoria_tarifaria: string, cod_tarifa:string, meters_installed?:any}): Observable<boolean> {
+      return this.http.put<any>(`${environment.productionApiUrl}/asset/${id}`, asset)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
   
   errorHandl(error) {
     let errorMessage = '';
@@ -113,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);
 

+ 6 - 2
src/assets/scss/material-dashboard.scss

@@ -79,7 +79,7 @@
 }
 
 .green-skin {
-  background-color: #548c2f;
+  background-color: #47a44b;
   border-color: #104911 !important;
 }
 
@@ -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;
-}
+}
+

+ 1 - 0
src/environments/environment.ts

@@ -7,6 +7,7 @@ export const environment = {
   apiUrl: 'https://api.inverlec.solar',
   //productionApiUrl: 'http://192.168.98.10:8888/api/v1',
   productionApiUrl: 'http://192.168.98.24:5000/api/v1',
+  //productionApiUrl: 'https://denmark.inverlec.solar/api/v1',
   appID: '55899b9ea53834f2736b65a3582b734b',
   gKey: '',
   config: {

+ 1 - 1
src/index.html

@@ -7,7 +7,7 @@
 
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
 
-    <title>Plant Viewer</title>
+    <title>DENMARK - Aplicación para visualizar sistemas fotovoltaicos @Inverlec</title>
 
     <meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
     <meta name="viewport" content="width=device-width" />