Bladeren bron

Se agregan los siguientes features: Crear usuario, recuperar password, pantalla perfil, seccion de tarifas, validacion de tipo de usuario

Oscar José Nuñez Chávez 5 jaren geleden
bovenliggende
commit
a680f35287
45 gewijzigde bestanden met toevoegingen van 3470 en 586 verwijderingen
  1. 1 1
      .dockerignore
  2. 10 4
      src/app/app.module.ts
  3. 38 31
      src/app/app.routing.ts
  4. 13 1
      src/app/components/assets/assets.component.html
  5. 90 84
      src/app/components/assets/assets.component.ts
  6. 106 34
      src/app/components/confirm-account/confirm-account.component.html
  7. 107 67
      src/app/components/confirm-account/confirm-account.component.ts
  8. 148 2
      src/app/components/dashboard/dashboard.component.html
  9. 7 0
      src/app/components/dashboard/dashboard.component.scss
  10. 77 44
      src/app/components/dashboard/dashboard.component.ts
  11. 10 0
      src/app/components/login/login.component.html
  12. 17 0
      src/app/components/login/login.component.scss
  13. 46 42
      src/app/components/login/login.component.ts
  14. 56 0
      src/app/components/lost-account/lost-account.component.html
  15. 112 0
      src/app/components/lost-account/lost-account.component.scss
  16. 113 0
      src/app/components/lost-account/lost-account.component.ts
  17. 152 0
      src/app/components/new-account/new-account.component.html
  18. 112 0
      src/app/components/new-account/new-account.component.scss
  19. 154 0
      src/app/components/new-account/new-account.component.ts
  20. 94 0
      src/app/components/new-password/new-password.component.html
  21. 112 0
      src/app/components/new-password/new-password.component.scss
  22. 155 0
      src/app/components/new-password/new-password.component.ts
  23. 283 10
      src/app/components/profile/profile.component.html
  24. 15 2
      src/app/components/profile/profile.component.scss
  25. 156 9
      src/app/components/profile/profile.component.ts
  26. 260 0
      src/app/components/rates/rates.component.html
  27. 115 0
      src/app/components/rates/rates.component.scss
  28. 310 0
      src/app/components/rates/rates.component.ts
  29. 59 38
      src/app/components/shared/sidebar/sidebar.component.ts
  30. 6 4
      src/app/layouts/admin/admin.module.ts
  31. 87 72
      src/app/layouts/admin/admin.routing.ts
  32. 59 43
      src/app/services/auth2.service.ts
  33. 12 0
      src/app/services/catalogs.service.spec.ts
  34. 47 0
      src/app/services/catalogs.service.ts
  35. 12 0
      src/app/services/rates.service.spec.ts
  36. 72 0
      src/app/services/rates.service.ts
  37. 70 46
      src/app/services/token.interceptor.ts
  38. 149 30
      src/app/services/user.service.ts
  39. BIN
      src/assets/img/AESFactura.png
  40. BIN
      src/assets/img/DelSurFactura2.png
  41. BIN
      src/assets/img/money-rate.png
  42. BIN
      src/assets/img/time-is-money.png
  43. 18 17
      src/assets/scss/core/variables/_colors.scss
  44. 6 1
      src/assets/scss/material-dashboard.scss
  45. 4 4
      src/environments/environment.ts

+ 1 - 1
.dockerignore

@@ -2,4 +2,4 @@ e2e
 node_modules
 .gitignore
 README.md
-dist
+dist

+ 10 - 4
src/app/app.module.ts

@@ -34,13 +34,19 @@ 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";
+import { LostAccountComponent } from "@app/components/lost-account/lost-account.component";
+import { NewAccountComponent } from "@app/components/new-account/new-account.component";
+import { NewPasswordComponent } from "./components/new-password/new-password.component";
 
 @NgModule({
   declarations: [
     AppComponent,
     AdminComponent,
     LoginComponent,
-    ConfirmAccountComponent
+    ConfirmAccountComponent,
+    LostAccountComponent,
+    NewAccountComponent,
+    NewPasswordComponent,
   ],
   imports: [
     BrowserModule,
@@ -57,17 +63,17 @@ import { MatPasswordStrengthModule } from "@angular-material-extensions/password
     NgxDatatableModule,
     MatSelectModule,
     AngularMyDatePickerModule,
-    MatPasswordStrengthModule.forRoot()
+    MatPasswordStrengthModule.forRoot(),
   ],
   providers: [
     { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
-    [DatePipe]
+    [DatePipe],
 
     //{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
     //AuthenticationService,
     // provider used to create fake backend
     //fakeBackendProvider
   ],
-  bootstrap: [AppComponent]
+  bootstrap: [AppComponent],
 })
 export class AppModule {}

+ 38 - 31
src/app/app.routing.ts

@@ -1,46 +1,53 @@
-import { NgModule } from '@angular/core';
-import { CommonModule, } from '@angular/common';
-import { BrowserModule  } from '@angular/platform-browser';
-import { Routes, RouterModule } from '@angular/router';
-import { AdminModule } from './layouts/admin/admin.module';
+import { NgModule } from "@angular/core";
+import { CommonModule } from "@angular/common";
+import { BrowserModule } from "@angular/platform-browser";
+import { Routes, RouterModule } from "@angular/router";
+import { AdminModule } from "./layouts/admin/admin.module";
 
-import { AdminComponent } from './layouts/admin/admin.component';
-import { LoginComponent } from './components/login/login.component';
+import { AdminComponent } from "./layouts/admin/admin.component";
+import { LoginComponent } from "./components/login/login.component";
 //import { AuthGuard } from './services/authentication.service';
-import { AuthGuard } from './services/auth.guard';
-import { Role } from './models/role';
-import { ConfirmAccountComponent } from './components/confirm-account/confirm-account.component';
+import { AuthGuard } from "./services/auth.guard";
+import { Role } from "./models/role";
+import { ConfirmAccountComponent } from "./components/confirm-account/confirm-account.component";
+import { LostAccountComponent } from "./components/lost-account/lost-account.component";
+import { NewAccountComponent } from "./components/new-account/new-account.component";
+import { NewPasswordComponent } from "./components/new-password/new-password.component";
 
-const routes: Routes =[
+const routes: Routes = [
   {
-    path: '',
-    redirectTo: 'dashboard',
-    pathMatch: 'full',
-  }, 
+    path: "",
+    redirectTo: "dashboard",
+    pathMatch: "full",
+  },
   {
-    path: '',
+    path: "",
     component: AdminComponent,
     canActivate: [AuthGuard],
-    children: [{
-      path: '',
-      loadChildren : './layouts/admin/admin.module#AdminModule' //() => AdminModule
-    }]
-  }, 
-  { path: 'login', component: LoginComponent },
-  { path: 'confirm-account', component: ConfirmAccountComponent },
-  { path: '**', redirectTo: '' }
+    children: [
+      {
+        path: "",
+        loadChildren: "./layouts/admin/admin.module#AdminModule", //() => AdminModule
+      },
+    ],
+  },
+  { path: "login", component: LoginComponent },
+  { path: "recover-password", component: LostAccountComponent },
+  { path: "password-recovery", component: NewPasswordComponent },
+  { path: "new-account", component: NewAccountComponent },
+  { path: "confirm-account", component: ConfirmAccountComponent },
+  { path: "**", redirectTo: "" },
 ];
 
 @NgModule({
   imports: [
     CommonModule,
     BrowserModule,
-    RouterModule.forRoot(routes,{
-       useHash: true,
-       onSameUrlNavigation: 'reload'
-    })
-  ],
-  exports: [
+    RouterModule.forRoot(routes, {
+      useHash: true,
+      onSameUrlNavigation: "reload",
+    }),
   ],
+  exports: [],
 })
-export class AppRoutingModule { }
+export class AppRoutingModule {}

+ 13 - 1
src/app/components/assets/assets.component.html

@@ -1,6 +1,6 @@
 <h2 class="floating-title">{{ title }}</h2>
 <div class="main-content">
-  <div class="container-fluid">
+  <div class="container-fluid" *ngIf="!noPlants">
     <div class="row align-container">
       <div class="col-12">
         <label for="sel3">Plantas</label>
@@ -444,4 +444,16 @@
       </div>
     </div>
   </div>
+
+  <div class="container-fluid" *ngIf="noPlants">
+    <div class="row">
+      <div class="col-lg-12">
+        <div class="card border-danger">
+          <div class="card-body">
+            No posee organizaciones/plantas asociadas a su cuenta
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </div>

+ 90 - 84
src/app/components/assets/assets.component.ts

@@ -22,7 +22,7 @@ import * as $ from "jquery";
 @Component({
   selector: "app-assets",
   templateUrl: "./assets.component.html",
-  styleUrls: ["./assets.component.scss"]
+  styleUrls: ["./assets.component.scss"],
 })
 export class AssetsComponent implements OnInit {
   exportExcel = ExportType.XLSX;
@@ -31,7 +31,7 @@ export class AssetsComponent implements OnInit {
   ExcelOptions: ExcelOptions = {
     fileName: "data-Medidores",
     sheet: "Hoja 1",
-    Props: { Author: "INVERLEC" }
+    Props: { Author: "INVERLEC" },
   };
   CSVOptions: Options = { fileName: "data-Medidores" };
   JSONOptions: Options = { fileName: "data-Medidores" };
@@ -83,7 +83,7 @@ export class AssetsComponent implements OnInit {
     we: "Mie",
     th: "Jue",
     fr: "Vie",
-    sa: "Sab"
+    sa: "Sab",
   };
   monthsLabels: any = {
     1: "Ene",
@@ -97,13 +97,13 @@ export class AssetsComponent implements OnInit {
     9: "Sep",
     10: "Oct",
     11: "Nov",
-    12: "Dic"
+    12: "Dic",
   };
   myDpOptions: IAngularMyDpOptions = {
     dateRange: false,
     dateFormat: "dd/mm/yyyy",
     dayLabels: this.daysLabels,
-    monthLabels: this.monthsLabels
+    monthLabels: this.monthsLabels,
   };
   myDateInit: boolean = true;
   model: IMyDateModel = null;
@@ -135,7 +135,7 @@ export class AssetsComponent implements OnInit {
     "#f2e8cf",
     "#3c8dbc",
     "#f56954",
-    "#ee964b"
+    "#ee964b",
   ];
   borderChartColors: any = [
     "#4573a5",
@@ -145,7 +145,7 @@ export class AssetsComponent implements OnInit {
     "#d3cbb7",
     "#539dc8",
     "#e4604c",
-    "#d38d51"
+    "#d38d51",
   ];
   public barChartType: ChartType;
   public barChartLegend: boolean;
@@ -158,6 +158,7 @@ export class AssetsComponent implements OnInit {
   last_day_savings: number;
   savings_logs: any;
   savings_logs_data = [];
+  noPlants: boolean;
 
   constructor(
     private orgService: OrganizationsService,
@@ -172,14 +173,14 @@ export class AssetsComponent implements OnInit {
     // DEMO
     this.userLevel = +this.authService.getUserLevel();
     // Get selected asset
-    this.route.queryParams.subscribe(params => {
+    this.route.queryParams.subscribe((params) => {
       this.assetID = params["id"];
     });
 
     Swal.fire({
       allowOutsideClick: false,
       type: "info",
-      text: "Espere por favor..."
+      text: "Espere por favor...",
     });
     Swal.showLoading();
   }
@@ -195,9 +196,9 @@ export class AssetsComponent implements OnInit {
           date: {
             year: begin.getFullYear(),
             month: begin.getMonth() + 1,
-            day: begin.getDate()
-          }
-        }
+            day: begin.getDate(),
+          },
+        },
       };
     }
 
@@ -211,14 +212,19 @@ export class AssetsComponent implements OnInit {
       false,
       false,
       false,
-      false
+      false,
     ];
 
     this.chartActive = [true, false, false, false];
 
     // Get all assets (according to user's assigned organizations)
     let plants = this.plantsService.getAllAssets().subscribe(
-      res => {
+      (res) => {
+        if (res == null) {
+          this.noPlants = true;
+          return;
+        }
+
         this.listAssets = res["data"]["assets"];
 
         // Default values for chart
@@ -230,12 +236,12 @@ export class AssetsComponent implements OnInit {
           this.city = this.listAssets[0].city;
         } else {
           this.city = this.listAssets.find(
-            object => object.id == this.assetID
+            (object) => object.id == this.assetID
           ).city;
         }
 
         let tempAsset = this.listAssets.find(
-          object => object.id == this.assetID
+          (object) => object.id == this.assetID
         );
 
         if (tempAsset["meters_installed"].length > 0) {
@@ -254,11 +260,11 @@ export class AssetsComponent implements OnInit {
           this.metersInstalled = false;
         }
       },
-      err => {
+      (err) => {
         Swal.fire({
           type: "error",
           title: "Error en el servidor",
-          text: err.message
+          text: err.message,
         });
       }
     );
@@ -287,7 +293,7 @@ export class AssetsComponent implements OnInit {
   // Trigger again the chart with the selected assetID
   onChangeObj(event: any) {
     this.assetID = event.target.value;
-    let tempAsset = this.listAssets.find(object => object.id == this.assetID);
+    let tempAsset = this.listAssets.find((object) => object.id == this.assetID);
     this.city = tempAsset["city"];
     this.total_savings = 0;
     this.last_day_savings = 0;
@@ -301,7 +307,7 @@ export class AssetsComponent implements OnInit {
       Swal.fire({
         allowOutsideClick: false,
         type: "info",
-        text: "Espere por favor..."
+        text: "Espere por favor...",
       });
       Swal.showLoading();
       setTimeout(() => {
@@ -325,7 +331,7 @@ export class AssetsComponent implements OnInit {
   }
 
   getSavings(assetID: string) {
-    this.plantsService.getAssetSavings(this.assetID).subscribe(res => {
+    this.plantsService.getAssetSavings(this.assetID).subscribe((res) => {
       this.total_savings = res["data"]["ahorro_total"];
       this.total_savings =
         +this.total_savings > 10000
@@ -337,7 +343,7 @@ export class AssetsComponent implements OnInit {
   }
 
   getEnvironmentalData(assetID: string) {
-    this.logsService.getAssetEnviromentalStats(assetID).subscribe(resp => {
+    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);
@@ -352,7 +358,7 @@ export class AssetsComponent implements OnInit {
     let energy_produced = this.logsService
       .getEnergySummaryByAsset(assetID)
       .subscribe(
-        resp => {
+        (resp) => {
           this.eProduced = resp["data"]["energy"]; //results[1];
           this.energyDay = this.eProduced.today.total_energy_kWh || 0;
           this.energyWeek =
@@ -368,11 +374,11 @@ export class AssetsComponent implements OnInit {
               ? (this.eProduced.lifeTime.total_energy_kWh / 1000).toFixed(2)
               : this.eProduced.lifeTime.total_energy_kWh || 0;
         },
-        err => {
+        (err) => {
           Swal.fire({
             type: "error",
             title: "Error en el servidor",
-            text: "No su pudo obtener la informacion"
+            text: "No su pudo obtener la informacion",
           });
         }
       );
@@ -389,7 +395,7 @@ export class AssetsComponent implements OnInit {
       Swal.fire({
         allowOutsideClick: false,
         type: "info",
-        text: "Espere por favor..."
+        text: "Espere por favor...",
       });
       Swal.showLoading();
     }
@@ -431,7 +437,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "week":
@@ -445,7 +451,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "WTD":
@@ -459,7 +465,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "month":
@@ -473,7 +479,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "MTD":
@@ -487,7 +493,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "3m":
@@ -501,7 +507,7 @@ export class AssetsComponent implements OnInit {
           true,
           false,
           false,
-          false
+          false,
         ];
         break;
       case "12m":
@@ -515,7 +521,7 @@ export class AssetsComponent implements OnInit {
           false,
           true,
           false,
-          false
+          false,
         ];
         break;
       case "YTD":
@@ -529,7 +535,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           true,
-          false
+          false,
         ];
         break;
       case "total":
@@ -543,7 +549,7 @@ export class AssetsComponent implements OnInit {
           false,
           false,
           false,
-          true
+          true,
         ];
         break;
       default:
@@ -600,7 +606,7 @@ export class AssetsComponent implements OnInit {
               borderColor: "#ce2525",
               borderWidth: 2,
               yAxisID: "y-axis-ahorro",
-              fill: false
+              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
@@ -615,18 +621,18 @@ export class AssetsComponent implements OnInit {
 
               let measure_values = Object.values(
                 this.metersValues[prop]["data"]
-                  .map(obj => obj.total_energy_kWh)
+                  .map((obj) => obj.total_energy_kWh)
                   .reverse()
               );
               this.metersData.push({
                 label: label,
                 backgroundColor: this.barChartColors[prop],
                 data: measure_values,
-                borderColor: this.borderChartColors[prop]
+                borderColor: this.borderChartColors[prop],
               });
               this.tableData.push({
                 headers: label,
-                dataValues: measure_values
+                dataValues: measure_values,
               });
             }
 
@@ -641,7 +647,7 @@ export class AssetsComponent implements OnInit {
               //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.metersValues[prop]["data"].map((obj) => obj).reverse()
               );
 
               for (let prop2 in quantity) {
@@ -682,28 +688,28 @@ export class AssetsComponent implements OnInit {
             switch (view) {
               case "day":
                 this.barChartLabels = this.metersValues[0]["data"]
-                  .map(obj =>
+                  .map((obj) =>
                     formatDate(obj.dateMin, "HH:mm ", "es-Es", "-0600")
                   )
                   .reverse();
                 break;
               case "week":
                 this.barChartLabels = this.metersValues[0]["data"]
-                  .map(obj =>
+                  .map((obj) =>
                     formatDate(obj.dateMax, "EEEE dd", "es-Es", "-0600")
                   )
                   .reverse();
                 break;
               case "year":
                 this.barChartLabels = this.metersValues[0]["data"]
-                  .map(obj =>
+                  .map((obj) =>
                     formatDate(obj.dateMax, "MM/yyyy", "es-Es", "-0600")
                   )
                   .reverse();
                 break;
               default:
                 this.barChartLabels = this.metersValues[0]["data"]
-                  .map(obj =>
+                  .map((obj) =>
                     formatDate(obj.dateMax, "dd/MM", "es-Es", "-0600")
                   )
                   .reverse();
@@ -713,7 +719,7 @@ export class AssetsComponent implements OnInit {
             this.tableData.push({ headers: "Ahorro[US$]" });
             this.tableData.push({
               headers: "Fecha/Hora",
-              dataValues: this.barChartLabels
+              dataValues: this.barChartLabels,
             });
             this.tableData.reverse();
             for (let v in Object.keys(this.tableData)) {
@@ -739,13 +745,13 @@ export class AssetsComponent implements OnInit {
               type: this.chart1Type,
               options: {
                 title: {
-                  display: true
+                  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) {
+                    label: function (tooltipItem, data) {
                       var label =
                         data.datasets[tooltipItem.datasetIndex].label || "";
                       var value =
@@ -765,11 +771,11 @@ export class AssetsComponent implements OnInit {
                       } else {
                         return [
                           label + " : " + value,
-                          "TOTAL[kWh] : " + Math.round(total)
+                          "TOTAL[kWh] : " + Math.round(total),
                         ];
                       }
-                    }
-                  }
+                    },
+                  },
                 },
                 responsive: true,
                 maintainAspectRatio: false,
@@ -777,8 +783,8 @@ export class AssetsComponent implements OnInit {
                   xAxes: [
                     {
                       stacked: true,
-                      barPercentage: 0.7
-                    }
+                      barPercentage: 0.7,
+                    },
                   ],
                   yAxes: [
                     {
@@ -786,8 +792,8 @@ export class AssetsComponent implements OnInit {
                       position: "left",
                       scaleLabel: {
                         display: true,
-                        labelString: "Kilowatts hora [kWh]"
-                      }
+                        labelString: "Kilowatts hora [kWh]",
+                      },
                     },
                     {
                       display: true,
@@ -796,20 +802,20 @@ export class AssetsComponent implements OnInit {
                       id: "y-axis-ahorro",
                       scaleLabel: {
                         display: true,
-                        labelString: "Ahorro [US$]"
+                        labelString: "Ahorro [US$]",
                       },
                       gridLines: {
-                        drawOnChartArea: false
-                      }
-                    }
-                  ]
-                }
+                        drawOnChartArea: false,
+                      },
+                    },
+                  ],
+                },
               },
 
               data: {
                 labels: this.barChartLabels,
-                datasets: this.metersData
-              }
+                datasets: this.metersData,
+              },
             });
             this.chartjs = true;
           });
@@ -854,7 +860,7 @@ export class AssetsComponent implements OnInit {
       //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; }"
+      style: ".report-header{ color: #075D9D; font-size: 24px; }",
     });
   }
 
@@ -869,20 +875,20 @@ export class AssetsComponent implements OnInit {
     switch (chartType) {
       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";
+        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";
+        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";
+        this.metersData.find((v) => v.type == "line").type = "radar";
         break;
       default:
     }
@@ -898,13 +904,13 @@ export class AssetsComponent implements OnInit {
 
       options: {
         title: {
-          display: true
+          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) {
+            label: function (tooltipItem, data) {
               var label = data.datasets[tooltipItem.datasetIndex].label || "";
               var value =
                 data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
@@ -918,8 +924,8 @@ export class AssetsComponent implements OnInit {
               } else {
                 return [label + " : " + value, "TOTAL : " + Math.round(total)];
               }
-            }
-          }
+            },
+          },
         },
         responsive: true,
         maintainAspectRatio: false,
@@ -927,8 +933,8 @@ export class AssetsComponent implements OnInit {
           xAxes: [
             {
               stacked: true,
-              barPercentage: 0.7
-            }
+              barPercentage: 0.7,
+            },
           ],
           yAxes: [
             {
@@ -936,8 +942,8 @@ export class AssetsComponent implements OnInit {
               position: "left",
               scaleLabel: {
                 display: true,
-                labelString: "Kilowatts hora [kWh]"
-              }
+                labelString: "Kilowatts hora [kWh]",
+              },
             },
             {
               display: true,
@@ -946,19 +952,19 @@ export class AssetsComponent implements OnInit {
               id: "y-axis-ahorro",
               scaleLabel: {
                 display: true,
-                labelString: "Ahorro [US$]"
+                labelString: "Ahorro [US$]",
               },
               gridLines: {
-                drawOnChartArea: false
-              }
-            }
-          ]
-        }
+                drawOnChartArea: false,
+              },
+            },
+          ],
+        },
       },
       data: {
         labels: this.barChartLabels,
-        datasets: this.metersData
-      }
+        datasets: this.metersData,
+      },
     });
   }
 }

+ 106 - 34
src/app/components/confirm-account/confirm-account.component.html

@@ -3,38 +3,58 @@
     <div class="vertical-align-middle auth-main">
       <div class="auth-box">
         <div class="top">
-          <img alt="Inverlec" src="./assets/img/inverlec_logo.png">
+          <img alt="Inverlec" src="./assets/img/inverlec_logo.png" />
         </div>
 
-        <div class="card">
+        <div class="card" *ngIf="!customer">
           <div class="header">
             <h1 class="lead">Confirmación de cuenta</h1>
           </div>
           <div class="body" *ngIf="validToken">
             <form [formGroup]="activateForm" (ngSubmit)="activateAccount()">
-
               <div class="form-group">
                 <label for="email">Correo electrónico</label>
-                <input type="text" class="form-control"
-                formControlName="email" required email readonly />
+                <input
+                  type="text"
+                  class="form-control"
+                  formControlName="email"
+                  required
+                  email
+                  readonly
+                />
               </div>
 
               <div class="form-group">
                 <label for="name">Nombre</label>
-                <input type="text" class="form-control"
-                formControlName="first_name" [ngClass]="{ 'is-invalid': submitted && f.first_name.errors }"/>
-                <div *ngIf="submitted && f.first_name.errors" class="invalid-feedback">
-                  <div *ngIf="f.first_name.errors.required">Campo requerido</div>
+                <input
+                  type="text"
+                  class="form-control"
+                  formControlName="first_name"
+                  [ngClass]="{ 'is-invalid': submitted && f.first_name.errors }"
+                />
+                <div
+                  *ngIf="submitted && f.first_name.errors"
+                  class="invalid-feedback"
+                >
+                  <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">
+                <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>
 
@@ -42,47 +62,72 @@
                 <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.
+                    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">
+                <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 *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>
 
               <div class="form-group">
                 <label for="confirm_password">Confirmar contraseña</label>
-                <input type="password" class="form-control"
-                formControlName="confirm_password" [ngClass]="{ 'is-invalid': submitted && f.confirm_password.errors }"/>
-                <div *ngIf="submitted && f.confirm_password.errors" class="invalid-feedback">
-                  <div *ngIf="f.confirm_password.errors.required">Campo requerido</div>
-                  <div *ngIf="f.confirm_password.errors.mustMatch">Las contraseñas deben coincidir</div>
+                <input
+                  type="password"
+                  class="form-control"
+                  formControlName="confirm_password"
+                  [ngClass]="{
+                    'is-invalid': submitted && f.confirm_password.errors
+                  }"
+                />
+                <div
+                  *ngIf="submitted && f.confirm_password.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.confirm_password.errors.required">
+                    Campo requerido
+                  </div>
+                  <div *ngIf="f.confirm_password.errors.mustMatch">
+                    Las contraseñas deben coincidir
+                  </div>
                 </div>
               </div>
-    
-              <br>
+
+              <br />
 
               <div class="div-center">
-                <button class="btn btn-primary" type="submit"> 
+                <button class="btn btn-primary" type="submit">
                   Confirmar cuenta
                 </button>
               </div>
-
             </form>
           </div>
 
           <div class="body" *ngIf="invalidToken">
             <div class="tokenError">
               <h3>
-                {{errorMessage}}
+                {{ errorMessage }}
               </h3>
 
               <div *ngIf="userActivated">
@@ -90,24 +135,51 @@
                   Ir a inicio de sesion
                 </a>
               </div>
-
             </div>
           </div>
 
           <div class="body" *ngIf="successActivation">
             <div class="tokenSuccess">
               <h3>
-                {{activateMessage}}
+                {{ activateMessage }}
               </h3>
 
               <a class="btn btn-primary" [routerLink]="['/login']">
                 Ir a inicio de sesion
               </a>
+            </div>
+          </div>
+        </div>
 
+        <div class="card" *ngIf="customer">
+          <div class="header">
+            <h1 class="lead">Confirmación de cuenta</h1>
+          </div>
+          <div class="body" *ngIf="invalidToken">
+            <div class="tokenError">
+              <h3>
+                {{ errorMessage }}
+              </h3>
+
+              <div *ngIf="userActivated">
+                <a class="btn btn-primary" [routerLink]="['/login']">
+                  Ir a inicio de sesion
+                </a>
+              </div>
             </div>
           </div>
 
+          <div class="body" *ngIf="successActivation">
+            <div class="tokenSuccess">
+              <h3>
+                {{ activateMessage }}
+              </h3>
 
+              <a class="btn btn-primary" [routerLink]="['/login']">
+                Ir a inicio de sesion
+              </a>
+            </div>
+          </div>
         </div>
       </div>
     </div>

+ 107 - 67
src/app/components/confirm-account/confirm-account.component.ts

@@ -1,19 +1,21 @@
-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, PasswordStrengthValidator } from '../plugins/validator/validator.component';
-
+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,
+  PasswordStrengthValidator,
+} from "../plugins/validator/validator.component";
 
 @Component({
-  selector: 'app-confirm-account',
-  templateUrl: './confirm-account.component.html',
-  styleUrls: ['./confirm-account.component.scss']
+  selector: "app-confirm-account",
+  templateUrl: "./confirm-account.component.html",
+  styleUrls: ["./confirm-account.component.scss"],
 })
 export class ConfirmAccountComponent implements OnInit {
-  token:string;
-  validToken:boolean;
+  token: string;
+  validToken: boolean;
   activateForm: FormGroup;
   errorMessage: string;
   userActivated: boolean;
@@ -22,55 +24,94 @@ export class ConfirmAccountComponent implements OnInit {
   invalidToken: boolean;
   submitted: boolean = false;
   showDetails: boolean;
-
-  constructor(private formBuilder: FormBuilder, private route: ActivatedRoute, private userService: UserService) {
-    this.route.queryParams.subscribe(params => {
-      this.token = params['token'];
+  customer: boolean;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private route: ActivatedRoute,
+    private userService: UserService
+  ) {
+    this.route.queryParams.subscribe((params) => {
+      console.log(params);
+      this.token = params["token"];
+      this.customer =
+        params["customer"] == undefined ? false : params["customer"];
     });
   }
 
   ngOnInit() {
-    if (this.token !== null){
-      
-      this.userService.validateUserToken(
-        {
-          token: this.token
-        }
-      ).subscribe(res => {
-        
-        let userData = res["data"].user;
-
-        this.validToken = true;
-        this.activateForm = this.formBuilder.group({
-          // Load information
-          email: [(userData.email)],
-          first_name: [(userData.first_name),Validators.required],
-          last_name: [(userData.last_name),Validators.required],
-          password: ['',[Validators.required, Validators.minLength(8), PasswordStrengthValidator]],
-          confirm_password: ['',Validators.required]
-        },{
-          
-            validator: ValidatorComponent('password', 'confirm_password')
-        });
-      }, (err) => {
-       
-        this.userActivated = true;
-        this.invalidToken = true;
-        this.errorMessage = err.message;
-      });
-    
-    }
-    else {
+    if (this.token !== null) {
+      this.userService
+        .validateUserToken({
+          token: this.token,
+          process: "new_account",
+        })
+        .subscribe(
+          (res) => {
+            let userData = res["data"].user;
+            console.log(userData);
+            this.validToken = true;
+
+            if (this.customer) {
+              this.userService
+                .activateCustomer({
+                  email: userData.email,
+                  first_name: userData.first_name,
+                  last_name: userData.last_name,
+                  phone_number: userData.phone_number,
+                })
+                .subscribe(
+                  (res) => {
+                    this.successActivation = true;
+                    this.validToken = false;
+                    this.activateMessage =
+                      "Su cuenta ha sido activada exitosamente";
+                  },
+                  (err) => {
+                    this.validToken = false;
+                    this.errorMessage = err.message;
+                  }
+                );
+            } else {
+              this.activateForm = this.formBuilder.group(
+                {
+                  // Load information
+                  email: [userData.email],
+                  first_name: [userData.first_name, Validators.required],
+                  last_name: [userData.last_name, Validators.required],
+                  password: [
+                    "",
+                    [
+                      Validators.required,
+                      Validators.minLength(8),
+                      PasswordStrengthValidator,
+                    ],
+                  ],
+                  confirm_password: ["", Validators.required],
+                },
+                {
+                  validator: ValidatorComponent("password", "confirm_password"),
+                }
+              );
+            }
+          },
+          (err) => {
+            this.userActivated = true;
+            this.invalidToken = true;
+            this.errorMessage = err.message;
+          }
+        );
+    } else {
       this.invalidToken = true;
       this.errorMessage = "No existe el token";
     }
   }
 
-  get f() { return this.activateForm.controls; }
-
-
-  activateAccount(){
+  get f() {
+    return this.activateForm.controls;
+  }
 
+  activateAccount() {
     this.submitted = true;
 
     // stop here if form is invalid
@@ -78,30 +119,29 @@ export class ConfirmAccountComponent implements OnInit {
       return;
     }
 
-    this.userService.activateUser(
-      {
+    this.userService
+      .activateUser({
         email: this.f.email.value,
         first_name: this.f.first_name.value,
         last_name: this.f.last_name.value,
         password: this.f.password.value,
         confirm_password: this.f.confirm_password.value,
-      }
-    ).subscribe(res => {
-      this.successActivation = true;
-      this.validToken = false;
-      this.activateMessage = "Usuario registrado con exito";
-
-    }, (err) => {
-      this.validToken = false;
-      this.errorMessage = err.message;
-    });
+      })
+      .subscribe(
+        (res) => {
+          this.successActivation = true;
+          this.validToken = false;
+          this.activateMessage = "Usuario registrado con exito";
+        },
+        (err) => {
+          this.validToken = false;
+          this.errorMessage = err.message;
+        }
+      );
     //
-
   }
-
 }
 
-
 /*
 
 Swal.fire({
@@ -136,4 +176,4 @@ Swal.fire({
       });
     });
 
-    */
+    */

+ 148 - 2
src/app/components/dashboard/dashboard.component.html

@@ -2,6 +2,152 @@
 
 <div class="main-content">
   <div class="container-fluid">
+    <div class="row">
+      <div class="col-lg-12">
+        <a href="#!" class="btn btn-sm btn-warning pull-right"
+          >Actualizar a usuario pro</a
+        >
+      </div>
+    </div>
+    <br />
+    <div class="row">
+      <div class="col-lg-12">
+        <div class="card white-skin bg-gradient-danger card-img-holder">
+          <div class="card-body">
+            <img
+              alt="circle-image"
+              class="card-img-absolute"
+              src="assets/img/money-rate.png"
+            />
+            <h4 class="font-weight-normal mb-3">
+              Tarifa por distribuidora y código
+              <i class="mdi mdi-chart-line mdi-24px float-right"></i>
+            </h4>
+            <div class="row no-gutters">
+              <div class="col-sm-12 col-md-6">
+                <h3 class="mb-3">
+                  Distribuidora:
+                  <strong *ngIf="rates.distribuidora">
+                    {{ rates.distribuidora }}
+                  </strong>
+                </h3>
+                <div>
+                  <h3>
+                    Vigencia:
+                    <strong *ngIf="rates.tarifa_actual.dateMin">
+                      {{ rates.tarifa_actual.dateMin | date: "dd/MM/yyyy" }} -
+                      {{ rates.tarifa_actual.dateMax | date: "dd/MM/yyyy" }}
+                    </strong>
+                  </h3>
+                </div>
+              </div>
+              <div class="col-sm-12 col-md-6">
+                <h4>Tarifa actual:</h4>
+                <div *ngIf="rates.tarifa_actual.cargo_energia">
+                  Cargo de energia:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b1">
+                  Cargo de energia por bloque 1:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b1 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b2">
+                  Cargo de energia por bloque 2:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b2 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b3">
+                  Cargo de energia por bloque 3:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b3 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_punta">
+                  Cargo de energia punta:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_punta | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_resto">
+                  Cargo de energia resto:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_resto | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_valle">
+                  Cargo de energia valle:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_valle | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <br />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!--
+      <div class="col-lg-6">
+        <div class="card cream-skin bg-gradient-danger card-img-holder">
+          <div class="card-body">
+            <img
+              alt="circle-image"
+              class="card-img-absolute"
+              src="assets/img/money-rate.png"
+            />
+            <h4 class="font-weight-normal mb-3">
+              Historico de tarifa por distribuidora y código
+              <i class="mdi mdi-chart-line mdi-24px float-right"></i>
+            </h4>
+            <h3 class="mb-3" *ngIf="listAssets">
+              Distribuidora {{ rates.distribuidora }}
+            </h3>
+            <div *ngIf="rates.tarifa_actual.cargo_energia">
+              Cargo de energia: {{ rates.tarifa_actual.cargo_energia }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_b1">
+              Cargo de energia por bloque 1:
+              {{ rates.tarifa_actual.cargo_energia_b1 }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_b2">
+              Cargo de energia por bloque 2:
+              {{ rates.tarifa_actual.cargo_energia_b2 }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_b3">
+              Cargo de energia por bloque 3:
+              {{ rates.tarifa_actual.cargo_energia_b3 }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_punta">
+              Cargo de energia punta:
+              {{ rates.tarifa_actual.cargo_energia_punta }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_resto">
+              Cargo de energia resto:
+              {{ rates.tarifa_actual.cargo_energia_resto }}
+            </div>
+            <div *ngIf="rates.tarifa_actual.cargo_energia_valle">
+              Cargo de energia valle:
+              {{ rates.tarifa_actual.cargo_energia_valle }}
+            </div>
+          </div>
+        </div>
+      </div>
+    --></div>
+  </div>
+  <br />
+  <div class="container-fluid" *ngIf="listAssets">
     <div class="row">
       <div class="col-lg-6 col-md-6 col-sm-6">
         <div
@@ -17,7 +163,7 @@
               Total de plantas instaladas
               <i class="mdi mdi-chart-line mdi-24px float-right"></i>
             </h4>
-            <h2 class="mb-3" *ngIf="listAssets">{{ totalAssetsInstalled }}</h2>
+            <h2 class="mb-3">{{ totalAssetsInstalled }}</h2>
           </div>
         </div>
       </div>
@@ -41,7 +187,7 @@
       </div>
     </div>
 
-    <div class="row ">
+    <div class="row">
       <div *ngIf="error; then showAlert"></div>
       <ng-template #showAlert>
         <div class="col-lg-12">

+ 7 - 0
src/app/components/dashboard/dashboard.component.scss

@@ -26,3 +26,10 @@ agm-map {
   }
 }
 
+h3 {
+  margin: 5px 0 10px;
+  font-size: 1.2rem;
+  strong {
+    font-size: 1.3rem;
+  }
+}

+ 77 - 44
src/app/components/dashboard/dashboard.component.ts

@@ -12,7 +12,7 @@ import {
   Map,
   latLngBounds,
   LatLng,
-  point
+  point,
 } from "leaflet";
 
 import { MatPaginator } from "@angular/material/paginator";
@@ -24,11 +24,12 @@ import { of as observableOf, Observable, throwError, from } from "rxjs";
 
 import * as moment from "moment";
 import Swal from "sweetalert2";
+import { RatesService } from "@app/services/rates.service";
 
 @Component({
   selector: "app-dashboard",
   templateUrl: "./dashboard.component.html",
-  styleUrls: ["./dashboard.component.scss"]
+  styleUrls: ["./dashboard.component.scss"],
 })
 export class DashboardComponent implements OnInit {
   title = "Dashboard";
@@ -65,21 +66,21 @@ export class DashboardComponent implements OnInit {
   icon = icon({
     iconSize: [25, 41],
     iconAnchor: [13, 41],
-    iconUrl: "assets/img/marker-icon.png"
+    iconUrl: "assets/img/marker-icon.png",
     //shadowUrl: 'marker-shadow.png'
   });
 
   // Open Street Map definitions
   LAYER_OSM = tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
     maxZoom: 18,
-    attribution: "&copy; OpenStreetMap contributors"
+    attribution: "&copy; OpenStreetMap contributors",
   });
 
   // Values to bind to Leaflet Directive
   options = {
     layers: [this.LAYER_OSM],
     zoom: 10,
-    center: latLng([13.661714, -89.25153])
+    center: latLng([13.661714, -89.25153]),
   };
 
   @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@@ -87,13 +88,39 @@ export class DashboardComponent implements OnInit {
   userLevel: number;
   totalAssetsInstalled: any;
 
+  rates: Object = {
+    distribuidora: "-",
+    codigo_tarifa: "-",
+    tarifa_actual: {
+      dateMin: null,
+      dateMax: null,
+      cargo_energia: null,
+      cargo_energia_b1: null,
+      cargo_energia_b2: null,
+      cargo_energia_b3: null,
+      cargo_energia_punta: null,
+      cargo_energia_resto: null,
+      cargo_energia_valle: null,
+    },
+    incremento_porcentaje: {
+      cargo_energia: null,
+      cargo_energia_b1: null,
+      cargo_energia_b2: null,
+      cargo_energia_b3: null,
+      cargo_energia_punta: null,
+      cargo_energia_resto: null,
+      cargo_energia_valle: null,
+    },
+  };
+
   constructor(
     private plantsService: PlantsService,
     private route: ActivatedRoute,
     private orgService: OrganizationsService,
     private router: Router,
     private zone: NgZone,
-    private authService: AuthService
+    private authService: AuthService,
+    private ratesService: RatesService
   ) {
     //DEMO
     this.userLevel = +this.authService.getUserLevel();
@@ -101,37 +128,39 @@ export class DashboardComponent implements OnInit {
     Swal.fire({
       allowOutsideClick: false,
       type: "info",
-      text: "Espere por favor..."
+      text: "Espere por favor...",
     });
     Swal.showLoading();
 
     this.plantsService.getAllAssets().subscribe(
-      res => {
-        this.listAssets = res["data"]["assets"];
-        this.dataSource.data = this.listAssets;
-        this.dataSource.paginator = this.paginator;
-        this.dataSource.sort = this.sort;
-        this.assetKeys = Object.keys(this.listAssets);
+      (res) => {
+        if (res != null) {
+          this.listAssets = res["data"]["assets"];
+          this.dataSource.data = this.listAssets;
+          this.dataSource.paginator = this.paginator;
+          this.dataSource.sort = this.sort;
+          this.assetKeys = Object.keys(this.listAssets);
 
-        //DEMO
-        if (this.userLevel == 0) {
-          this.sumarize = 320;
-          this.totalAssetsInstalled = 1;
-        } else {
-          this.totalAssetsInstalled = this.listAssets.length || 0;
-          for (let prop in this.assetKeys) {
-            this.meterKeys2 = Object.keys(
-              this.listAssets.map(item => item["meters_installed"])[prop]
-            );
-            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);
+          //DEMO
+          if (this.userLevel == 0) {
+            this.sumarize = 320;
+            this.totalAssetsInstalled = 1;
+          } else {
+            this.totalAssetsInstalled = this.listAssets.length || 0;
+            for (let prop in this.assetKeys) {
+              this.meterKeys2 = Object.keys(
+                this.listAssets.map((item) => item["meters_installed"])[prop]
+              );
+              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);
+                }
               }
             }
           }
@@ -145,11 +174,11 @@ export class DashboardComponent implements OnInit {
           Swal.close();
         }, 1800);
       },
-      err => {
+      (err) => {
         Swal.fire({
           type: "error",
           title: "Error en el servidor",
-          text: err.message
+          text: err.message,
         });
       }
     );
@@ -160,18 +189,22 @@ export class DashboardComponent implements OnInit {
       Swal.close();
     }, 3000);
 
+    this.ratesService.getDefaultRate().subscribe((res) => {
+      this.rates = res["data"];
+    });
+
     var responsiveOptions: any[] = [
       [
         "screen and (max-width: 640px)",
         {
           seriesBarDistance: 5,
           axisX: {
-            labelInterpolationFnc: function(value) {
+            labelInterpolationFnc: function (value) {
               return value[0];
-            }
-          }
-        }
-      ]
+            },
+          },
+        },
+      ],
     ];
   }
 
@@ -183,7 +216,7 @@ export class DashboardComponent implements OnInit {
   }
 
   getAsset(id: string) {
-    return observableOf(this.listAssets.find(e => e.id === id));
+    return observableOf(this.listAssets.find((e) => e.id === id));
   }
 
   addMarkers() {
@@ -227,20 +260,20 @@ export class DashboardComponent implements OnInit {
         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
+        installedCapacity_kW: 300,
       };
       this.totalInstalled = 320;
     } else {
       this.totalInstalled = 0;
-      this.selectedPlant = this.listAssets.find(e => e.id === this.plantId);
+      this.selectedPlant = this.listAssets.find((e) => e.id === this.plantId);
       let keys = Object.keys(
         this.selectedPlant.meters_installed.map(
-          item => item["meters_installed"]
+          (item) => item["meters_installed"]
         )
       );
       for (let prop2 in keys) {
         this.totalInstalled += this.selectedPlant.meters_installed.map(
-          response => response["installedCapacity_kW"]
+          (response) => response["installedCapacity_kW"]
         )[prop2];
       }
     }

+ 10 - 0
src/app/components/login/login.component.html

@@ -52,6 +52,16 @@
               <button class="btn btn-primary" type="submit">
                 Iniciar sesión
               </button>
+
+              <div class="account-related">
+                <a class="lost-account" href="#/recover-password">
+                  Olvidé mi contraseña
+                </a>
+                <a class="new-account" href="#/new-account">
+                  Crear una cuenta
+                </a>
+              </div>
+              <br />
             </form>
           </div>
         </div>

+ 17 - 0
src/app/components/login/login.component.scss

@@ -54,6 +54,23 @@
   box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
 }
 
+.account-related {
+  margin: 20px 0 25px;
+  .lost-account,
+  .new-account {
+    text-transform: uppercase;
+    font-size: 0.7rem;
+    color: #1e88e5;
+    cursor: pointer;
+  }
+  .lost-account {
+    float: left;
+  }
+  .new-account {
+    float: right;
+  }
+}
+
 @media screen and (max-width: 640px) {
   .auth-box {
     width: 90%;

+ 46 - 42
src/app/components/login/login.component.ts

@@ -1,33 +1,37 @@
-import { Component, OnInit } from '@angular/core';
-import { FormGroup, FormBuilder, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
-import { AuthService } from '@app/services/auth2.service';
-import Swal from 'sweetalert2';
+import { Component, OnInit } from "@angular/core";
+import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+import { Router } from "@angular/router";
+import { AuthService } from "@app/services/auth2.service";
+import Swal from "sweetalert2";
 
 @Component({
-  selector: 'app-login',
-  templateUrl: './login.component.html',
-  styleUrls: ['./login.component.scss']
+  selector: "app-login",
+  templateUrl: "./login.component.html",
+  styleUrls: ["./login.component.scss"],
 })
 export class LoginComponent implements OnInit {
-
   loginForm: FormGroup;
-  submitted:boolean = false;
+  submitted: boolean = false;
 
-  constructor(private authService: AuthService, private formBuilder: FormBuilder, private router: Router) { }
+  constructor(
+    private authService: AuthService,
+    private formBuilder: FormBuilder,
+    private router: Router
+  ) {}
 
   ngOnInit() {
     this.loginForm = this.formBuilder.group({
-      email: ['', [Validators.required, Validators.email]],
-      password: ['', Validators.required]
+      email: ["", [Validators.required, Validators.email]],
+      password: ["", Validators.required],
     });
   }
 
-  get f() { return this.loginForm.controls; }
+  get f() {
+    return this.loginForm.controls;
+  }
 
   login() {
     this.submitted = true;
-
     // stop here if form is invalid
     if (this.loginForm.invalid) {
       return;
@@ -35,37 +39,37 @@ export class LoginComponent implements OnInit {
 
     Swal.fire({
       allowOutsideClick: false,
-      type: 'info',
-      text: 'Espere por favor...'
+      type: "info",
+      text: "Espere por favor...",
     });
     Swal.showLoading();
 
-    this.authService.login(
-      {
+    this.authService
+      .login({
         email: this.f.email.value,
-        password: this.f.password.value
-      }
-    )
-    .subscribe(success => {
-      if (success) {
-        window.location.href="#/dashboard";
-      }
-      else {
-        Swal.fire({
-          type: 'error',
-          title: 'No se pudo auntenticar',
-          text: "Email o contraseña incorrecta"
-        })  
-      }
-    },(err) => {
-      Swal.fire({
-        type: 'error',
-        title: 'Error en el servidor',
-        text: err.message
-      });
-    });
+        password: this.f.password.value,
+      })
+      .subscribe(
+        (success) => {
+          if (success) {
+            window.location.href = "#/dashboard";
+          } else {
+            Swal.fire({
+              type: "error",
+              title: "No se pudo auntenticar",
+              text: "Email o contraseña incorrecta",
+            });
+          }
+        },
+        (err) => {
+          Swal.fire({
+            type: "error",
+            title: "Error en el servidor",
+            text: err.message,
+          });
+        }
+      );
   }
-
 }
 
 /* import { Component, OnInit } from '@angular/core';
@@ -156,4 +160,4 @@ export class LoginComponent implements OnInit {
   }
 
 }
-*/
+*/

+ 56 - 0
src/app/components/lost-account/lost-account.component.html

@@ -0,0 +1,56 @@
+<div id="wrapper">
+  <div class="vertical-align-wrap">
+    <div class="vertical-align-middle auth-main">
+      <div class="auth-box">
+        <div class="top">
+          <img alt="Inverlec" src="./assets/img/inverlec_logo.png" />
+        </div>
+
+        <div class="card">
+          <div class="header">
+            <h1 class="lead">Recuperación de contraseña</h1>
+          </div>
+          <div class="body">
+            <form
+              *ngIf="!hideForm"
+              [formGroup]="activateForm"
+              (ngSubmit)="sendInstructions()"
+            >
+              <div class="form-group">
+                <label for="email">Correo electrónico</label>
+                <input
+                  type="text"
+                  class="form-control"
+                  formControlName="email"
+                  required
+                  email
+                />
+              </div>
+
+              <br />
+
+              <div class="div-center">
+                <button class="btn btn-primary" type="submit">
+                  Enviar instrucciones
+                </button>
+              </div>
+            </form>
+            <div *ngIf="hideForm">
+              <div class="tokenSuccess">
+                <h3>
+                  {{ activateMessage }}
+                </h3>
+              </div>
+            </div>
+
+            <div *ngIf="error">
+              <h3>
+                {{ errorMessage }}
+              </h3>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 112 - 0
src/app/components/lost-account/lost-account.component.scss

@@ -0,0 +1,112 @@
+.hint-wrapper {
+  background: #eee;
+  border: 1px solid #aaa;
+  padding: 5px;
+  margin-bottom: 5px;
+}
+
+.vertical-align-wrap {
+  //position: absolute;
+  width: 100%;
+  height: 100%;
+  //display: table;
+}
+
+.vertical-align-middle {
+  //display: table-cell;
+  vertical-align: middle;
+}
+
+.auth-box {
+  width: 800px;
+  height: auto;
+  margin: 20px auto;
+
+  .top {
+    margin: 0 auto;
+    text-align: center;
+  }
+
+  .card {
+    padding: 25px;
+
+    .lead {
+      text-align: center;
+      font-size: 1.5rem;
+      color: #223d7d;
+    }
+  }
+
+  .div-center {
+    text-align: center;
+    margin: 0 auto;
+  }
+}
+
+.auth-main::before {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 200px;
+  height: 100%;
+  z-index: -1;
+  background: #223d7d; //#f0f0f0;
+}
+
+.auth-main:after {
+  content: "";
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: -2;
+  //background: #223d7d;
+  //background: url(../../assets/images/auth_bg.jpg) no-repeat top left fixed;
+}
+
+.card {
+  background: #fff;
+  transition: 0.5s;
+  border: 0;
+  margin-bottom: 30px;
+  border-radius: 0.55rem;
+  position: relative;
+  width: 100%;
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
+}
+
+.tokenError,
+.tokenSuccess {
+  padding: 40px 20px;
+  text-align: center;
+  margin-bottom: 20px;
+  font-weight: bold;
+}
+
+.tokenError {
+  color: #f44336;
+}
+
+.tokenSuccess {
+  color: #4caf50;
+}
+
+h3 {
+  font-size: 1.6rem;
+  margin-bottom: 30px;
+}
+
+@media screen and (max-width: 640px) {
+  .auth-box {
+    width: 90%;
+  }
+}
+
+@media screen and (max-width: 992px) {
+  .auth-box {
+    width: 80%;
+    margin: 0 auto;
+  }
+}

+ 113 - 0
src/app/components/lost-account/lost-account.component.ts

@@ -0,0 +1,113 @@
+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,
+  PasswordStrengthValidator,
+} from "../plugins/validator/validator.component";
+
+@Component({
+  selector: "app-lost-account",
+  templateUrl: "./lost-account.component.html",
+  styleUrls: ["./lost-account.component.scss"],
+})
+export class LostAccountComponent implements OnInit {
+  token: string;
+  validToken: boolean;
+  activateForm: FormGroup;
+  errorMessage: string;
+  userActivated: boolean;
+  successActivation: boolean;
+  invalidToken: boolean;
+  submitted: boolean = false;
+  showDetails: boolean;
+  hideForm: boolean = false;
+  activateMessage: string = "Se han enviado instrucciones al correo ingresado";
+  error: boolean;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private route: ActivatedRoute,
+    private userService: UserService
+  ) {
+    this.route.queryParams.subscribe((params) => {
+      this.token = params["token"];
+    });
+  }
+
+  ngOnInit() {
+    this.activateForm = this.formBuilder.group({
+      // Load information
+      email: ["", Validators.required],
+    });
+  }
+
+  get f() {
+    return this.activateForm.controls;
+  }
+
+  sendInstructions() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.activateForm.invalid) {
+      return;
+    }
+
+    this.userService
+      .retrieveAccount({
+        email: this.f.email.value,
+      })
+      .subscribe(
+        (res) => {
+          this.hideForm = true;
+          this.activateMessage =
+            "Se han enviado instrucciones al correo ingresado";
+        },
+        (err) => {
+          this.error = true;
+          this.validToken = false;
+          this.errorMessage = err.message;
+        }
+      );
+    //
+  }
+}
+
+/*
+
+Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+
+    this.authService.login(
+      {
+        email: this.f.email.value,
+        password: this.f.password.value
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        window.location.href="#/dashboard";
+      }
+      else {
+        Swal.fire({
+          type: 'error',
+          title: 'No se pudo auntenticar',
+          text: "Email o contraseña incorrecta"
+        })  
+      }
+    },(err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
+
+    */

+ 152 - 0
src/app/components/new-account/new-account.component.html

@@ -0,0 +1,152 @@
+<div id="wrapper">
+  <div class="vertical-align-wrap">
+    <div class="vertical-align-middle auth-main">
+      <div class="auth-box">
+        <div class="top">
+          <img alt="Inverlec" src="./assets/img/inverlec_logo.png" />
+        </div>
+
+        <div class="card">
+          <div class="header">
+            <h1 class="lead">Registro de usuario</h1>
+          </div>
+          <div class="body">
+            <form
+              class="form-auth-small ng-untouched ng-pristine ng-valid"
+              [formGroup]="userForm"
+              (ngSubmit)="newAccount()"
+            >
+              <div class="form-group">
+                <label for="first_name">Nombre: </label>
+                <input
+                  type="text"
+                  formControlName="first_name"
+                  class="form-control"
+                  [ngClass]="{ 'is-invalid': submitted && f.first_name.errors }"
+                />
+                <div
+                  *ngIf="submitted && f.first_name.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.first_name.errors.required">
+                    Campo requerido
+                  </div>
+                </div>
+              </div>
+
+              <div class="form-group">
+                <label for="last_name">Apellido: </label>
+                <input
+                  type="text"
+                  formControlName="last_name"
+                  class="form-control"
+                  [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">
+                <label for="email">Email: </label>
+                <input
+                  type="text"
+                  formControlName="email"
+                  class="form-control"
+                  [ngClass]="{ 'is-invalid': submitted && f.email.errors }"
+                />
+                <div
+                  *ngIf="submitted && f.email.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.email.errors.required">Campo requerido</div>
+                </div>
+              </div>
+
+              <div class="form-group">
+                <label for="phone">Teléfono: </label>
+                <input
+                  type="text"
+                  formControlName="phone"
+                  class="form-control"
+                  [ngClass]="{ 'is-invalid': submitted && f.phone.errors }"
+                />
+                <div
+                  *ngIf="submitted && f.phone.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.phone.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 mayúsculas, minúsculas,
+                    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>
+
+              <div class="form-group">
+                <label for="confirm_password">Confirmar contraseña</label>
+                <input
+                  type="password"
+                  class="form-control"
+                  formControlName="confirm_password"
+                  [ngClass]="{
+                    'is-invalid': submitted && f.confirm_password.errors
+                  }"
+                />
+                <div
+                  *ngIf="submitted && f.confirm_password.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.confirm_password.errors.required">
+                    Campo requerido
+                  </div>
+                  <div *ngIf="f.confirm_password.errors.mustMatch">
+                    Las contraseñas deben coincidir
+                  </div>
+                </div>
+              </div>
+
+              <br />
+
+              <button class="btn btn-primary">
+                Crear cuenta
+              </button>
+            </form>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 112 - 0
src/app/components/new-account/new-account.component.scss

@@ -0,0 +1,112 @@
+.hint-wrapper {
+  background: #eee;
+  border: 1px solid #aaa;
+  padding: 5px;
+  margin-bottom: 5px;
+}
+
+.vertical-align-wrap {
+  //position: absolute;
+  width: 100%;
+  height: 100%;
+  //display: table;
+}
+
+.vertical-align-middle {
+  //display: table-cell;
+  vertical-align: middle;
+}
+
+.auth-box {
+  width: 800px;
+  height: auto;
+  margin: 20px auto;
+
+  .top {
+    margin: 0 auto;
+    text-align: center;
+  }
+
+  .card {
+    padding: 25px;
+
+    .lead {
+      text-align: center;
+      font-size: 1.5rem;
+      color: #223d7d;
+    }
+  }
+
+  .div-center {
+    text-align: center;
+    margin: 0 auto;
+  }
+}
+
+.auth-main::before {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 200px;
+  height: 100%;
+  z-index: -1;
+  background: #223d7d; //#f0f0f0;
+}
+
+.auth-main:after {
+  content: "";
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: -2;
+  //background: #223d7d;
+  //background: url(../../assets/images/auth_bg.jpg) no-repeat top left fixed;
+}
+
+.card {
+  background: #fff;
+  transition: 0.5s;
+  border: 0;
+  margin-bottom: 30px;
+  border-radius: 0.55rem;
+  position: relative;
+  width: 100%;
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
+}
+
+.tokenError,
+.tokenSuccess {
+  padding: 40px 20px;
+  text-align: center;
+  margin-bottom: 20px;
+  font-weight: bold;
+}
+
+.tokenError {
+  color: #f44336;
+}
+
+.tokenSuccess {
+  color: #4caf50;
+}
+
+h3 {
+  font-size: 1.6rem;
+  margin-bottom: 30px;
+}
+
+@media screen and (max-width: 640px) {
+  .auth-box {
+    width: 90%;
+  }
+}
+
+@media screen and (max-width: 992px) {
+  .auth-box {
+    width: 80%;
+    margin: 0 auto;
+  }
+}

+ 154 - 0
src/app/components/new-account/new-account.component.ts

@@ -0,0 +1,154 @@
+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,
+  PasswordStrengthValidator,
+} from "../plugins/validator/validator.component";
+
+@Component({
+  selector: "app-new-account",
+  templateUrl: "./new-account.component.html",
+  styleUrls: ["./new-account.component.scss"],
+})
+export class NewAccountComponent implements OnInit {
+  token: string;
+  validToken: boolean;
+  userForm: FormGroup;
+  errorMessage: string;
+  userActivated: boolean;
+  successActivation: boolean;
+  activateMessage: string;
+  invalidToken: boolean;
+  submitted: boolean = false;
+  showDetails: boolean;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private route: ActivatedRoute,
+    private userService: UserService
+  ) {
+    this.route.queryParams.subscribe((params) => {
+      this.token = params["token"];
+    });
+  }
+
+  ngOnInit() {
+    this.userForm = this.formBuilder.group(
+      {
+        first_name: ["", Validators.required],
+        last_name: ["", Validators.required],
+        email: ["", Validators.required],
+        phone: ["", Validators.required],
+        password: [
+          "",
+          [
+            Validators.required,
+            Validators.minLength(8),
+            PasswordStrengthValidator,
+          ],
+        ],
+        confirm_password: ["", Validators.required],
+        distribuidora: [""],
+        tarifa: [""],
+      },
+      {
+        validator: ValidatorComponent("password", "confirm_password"),
+      }
+    );
+  }
+
+  get f() {
+    return this.userForm.controls;
+  }
+
+  newAccount() {
+    console.log(this.userForm);
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.userForm.invalid) {
+      return;
+    }
+
+    Swal.fire({
+      allowOutsideClick: false,
+      type: "info",
+      text: "Espere por favor...",
+    });
+    Swal.showLoading();
+
+    this.userService
+      .createCustomer({
+        email: this.f.email.value,
+        first_name: this.f.first_name.value,
+        last_name: this.f.last_name.value,
+        phone_number: this.f.phone.value,
+        password: this.f.password.value,
+        confirm_password: this.f.confirm_password.value,
+      })
+      .subscribe(
+        (success) => {
+          if (success) {
+            Swal.fire({
+              allowOutsideClick: false,
+              type: "success",
+              showCancelButton: false,
+              title: "Exito",
+              confirmButtonText: "Su registro ha sido completado exitosamente",
+            }).then((result) => {
+              if (result.value) {
+                window.location.href = "/";
+              }
+            });
+          }
+        },
+        (err) => {
+          Swal.fire({
+            type: "error",
+            title: "Error al guardar",
+            text: err.message,
+          });
+        }
+      );
+    //
+  }
+}
+
+/*
+
+Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+
+    this.authService.login(
+      {
+        email: this.f.email.value,
+        password: this.f.password.value
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        window.location.href="#/dashboard";
+      }
+      else {
+        Swal.fire({
+          type: 'error',
+          title: 'No se pudo auntenticar',
+          text: "Email o contraseña incorrecta"
+        })  
+      }
+    },(err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
+
+    */

+ 94 - 0
src/app/components/new-password/new-password.component.html

@@ -0,0 +1,94 @@
+<div id="wrapper">
+  <div class="vertical-align-wrap">
+    <div class="vertical-align-middle auth-main">
+      <div class="auth-box">
+        <div class="top">
+          <a href="/">
+            <img alt="Inverlec" src="./assets/img/inverlec_logo.png" />
+          </a>
+        </div>
+
+        <div class="card">
+          <div *ngIf="!newPass" class="header">
+            <h1 class="lead">{{ errorMessage }}</h1>
+          </div>
+          <div class="header" *ngIf="newPass">
+            <h1 class="lead">Nueva contraseña</h1>
+          </div>
+          <div class="body" *ngIf="newPass">
+            <form [formGroup]="activateForm" (ngSubmit)="newPassword()">
+              <div class="form-group">
+                <br />
+                <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>
+
+              <div class="form-group">
+                <label for="confirm_password">Confirmar contraseña</label>
+                <input
+                  type="password"
+                  class="form-control"
+                  formControlName="confirm_password"
+                  [ngClass]="{
+                    'is-invalid': submitted && f.confirm_password.errors
+                  }"
+                />
+                <div
+                  *ngIf="submitted && f.confirm_password.errors"
+                  class="invalid-feedback"
+                >
+                  <div *ngIf="f.confirm_password.errors.required">
+                    Campo requerido
+                  </div>
+                  <div *ngIf="f.confirm_password.errors.mustMatch">
+                    Las contraseñas deben coincidir
+                  </div>
+                </div>
+              </div>
+
+              <br />
+
+              <br />
+
+              <div class="div-center">
+                <button class="btn btn-primary" type="submit">
+                  Enviar instrucciones
+                </button>
+              </div>
+            </form>
+          </div>
+
+          <div></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 112 - 0
src/app/components/new-password/new-password.component.scss

@@ -0,0 +1,112 @@
+.hint-wrapper {
+  background: #eee;
+  border: 1px solid #aaa;
+  padding: 5px;
+  margin-bottom: 5px;
+}
+
+.vertical-align-wrap {
+  //position: absolute;
+  width: 100%;
+  height: 100%;
+  //display: table;
+}
+
+.vertical-align-middle {
+  //display: table-cell;
+  vertical-align: middle;
+}
+
+.auth-box {
+  width: 800px;
+  height: auto;
+  margin: 20px auto;
+
+  .top {
+    margin: 0 auto;
+    text-align: center;
+  }
+
+  .card {
+    padding: 25px;
+
+    .lead {
+      text-align: center;
+      font-size: 1.5rem;
+      color: #223d7d;
+    }
+  }
+
+  .div-center {
+    text-align: center;
+    margin: 0 auto;
+  }
+}
+
+.auth-main::before {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 200px;
+  height: 100%;
+  z-index: -1;
+  background: #223d7d; //#f0f0f0;
+}
+
+.auth-main:after {
+  content: "";
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: -2;
+  //background: #223d7d;
+  //background: url(../../assets/images/auth_bg.jpg) no-repeat top left fixed;
+}
+
+.card {
+  background: #fff;
+  transition: 0.5s;
+  border: 0;
+  margin-bottom: 30px;
+  border-radius: 0.55rem;
+  position: relative;
+  width: 100%;
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
+}
+
+.tokenError,
+.tokenSuccess {
+  padding: 40px 20px;
+  text-align: center;
+  margin-bottom: 20px;
+  font-weight: bold;
+}
+
+.tokenError {
+  color: #f44336;
+}
+
+.tokenSuccess {
+  color: #4caf50;
+}
+
+h3 {
+  font-size: 1.6rem;
+  margin-bottom: 30px;
+}
+
+@media screen and (max-width: 640px) {
+  .auth-box {
+    width: 90%;
+  }
+}
+
+@media screen and (max-width: 992px) {
+  .auth-box {
+    width: 80%;
+    margin: 0 auto;
+  }
+}

+ 155 - 0
src/app/components/new-password/new-password.component.ts

@@ -0,0 +1,155 @@
+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,
+  PasswordStrengthValidator,
+} from "../plugins/validator/validator.component";
+
+@Component({
+  selector: "app-new-password",
+  templateUrl: "./new-password.component.html",
+  styleUrls: ["./new-password.component.scss"],
+})
+export class NewPasswordComponent implements OnInit {
+  token: string;
+  validToken: boolean;
+  activateForm: FormGroup;
+  errorMessage: string;
+  userActivated: boolean;
+  successActivation: boolean;
+  activateMessage: string;
+  invalidToken: boolean;
+  submitted: boolean = false;
+  showDetails: boolean;
+  newPass: boolean;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private route: ActivatedRoute,
+    private userService: UserService
+  ) {
+    this.route.queryParams.subscribe((params) => {
+      this.token = params["token"];
+    });
+  }
+
+  ngOnInit() {
+    if (this.token !== null) {
+      this.userService
+        .validateUserToken({
+          token: this.token,
+          process: "pwd_recovery",
+        })
+        .subscribe(
+          (res) => {
+            console.log(res);
+            let userData = res["data"].user;
+            this.newPass = true;
+            this.validToken = true;
+            this.activateForm = this.formBuilder.group(
+              {
+                // Load information
+                password: [
+                  "",
+                  [
+                    Validators.required,
+                    Validators.minLength(8),
+                    PasswordStrengthValidator,
+                  ],
+                ],
+                confirm_password: ["", Validators.required],
+              },
+              {
+                validator: ValidatorComponent("password", "confirm_password"),
+              }
+            );
+          },
+          (err) => {
+            this.newPass = false;
+            this.userActivated = true;
+            this.invalidToken = true;
+            this.errorMessage = err.message;
+          }
+        );
+    } else {
+      this.invalidToken = true;
+      this.errorMessage = "No existe el token";
+    }
+  }
+
+  get f() {
+    return this.activateForm.controls;
+  }
+
+  newPassword() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.activateForm.invalid) {
+      return;
+    }
+
+    this.userService
+      .activateUser({
+        email: this.f.email.value,
+        first_name: this.f.first_name.value,
+        last_name: this.f.last_name.value,
+        password: this.f.password.value,
+        confirm_password: this.f.confirm_password.value,
+        /*phone_number: this.f.phone_number.value,
+        distribuidora: this.f.distribuidora.value,
+        cod_tarifa: this.f.cod_tarifa.value*/
+      })
+      .subscribe(
+        (res) => {
+          this.successActivation = true;
+          this.validToken = false;
+          this.activateMessage = "Usuario registrado con exito";
+        },
+        (err) => {
+          this.validToken = false;
+          this.errorMessage = err.message;
+        }
+      );
+    //
+  }
+}
+
+/*
+
+Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+
+    this.authService.login(
+      {
+        email: this.f.email.value,
+        password: this.f.password.value
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        window.location.href="#/dashboard";
+      }
+      else {
+        Swal.fire({
+          type: 'error',
+          title: 'No se pudo auntenticar',
+          text: "Email o contraseña incorrecta"
+        })  
+      }
+    },(err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
+
+    */

+ 283 - 10
src/app/components/profile/profile.component.html

@@ -1,16 +1,289 @@
-<h2 class="floating-title">{{title}}</h2>
-
+<h2 class="floating-title">{{ title }}</h2>
+<div
+  class="header pb-6 d-flex align-items-center"
+  style="
+    min-height: 240px;
+    background-size: cover;
+    background-position: center top;
+  "
+>
+  <!-- Mask -->
+  <span class="mask bg-gradient-default opacity-8"></span
+  ><!-- Header container -->
+  <div class="container-fluid d-flex align-items-center">
+    <div class="row">
+      <div class="col-lg-7 col-md-10">
+        <h1 class="display-2 text-white">Hola {{ first_name }}</h1>
+        <p class="text-white mt-0 mb-5">
+          Aquí podra editar la información general de la cuenta. Si eres usuario
+          pro, podras editar la información de la tarifa.
+        </p>
+      </div>
+    </div>
+  </div>
+</div>
 <div class="main-content">
-  
   <div class="container-fluid">
+    <div class="row justify-content-center">
+      <div class="col-12">
+        <div class="align-container">
+          <div class="card">
+            <div class="card-header">
+              <div class="row align-items-center">
+                <div class="col-8"><h3 class="mb-0">Editar perfil</h3></div>
+                <div class="col-4 text-right">
+                  <a href="#!" class="btn btn-sm btn-warning"
+                    >Actualizar a usuario pro</a
+                  >
+                </div>
+              </div>
+            </div>
+            <div class="card-body" *ngIf="editProfile">
+              <form
+                class="form-auth-small ng-untouched ng-pristine ng-valid"
+                [formGroup]="userForm"
+                (ngSubmit)="editUser()"
+              >
+                <h6 class="heading-small text-muted mb-4">
+                  Información de usuario
+                </h6>
+                <div class="pl-lg-4">
+                  <div class="row">
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="first_name">Nombre: </label>
+                        <input
+                          type="text"
+                          formControlName="first_name"
+                          class="form-control"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.first_name.errors
+                          }"
+                        />
+                        <div
+                          *ngIf="submitted && f.first_name.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.first_name.errors.required">
+                            Campo requerido
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="last_name">Apellido: </label>
+                        <input
+                          type="text"
+                          formControlName="last_name"
+                          class="form-control"
+                          [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>
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="email">Email: </label>
+                        <input
+                          type="text"
+                          formControlName="email"
+                          class="form-control"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.email.errors
+                          }"
+                          readonly
+                        />
+                        <div
+                          *ngIf="submitted && f.email.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.email.errors.required">
+                            Campo requerido
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="phone">Telefono: </label>
+                        <input
+                          type="text"
+                          formControlName="phone"
+                          class="form-control"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.phone.errors
+                          }"
+                        />
+                        <div
+                          *ngIf="submitted && f.phone.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.phone.errors.required">
+                            Campo requerido
+                          </div>
+                        </div>
+                      </div>
+                    </div>
 
-    <div class="row">
-      <div class="col-lg-12 col-md-12 col-sm-12">
-        <div class="card dark-yellow-skin bg-gradient-danger card-img-holder text-white profile">
-          <div class="card-body">
-            <img alt="circle-image" class="card-img-absolute" src="assets/img/waves-opt.png">
-            <h4 class="font-weight-normal mb-3">Administrador</h4>
-            <h2 class="mb-3">{{email}}</h2>
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="distribuidora">Distribuidora: </label>
+
+                        <input
+                          *ngIf="user.distribuidora"
+                          type="text"
+                          class="form-control"
+                          [value]="user.distribuidora"
+                          readonly
+                        />
+                        <select
+                          *ngIf="!user.distribuidora"
+                          class="custom-select"
+                          formControlName="distribuidora"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.distribuidora.errors
+                          }"
+                        >
+                          <option
+                            *ngFor="let item of distributor"
+                            [value]="item.codigo"
+                            [selected]="item.codigo == user.distribuidora"
+                            >{{ item.nombre }}</option
+                          >
+                        </select>
+                        <div
+                          *ngIf="submitted && f.distribuidora.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.distribuidora.errors.required">
+                            Campo requerido
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <div class="form-group">
+                        <label for="tarifa">Código de tarifa: </label>
+                        <input
+                          *ngIf="user.cod_tarifa"
+                          type="text"
+                          class="form-control"
+                          [value]="user.cod_tarifa"
+                          readonly
+                        />
+                        <select
+                          *ngIf="!user.cod_tarifa"
+                          class="custom-select"
+                          formControlName="tarifa"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.tarifa.errors
+                          }"
+                        >
+                          <option
+                            *ngFor="let item of codigo_tarifa"
+                            [value]="item.codigo"
+                            [selected]="item.codigo == user.cod_tarifa"
+                            >{{ item.nombre }}</option
+                          >
+                        </select>
+                        <div
+                          *ngIf="submitted && f.tarifa.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.tarifa.errors.required">
+                            Campo requerido
+                          </div>
+                        </div>
+                        <button
+                          class="btn btn-sm btn-info"
+                          (click)="helpModal()"
+                        >
+                          ¿Donde encuentro mi tarifa?
+                        </button>
+                      </div>
+                    </div>
+
+                    <div class="col-lg-12">
+                      <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>
+                      <div class="form-group">
+                        <label for="confirm_password"
+                          >Confirmar contraseña</label
+                        >
+                        <input
+                          type="password"
+                          class="form-control"
+                          formControlName="confirm_password"
+                          [ngClass]="{
+                            'is-invalid': submitted && f.confirm_password.errors
+                          }"
+                        />
+                        <div
+                          *ngIf="submitted && f.confirm_password.errors"
+                          class="invalid-feedback"
+                        >
+                          <div *ngIf="f.confirm_password.errors.required">
+                            Campo requerido
+                          </div>
+                          <div *ngIf="f.confirm_password.errors.mustMatch">
+                            Las contraseñas deben coincidir
+                          </div>
+                        </div>
+                      </div>
+
+                      <br />
+                      <button class="btn btn-primary">
+                        Actualizar información
+                      </button>
+                    </div>
+                  </div>
+                </div>
+              </form>
+            </div>
           </div>
         </div>
       </div>

+ 15 - 2
src/app/components/profile/profile.component.scss

@@ -1,6 +1,19 @@
-
 .profile {
   background: url("/assets/img/dawn.jpg") 0 320px;
 }
+.header {
+  position: relative;
+  padding: 10px 20px;
+}
+.bg-gradient-default {
+  background: linear-gradient(87deg, #172b4d 0, #1a174d 100%) !important;
+}
 
-
+.mask {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  transition: all 0.15s ease;
+}

+ 156 - 9
src/app/components/profile/profile.component.ts

@@ -1,23 +1,170 @@
-import { Component, OnInit } from '@angular/core';
-import { Title } from '@angular/platform-browser';
+import { Component, OnInit } from "@angular/core";
+import { Title } from "@angular/platform-browser";
+import { FormGroup, FormBuilder, Validators, FormArray } from "@angular/forms";
+import { UserService } from "@app/services/user.service";
+import { CatalogsService } from "@app/services/catalogs.service";
+
+import Swal from "sweetalert2";
+import { AuthService } from "@app/services/auth2.service";
+import {
+  ValidatorComponent,
+  PasswordStrengthValidator,
+} from "../plugins/validator/validator.component";
 
 @Component({
-  selector: 'app-profile',
-  templateUrl: './profile.component.html',
-  styleUrls: ['./profile.component.scss']
+  selector: "app-profile",
+  templateUrl: "./profile.component.html",
+  styleUrls: ["./profile.component.scss"],
 })
 export class ProfileComponent implements OnInit {
-
   title = "Perfil del usuario";
+  userForm: FormGroup;
+  submitted: boolean = false;
+  isLoadingOrganization: boolean;
+  first_name: string = "Usuario";
+  user: any;
+  editProfile: boolean;
+  distributor: any;
+  codigo_tarifa: any;
 
-  constructor(private titleService: Title) { }
+  constructor(
+    private titleService: Title,
+    private userService: UserService,
+    private catalogsService: CatalogsService,
 
-  email:string;
+    private formBuilder: FormBuilder,
+    private authService: AuthService
+  ) {}
+
+  email: string;
 
   ngOnInit() {
+    Swal.fire({
+      allowOutsideClick: false,
+      type: "info",
+      text: "Espere por favor...",
+    });
+    Swal.showLoading();
+
+    this.catalogsService.getCatalogs().subscribe((res) => {
+      console.log(res);
+    });
+
+    this.catalogsService.getCatalogByName("distribuidoras").subscribe((res) => {
+      this.distributor = res["data"]["catalogo"]["records"];
+    });
+
+    this.catalogsService.getCatalogByName("tarifas").subscribe((res) => {
+      this.codigo_tarifa = res["data"]["catalogo"]["records"];
+    });
+
+    this.userService
+      .getUserById(this.authService.getUserId())
+      .subscribe((ans) => {
+        this.user = ans["data"]["user"];
+        console.log(this.user);
+        Swal.close();
+        this.editProfile = true;
+        this.first_name = this.user.first_name;
+        this.userForm = this.formBuilder.group(
+          {
+            first_name: [this.user.first_name, Validators.required],
+            last_name: [this.user.last_name, Validators.required],
+            email: [this.user.email, Validators.required],
+            phone: [
+              this.user.phone_number == undefined ? "" : this.user.phone_number,
+              Validators.required,
+            ],
+            distribuidora: [this.user.distribuidora],
+            tarifa: [this.user.cod_tarifa],
+            password: [
+              "",
+              [Validators.minLength(8), PasswordStrengthValidator],
+            ],
+            confirm_password: [""],
+          },
+          {
+            validator: ValidatorComponent("password", "confirm_password"),
+          }
+        );
+      });
+
     this.titleService.setTitle(this.title);
-    this.email = localStorage.getItem("email");
+  }
 
+  get f() {
+    return this.userForm.controls;
   }
 
+  editUser(form: any) {
+    this.submitted = true;
+    console.log(this.userForm);
+    // stop here if form is invalid
+    if (this.userForm.invalid) {
+      return;
+    }
+
+    let user = {
+      first_name: this.f.first_name.value,
+      last_name: this.f.last_name.value,
+      email: this.f.email.value,
+      phone_number: this.f.phone.value,
+      distribuidora: this.f.distribuidora.value,
+      cod_tarifa: this.f.tarifa.value,
+    };
+
+    if (this.f.password.value != "" && this.f.confirm_password.value != "") {
+      user["password"] = this.f.password.value;
+      user["confirm_password"] = this.f.confirm_password.value;
+    }
+
+    Swal.fire({
+      allowOutsideClick: false,
+      type: "info",
+      text: "Espere por favor...",
+    });
+    Swal.showLoading();
+    this.userService.editCustomer(this.authService.getUserId(), user).subscribe(
+      (success) => {
+        if (success) {
+          Swal.fire({
+            allowOutsideClick: false,
+            type: "success",
+            showCancelButton: false,
+            title: "Exito",
+            confirmButtonText: "Se ha actualizado su perfil exitosamente",
+          }).then((result) => {
+            Swal.close();
+          });
+        }
+      },
+      (err) => {
+        Swal.fire({
+          type: "error",
+          title: "Error al guardar",
+          text: err.message,
+        });
+      }
+    );
+  }
+
+  helpModal() {
+    Swal.fire({
+      title: "<strong>¿Dónde encuentro mi tipo de tarifa?</strong>",
+      type: "info",
+      html:
+        "<p style='text-align: left'>Si tu distribuidora de energía es del grupo de empresas AES, puedes verificar el tipo de tarifa en la siguiente imagen:</p>" +
+        '<img alt="Factura AES" src="./assets/img/AESFactura.png" style="width: 100%;"/><br><br> ' +
+        '<p style="text-align: left">Para más información puedes visitar el sitio oficial de <a href="http://www.aes-elsalvador.com/servicio-al-cliente/conoce-tu-factura/" target="_blank">AES</a></p>' +
+        "<br><br>" +
+        "<p style='text-align: left'>Si tu distribuidora de energía es DelSur, puedes verificar el tipo de tarifa en la siguiente imagen:</p>" +
+        '<img alt="Factura AES" src="./assets/img/DelSurFactura2.png" style="width: 100%;"/>' +
+        '<p style="text-align: left">Para más información puedes visitar el sitio oficial de <a href="https://www.delsur.com.sv/conoce-tu-factura/" target="_blank">DelSur</a></p>' +
+        "<br><br>",
+      showCloseButton: true,
+      width: 600,
+      confirmButtonText: '<i class="fa fa-thumbs-up"></i> Cerrar',
+      confirmButtonAriaLabel: "Thumbs up, great!",
+    });
+  }
 }

+ 260 - 0
src/app/components/rates/rates.component.html

@@ -0,0 +1,260 @@
+<h2 class="floating-title">{{ title }}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      <div class="col-lg-12">
+        <h4><b>Histórico de tarifa por distribuidora y código </b></h4>
+        <div class="card cream-skin bg-gradient-danger card-img-holder">
+          <div class="card-body">
+            <img
+              alt="circle-image"
+              class="card-img-absolute"
+              src="assets/img/time-is-money.png"
+            />
+
+            <div class="row no-gutters">
+              <div class="col-sm-12 col-md-6">
+                <h3 class="mb-3">
+                  Distribuidora:
+                  <strong *ngIf="rates.distribuidora">
+                    {{ rates.distribuidora }}
+                  </strong>
+                </h3>
+                <h4>Tarifa actual:</h4>
+                <div *ngIf="rates.tarifa_actual.cargo_energia">
+                  Cargo de energia:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b1">
+                  Cargo de energia por bloque 1:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b1 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b2">
+                  Cargo de energia por bloque 2:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b2 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_b3">
+                  Cargo de energia por bloque 3:
+                  <strong>
+                    {{ rates.tarifa_actual.cargo_energia_b3 | number: "1.2-4" }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_punta">
+                  Cargo de energia punta:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_punta | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_resto">
+                  Cargo de energia resto:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_resto | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <div *ngIf="rates.tarifa_actual.cargo_energia_valle">
+                  Cargo de energia valle:
+                  <strong>
+                    {{
+                      rates.tarifa_actual.cargo_energia_valle | number: "1.2-4"
+                    }}
+                  </strong>
+                </div>
+                <br />
+              </div>
+              <div class="col-sm-12 col-md-6">
+                <div>
+                  <h3>
+                    Vigencia:
+                    <strong *ngIf="rates.tarifa_actual.dateMin">
+                      {{ rates.tarifa_actual.dateMin | date: "dd/MM/yyyy" }} -
+                      {{ rates.tarifa_actual.dateMax | date: "dd/MM/yyyy" }}
+                    </strong>
+                  </h3>
+                </div>
+                <h4>Incremento con respecto a tarifa anterior (%)</h4>
+
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia">
+                  Cargo de energia:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_b1">
+                  Cargo de energia por bloque 1:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_b1
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_b2">
+                  Cargo de energia por bloque 2:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_b2
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_b3">
+                  Cargo de energia por bloque 3:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_b3
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_punta">
+                  Cargo de energia punta:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_punta
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_resto">
+                  Cargo de energia resto:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_resto
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+                <div *ngIf="rates.incremento_porcentaje.cargo_energia_valle">
+                  Cargo de energia valle:
+                  <strong>
+                    {{
+                      rates.incremento_porcentaje.cargo_energia_valle
+                        | number: "1.2-4"
+                    }}%
+                  </strong>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <br />
+    <div class="row">
+      <div class="col-lg-12">
+        <h4><b>Histórico de tarifa por distribuidora y código </b></h4>
+
+        <div class="widget">
+          <div class="mini-stats">
+            <div class="chart-container" id="chart-wrapper">
+              <canvas
+                baseChart
+                *ngIf="barChartData"
+                id="canvas"
+                [datasets]="barChartData"
+                [labels]="barChartLabels"
+                [options]="barChartOptions"
+                [plugins]="barChartPlugins"
+                [chartType]="barChartType"
+              ></canvas>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <br />
+    <br />
+    <div class="row">
+      <div class="col-lg-12">
+        <h4>
+          <b>Histórico de tarifas de otras distribuidoras </b>
+        </h4>
+
+        <div class="card border-danger">
+          <div class="card-body">
+            <p>
+              Para ver las tarifas de otras distribuidoras, debes de tener una
+              cuenta PRO
+            </p>
+            <a href="#!" class="btn btn-sm btn-warning pull-right"
+              >Actualizar a usuario pro</a
+            >
+          </div>
+        </div>
+      </div>
+    </div>
+    <br />
+    <div class="row">
+      <div class="col-lg-6 col-sm-6">
+        <div class="row">
+          <div class="col-lg-12 col-sm-12">
+            <label for="sel3">Distribuidora</label>
+            <select
+              class="custom-select"
+              (change)="onChangeObj($event)"
+              name="sel3"
+            >
+              <option
+                *ngFor="let item of listAssets"
+                [selected]="item.id === assetID"
+                [value]="item.id"
+              >
+                {{ userLevel == 0 ? "PLANTA INVERLEC" : item.name }}</option
+              >
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div class="col-lg-6 col-sm-6">
+        <div class="row">
+          <div class="col-lg-12 col-sm-12">
+            <label for="sel3">Tipo de tarifa</label>
+            <select
+              class="custom-select"
+              (change)="onChangeObj($event)"
+              name="sel3"
+            >
+              <option
+                *ngFor="let item of listAssets"
+                [selected]="item.id === assetID"
+                [value]="item.id"
+              >
+                {{ userLevel == 0 ? "PLANTA INVERLEC" : item.name }}</option
+              >
+            </select>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div *ngIf="error; then showAlert"></div>
+      <ng-template #showAlert>
+        <div class="col-lg-12">
+          <div class="card border-danger">
+            <div class="card-body">
+              Error en el servidor, no se pudo obtener el historico de tarifas
+            </div>
+          </div>
+        </div>
+      </ng-template>
+    </div>
+    <br />
+  </div>
+  <br />
+</div>

+ 115 - 0
src/app/components/rates/rates.component.scss

@@ -0,0 +1,115 @@
+.chart-container {
+  display: block;
+  height: 420px;
+  @media screen and (max-width: 960px) {
+    height: 350px;
+  }
+  position: relative;
+}
+
+.card-img-holder {
+  margin-bottom: 10px;
+}
+
+.hidden-element {
+  display: none;
+}
+.widget {
+  background: #ffffff none repeat scroll 0 0;
+  float: left;
+  margin-top: 14px;
+  position: relative;
+  width: 100%;
+  border-radius: 5px;
+
+  .chart-padding {
+    padding: 10px;
+  }
+
+  .mini-stats {
+    background: #ffffff none repeat scroll 0 0;
+    border-radius: 5px;
+    float: left;
+    padding: 10px 15px;
+    width: 100%;
+    height: inherit;
+
+    p {
+      color: #878888;
+      //display: block;
+      font-size: 14px;
+      line-height: 25px;
+      margin: 6px 0 0;
+      text-transform: uppercase;
+      width: auto;
+    }
+
+    span {
+      border: 1px solid;
+      border-radius: 50%;
+      color: #fff;
+      //float: left;
+      font-size: 30px;
+      height: 53px;
+      line-height: 53px;
+      text-align: center;
+      width: 53px;
+      position: absolute;
+      right: 15px;
+      bottom: 15px;
+      @media screen and (max-width: 960px) {
+        height: 48px;
+        width: 48px;
+        line-height: 48px;
+        font-size: 24px;
+      }
+      @media screen and (max-width: 800px) {
+        height: 44px;
+        width: 44px;
+        line-height: 44px;
+        font-size: 24px;
+        right: 10px;
+      }
+      @media screen and (min-width: 960px) and (max-width: 1024px) {
+        height: 48px;
+        width: 48px;
+        line-height: 48px;
+        font-size: 24px;
+      }
+      //display: block;
+    }
+
+    span.savings-skin {
+      color: #47a44b;
+      background: #fff;
+      border: none;
+      font-size: 48px;
+      line-height: 50px;
+      width: 64px;
+      position: absolute;
+
+      @media screen and (max-width: 960px) {
+        top: 0;
+        width: 50px;
+        i {
+          font-size: 36px;
+        }
+      }
+    }
+
+    h3 {
+      margin: 0;
+      font-size: 1.8rem;
+      @media screen and (max-width: 1152px) {
+        font-size: 1.5rem;
+      }
+      @media screen and (min-width: 960px) and (max-width: 1024px) {
+        font-size: 1.3rem;
+      }
+      @media screen and (max-width: 800px) {
+        font-size: 1.4rem;
+      }
+      font-weight: 400;
+    }
+  }
+}

+ 310 - 0
src/app/components/rates/rates.component.ts

@@ -0,0 +1,310 @@
+import { ActivatedRoute, Router } from "@angular/router";
+import { Component, OnInit, NgZone, ViewChild } from "@angular/core";
+import { Chart, ChartOptions, ChartType, ChartDataSets } from "chart.js";
+import { Label, BaseChartDirective } from "ng2-charts";
+import { PlantsService } from "src/app/services/plants.service";
+import { OrganizationsService } from "src/app/services/organizations.service";
+
+import { MatPaginator } from "@angular/material/paginator";
+import { MatSort } from "@angular/material/sort";
+import { MatTableDataSource } from "@angular/material/table";
+import { AuthService } from "@app/services/auth2.service";
+import { formatDate, DatePipe } from "@angular/common";
+
+import * as moment from "moment";
+import Swal from "sweetalert2";
+import { RatesService } from "@app/services/rates.service";
+
+@Component({
+  selector: "app-rates",
+  templateUrl: "./rates.component.html",
+  styleUrls: ["./rates.component.scss"],
+})
+export class RatesComponent implements OnInit {
+  title = "Tarifas";
+
+  listData: any;
+  rows = [];
+
+  listAssets: any;
+  dataSource = new MatTableDataSource(this.listAssets);
+  displayedColumns: string[] = ["name", "country", "city", "id"];
+
+  error: boolean;
+  listOrganizations: any;
+  plantId: string;
+  plant: any;
+  sub: any;
+  plantNotFound: boolean;
+  selectedPlant: any;
+  meterKeys: any;
+  meterKeys2: any;
+  assetKeys: any;
+
+  sumarize: number = 0;
+  totalInstalled: number = 0;
+
+  totalMetersInstalled: string;
+
+  lastUpdate = new Date().toLocaleString();
+  i: number;
+
+  userLevel: number;
+  totalAssetsInstalled: any;
+
+  rates: Object = {
+    distribuidora: "-",
+    codigo_tarifa: "-",
+    tarifa_actual: {
+      dateMin: null,
+      dateMax: null,
+      cargo_energia: null,
+      cargo_energia_b1: null,
+      cargo_energia_b2: null,
+      cargo_energia_b3: null,
+      cargo_energia_punta: null,
+      cargo_energia_resto: null,
+      cargo_energia_valle: null,
+    },
+    incremento_porcentaje: {
+      cargo_energia: null,
+      cargo_energia_b1: null,
+      cargo_energia_b2: null,
+      cargo_energia_b3: null,
+      cargo_energia_punta: null,
+      cargo_energia_resto: null,
+      cargo_energia_valle: null,
+    },
+  };
+
+  // For chartjs
+  chartjs: boolean;
+  chart1: Chart;
+  chart1Type;
+  barChartColors: any = [
+    "#5c90aa",
+    "#a7c1d3",
+    "#4e5151",
+    "#916458",
+    "#ee964b",
+    "#a9d0ea",
+    "#a7c957",
+    "#f2e8cf",
+  ];
+  borderChartColors: any = [
+    "#336b87",
+    "#90afc5",
+    "#2a3132",
+    "#763626",
+    "#d38d51",
+    "#a2cae4",
+    "#91b43d",
+    "#d3cbb7",
+  ];
+  public barChartType: ChartType;
+  public barChartLegend: boolean;
+  public barChartLabels: Label[];
+  public barChartOptions: ChartOptions;
+  public barChartData: ChartDataSets[];
+  @ViewChild("baseChart", null) chart: BaseChartDirective;
+  metersData: any[];
+  metersValues: unknown[];
+
+  constructor(
+    private plantsService: PlantsService,
+    private route: ActivatedRoute,
+    private orgService: OrganizationsService,
+    private router: Router,
+    private zone: NgZone,
+    private authService: AuthService,
+    private ratesService: RatesService
+  ) {
+    //DEMO
+    this.userLevel = +this.authService.getUserLevel();
+
+    Swal.fire({
+      allowOutsideClick: false,
+      type: "info",
+      text: "Espere por favor...",
+    });
+    Swal.showLoading();
+  }
+
+  ngOnInit() {
+    setTimeout(() => {
+      Swal.close();
+    }, 3000);
+
+    this.ratesService.getDefaultRate().subscribe((res) => {
+      console.log("tarifa");
+      console.log(res);
+      this.rates = res["data"];
+    });
+
+    this.ratesService.getHistoricalRate().subscribe((res) => {
+      console.log("historico");
+      console.log(res);
+      this.metersData = [];
+      this.metersValues = Object.values(res["data"]["tarifas"]);
+
+      console.log(this.metersValues);
+      let meterKeys2 = Object.keys(this.metersValues);
+
+      let labels = [];
+      let cargo_energia = [];
+      let cargo_energia_b1 = [];
+      let cargo_energia_b2 = [];
+      let cargo_energia_b3 = [];
+
+      for (let prop in meterKeys2) {
+        console.log();
+        //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
+
+        //label = this.metersValues["dateMin"] + "-" + this.metersValues["dateMax"] ;
+        /*
+        let measure_values = Object.values(
+          this.metersValues[prop]["data"]
+            .map((obj) => obj.total_energy_kWh)
+            .reverse()
+        );*/
+
+        labels.push(
+          `${this.metersValues[prop]["dateMin"]} - ${this.metersValues[prop]["dateMax"]}`
+        );
+
+        if (this.metersValues[prop]["cargo_energia"] != "") {
+          cargo_energia.push(this.metersValues[prop]["cargo_energia"]);
+        }
+
+        if (this.metersValues[prop]["cargo_energia_b1"] != "") {
+          cargo_energia_b1.push(this.metersValues[prop]["cargo_energia_b1"]);
+        }
+
+        if (this.metersValues[prop]["cargo_energia_b2"] != "") {
+          cargo_energia_b2.push(this.metersValues[prop]["cargo_energia_b2"]);
+        }
+
+        if (this.metersValues[prop]["cargo_energia_b3"] != "") {
+          cargo_energia_b3.push(this.metersValues[prop]["cargo_energia_b3"]);
+        }
+
+        /*
+        
+        
+        this.metersData.push(
+          {
+            label: label,
+            backgroundColor: this.barChartColors[prop],
+            data: measure_values,
+            barPercentage: 0.7,
+            borderColor: this.borderChartColors[prop],
+          }
+        );*/
+      }
+      console.log(labels);
+      this.barChartType = "line";
+      this.barChartLabels = labels;
+
+      this.metersData.push(
+        {
+          label: "Cargo energia",
+          backgroundColor: this.barChartColors[0],
+          data: cargo_energia,
+          barPercentage: 0.7,
+          borderColor: this.borderChartColors[0],
+        },
+        {
+          label: "Cargo energia bloque 1",
+          backgroundColor: this.barChartColors[1],
+          data: cargo_energia_b1,
+          barPercentage: 0.7,
+          borderColor: this.borderChartColors[1],
+        },
+        {
+          label: "Cargo energia bloque 2",
+          backgroundColor: this.barChartColors[2],
+          data: cargo_energia_b2,
+          barPercentage: 0.7,
+          borderColor: this.borderChartColors[2],
+        },
+        {
+          label: "Cargo energia bloque 3",
+          backgroundColor: this.barChartColors[3],
+          data: cargo_energia_b3,
+          barPercentage: 0.7,
+          borderColor: this.borderChartColors[3],
+        }
+      );
+      /*
+      
+      this.metersValues[0]["data"]
+                  .map((obj) =>
+                    formatDate(
+                      obj.dateMin.replace(/-/g, "/").replace("T", " "),
+                      "HH:mm ",
+                      "es-Es",
+                      "-0600"
+                    )
+                  )
+                  .reverse();
+      */
+
+      this.barChartOptions = {
+        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];
+
+              return label + " : " + value;
+            },
+          },
+        },
+        responsive: true,
+        maintainAspectRatio: false,
+        scales: {
+          xAxes: [
+            {
+              stacked: false,
+            },
+          ],
+          yAxes: [
+            {
+              stacked: false,
+              position: "left",
+              scaleLabel: {
+                display: true,
+                labelString: "Tarifa en $USD por kWh",
+              },
+            },
+          ],
+        },
+      };
+
+      this.barChartLegend = true;
+      this.barChartData = this.metersData;
+    });
+
+    var responsiveOptions: any[] = [
+      [
+        "screen and (max-width: 640px)",
+        {
+          seriesBarDistance: 5,
+          axisX: {
+            labelInterpolationFnc: function (value) {
+              return value[0];
+            },
+          },
+        },
+      ],
+    ];
+  }
+
+  ngOnChanges(): void {}
+}

+ 59 - 38
src/app/components/shared/sidebar/sidebar.component.ts

@@ -1,83 +1,104 @@
-import { Component, OnInit } from '@angular/core';
-import { AuthService } from '../../../services/auth2.service';
-import { Router } from '@angular/router';
-import * as CryptoJS from 'crypto-js';
-import Swal from 'sweetalert2';
+import { Component, OnInit } from "@angular/core";
+import { AuthService } from "../../../services/auth2.service";
+import { Router } from "@angular/router";
+import * as CryptoJS from "crypto-js";
+import Swal from "sweetalert2";
 
 declare const $: any;
 declare interface RouteInfo {
-    path: string;
-    title: string;
-    icon: string;
-    class: string;
-    allowed_roles?:any;
+  path: string;
+  title: string;
+  icon: string;
+  class: string;
+  allowed_roles?: any;
 }
 export const ROUTES: RouteInfo[] = [
-    { path: '/dashboard', title: 'Dashboard',  icon: 'dashboard', class: '' },
-    { path: '/assets', title: 'Plantas',  icon: 'wb_sunny', class: '' },
-    //{ path: '/examples', title: 'Ejemplo',  icon: 'wb_sunny', class: '' },
-    
-    //{ path: '/profile', title: 'Perfil',  icon:'person', class: '' },
-    /*{ path: '/table-list', title: 'Table List',  icon:'content_paste', class: '' },
+  { path: "/dashboard", title: "Dashboard", icon: "dashboard", class: "" },
+  { path: "/assets", title: "Plantas", icon: "wb_sunny", class: "" },
+  { path: "/rates", title: "Tarifas", icon: "bubble_chart", class: "" },
+
+  { path: "/profile", title: "Perfil", icon: "person", class: "" },
+  /*{ path: '/table-list', title: 'Table List',  icon:'content_paste', class: '' },
     { path: '/typography', title: 'Typography',  icon:'library_books', class: '' },
     { path: '/icons', title: 'Icons',  icon:'bubble_chart', class: '' },
     { path: '/maps', title: 'Maps',  icon:'location_on', class: '' },
     { path: '/notifications', title: 'Notifications',  icon:'notifications', class: '' },
+    {
+    
     { path: '/upgrade', title: 'Upgrade to PRO',  icon:'unarchive', class: 'active-pro' },*/
 ];
 
 // Extra options to show to the admin
 export const ADMIN_ROUTES: RouteInfo[] = [
-    { path: '/organizations', title: 'Organizaciones',  icon:'location_city', class: '', allowed_roles: [2,3] },
-    { path: '/plants', title: 'Plantas',  icon: 'poll', class: '', allowed_roles: [2, 3] },
-    { path: '/users', title: 'Usuarios',  icon: 'people', class: '', allowed_roles: [3] },
+  {
+    path: "/organizations",
+    title: "Organizaciones",
+    icon: "location_city",
+    class: "",
+    allowed_roles: [2, 3],
+  },
+  {
+    path: "/plants",
+    title: "Plantas",
+    icon: "poll",
+    class: "",
+    allowed_roles: [2, 3],
+  },
+  {
+    path: "/users",
+    title: "Usuarios",
+    icon: "people",
+    class: "",
+    allowed_roles: [3],
+  },
 ];
 
 @Component({
-  selector: 'app-sidebar',
-  templateUrl: './sidebar.component.html',
-  styleUrls: ['./sidebar.component.scss']
+  selector: "app-sidebar",
+  templateUrl: "./sidebar.component.html",
+  styleUrls: ["./sidebar.component.scss"],
 })
 export class SidebarComponent implements OnInit {
   menuItems: any[];
   adminMenuItems: any[];
-  adminMenu:boolean = false;
-  role_number:any;
+  adminMenu: boolean = false;
+  role_number: any;
 
   constructor(private auth: AuthService, private router: Router) {
-    var bytes  = CryptoJS.AES.decrypt(localStorage.getItem("USER_MENU"), 'soma-inverlec-2019');
+    var bytes = CryptoJS.AES.decrypt(
+      localStorage.getItem("USER_MENU"),
+      "soma-inverlec-2019"
+    );
     this.role_number = bytes.toString(CryptoJS.enc.Utf8);
   }
 
   ngOnInit() {
-    this.menuItems = ROUTES.filter(menuItem => menuItem);
-    this.adminMenuItems = ADMIN_ROUTES.filter(menuItem => menuItem);
+    this.menuItems = ROUTES.filter((menuItem) => menuItem);
+    this.adminMenuItems = ADMIN_ROUTES.filter((menuItem) => menuItem);
 
     // must be changed for the method that returns if it is an admin
     if (this.auth.isLoggedIn() == true && this.role_number > 1) {
       this.adminMenu = true;
     }
-
   }
-  
+
   isMobileMenu() {
-      if ($(window).width() > 991) {
-          return false;
-      }
-      return true;
-  };
+    if ($(window).width() > 991) {
+      return false;
+    }
+    return true;
+  }
 
   logout() {
     Swal.fire({
       allowOutsideClick: false,
-      type: 'info',
-      text: 'Espere por favor...'
+      type: "info",
+      text: "Espere por favor...",
     });
     Swal.showLoading();
 
     this.auth.logout();
 
     //this.router.navigateByUrl("login");
-  };
-
+  }
 }

+ 6 - 4
src/app/layouts/admin/admin.module.ts

@@ -36,11 +36,12 @@ import {
   MatTableModule,
   MatPaginatorModule,
   MatProgressSpinnerModule,
-  MatSortModule
+  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";
+import { RatesComponent } from "../../components/rates/rates.component";
 
 @NgModule({
   imports: [
@@ -64,7 +65,7 @@ import { TermsComponent } from "@app/components/terms/terms.component";
     ChartsModule,
     NgbModule,
     AngularMyDatePickerModule,
-    MatPasswordStrengthModule
+    MatPasswordStrengthModule,
   ],
   declarations: [
     DashboardComponent,
@@ -80,7 +81,8 @@ import { TermsComponent } from "@app/components/terms/terms.component";
     PlantComponent,
     UsersComponent,
     NewUserComponent,
-    TermsComponent
-  ]
+    TermsComponent,
+    RatesComponent,
+  ],
 })
 export class AdminModule {}

+ 87 - 72
src/app/layouts/admin/admin.routing.ts

@@ -1,137 +1,152 @@
-import { Routes } from '@angular/router';
+import { Routes } from "@angular/router";
 
-import { DashboardComponent } from '../../components/dashboard/dashboard.component';
-import { ProfileComponent } from '../../components/profile/profile.component';
-import { AssetsComponent } from '@app/components/assets/assets.component';
-import { PlantsComponent } from '@app/components/plants/plants.component';
-
-import { OrganizationsComponent } from '@app/components/organizations/organizations.component';
-import { OrganizationComponent } from '@app/components/organizations/organization/organization.component';
-import { NewOrganizationComponent } from '@app/components/organizations/new-organization/new-organization.component';
-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';
+import { DashboardComponent } from "../../components/dashboard/dashboard.component";
+import { ProfileComponent } from "../../components/profile/profile.component";
+import { AssetsComponent } from "@app/components/assets/assets.component";
+import { PlantsComponent } from "@app/components/plants/plants.component";
 
+import { OrganizationsComponent } from "@app/components/organizations/organizations.component";
+import { OrganizationComponent } from "@app/components/organizations/organization/organization.component";
+import { NewOrganizationComponent } from "@app/components/organizations/new-organization/new-organization.component";
+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";
+import { RatesComponent } from "../../components/rates/rates.component";
 
 export const AdminLayoutRoutes: Routes = [
-  { path: 'dashboard',
+  {
+    path: "dashboard",
     component: DashboardComponent,
-    data: {title: "Dashboard"}
+    data: { title: "Dashboard" },
   },
-  { path: 'profile',
+  {
+    path: "profile",
     component: ProfileComponent,
-    data: {title: "Perfil de usuario"}
+    data: { title: "Perfil de usuario" },
   },
-  { path: 'assets', 
+  {
+    path: "assets",
     component: AssetsComponent,
-    data: {title: "Listado de plantas"}
+    data: { title: "Listado de plantas" },
+  },
+  {
+    path: "rates",
+    component: RatesComponent,
+    data: { title: "Listado de tarifas" },
   },
-  { path: 'terms', 
+  {
+    path: "terms",
     component: TermsComponent,
-    data: {title: "Términos y condiciones"}
+    data: { title: "Términos y condiciones" },
   },
   // General management
-  { path: 'plants', 
+  {
+    path: "plants",
     component: PlantsComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Listado de plantas",
       breadcrumb: "Plantas",
-      roles: [2,3]
+      roles: [2, 3],
     },
   },
-  { path: 'plants/new', 
+  {
+    path: "plants/new",
     component: NewPlantComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Nueva planta",
       breadcrumb: "Nueva planta",
-      roles: [3]
+      roles: [3],
     },
   },
-  { path: 'plant/:id', 
-  component: PlantComponent,
-  canActivate: [AuthGuard], 
-  data: {
-    title: "Detalle de planta",
-    breadcrumb: "Detalle de planta",
-    roles: [2,3]
+  {
+    path: "plant/:id",
+    component: PlantComponent,
+    canActivate: [AuthGuard],
+    data: {
+      title: "Detalle de planta",
+      breadcrumb: "Detalle de planta",
+      roles: [2, 3],
+    },
   },
-},
-  { path: 'plant/:id/edit', 
+  {
+    path: "plant/:id/edit",
     component: EditPlantComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Editar planta",
       breadcrumb: "Editar planta",
-      roles: [2,3]
+      roles: [2, 3],
     },
   },
-  
-  { path: 'users', 
+
+  {
+    path: "users",
     component: UsersComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Listado de usuarios",
       breadcrumb: "Usuarios",
-      roles: [3]
+      roles: [3],
     },
   },
-    
-  { path: 'users/new', 
+
+  {
+    path: "users/new",
     component: NewUserComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Nuevo usuario",
       breadcrumb: "Nuevo usuario",
-      roles: [3]
+      roles: [3],
     },
   },
 
   // Organizations actions
-  { path: 'organizations', 
+  {
+    path: "organizations",
     component: OrganizationsComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
       title: "Listado de organizaciones",
       breadcrumb: "Organizaciones",
-      roles: [2,3]
+      roles: [2, 3],
     },
   },
   {
-    path: 'organizations/new',
+    path: "organizations/new",
     component: NewOrganizationComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
-      title: 'Nueva organización',
-      breadcrumb:'Nueva organización',
-      roles: [3]
-    }
+      title: "Nueva organización",
+      breadcrumb: "Nueva organización",
+      roles: [3],
+    },
   },
   {
-    path: 'organization/:id',
+    path: "organization/:id",
     component: OrganizationComponent,
-    canActivate: [AuthGuard], 
+    canActivate: [AuthGuard],
     data: {
-      title: 'Organización',
-      breadcrumb: 'Detalle',
-      roles: [2,3]
-    }
+      title: "Organización",
+      breadcrumb: "Detalle",
+      roles: [2, 3],
+    },
   },
   {
-    path: 'organization/:id/edit',
-    canActivate: [AuthGuard], 
+    path: "organization/:id/edit",
+    canActivate: [AuthGuard],
     component: EditOrganizationComponent,
     data: {
-      title: 'Editar organización',
-      breadcrumb: 'Editar organización',
-      roles: [3]
-    }
+      title: "Editar organización",
+      breadcrumb: "Editar organización",
+      roles: [3],
+    },
   },
-
 ];

+ 59 - 43
src/app/services/auth2.service.ts

@@ -1,44 +1,46 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-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';
-import * as CryptoJS from 'crypto-js';
-import Swal from 'sweetalert2';
+import { Injectable } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+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";
+import * as CryptoJS from "crypto-js";
+import Swal from "sweetalert2";
 
 @Injectable({
-  providedIn: 'root'
+  providedIn: "root",
 })
-
 export class AuthService {
-
-  private readonly JWT_TOKEN = 'JWT_TOKEN';
-  private readonly REFRESH_TOKEN = 'REFRESH_TOKEN';
-  private readonly USER_MENU = 'USER_MENU';
+  private readonly JWT_TOKEN = "JWT_TOKEN";
+  private readonly REFRESH_TOKEN = "REFRESH_TOKEN";
+  private readonly USER_MENU = "USER_MENU";
+  private readonly USER_ID = "USER_ID";
   private loggedUser: string;
 
   constructor(private http: HttpClient) {}
 
-  login(user: { email: string, password: string}): Observable<boolean> {
-    return this.http.post<any>(`${environment.productionApiUrl}/auth/login`, user)
+  login(user: { email: string; password: string }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/auth/login`, user)
       .pipe(
-        tap(tokens => this.doLoginUser(user.email, tokens)),
+        tap((tokens) => this.doLoginUser(user.email, tokens)),
         mapTo(true),
         catchError(this.errorHandl)
       );
   }
 
-
   logout() {
-    let refreshToken:string = this.getRefreshToken();
-    this.http.post<any>(`${environment.productionApiUrl}/auth/logout`, {})
-    .subscribe( results => {});
-    this.http.post<any>(`${environment.productionApiUrl}/auth/logout2`, {}).subscribe( results => {
-      this.doLogoutUser();
-      Swal.close();  
-      window.location.href = "";
-    });
+    let refreshToken: string = this.getRefreshToken();
+    this.http
+      .post<any>(`${environment.productionApiUrl}/auth/logout`, {})
+      .subscribe((results) => {});
+    this.http
+      .post<any>(`${environment.productionApiUrl}/auth/logout2`, {})
+      .subscribe((results) => {
+        this.doLogoutUser();
+        Swal.close();
+        window.location.href = "";
+      });
   }
 
   isLoggedIn() {
@@ -46,26 +48,35 @@ export class AuthService {
   }
 
   getUserLevel() {
-    var bytes  = CryptoJS.AES.decrypt(localStorage.getItem("USER_MENU"), 'soma-inverlec-2019');
+    var bytes = CryptoJS.AES.decrypt(
+      localStorage.getItem("USER_MENU"),
+      "soma-inverlec-2019"
+    );
     var role_number = bytes.toString(CryptoJS.enc.Utf8);
     return role_number;
   }
 
   refreshToken() {
-    let refreshToken:string = this.getRefreshToken();
-    return this.http.post<any>(`${environment.productionApiUrl}/auth/refresh`, {
-      'Authorization': `Bearer ${refreshToken}`
-    }).pipe(tap((tokens: Token) => {
-      this.storeJwtToken(tokens["data"]["access_token"]);
-      },
-      catchError(this.errorHandl)
-    ));
+    let refreshToken: string = this.getRefreshToken();
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/auth/refresh`, {
+        Authorization: `Bearer ${refreshToken}`,
+      })
+      .pipe(
+        tap((tokens: Token) => {
+          this.storeJwtToken(tokens["data"]["access_token"]);
+        }, catchError(this.errorHandl))
+      );
   }
 
   getJwtToken() {
     return localStorage.getItem(this.JWT_TOKEN);
   }
 
+  getUserId() {
+    return localStorage.getItem(this.USER_ID);
+  }
+
   private doLoginUser(email: string, tokens: Token) {
     this.loggedUser = email;
     this.storeTokens(tokens);
@@ -86,9 +97,16 @@ export class AuthService {
 
   private storeTokens(tokens: Token) {
     localStorage.clear();
-    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.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);
+    localStorage.setItem(this.USER_ID, tokens["data"]["user"].id);
   }
 
   removeTokens() {
@@ -97,17 +115,15 @@ export class AuthService {
     localStorage.removeItem(this.REFRESH_TOKEN);
   }
 
-
   errorHandl(error) {
-    let errorMessage = '';
-    if(error.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}`;      
+      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
     }
     return throwError(errorMessage);
   }
-
-}
+}

+ 12 - 0
src/app/services/catalogs.service.spec.ts

@@ -0,0 +1,12 @@
+import { TestBed } from '@angular/core/testing';
+
+import { CatalogsService } from './catalogs.service';
+
+describe('CatalogsService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: CatalogsService = TestBed.get(CatalogsService);
+    expect(service).toBeTruthy();
+  });
+});

+ 47 - 0
src/app/services/catalogs.service.ts

@@ -0,0 +1,47 @@
+import { Injectable } from "@angular/core";
+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";
+
+@Injectable({
+  providedIn: "root",
+})
+export class CatalogsService {
+  constructor(private http: HttpClient) {}
+
+  getCatalogs() {
+    return this.http.get<any>(`${environment.productionApiUrl}/catalogos`).pipe(
+      timeout(5000),
+      map((response) => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    );
+  }
+
+  getCatalogByName(name: string) {
+    return this.http
+      .get<any>(`${environment.productionApiUrl}/catalogo/${name}`)
+      .pipe(
+        timeout(5000),
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  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);
+  }
+}

+ 12 - 0
src/app/services/rates.service.spec.ts

@@ -0,0 +1,12 @@
+import { TestBed } from '@angular/core/testing';
+
+import { RatesService } from './rates.service';
+
+describe('RatesService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: RatesService = TestBed.get(RatesService);
+    expect(service).toBeTruthy();
+  });
+});

+ 72 - 0
src/app/services/rates.service.ts

@@ -0,0 +1,72 @@
+import { Injectable } from "@angular/core";
+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";
+
+@Injectable({
+  providedIn: "root",
+})
+export class RatesService {
+  constructor(private http: HttpClient) {}
+
+  getDefaultRate() {
+    return this.http.get<any>(`${environment.productionApiUrl}/tarifa`).pipe(
+      timeout(5000),
+      map((response) => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    );
+  }
+
+  getHistoricalRate() {
+    return this.http
+      .get<any>(`${environment.productionApiUrl}/tarifa/historical`)
+      .pipe(
+        timeout(5000),
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  getHistoricalRateBySelection(distribuidora: string, tarifa: string) {
+    return this.http
+      .get<any>(
+        `${environment.productionApiUrl}/tarifa/historical/${distribuidora}/${tarifa}`
+      )
+      .pipe(
+        timeout(5000),
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  getOrganization(id: string) {
+    return this.http
+      .get<any>(`${environment.productionApiUrl}/organization/${id}`)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  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);
+  }
+}

+ 70 - 46
src/app/services/token.interceptor.ts

@@ -1,62 +1,83 @@
-import { Injectable } from '@angular/core';
-import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
-import { AuthService } from '@app/services/auth2.service';
-import { Observable, throwError, BehaviorSubject } from 'rxjs';
-import { catchError, filter, take, switchMap } from 'rxjs/operators';
-import Swal from 'sweetalert2';
+import { Injectable } from "@angular/core";
+import {
+  HttpRequest,
+  HttpHandler,
+  HttpEvent,
+  HttpInterceptor,
+  HttpErrorResponse,
+} from "@angular/common/http";
+import { AuthService } from "@app/services/auth2.service";
+import { Observable, throwError, BehaviorSubject } from "rxjs";
+import { catchError, filter, take, switchMap } from "rxjs/operators";
+import Swal from "sweetalert2";
 
 @Injectable()
 export class TokenInterceptor implements HttpInterceptor {
-
   private isRefreshing = false;
-  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
+  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
+    null
+  );
 
-  constructor(public authService: AuthService) { }
+  constructor(public authService: AuthService) {}
 
-  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
-    if (request.url.indexOf("auth/refresh")!= -1 || request.url.indexOf("auth/logout2") != -1){
+  intercept(
+    request: HttpRequest<any>,
+    next: HttpHandler
+  ): Observable<HttpEvent<any>> {
+    if (
+      request.url.indexOf("auth/refresh") != -1 ||
+      request.url.indexOf("auth/logout2") != -1
+    ) {
       request = this.addToken(request, this.authService.getRefreshToken());
-    }
-    else if (request.url.indexOf("openweather")!= -1 ){
+    } else if (
+      request.url.indexOf("openweather") != -1 ||
+      request.url.indexOf("user/password-recovery") != -1 ||
+      request.url.indexOf("user/user/change-password") != -1
+    ) {
       request;
-    }
-    else {
+    } else {
       if (this.authService.getJwtToken()) {
         request = this.addToken(request, this.authService.getJwtToken());
       }
     }
-    
-    return next.handle(request).pipe(catchError(error => {
-      // Erase storage if response code from auth refresh gives 401 or 422 error
-      if (request.url.indexOf("auth/refresh")!= -1 && error instanceof HttpErrorResponse){
-        Swal.fire({
-          allowOutsideClick: false,
-          type: 'warning',
-          title: 'La sesión ha expirado',
-          showConfirmButton: false,
-        });  
-        this.authService.removeTokens();
-        setTimeout(function(){
-          Swal.close();
-          window.location.href = "#/login";
-        }, 2000);
-      }
 
-      if (error instanceof HttpErrorResponse && (error.status === 401 || error.status === 422)) {
-        return this.handle4xxError(request, next);
-      } 
-      else {
-        return throwError(error);
-      }
-    }));      
+    return next.handle(request).pipe(
+      catchError((error) => {
+        // Erase storage if response code from auth refresh gives 401 or 422 error
+        if (
+          request.url.indexOf("auth/refresh") != -1 &&
+          error instanceof HttpErrorResponse
+        ) {
+          Swal.fire({
+            allowOutsideClick: false,
+            type: "warning",
+            title: "La sesión ha expirado",
+            showConfirmButton: false,
+          });
+          this.authService.removeTokens();
+          setTimeout(function () {
+            Swal.close();
+            window.location.href = "#/login";
+          }, 2000);
+        }
 
+        if (
+          error instanceof HttpErrorResponse &&
+          (error.status === 401 || error.status === 422)
+        ) {
+          return this.handle4xxError(request, next);
+        } else {
+          return throwError(error);
+        }
+      })
+    );
   }
 
   private addToken(request: HttpRequest<any>, token: string) {
     return request.clone({
       setHeaders: {
-        'Authorization': `Bearer ${token}`
-      }
+        Authorization: `Bearer ${token}`,
+      },
     });
   }
 
@@ -70,18 +91,21 @@ export class TokenInterceptor implements HttpInterceptor {
         switchMap((token: any) => {
           this.isRefreshing = false;
           this.refreshTokenSubject.next(token["data"]["access_token"]);
-          return next.handle(this.addToken(request, token["data"]["access_token"]));
-        }));
-
+          return next.handle(
+            this.addToken(request, token["data"]["access_token"])
+          );
+        })
+      );
     } else {
       this.isRefreshing = false;
 
       return this.refreshTokenSubject.pipe(
-        filter(token => token != null),
+        filter((token) => token != null),
         take(2),
-        switchMap(jwt => {
+        switchMap((jwt) => {
           return next.handle(this.addToken(request, jwt));
-        }));
+        })
+      );
     }
   }
 }

+ 149 - 30
src/app/services/user.service.ts

@@ -1,60 +1,180 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { Injectable } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
 
-import { environment } from '@environments/environment';
-import { User } from '@app/models';
-import { Observable } from 'rxjs/internal/Observable';
-import { throwError } from 'rxjs/internal/observable/throwError';
-import { map, catchError } from 'rxjs/operators';
+import { environment } from "@environments/environment";
+import { User } from "@app/models";
+import { Observable } from "rxjs/internal/Observable";
+import { throwError } from "rxjs/internal/observable/throwError";
+import { map, catchError } from "rxjs/operators";
 
-@Injectable({ providedIn: 'root' })
+@Injectable({ providedIn: "root" })
 export class UserService {
-  constructor(private http: HttpClient) { }
+  constructor(private http: HttpClient) {}
 
   getAllUsers() {
-      return this.http.get<User[]>(`${environment.productionApiUrl}/users`);
+    return this.http.get<User[]>(`${environment.productionApiUrl}/users`);
   }
 
   getById(id: number) {
-      return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
+    return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
   }
 
-  createUser(user: { first_name :string, last_name :string, 
-    email :string, role :number, organizations?: any }): Observable<boolean> {
-      return this.http.post<any>(`${environment.productionApiUrl}/users`, user)
+  getUserById(id: string) {
+    return this.http
+      .get<any>(`${environment.productionApiUrl}/customer/${id}`)
       .pipe(
-        map(response => {
+        map((response) => {
           return response;
         }),
         catchError(this.errorHandl)
-      )  
+      );
   }
 
-  validateUserToken(user: {token :string}): Observable<boolean> {
-      return this.http.post<any>(`${environment.productionApiUrl}/user/tokenvalidation`, user)
+  createCustomer(user: {
+    first_name: string;
+    last_name: string;
+    email: string;
+    phone_number: string;
+    password: string;
+    confirm_password: string;
+    distribuidora?: string;
+    cod_tarifa?: string;
+    role?: string;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/customers`, user)
       .pipe(
-        map(response => {
+        map((response) => {
           return response;
         }),
         catchError(this.errorHandl)
-      )  
+      );
   }
-  
-  activateUser(user: { first_name :string, last_name :string, 
-    email :string, password: string, confirm_password :string }): Observable<boolean> {
-      return this.http.post<any>(`${environment.productionApiUrl}/user/activate`, user)
+
+  activateCustomer(user: {
+    first_name: string;
+    last_name: string;
+    email: string;
+    phone_number: string;
+    password?: string;
+    confirm_password?: string;
+    distribuidora?: string;
+    cod_tarifa?: string;
+    role?: string;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(
+        `${environment.productionApiUrl}/customer/activate-customer`,
+        user
+      )
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  editCustomer(
+    id: string,
+    user: {
+      first_name: string;
+      last_name: string;
+      email: string;
+      phone_number: string;
+      password?: string;
+      confirm_password?: string;
+      distribuidora?: string;
+      cod_tarifa?: string;
+      role?: string;
+    }
+  ): Observable<boolean> {
+    return this.http
+      .put<any>(`${environment.productionApiUrl}/customer/${id}`, user)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  createUser(user: {
+    first_name: string;
+    last_name: string;
+    email: string;
+    role: number;
+    organizations?: any;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/users`, user)
       .pipe(
-        map(response => {
+        map((response) => {
           return response;
         }),
         catchError(this.errorHandl)
-      )  
+      );
   }
 
+  validateUserToken(user: {
+    token: string;
+    process: string;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/user/tokenvalidation`, user)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  activateUser(user: {
+    first_name: string;
+    last_name: string;
+    email: string;
+    password: string;
+    confirm_password: string;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/user/activate`, user)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  retrieveAccount(user: { email: string }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/user/password-recovery`, user)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
+
+  changePassword(user: {
+    password: string;
+    confirm_password: string;
+  }): Observable<boolean> {
+    return this.http
+      .post<any>(`${environment.productionApiUrl}/user/change-password`, user)
+      .pipe(
+        map((response) => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      );
+  }
 
   errorHandl(error) {
-    let errorMessage = '';
-    if(error.error) {
+    let errorMessage = "";
+    if (error.error) {
       // Get client-side error
       errorMessage = error.error;
     } else {
@@ -62,6 +182,5 @@ export class UserService {
       errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
     }
     return throwError(errorMessage);
- }
-
+  }
 }

BIN
src/assets/img/AESFactura.png


BIN
src/assets/img/DelSurFactura2.png


BIN
src/assets/img/money-rate.png


BIN
src/assets/img/time-is-money.png


+ 18 - 17
src/assets/scss/core/variables/_colors.scss

@@ -308,25 +308,26 @@ $black: #000;
 $white: #fff;
 
 // New colors
-$gray-color:  #999999 !default;
-$black-color: #3C4858 !default;
+$gray-color: #999999 !default;
+$black-color: #3c4858 !default;
 
-$black: #000000; $rgb-black: "0,0,0" !default;
-$white: #ffffff; $rgb-white: "255,255,255" !default;
+$black: #000000;
+$rgb-black: "0,0,0" !default;
+$white: #ffffff;
+$rgb-white: "255,255,255" !default;
 
 //## Gray and brand colors for use across Bootstrap.
 
-$gray-base:              #000 !default;
-$gray-darker:            lighten($gray-base, 13.5%) !default; // #222
-$gray-dark:              lighten($gray-base, 20%) !default;   // #333
-$gray:                   lighten($gray-base, 33.5%) !default; // #555
-$gray-light:             #999999 !default; // #999999
-$gray-lighter:           lighten($gray-base, 93.5%) !default; // #eee
-$gray-custom:            #80888f !default;
+$gray-base: #000 !default;
+$gray-darker: lighten($gray-base, 13.5%) !default; // #222
+$gray-dark: lighten($gray-base, 20%) !default; // #333
+$gray: lighten($gray-base, 33.5%) !default; // #555
+$gray-light: #999999 !default; // #999999
+$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee
+$gray-custom: #80888f !default;
 
-
-$palette-blue:          #0d3b66;
-$palette-cream:         #faf0ca;
-$palette-yellow:        #fad35e;
-$palette-dark-yellow:   #ee964b;
-$palette-orange:        #f95738;
+$palette-blue: #0d3b66;
+$palette-cream: #e4dfca;
+$palette-yellow: #020202;
+$palette-dark-yellow: #ee964b;
+$palette-orange: #f95738;

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

@@ -60,7 +60,12 @@
 
 .cream-skin {
   background-color: $palette-cream;
-  border-color: #fdeaa7 !important;
+  border-color: #666 !important;
+}
+
+.white-skin {
+  background-color: $white;
+  border-color: #104911 !important;
 }
 
 .yellow-skin {

+ 4 - 4
src/environments/environment.ts

@@ -6,8 +6,8 @@ export const environment = {
   production: false,
   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",
+  productionApiUrl: "http://192.168.98.140:5000/api/v1",
+  //productionApiUrl: "https://denmark.inverlec.solar/api/v1",
   appID: "55899b9ea53834f2736b65a3582b734b",
   gKey: "",
   config: {
@@ -16,8 +16,8 @@ export const environment = {
     databaseURL: "",
     projectId: "",
     storageBucket: "",
-    messagingSenderId: ""
-  }
+    messagingSenderId: "",
+  },
 };
 
 /*