فهرست منبع

Merge branch 'release-1.1' of onunez/soma-frontend into master

Oscar Alfredo Leiva Salomón 6 سال پیش
والد
کامیت
cc432309f7
82فایلهای تغییر یافته به همراه3308 افزوده شده و 841 حذف شده
  1. 5 0
      .dockerignore
  2. 18 0
      Dockerfile
  3. 49 0
      nginx/nginx.conf
  4. 272 226
      package-lock.json
  5. 7 2
      package.json
  6. 6 4
      src/app/app.module.ts
  7. 6 2
      src/app/app.routing.ts
  8. 48 32
      src/app/components/assets/assets.component.html
  9. 19 3
      src/app/components/assets/assets.component.scss
  10. 176 373
      src/app/components/assets/assets.component.ts
  11. 104 0
      src/app/components/confirm-account/confirm-account.component.html
  12. 106 0
      src/app/components/confirm-account/confirm-account.component.scss
  13. 6 6
      src/app/components/confirm-account/confirm-account.component.spec.ts
  14. 139 0
      src/app/components/confirm-account/confirm-account.component.ts
  15. 2 2
      src/app/components/dashboard/dashboard.component.html
  16. 28 25
      src/app/components/dashboard/dashboard.component.ts
  17. 13 6
      src/app/components/login/login.component.html
  18. 71 0
      src/app/components/login/login.component.ts
  19. 80 0
      src/app/components/organizations/edit-organization/edit-organization.component.html
  20. 0 0
      src/app/components/organizations/edit-organization/edit-organization.component.scss
  21. 25 0
      src/app/components/organizations/edit-organization/edit-organization.component.spec.ts
  22. 103 0
      src/app/components/organizations/edit-organization/edit-organization.component.ts
  23. 97 0
      src/app/components/organizations/new-organization/new-organization.component.html
  24. 5 0
      src/app/components/organizations/new-organization/new-organization.component.scss
  25. 25 0
      src/app/components/organizations/new-organization/new-organization.component.spec.ts
  26. 77 0
      src/app/components/organizations/new-organization/new-organization.component.ts
  27. 19 0
      src/app/components/organizations/organization/organization.component.html
  28. 0 0
      src/app/components/organizations/organization/organization.component.scss
  29. 25 0
      src/app/components/organizations/organization/organization.component.spec.ts
  30. 16 0
      src/app/components/organizations/organization/organization.component.ts
  31. 84 0
      src/app/components/organizations/organizations.component.html
  32. 1 0
      src/app/components/organizations/organizations.component.scss
  33. 25 0
      src/app/components/organizations/organizations.component.spec.ts
  34. 148 0
      src/app/components/organizations/organizations.component.ts
  35. 22 0
      src/app/components/plants/edit-plant/edit-plant.component.html
  36. 0 0
      src/app/components/plants/edit-plant/edit-plant.component.scss
  37. 25 0
      src/app/components/plants/edit-plant/edit-plant.component.spec.ts
  38. 16 0
      src/app/components/plants/edit-plant/edit-plant.component.ts
  39. 67 0
      src/app/components/plants/new-plant/new-plant.component.html
  40. 0 0
      src/app/components/plants/new-plant/new-plant.component.scss
  41. 25 0
      src/app/components/plants/new-plant/new-plant.component.spec.ts
  42. 17 0
      src/app/components/plants/new-plant/new-plant.component.ts
  43. 80 0
      src/app/components/plants/plants.component.html
  44. 0 0
      src/app/components/plants/plants.component.scss
  45. 25 0
      src/app/components/plants/plants.component.spec.ts
  46. 78 0
      src/app/components/plants/plants.component.ts
  47. 1 1
      src/app/components/plugins/maps/maps.component.ts
  48. 0 32
      src/app/components/plugins/organization-cards/organization-cards.component.html
  49. 0 4
      src/app/components/plugins/organization-cards/organization-cards.component.scss
  50. 0 35
      src/app/components/plugins/organization-cards/organization-cards.component.ts
  51. 1 2
      src/app/components/plugins/plugins.module.ts
  52. 21 0
      src/app/components/plugins/validator/validator.component.ts
  53. 2 2
      src/app/components/plugins/weather-card/weather-card.component.html
  54. 4 3
      src/app/components/shared/navbar/navbar.component.html
  55. 9 3
      src/app/components/shared/navbar/navbar.component.ts
  56. 14 38
      src/app/components/shared/sidebar/sidebar.component.html
  57. 0 1
      src/app/components/shared/sidebar/sidebar.component.scss
  58. 24 4
      src/app/components/shared/sidebar/sidebar.component.ts
  59. 113 0
      src/app/components/users/new-user/new-user.component.html
  60. 0 0
      src/app/components/users/new-user/new-user.component.scss
  61. 25 0
      src/app/components/users/new-user/new-user.component.spec.ts
  62. 68 0
      src/app/components/users/new-user/new-user.component.ts
  63. 80 0
      src/app/components/users/users.component.html
  64. 0 0
      src/app/components/users/users.component.scss
  65. 25 0
      src/app/components/users/users.component.spec.ts
  66. 74 0
      src/app/components/users/users.component.ts
  67. 94 0
      src/app/data/organizations.data.ts
  68. 28 1
      src/app/layouts/admin/admin.module.ts
  69. 101 14
      src/app/layouts/admin/admin.routing.ts
  70. 1 1
      src/app/models/organization.ts
  71. 4 0
      src/app/models/token.ts
  72. 28 0
      src/app/services/auth.guard.ts
  73. 96 0
      src/app/services/auth2.service.ts
  74. 28 5
      src/app/services/logs.service.ts
  75. 63 3
      src/app/services/organizations.service.ts
  76. 24 3
      src/app/services/plants.service.ts
  77. 67 0
      src/app/services/token.interceptor.ts
  78. 55 6
      src/app/services/user.service.ts
  79. 1 1
      src/assets/scss/core/_sidebar-and-main-panel.scss
  80. 94 0
      src/assets/scss/material-dashboard.scss
  81. 1 0
      src/environments/environment.prod.ts
  82. 2 1
      src/environments/environment.ts

+ 5 - 0
.dockerignore

@@ -0,0 +1,5 @@
+e2e
+node_modules
+.gitignore
+README.md
+dist

+ 18 - 0
Dockerfile

@@ -0,0 +1,18 @@
+# Step 1: Build the app in image "builder"
+FROM node:lts-alpine AS builder
+
+COPY package.json package-lock.json ./
+RUN npm i && mkdir /app && mv ./node_modules ./app
+WORKDIR /app
+COPY . .
+RUN node --max-old-space-size=4096 ./node_modules/@angular/cli/bin/ng build --prod
+
+# Step 2: Use build output from 'builder'
+
+FROM nginx:stable-alpine
+LABEL version="1.0"
+
+COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
+
+WORKDIR /usr/share/nginx/html
+COPY --from=builder /app/dist/plant-viewer/ .

+ 49 - 0
nginx/nginx.conf

@@ -0,0 +1,49 @@
+worker_processes  1;
+
+events {
+    worker_connections  1024;
+}
+
+http {
+    upstream api {
+        server backend:8000;
+    }
+    server {
+        listen 80;
+        server_name  localhost;
+
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+        include /etc/nginx/mime.types;
+
+        location /api/ {
+            proxy_pass http://api;
+            proxy_http_version 1.1;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection 'upgrade';
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_set_header X-NginX-Proxy true;
+            proxy_cache_bypass $http_upgrade;
+        }
+
+        location /assets/ {
+            access_log off;
+            expires 1d;
+        }
+        location ~ \.(css|js|svg|ico)$ {
+            access_log off;
+            expires 1d;
+        }
+        
+        gzip on;
+        gzip_min_length 1000;
+        gzip_proxied expired no-cache no-store private auth;
+        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+
+        location / {
+            try_files $uri $uri/ /index.html;
+        }
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 272 - 226
package-lock.json


+ 7 - 2
package.json

@@ -25,13 +25,17 @@
     "@asymmetrik/ngx-leaflet": "^6.0.1",
     "@ng-bootstrap/ng-bootstrap": "^5.1.0",
     "@swimlane/ngx-datatable": "^15.0.2",
+    "@types/crypto-js": "^3.1.43",
     "@types/googlemaps": "^3.37.4",
     "angular-bootstrap-md": "^8.1.1",
     "angular-datatables": "^8.0.0",
+    "angular-mydatepicker": "^0.1.9",
     "arrive": "^2.4.1",
     "bootstrap": "^4.3.1",
     "bootstrap-material-design": "^4.1.2",
+    "c": "^0.1.0",
     "chart.js": "^2.8.0",
+    "crypto-js": "^3.1.9-1",
     "datatables.net": "^1.10.19",
     "datatables.net-dt": "^1.10.19",
     "font-awesome": "^4.7.0",
@@ -41,8 +45,9 @@
     "leaflet-routing-machine": "^3.2.12",
     "moment": "^2.24.0",
     "ng2-charts": "^2.3.0",
+    "ngx-animating-datepicker": "^1.2.1",
     "ngx-bootstrap": "^5.1.1",
-    "package.json": "^2.0.1",
+    "ngx-daterangepicker": "^1.1.1",
     "perfect-scrollbar": "^1.4.0",
     "popper.js": "^1.15.0",
     "rxjs": "6.5.2",
@@ -76,4 +81,4 @@
     "tslint": "~5.15.0",
     "typescript": "~3.5.3"
   }
-}
+}

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

@@ -27,22 +27,23 @@ import { SharedModule } from './components/shared/shared.module';
 
 import { AdminComponent } from './layouts/admin/admin.component';
 
-
 import { NgxDatatableModule } from '@swimlane/ngx-datatable';
 import { MatSelectModule } from '@angular/material/select';
-
+import { AngularMyDatePickerModule } from 'angular-mydatepicker';
 
 import { PluginsModule } from './components/plugins/plugins.module';
 import { LoginComponent } from './components/login/login.component';
 
+import { TokenInterceptor } from '@app/services/token.interceptor';
+import { ConfirmAccountComponent } from './components/confirm-account/confirm-account.component';
 
-//import { DashboardComponent } from './dashboard/dashboard.component';
 
 @NgModule({
   declarations: [
     AppComponent,
     AdminComponent,
     LoginComponent,
+    ConfirmAccountComponent,
   ],
   imports: [
     BrowserModule,
@@ -58,9 +59,10 @@ import { LoginComponent } from './components/login/login.component';
     PluginsModule,
     NgxDatatableModule,
     MatSelectModule,
+    AngularMyDatePickerModule
   ],
   providers: [
-    //{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
+    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
     //{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
     //AuthenticationService,
     // provider used to create fake backend

+ 6 - 2
src/app/app.routing.ts

@@ -6,8 +6,10 @@ import { AdminModule } from './layouts/admin/admin.module';
 
 import { AdminComponent } from './layouts/admin/admin.component';
 import { LoginComponent } from './components/login/login.component';
-import { AuthGuard } from './services/authentication.service';
+//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';
 
 const routes: Routes =[
   {
@@ -23,7 +25,9 @@ const routes: Routes =[
       path: '',
       loadChildren : './layouts/admin/admin.module#AdminModule' //() => AdminModule
     }]
-  }, { path: 'login', component: LoginComponent },
+  }, 
+  { path: 'login', component: LoginComponent },
+  { path: 'confirm-account', component: ConfirmAccountComponent },
   { path: '**', redirectTo: '' }
 ];
 

+ 48 - 32
src/app/components/assets/assets.component.html

@@ -5,19 +5,35 @@
       <label for="sel3">Plantas</label>
     </div>
   </div>
-  <div class="row">
-    <div class="col-lg-6 col-sm-4">
-      <div class="align-container">
-        <select class="custom-select"(onChange)="onChangeObj($event)" name="sel3">
-          <option *ngFor="let item of listAssets" [selected]="item._id===organizationId" [value]="item._id" >{{item.name}}</option>
-        </select>
+  <div class="row align-container" style="padding: 0 25px;">
+    <div class="col-lg-6 col-sm-4 p-1">
+      <div class="row">
+        <div class="col-lg-12 col-sm-12">
+          <select class="custom-select" (change)="onChangeObj($event)" name="sel3">
+            <option *ngFor="let item of listAssets" [selected]="item.id===organizationId" [value]="item.id" >{{item.name}}</option>
+          </select>
+        
+        </div>
+      </div>
+      <!--<div class="row">
+        <div class="col-lg-12">
+            <div class="widget environment-meters">
+                <div class="mini-stats">
+                  <span class="dark-yellow-skin"><i class="fa fa-bolt"></i></span>
+                  <h5 *ngIf="eProduced">
+                    {{eProduced.thisWeek.total_energy_kWh/1000 | number}}
+                    <small>MWh</small>
+                  </h5>
+                </div>
+              </div>
+          </div>
       </div>
-      <br>
+    -->  
     </div>
 
     <!-- Weather card -->
-    <div class="col-lg-6 col-sm-8">
-      <div class="row align-container">
+    <div class="col-lg-6 col-sm-8 p-1">
+      <div class="row">
         <div class="col-lg-12">
           <app-weather-card></app-weather-card>
         </div>
@@ -33,8 +49,8 @@
       <div class="widget energy-stats">
         <div class="mini-stats ">
           <span class="sky-skin"><i class="fa fa-bolt"></i></span>
-          <h3 *ngIf="listEnergyProduced">
-            {{listEnergyProduced.today.total_energy_kWh}}
+          <h3 *ngIf="eProduced">
+            {{energyDay}}
             <small>kW</small>
           </h3>
           <p>Ultimo dia</p>
@@ -46,8 +62,8 @@
       <div class="widget energy-stats">
         <div class="mini-stats ">
           <span class="dark-yellow-skin"><i class="fa fa-bolt"></i></span>
-          <h3 *ngIf="listEnergyProduced">
-            {{listEnergyProduced.thisWeek.total_energy_kWh/1000 | number}}
+          <h3 *ngIf="eProduced">
+            {{energyWeek}}
             <small>MWh</small>
           </h3>
           <p>Ultima semana</p>
@@ -59,8 +75,8 @@
       <div class="widget energy-stats">
         <div class="mini-stats ">
           <span class="yellow-skin"><i class="fa fa-bolt"></i></span>
-          <h3 *ngIf="listEnergyProduced">
-            {{listEnergyProduced.thisMonth.total_energy_kWh/1000 | number}}
+          <h3 *ngIf="eProduced">
+            {{energyMonth}}
             <small>MWh</small>
           </h3>
           <p>Ultimos 30 días</p>
@@ -72,8 +88,8 @@
       <div class="widget energy-stats">
         <div class="mini-stats ">
           <span class="dark-yellow-skin"><i class="fa fa-bolt"></i></span>
-          <h3 *ngIf="listEnergyProduced">
-            {{listEnergyProduced.lifeTime.total_energy_kWh/1000 | number}}
+          <h3 *ngIf="eProduced">
+            {{energyYear}}
             <small>MWh</small>
           </h3>
           <p>Total</p>
@@ -88,33 +104,33 @@
     <div class="col-lg-12 col-md-12 col-sm-12">
       <div class="widget">
         <div class="mini-stats">
-          <!--
-          <div class="chart-container" style="display:none;">
-            <canvas baseChart #baseChart="base-chart" [datasets]="barChartData" [labels]="barChartLabels" [options]="barChartOptions"
-              [legend]="barChartLegend" [chartType]="barChartType"></canvas>
-          </div>
-          -->
 
-          <div class="chart-container" >
+          <div class="chart-container">
             <canvas id="canvas">{{ chart1 }}</canvas>
           </div>
 
           <p>Seleccione un rango de visualización</p>
 
-          <button class="btn" [class.btn-success]='isActive[0]' (click)="onMeasureClickDay()">Día</button>
-          <button class="btn" [class.btn-success]='isActive[1]' (click)="onMeasureClickWeek()">Semana</button>
-          <button class="btn" [class.btn-success]='isActive[2]' (click)="onMeasureClickMonth()">Mes</button>
-          <button class="btn" [class.btn-success]='isActive[3]' (click)="onMeasureYear()">Año</button>
-
+          <button class="btn" [class.btn-success]='isActive[0]' (click)="onMeasureClickRange('day')">Día</button>
+          <button class="btn" [class.btn-success]='isActive[1]' (click)="onMeasureClickRange('week')">Semana</button>
+          <button class="btn" [class.btn-success]='isActive[2]' (click)="onMeasureClickRange('month')">Mes</button>
+          <button class="btn" [class.btn-success]='isActive[3]' (click)="onMeasureClickRange('year')">Año</button>
+          <div class="input-box-container">
+            <input class="input-box" placeholder="Click to select a date" 
+              angular-mydatepicker name="mydate" (click)="dp.toggleCalendar()" 
+              [(ngModel)]="model" [options]="myDpOptions" (dateChanged)="onDateChanged($event)" 
+              #dp="angular-mydatepicker"/>
+          </div>
         </div>
       </div>
+
     </div>
   </div>
   
   <!-- Enviromental cool stats cards -->
   <div class="row align-container">
 
-    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6">
+    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6 p-1">
       <div class="widget">
         <div class="enviroment-stats">
           <div class="enviromental-icon">
@@ -129,7 +145,7 @@
       </div>
     </div>
     
-    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6">
+    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6 p-1">
       <div class="widget">
         <div class="enviroment-stats">
           <div class="enviromental-icon">
@@ -144,7 +160,7 @@
       </div>
     </div>
     
-    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6">
+    <div class="col-xl-4 col-lg-6 col-md-6 col-sm-6 p-1">
       <div class="widget">
         <div class="enviroment-stats">
           <div class="enviromental-icon">

+ 19 - 3
src/app/components/assets/assets.component.scss

@@ -1,3 +1,18 @@
+.input-box-container {
+  display: inline-block;
+  margin-left: 15px;
+  position: relative;
+  border-collapse: separate;
+  
+  input {
+    margin: 5px 0 0;
+    font-family: inherit;
+    font-size: inherit;
+    line-height: inherit;
+    padding: 6px;
+  }
+}
+
 table {
   width: 100%;
 }
@@ -42,6 +57,10 @@ table {
   height: 122px;
 }
 
+.enviroment-meter {
+  height: 60px;
+}
+
 .widget {
   background: #ffffff none repeat scroll 0 0;
   float: left;
@@ -93,7 +112,6 @@ table {
       font-size: 2rem;
       font-weight: 400;
     }
-
   }
 
   .enviroment-stats {
@@ -118,9 +136,7 @@ table {
 
       span {
         float: none;
-        height: auto;
         margin: 0;
-        width: auto;
         display: inline;
         border: none;
         font-size: 1.2rem;

+ 176 - 373
src/app/components/assets/assets.component.ts

@@ -19,8 +19,7 @@ import { environment } from '@environments/environment';
 import { HttpClient } from '@angular/common/http';
 import { formatDate } from '@angular/common';
 
-
-
+import {AngularMyDatePickerDirective,IAngularMyDpOptions, IMyDateModel} from 'angular-mydatepicker';
 
 @Component({
   selector: 'app-assets',
@@ -28,31 +27,49 @@ import { formatDate } from '@angular/common';
   styleUrls: ['./assets.component.scss']
 })
 
-
 export class AssetsComponent implements OnInit {
- 
+  
   title = "Plantas";
 
   organizationId:string;
   listAssets:any;
-  listEnergyProduced:any;
+
+  eProduced:any;
   error:boolean;
   errorMessage:string;
-  test:any;
   errortest:any;
   chartjs:boolean;
   chart1: Chart;
   metersKeys:any;
   metersValues:any;
+  view:string;
+  energyDay:any;
+  energyWeek:any;
+  energyMonth:any;
+  energyYear:any;
+
+  //For daterange
+  daysLabels:any = {su: 'Dom', mo: 'Lun', tu: 'Mar', we: 'Mie', th: 'Jue', fr: 'Vie', sa: 'Sab'};
+  monthsLabels:any = { 1: 'Ene', 2: 'Feb', 3: 'Mar', 4: 'Abr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Ago', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dic' };
+  todayBtnTxt:string = 'Hoy';
+  
+  myDpOptions: IAngularMyDpOptions = {
+    dateRange: false,
+    dateFormat: 'dd/mm/yyyy',
+    dayLabels: this.daysLabels,
+    monthLabels: this.monthsLabels,
+    // other options are here...
+  };
+
+  myDateInit: boolean = true;
+  model: IMyDateModel = null;
+
   // For chartjs
   tsLabels: any[];
-  polarLabels: any[];
-  serieA: any[];
-  serieB: any[];
-  serieC: any[];
   barChartColors:any = ['#3c8dbc', '#00a65a', '#f56954']
-  
   metersData:any;
+  initialDate:any;
+
 
   public barChartType: ChartType;
   public barChartLegend:boolean;
@@ -70,6 +87,10 @@ export class AssetsComponent implements OnInit {
     private logsService: LogsService,
     private measService: MeasuresService) {
       
+      this.route.queryParams.subscribe(params => {
+        this.organizationId = params['id'];
+      });
+      
       Swal.fire({
         allowOutsideClick: false,
         type: 'info',
@@ -82,26 +103,40 @@ export class AssetsComponent implements OnInit {
   @ViewChild("baseChart",null) chart: BaseChartDirective;
 
   ngOnInit() {
-    
-    let plants = this.plantsService.getAssets();
-    let energy_produced = this.plantsService.getAssetsProducedEnergy();
-
-    // Observable of two objects
-    forkJoin([plants, energy_produced]).subscribe(results => {
-      if (localStorage.getItem("email") == "inverlec@grupomerelec.com"){
-        this.listAssets = [{
-          name: "Inversiones MERELEC S.A de C.V",
-          id: 1
-        }]
-      }
-      else {
 
-        this.listAssets = results[0]["assets"];
+    // Default date is today and set it on a string var and initialize dateRange plugin
+    this.initialDate = new Date().toISOString().slice(0, 10);
+    if (this.myDateInit) {
+      let begin: Date = new Date();
+      this.model = {
+        isRange: false, 
+        singleDate: {
+          date: {
+            year: begin.getFullYear(), month: begin.getMonth() + 1, day: begin.getDate()
+          }
+        }
+      };
+    }
+    // Initialize on load
+    this.isActive = [false, false, false, false];
+
+    let plants = this.plantsService.getAllAssets().subscribe(res => {
+      this.listAssets = res["data"]["assets"];
+      this.view = "month";
+      if (this.organizationId == undefined){
+        this.organizationId = this.listAssets[0].id;
       }
-      
-      this.listEnergyProduced = results[1];   
-    },
-    (err) => {
+      this.onMeasureClickRange(this.view, this.initialDate);
+
+      let energy_produced = this.logsService.getEnergySummaryByAsset(this.organizationId).subscribe(resp => {
+        this.eProduced = resp["data"]["energy"];//results[1];
+        this.energyDay = this.eProduced.today.total_energy_kWh;
+        this.energyWeek = this.eProduced.thisWeek.total_energy_kWh > 0 ? (this.eProduced.thisWeek.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisWeek.total_energy_kWh
+        this.energyMonth = this.eProduced.thisMonth.total_energy_kWh > 0 ? (this.eProduced.thisMonth.total_energy_kWh/1000).toFixed(2) : this.eProduced.thisMonth.total_energy_kWh
+        this.energyYear = this.eProduced.lifeTime.total_energy_kWh > 0 ? (this.eProduced.lifeTime.total_energy_kWh/1000).toFixed(2) : this.eProduced.lifeTime.total_energy_kWh
+      });
+
+    }, (err) => {
       Swal.fire({
         type: 'error',
         title: 'Error en el servidor',
@@ -109,81 +144,13 @@ export class AssetsComponent implements OnInit {
       });
     });
 
-    
-      this.isActive = [false, false, true, false];
-      this.logsService.getEnergyProducedByMonth().toPromise()
-      .then((data: any) => {
-        // Get all the keys(each meter name) of the returned promise
-        this.metersKeys = Object.keys(data["1M"]);
-        
-        // Get all the values according to each index (meter) 
-        this.metersValues = Object.values(data["1M"]).map(sub => sub);
-        let meterKeys2 = Object.keys(this.metersValues);
-        
-        // Re-Initialize Json object
-        this.metersData = [];
-
-        // Json object construction for passing its values to the chartjs datasets according to its requirements
-        for (let prop in meterKeys2) { 
-          let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersKeys[prop];
-          this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: this.metersValues[prop].map(obj => obj.total_energy_kWh).reverse()});
-          this.barChartLabels = this.metersValues[prop].map(obj => formatDate(obj.dateMax, 'dd/MM','es-Es','-0600')).reverse();
-        }
-
-        // Chart draw
-        this.chart1 = new Chart('canvas', {
-          type: 'bar',
-          options: {
-            title: {
-              display: true,
-            },
-            tooltips: {
-              mode: 'index',
-              callbacks: {
-                // Get the total of the energy produced in every point and show it in the hover tooltip
-                label: function (tooltipItem, data) { 
-                  var label = data.datasets[tooltipItem.datasetIndex].label || '';
-                  var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
-                  var total = 0;
-
-                  for (var i = 0; i < data.datasets.length; i++)
-                    total += +data.datasets[i].data[tooltipItem.index];
-
-                  if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
-                    return label + " : " + value;
-                  }
-                  else {
-                    return [label + " : " + value, "TOTAL : " + Math.round(total)];
-                  }
-                }
-              }
-            },
-            responsive: true,
-            maintainAspectRatio: false,
-            scales: {
-              xAxes: [{
-                stacked: true,
-              }],
-              yAxes: [{
-                stacked: true
-              }]
-            }
-          },
-          data: {
-            labels: this.barChartLabels,
-            datasets: 
-              this.metersData,
-          }, 
-        });
-        this.chartjs = true;
-      });
-      this.chartjs = true;
-      setTimeout(()=>{
-        Swal.close();
-      }, 2700);
-
+    //this.chartjs = true;
+    setTimeout(()=>{
+      Swal.close();
+    }, 2700);
   }
  
+  // Default check asset dropdown if a value is equal to an assetID
   isSelected(){
     if(this.organizationId!=undefined){
       return true;
@@ -193,208 +160,129 @@ export class AssetsComponent implements OnInit {
     }
   }
 
-  onChangeObj(event) {
-    
+  // Trigger again the chart with the selected assetID
+  onChangeObj(event:any) {
+    this.organizationId = event.target.value;
+    this.onMeasureClickRange(this.view, this.initialDate, this.organizationId);
   }
+  
 
-  onMeasureClickDay(): void {
-    Swal.fire({
-      allowOutsideClick: false,
-      type: 'info',
-      text: 'Espere por favor...'
-    });
-    Swal.showLoading();
-    this.isActive = [true, false, false, false];
-    this.chart1.destroy();
-
-     
-    this.logsService.getEnergyProducedByDay().toPromise()
-    .then((data: any) => {
-      // Get all the keys(each meter name) of the returned promise
-      this.metersKeys = Object.keys(data["1D"]);
-              
-      // Get all the values according to each index (meter) 
-      this.metersValues = Object.values(data["1D"]).map(sub => sub);
-      let meterKeys2 = Object.keys(this.metersValues);
-
-      // Re-Initialize Json object
-      this.metersData = [];
-
-      // Json object construction for passing its values to the chartjs datasets according to its requirements
-      for (let prop in meterKeys2) { 
-        let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersKeys[prop];
-        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: this.metersValues[prop].map(obj => obj.total_energy_kWh).reverse()});
-        this.barChartLabels = this.metersValues[0].map(obj => formatDate(obj.dateMin, 'hh:mm:ss','es-Es','-0600')).reverse();
-      }
-      this.chart1.destroy();
-      this.chart1 = undefined;
-      // Chart draw
-      this.chart1 = new Chart('canvas', {
-        type: 'bar',
-        options: {
-          title: {
-            display: true,
-          },
-          tooltips: {
-            mode: 'index',
-            callbacks: {
-              // Get the total of the energy produced in every point and show it in the hover tooltip
-              label: function (tooltipItem, data) { 
-                var label = data.datasets[tooltipItem.datasetIndex].label || '';
-                var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
-                var total = 0;
-
-                for (var i = 0; i < data.datasets.length; i++)
-                  total += +data.datasets[i].data[tooltipItem.index];
-
-                if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
-                  return label + " : " + value;
-                }
-                else {
-                  return [label + " : " + value, "TOTAL : " + Math.round(total)];
-                }
-              }
-            }
-          },
-          responsive: true,
-          maintainAspectRatio: false,
-          scales: {
-            xAxes: [{
-              stacked: true,
-            }],
-            yAxes: [{
-              stacked: true
-            }]
-          }
-        },
-        data: {
-          labels: this.barChartLabels,
-          datasets: 
-            this.metersData,
-        }, 
-      });
-      this.chartjs = true;
-    });
-    setTimeout(()=>{
-      Swal.close();
-    }, 2700)
+  // Change the date range of the chart, according to the selected view (daily, weekly, ...)
+  onDateChanged(event: IMyDateModel) {
+    let endDate = `${event.singleDate.date.year}-${event.singleDate.date.month}-${event.singleDate.date.day}`;
+    this.initialDate  = endDate;
+    switch (this.view)
+    { 
+      case "day": 
+        this.onMeasureClickRange(this.view, endDate);
+        break; 
+      case "week": 
+        this.onMeasureClickRange(this.view, endDate);
+        break; 
+      case "month": 
+        this.onMeasureClickRange(this.view, endDate);
+        break; 
+      case "year":
+        this.onMeasureClickRange(this.view, endDate);
+        break;
+      default: 
+        console.log("nada");
+    } 
   }
 
-  onMeasureClickWeek(): void {;
-
+  // Draw a measure chart using chartjs, given some params, view, for daily, weekly, monthly or yearly measures;
+  // measureDate, for a date range given a view and a paramID, for an specific assetID selected in the dropdown  
+  onMeasureClickRange(view:string, measureDate?:string, paramId?:string): void {
     Swal.fire({
       allowOutsideClick: false,
       type: 'info',
       text: 'Espere por favor...'
     });
     Swal.showLoading();
-    this.isActive = [false, true, false, false];
-    this.chart1.destroy();
- 
-    this.logsService.getEnergyProducedByWeek().toPromise()
-    .then((data: any) => {
-      // Get all the keys(each meter name) of the returned promise
-      this.metersKeys = Object.keys(data["7D"]);
-      
-      // Get all the values according to each index (meter) 
-      this.metersValues = Object.values(data["7D"]).map(sub => sub);
-      let meterKeys2 = Object.keys(this.metersValues);
-      
-      // Re-Initialize Json object
-      this.metersData = [];
+    this.view = view;
 
-      // Json object construction for passing its values to the chartjs datasets according to its requirements
-      for (let prop in meterKeys2) { 
-        let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersKeys[prop];
-        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: this.metersValues[prop].map(obj => obj.total_energy_kWh).reverse()});
-        this.barChartLabels = this.metersValues[prop].map(obj => formatDate(obj.dateMax, 'EEEE dd','es-Es','-0600')).reverse();
-      }
+    // Chart (re)initialize, to prevent double load on changing date range or views
+    if (this.chart1 != undefined){
       this.chart1.destroy();
-      this.chart1 = undefined;
-      // Chart draw
-      this.chart1 = new Chart('canvas', {
-        type: 'bar',
-        options: {
-          title: {
-            display: true,
-          },
-          tooltips: {
-            mode: 'index',
-            callbacks: {
-              // Get the total of the energy produced in every point and show it in the hover tooltip
-              label: function (tooltipItem, data) { 
-                var label = data.datasets[tooltipItem.datasetIndex].label || '';
-                var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
-                var total = 0;
-
-                for (var i = 0; i < data.datasets.length; i++)
-                  total += +data.datasets[i].data[tooltipItem.index];
-
-                if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
-                  return label + " : " + value;
-                }
-                else {
-                  return [label + " : " + value, "TOTAL : " + Math.round(total)];
-                }
-              }
-            }
-          },
-          responsive: true,
-          maintainAspectRatio: false,
-          scales: {
-            xAxes: [{
-              stacked: true,
-            }],
-            yAxes: [{
-              stacked: true
-            }]
-          }
-        },
-        data: {
-          labels: this.barChartLabels,
-          datasets: 
-            this.metersData,
-        }, 
-      });
-      this.chartjs = true;
-    });
-
-    setTimeout(()=>{
-      Swal.close();
-    }, 2700)   
-  }
+    }
+    this.chart1 = undefined;
+    
+    let dateRange:string;
+    if (measureDate === undefined){
+      dateRange = this.initialDate;
+    }
+    else {
+      dateRange = measureDate;
+    }
 
-  onMeasureClickMonth(): void {
-    Swal.fire({
-      allowOutsideClick: false,
-      type: 'info',
-      text: 'Espere por favor...'
-    });
-    Swal.showLoading();
-    this.isActive = [false, false, true, false];
-    this.chart1.destroy();
-     
-    this.logsService.getEnergyProducedByMonth().toPromise()
+    // Set default assetID if paramId doesn't come in the function call
+    let assetId:string;
+    if (paramId === undefined){
+      assetId = this.listAssets[0]["id"]; // Grab the first object
+    }
+    else {
+      assetId = paramId;
+    }
+    
+    // Select an interval according to the API permitted param
+    let interval:string;
+    switch (view)
+    { 
+      case "day": 
+        interval = "1D";
+        this.isActive = [true, false, false, false];
+        break; 
+      case "week": 
+        interval = "7D";
+        this.isActive = [false, true, false, false];
+        break; 
+      case "month":
+        interval = "1M";
+        this.isActive = [false, false, true, false];
+        break; 
+      case "mtd": //TO DO
+        interval = "MTD";
+        break;
+      case "year":
+        interval = "YTD";
+        this.isActive = [false, false, false, true];
+        break;
+      default: 
+        console.log("nada");
+    }  
+
+    this.logsService.getEnergyProducedByParams(assetId,interval,dateRange).toPromise()
     .then((data: any) => {
-      // Get all the keys(each meter name) of the returned promise
-      this.metersKeys = Object.keys(data["1M"]);
-      
+      this.metersData = [];      
+      this.metersKeys = Object.keys(data["data"]["dataset"]);
+
       // Get all the values according to each index (meter) 
-      this.metersValues = Object.values(data["1M"]).map(sub => sub);
+      this.metersValues = Object.values(data["data"]["dataset"]);
+
+      // Get the keys of those values
       let meterKeys2 = Object.keys(this.metersValues);
-      
-      // Re-Initialize Json object
-      this.metersData = [];
 
-      // Json object construction for passing its values to the chartjs datasets according to its requirements
       for (let prop in meterKeys2) { 
-        let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersKeys[prop];
-        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: this.metersValues[prop].map(obj => obj.total_energy_kWh).reverse()});
-        this.barChartLabels = this.metersValues[prop].map(obj => formatDate(obj.dateMax, 'dd/MM','es-Es','-0600')).reverse();
+        //let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersValues[prop]["label"];
+        let label = this.metersValues[prop]["label"];
+        let measure_values = Object.values(this.metersValues[prop]["data"].map(obj => obj.total_energy_kWh).reverse())
+        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: measure_values });
       }
-      this.chart1.destroy();
-      this.chart1 = undefined;
-      // Chart draw
+
+      switch (view){
+        case "day": 
+          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'hh:mm:ss','es-Es','-0600')).reverse();
+          break; 
+        case 'week':
+          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'EEEE dd','es-Es','-0600')).reverse();
+          break;
+        case "year":
+          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'MM/yyyy','es-Es','-0600')).reverse();
+          break;
+        default: 
+          this.barChartLabels = this.metersValues[0]["data"].map(obj => formatDate(obj.dateMax, 'dd/MM','es-Es','-0600')).reverse(); 
+      }
+
       this.chart1 = new Chart('canvas', {
         type: 'bar',
         options: {
@@ -420,7 +308,7 @@ export class AssetsComponent implements OnInit {
                   return [label + " : " + value, "TOTAL : " + Math.round(total)];
                 }
               }
-            }
+            }         
           },
           responsive: true,
           maintainAspectRatio: false,
@@ -435,8 +323,7 @@ export class AssetsComponent implements OnInit {
         },
         data: {
           labels: this.barChartLabels,
-          datasets: 
-            this.metersData,
+          datasets: this.metersData,
         }, 
       });
       this.chartjs = true;
@@ -444,92 +331,8 @@ export class AssetsComponent implements OnInit {
 
     setTimeout(()=>{
       Swal.close();
-    }, 2700)
+    }, 1200)
 
   }
 
-  onMeasureYear(): void {
-
-    Swal.fire({
-      allowOutsideClick: false,
-      type: 'info',
-      text: 'Espere por favor...'
-    });
-    Swal.showLoading();
-
-    this.isActive = [false, false, false, true];  
-    this.chart1.destroy();
-
-    this.http.get(`${environment.apiUrl}/logs/energyProduced/YTD`).toPromise()
-    .then((data: any) => {
-      // Get all the keys(each meter name) of the returned promise
-      this.metersKeys = Object.keys(data["YTD"]);
-
-      // Get all the values according to each index (meter) 
-      this.metersValues = Object.values(data["YTD"]).map(sub => sub);
-      let meterKeys2 = Object.keys(this.metersValues);
-      
-      // Re-Initialize Json object
-      this.metersData = [];
-
-      // Json object construction for passing its values to the chartjs datasets according to its requirements
-      for (let prop in meterKeys2) { 
-        let label = localStorage.getItem("email") == "inverlec@grupomerelec.com" ? `INVERLEC ${prop}` : this.metersKeys[prop];
-        this.metersData.push({"label": label, backgroundColor: this.barChartColors[prop], data: this.metersValues[prop].map(obj => obj.total_energy_kWh).reverse()});
-        this.barChartLabels = this.metersValues[prop].map(obj => formatDate(obj.dateMax, 'MMM','es-Es','-0600')).reverse();
-      }
-      this.chart1.destroy();
-      this.chart1 = undefined;
-      // Chart draw
-      this.chart1 = new Chart('canvas', {
-        type: 'bar',
-        options: {
-          title: {
-            display: true,
-          },
-          tooltips: {
-            mode: 'index',
-            callbacks: {
-              // Get the total of the energy produced in every point and show it in the hover tooltip
-              label: function (tooltipItem, data) { 
-                var label = data.datasets[tooltipItem.datasetIndex].label || '';
-                var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
-                var total = 0;
-
-                for (var i = 0; i < data.datasets.length; i++)
-                  total += +data.datasets[i].data[tooltipItem.index];
-
-                if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
-                  return label + " : " + value;
-                }
-                else {
-                  return [label + " : " + value, "TOTAL : " + Math.round(total)];
-                }
-              }
-            }
-          },
-          responsive: true,
-          maintainAspectRatio: false,
-          scales: {
-            xAxes: [{
-              stacked: true,
-            }],
-            yAxes: [{
-              stacked: true
-            }]
-          }
-        },
-        data: {
-          labels: this.barChartLabels,
-          datasets: 
-            this.metersData,
-        }, 
-      });
-      this.chartjs = true;
-    });
-    
-    setTimeout(()=>{
-      Swal.close();
-    }, 3200)
-  }
 }

+ 104 - 0
src/app/components/confirm-account/confirm-account.component.html

@@ -0,0 +1,104 @@
+<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">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 />
+              </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>
+                </div>
+              </div>
+
+              
+              <div class="form-group">
+                  <label for="name">Apellido</label>
+                  <input type="text" class="form-control"
+                  formControlName="last_name" [ngClass]="{ 'is-invalid': submitted && f.last_name.errors }"/>
+                  <div *ngIf="submitted && f.last_name.errors" class="invalid-feedback">
+                    <div *ngIf="f.last_name.errors.required">Campo requerido</div>
+                  </div>
+                </div>
+
+              <div class="form-group">
+                <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>
+              </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>
+
+              <div class="div-center">
+                <button class="btn btn-primary" type="submit"> 
+                  Confirmar cuenta
+                </button>
+              </div>
+
+            </form>
+          </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>
+  </div>
+</div>

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

@@ -0,0 +1,106 @@
+.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: .5s;
+  border: 0;
+  margin-bottom: 30px;
+  border-radius: .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;
+  }
+}
+

+ 6 - 6
src/app/components/plugins/organization-cards/organization-cards.component.spec.ts → src/app/components/confirm-account/confirm-account.component.spec.ts

@@ -1,20 +1,20 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { OrganizationCardsComponent } from './organization-cards.component';
+import { ConfirmAccountComponent } from './confirm-account.component';
 
-describe('OrganizationCardsComponent', () => {
-  let component: OrganizationCardsComponent;
-  let fixture: ComponentFixture<OrganizationCardsComponent>;
+describe('ConfirmAccountComponent', () => {
+  let component: ConfirmAccountComponent;
+  let fixture: ComponentFixture<ConfirmAccountComponent>;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [ OrganizationCardsComponent ]
+      declarations: [ ConfirmAccountComponent ]
     })
     .compileComponents();
   }));
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(OrganizationCardsComponent);
+    fixture = TestBed.createComponent(ConfirmAccountComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

+ 139 - 0
src/app/components/confirm-account/confirm-account.component.ts

@@ -0,0 +1,139 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import { UserService } from '@app/services/user.service';
+import Swal from 'sweetalert2';
+import { ValidatorComponent } from '../plugins/validator/validator.component';
+
+
+
+@Component({
+  selector: 'app-confirm-account',
+  templateUrl: './confirm-account.component.html',
+  styleUrls: ['./confirm-account.component.scss']
+})
+export class ConfirmAccountComponent implements OnInit {
+  token:string;
+  validToken:boolean;
+  activateForm: FormGroup;
+  errorMessage: string;
+  userActivated: boolean;
+  successActivation: boolean;
+  activateMessage: string;
+  invalidToken: boolean;
+  submitted: boolean = false;
+
+  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
+        }
+      ).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(6)]],
+          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(){
+
+    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,
+      }
+    ).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"
+      });
+    });
+
+    */

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

@@ -11,7 +11,7 @@
             <h4 class="font-weight-normal mb-3">Total de plantas instaladas
               <i class="mdi mdi-chart-line mdi-24px float-right"></i>
             </h4>
-            <h2 class="mb-3" *ngIf="listData">{{listData.length}}</h2>
+            <h2 class="mb-3" *ngIf="listAssets">{{listAssets.length}}</h2>
           </div>
         </div>
       </div>
@@ -22,7 +22,7 @@
             <h4 class="font-weight-normal mb-3">Capacidad instalada total
               <i class="mdi mdi-chart-line mdi-24px float-right"></i>
             </h4>
-            <h2 class="mb-3">{{totalMetersInstalled}} kW</h2>
+            <h2 class="mb-3">{{sumarize}} kW</h2>
           </div>
         </div>
       </div>

+ 28 - 25
src/app/components/dashboard/dashboard.component.ts

@@ -21,19 +21,25 @@ export class DashboardComponent implements OnInit {
   title = "Dashboard";
 
 
-  listData: any;
+  listData:any;
   rows = [];
   markers: Layer[] = [];
   points: LatLng[] = [];
 
+
+  listAssets: any;
+  error:boolean;
   listOrganizations: any;
-  error: boolean;
   plantId: string;
   plant: any;
   sub: any;
   plantNotFound: boolean;
   selectedPlant: any;
+  meterKeys:any;
+  meterKeys2:any;
+  assetKeys:any;
 
+  sumarize:number = 0;
 
   totalMetersInstalled: string;
 
@@ -77,10 +83,18 @@ export class DashboardComponent implements OnInit {
       text: 'Espere por favor...'
     });
     Swal.showLoading();
-
-
-    this.plantsService.getAssets().subscribe(res => {
-      this.listData = res["assets"];
+    
+    this.plantsService.getAllAssets().subscribe(res => {
+      this.listAssets = res["data"]["assets"];
+      this.assetKeys = Object.keys(this.listAssets);
+      for (let prop in this.assetKeys){
+        this.meterKeys2 = Object.keys(this.listAssets.map(item => item["meters_installed"])[prop]);
+        for (let prop2 in this.meterKeys2){
+          this.sumarize += this.listAssets.map(item => item["meters_installed"])[prop].map(response => response["installedCapacity_kW"])[prop2];
+          //this.totalMetersInstalled = this.sumarize.toString();
+          //localStorage.setItem("installedCapacityTotal_kW", this.totalMetersInstalled);
+        }
+      }      
     }, (err) => {
       Swal.fire({
         type: 'error',
@@ -88,20 +102,9 @@ export class DashboardComponent implements OnInit {
         text: "No su pudo obtener la informacion"
       });
     });
-
-    if (localStorage.getItem("installedCapacityTotal_kW") == undefined) {
-      this.plantsService.getInstalledCapacity().subscribe(res => {
-        this.totalMetersInstalled = res["installedCapacityTotal_kW"];
-        localStorage.setItem("totalMetersInstalled", this.totalMetersInstalled);
-      });
-    }
-    else {
-      this.totalMetersInstalled = localStorage.getItem("installedCapacityTotal_kW");
-    }
-
+  
   }
 
-
   ngOnInit(): void {
 
     var responsiveOptions: any[] = [
@@ -117,24 +120,24 @@ export class DashboardComponent implements OnInit {
 
     setTimeout(() => {
 
-      if (this.listData != undefined) {
+      if (this.listAssets != undefined) {
         this.addMarkers();
       }
 
       Swal.close();
-    }, 2500);
+    }, 2700);
 
   }
 
   getAsset(id: string) {
-    return observableOf(this.listData.find(e => e.id === id));
+    return observableOf(this.listAssets.find(e => e.id === id));
   }
 
 
   addMarkers() {
     let lat, long, address, name2;
 
-    for (const plant of this.listData) {
+    for (const plant of this.listAssets) {
       if (localStorage.getItem("email") == "inverlec@grupomerelec.com") {
         lat = "13.6613819";
         long = "-89.2514334";
@@ -142,8 +145,8 @@ export class DashboardComponent implements OnInit {
         name2 = "Inversiones MERELEC S.A de C.V";
       }
       else {
-        lat = plant.latitud;
-        long = plant.longitud;
+        lat = plant["meters_installed"][0].gpsLat;
+        long = plant["meters_installed"][0].gpsLong;
         address = plant.address;
         name2 = plant.name;
       }
@@ -174,7 +177,7 @@ export class DashboardComponent implements OnInit {
       }
     }
     else {
-      this.selectedPlant = this.listData.find(e => e.id === this.plantId);
+      this.selectedPlant = this.listAssets.find(e => e.id === this.plantId);
     }
   }
 

+ 13 - 6
src/app/components/login/login.component.html

@@ -10,23 +10,30 @@
             <p class="lead">Iniciar sesión con tu cuenta</p>
           </div>
           <div class="body">
-            <form class="form-auth-small ng-untouched ng-pristine ng-valid" (ngSubmit)="login(f)" #f="ngForm" >
+            <form [formGroup]="loginForm" (ngSubmit)="login()">
               <div class="form-group">
                 <label for="email">Correo electrónico</label>
                 
                 <input type="text" name="email" class="form-control"
-                        [(ngModel)]="user.email"  required email />
-
+                formControlName="email" email [ngClass]="{ 'is-invalid': submitted && f.email.errors }"/>
+                <div *ngIf="submitted && f.email.errors" class="invalid-feedback">
+                  <div *ngIf="f.email.errors.required">Ingresar un correo válido</div>
+                </div>
+  
               </div>
               <div class="form-group">
                 <label for="password">Contraseña</label>
                 <input type="password" name="password"  class="form-control"
-                        [(ngModel)]="user.password" required minlength="6" />
+                formControlName="password" minlength="5"  [ngClass]="{ 'is-invalid': submitted && f.password.errors }"/>
+                <div *ngIf="submitted && f.password.errors" class="invalid-feedback">
+                  <div *ngIf="f.password.errors.required">Ingresar contraseña</div>
+                </div>
+  
               </div>
-              <button class="btn btn-primary">
+  
+              <button class="btn btn-primary" type="submit"> 
                 Iniciar sesión
               </button>
-              <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
             </form>
           </div>
         </div>

+ 71 - 0
src/app/components/login/login.component.ts

@@ -1,4 +1,74 @@
 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']
+})
+export class LoginComponent implements OnInit {
+
+  loginForm: FormGroup;
+  submitted:boolean = false;
+
+  constructor(private authService: AuthService, private formBuilder: FormBuilder, private router: Router) { }
+
+  ngOnInit() {
+    this.loginForm = this.formBuilder.group({
+      email: ['', Validators.required, Validators.email],
+      password: ['', Validators.required]
+    });
+  }
+
+  get f() { return this.loginForm.controls; }
+
+  login() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.loginForm.invalid) {
+      return;
+    }
+
+    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"
+      });
+    });
+  }
+
+}
+
+/* import { Component, OnInit } from '@angular/core';
 import { Location } from '@angular/common';
 
 import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
@@ -86,3 +156,4 @@ export class LoginComponent implements OnInit {
   }
 
 }
+*/

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

@@ -0,0 +1,80 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item"><a [routerLink]="['/organizations']">Organizaciones</a></li>
+              <li class="breadcrumb-item">Editar organización</li>
+            </ol>
+          </nav>
+        </div>
+
+      </div>
+
+    </div>
+    <br>
+    <div class="row justify-content-center">
+      
+      <div class="col-8">
+        <div class="align-container">
+
+          <div class="card" *ngIf="organizationExists">
+            <div class="card-header card-header-icon card-header-rose">
+              <div class="card-icon"><i class="material-icons">map</i></div>
+              <h4 class="card-title">Editar organización - <small class="category">Complete la información básica</small></h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="organizationForm" (ngSubmit)="editOrganization()">
+                  <div class="form-group">
+                    <label for="name">Nombre de la organización: </label>
+                    <input type="text" name="name" class="form-control" required formControlName="name" />
+                  </div>
+
+                  <div class="form-group">
+                    <label for="country">País: </label>
+                    <input type="text" name="country" class="form-control" required formControlName="country" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="city">Ciudad: </label>
+                    <input type="text" name="city" class="form-control" required formControlName="city" />
+                  </div>      
+                  
+                  <div class="form-group">
+                    <label for="contactName">Nombre contacto: </label>
+                    <input type="text" name="contactName" class="form-control" required formControlName="contactName" />
+                  </div>
+
+                  <div class="form-group">
+                    <label for="contactNumber">Teléfono contacto: </label>
+                    <input type="text" name="contactNumber" class="form-control" required formControlName="contactNumber" />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="address">Dirección: </label>
+                    <textarea name="address" class="form-control" rows="2" required formControlName="address"></textarea>
+                  </div>
+                  <br>
+                  <button class="btn btn-primary">
+                    Editar organización
+                  </button>
+                  <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+
+    </div>         
+    
+  </div>
+</div>

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


+ 25 - 0
src/app/components/organizations/edit-organization/edit-organization.component.spec.ts

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

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

@@ -0,0 +1,103 @@
+import { Component, OnInit } from '@angular/core';
+import { OrganizationsService } from '@app/services/organizations.service';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
+import Swal from 'sweetalert2';
+
+@Component({
+  selector: 'app-edit-organization',
+  templateUrl: './edit-organization.component.html',
+  styleUrls: ['./edit-organization.component.scss']
+})
+export class EditOrganizationComponent implements OnInit {
+  [x: string]: any;
+  title:string = "Editar organización"; 
+  listOrganization: any;
+  organizationExists:boolean;
+  organizationForm: FormGroup;
+  organizationId:any;
+  role_number: any;
+
+  constructor(private orgService: OrganizationsService, private formBuilder: FormBuilder, private route: ActivatedRoute) {
+
+    this.route.params.subscribe(params => {
+      this.organizationId = params['id'];
+    });
+
+    this.orgService.getOrganization(this.organizationId).subscribe(res => {
+      this.listOrganization = res["data"]["organization"];
+      this.organizationExists = true;
+
+      this.organizationForm = this.formBuilder.group({
+        name: [this.listOrganization.name],
+        address: [this.listOrganization.address],
+        city: [this.listOrganization.city],
+        country: [this.listOrganization.country],
+        contactName: [this.listOrganization.contactName],
+        contactNumber: [this.listOrganization.contactNumber],
+      });
+
+
+    }, (err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
+
+  }
+
+  ngOnInit() {
+    /*
+    this.organizationForm = this.formBuilder.group({
+      name: [this.listOrganization.name],
+      address: [this.listOrganization.address],
+      city: [this.listOrganization.city],
+      country: [this.listOrganization.country],
+      contactName: [this.listOrganization.contactName],
+      contactNumber: [this.listOrganization.contactNumber],
+    });
+
+    this.organizationForm = this.formBuilder.group({
+      name: [""], //this.listOrganization.name
+      address: [""], //this.listOrganization.address
+      city: [""], //this.listOrganization.city
+      country: [""], //this.listOrganization.country
+      contactName: [""], //this.listOrganization.contactName
+      contactNumber: [""], //this.listOrganization.contactNumber
+    });
+    */
+  }
+
+  get f() { return this.organizationForm.controls; }
+
+  editOrganization() {
+      this.orgService.updateOrganization(this.organizationId, 
+        {
+          name: this.f.name.value,
+          address: this.f.address.value,
+          city: this.f.city.value,
+          country: this.f.country.value,
+          contactName: this.f.contactName.value,
+          contactNumber: this.f.contactNumber.value,
+          assets: [""],
+        }
+      )
+    .subscribe(success => {
+      if (success) {
+        Swal.fire({
+          allowOutsideClick: false,
+          type: 'success',
+          text: 'Informacion actualizada con exito'
+        });
+        //window.location.href="#/dashboard";
+
+      }
+      
+    });
+  }
+
+
+
+}

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

@@ -0,0 +1,97 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+
+    <div class="row">
+      <div class="col-12">
+
+        <nav aria-label="breadcrumb">
+          <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+            <li class="breadcrumb-item"><a [routerLink]="['/organizations']">Organizaciones</a></li>
+            <li class="breadcrumb-item">Nueva organización</li>
+          </ol>
+        </nav>
+      </div>
+    </div>
+    <br>
+    <div class="row justify-content-center">
+      
+      <div class="col-8">
+        <div class="align-container">
+
+          <div class="card">
+            <div class="card-header card-header-icon card-header-rose">
+              <div class="card-icon"><i class="material-icons">map</i></div>
+              <h4 class="card-title">Nueva organización - <small class="category">Complete la información básica</small></h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="organizationForm" (ngSubmit)="createOrganization()">
+                  <div class="form-group">
+                    <label for="name">Nombre de la organización: </label>
+                    <input type="text" formControlName="name" class="form-control"  [ngClass]="{ 'is-invalid': submitted && f.name.errors }"/>
+                    <div *ngIf="submitted && f.name.errors" class="invalid-feedback">
+                      <div *ngIf="f.name.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+
+
+                  <div class="form-group">
+                    <label for="country">País: </label>
+                    <input type="text" formControlName="country" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.country.errors }"/>
+                    <div *ngIf="submitted && f.country.errors" class="invalid-feedback">
+                      <div *ngIf="f.country.errors.required">Campo requerido</div>
+                    </div>
+
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="city">Ciudad: </label>
+                    <input type="text" formControlName="city" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.city.errors }"/>
+                    <div *ngIf="submitted && f.city.errors" class="invalid-feedback">
+                      <div *ngIf="f.city.errors.required">Campo requerido</div>
+                    </div>
+
+                  </div>      
+                  
+                  <div class="form-group">
+                    <label for="contactName">Nombre contacto: </label>
+                    <input type="text" formControlName="contactName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.contactName.errors }"/>
+                    <div *ngIf="submitted && f.contactName.errors" class="invalid-feedback">
+                      <div *ngIf="f.contactName.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+
+                  <div class="form-group">
+                    <label for="contactNumber">Teléfono contacto: </label>
+                    <input type="text" formControlName="contactNumber" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.contactNumber.errors }"/>
+                    <div *ngIf="submitted && f.contactNumber.errors" class="invalid-feedback">
+                      <div *ngIf="f.contactNumber.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="address">Dirección: </label>
+                    <textarea formControlName="address" class="form-control" rows="2" [ngClass]="{ 'is-invalid': submitted && f.address.errors }" ></textarea>
+                    <div *ngIf="submitted && f.address.errors" class="invalid-feedback">
+                      <div *ngIf="f.address.errors.required">Campo requerido</div>
+                    </div>
+                  </div>
+                  <br>
+                  <button class="btn btn-primary">
+                    Crear organización
+                  </button>
+                  <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+
+    </div>          
+  </div>
+</div>

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

@@ -0,0 +1,5 @@
+.card-icon {
+  background-color: #F4A822;
+  color: #fff;
+}
+

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

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

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

@@ -0,0 +1,77 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { OrganizationsService } from '@app/services/organizations.service';
+import Swal from 'sweetalert2';
+import * as CryptoJS from 'crypto-js';
+
+@Component({
+  selector: 'app-new-organization',
+  templateUrl: './new-organization.component.html',
+  styleUrls: ['./new-organization.component.scss']
+})
+export class NewOrganizationComponent implements OnInit {
+  title:string = "Nueva organización"; 
+  organizationForm: FormGroup;
+  submitted: boolean = false;
+  role_number: any;
+
+
+  constructor(private orgService: OrganizationsService, private formBuilder: FormBuilder) {
+
+  }
+
+  ngOnInit() {
+    this.organizationForm = this.formBuilder.group({
+      name: ['', Validators.required],
+      address: ['', Validators.required],
+      city: ['', Validators.required],
+      country: ['', Validators.required],
+      contactName: ['', Validators.required],
+      contactNumber: ['', Validators.required],
+    });
+  }
+
+  get f() { return this.organizationForm.controls; }
+
+  createOrganization() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.organizationForm.invalid) {
+      return;
+    }
+
+    this.orgService.createOrganization(
+      {
+        name: this.f.name.value,
+        address: this.f.address.value,
+        city: this.f.city.value,
+        country: this.f.country.value,
+        contactName: this.f.contactName.value,
+        contactNumber: this.f.contactNumber.value,
+        assets: [""],
+      }
+    )
+    .subscribe(success => {
+      if (success) {
+        Swal.fire({
+          allowOutsideClick: false,
+          type: 'success',
+          showCancelButton: false,
+          title: 'Exito',
+          confirmButtonText: 'El registro ha sido guardado'
+        }).then((result) => {
+          if (result.value) {
+            window.location.href="#/organizations";
+          }
+        });
+      }
+      
+    });
+    
+
+
+  }
+
+
+}

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

@@ -0,0 +1,19 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+
+          <button class="btn btn-primary">
+            Nuevo registro
+          </button>
+        </div>
+      </div>
+
+
+    </div>          
+  </div>
+</div>

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


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

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

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

@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-organization',
+  templateUrl: './organization.component.html',
+  styleUrls: ['./organization.component.scss']
+})
+export class OrganizationComponent implements OnInit {
+  title:string = "Detalle de organización"; 
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 84 - 0
src/app/components/organizations/organizations.component.html

@@ -0,0 +1,84 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item">Organizaciones</li>
+            </ol>
+          </nav>
+
+          <a class="btn btn-primary" [routerLink]="['/organizations/new']" *ngIf="allowedUser()">
+            Nuevo registro
+          </a>
+        </div>
+      </div>
+
+      <div class="col-12">
+          <div class="align-container">
+
+            <h4><b>Listado de organizaciones</b></h4>
+            <div class="example-container mat-elevation-z8">
+              <div class="example-table-container">
+          
+                <table mat-table [dataSource]="dataSource" class="example-table">
+          
+                  <!-- Name Column -->
+                  <ng-container matColumnDef="name">
+                    <th mat-header-cell *matHeaderCellDef>Organizacion</th>
+                    <td mat-cell *matCellDef="let row">{{row.name}}</td>
+                  </ng-container>
+          
+                  <!-- State Column -->
+                  <ng-container matColumnDef="contactName">
+                    <th mat-header-cell *matHeaderCellDef>Nombre de contacto</th>
+                    <td mat-cell *matCellDef="let row">{{row.contactName}}</td>
+                  </ng-container>
+
+                  <!-- City Column -->
+                  <ng-container matColumnDef="contactNumber">
+                    <th mat-header-cell *matHeaderCellDef>Número de contacto</th>
+                    <td mat-cell *matCellDef="let row">{{row.contactNumber}}</td>
+                  </ng-container>
+
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="country">
+                    <th mat-header-cell *matHeaderCellDef>País</th>
+                    <td mat-cell *matCellDef="let row">{{row.country}}</td>
+                  </ng-container>
+          
+                  <!--  Column -->
+                  <ng-container matColumnDef="id">
+                    <th mat-header-cell *matHeaderCellDef>&nbsp;</th>
+                    <td mat-cell *matCellDef="let row">
+                      <div class="action-buttons">
+                        <a class="btn btn-primary btn-sm" [routerLink]="['/organization', row.id, 'edit']" *ngIf="allowedUser()" >
+                          Editar
+                        </a>
+                        <a class="btn btn-danger btn-sm" (click)="delete_organization(row.id)" *ngIf="allowedUser()" >
+                          Eliminar
+                        </a>
+                      </div>
+                    </td>
+                  </ng-container>
+          
+                  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+                  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+                </table>
+                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+              </div>
+            </div>
+          </div>
+      
+      </div>
+      <br>
+      
+    </div>          
+  </div>
+</div>

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

@@ -0,0 +1 @@
+

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

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

+ 148 - 0
src/app/components/organizations/organizations.component.ts

@@ -0,0 +1,148 @@
+import { Component, ViewChild, OnInit } from '@angular/core';
+import { Organization } from "@app/models/organization";
+import { OrganizationsService } from "@app/services/organizations.service";
+import { MatPaginator } from '@angular/material/paginator';
+import { MatSort } from '@angular/material/sort';
+import { MatTableDataSource } from '@angular/material/table';
+import Swal from 'sweetalert2';
+import { Router } from '@angular/router';
+import { AuthService } from '@app/services/auth2.service';
+
+@Component({
+  selector: 'app-organizations',
+  templateUrl: './organizations.component.html',
+  styleUrls: ['./organizations.component.scss']
+})
+export class OrganizationsComponent implements OnInit {
+
+  title:string = "Organizaciones";  
+ 
+  displayedColumns: string[] = ['name','contactName', 'contactNumber', 'country', 'id'];
+  //displayedColumns: string[] = ['state'];
+
+  listData: Organization[] = [];
+  listOrganization:any;
+  dataSource = new MatTableDataSource(this.listData);
+  resultsLength = 0;
+  isLoadingResults = true;
+  isRateLimitReached = false;
+
+  @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
+  @ViewChild(MatSort, {static: true}) sort: MatSort;
+  role_number: any;
+
+
+  constructor(private orgService: OrganizationsService, private router: Router, private authService: AuthService ) {
+    Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+  }
+
+  ngOnInit() {
+
+    this.orgService.getListOrganizations().subscribe(res =>
+      this.listData = res
+    );
+    
+    this.orgService.getAllOrganizations().subscribe(ans => {
+      this.listOrganization = ans["data"]["organizations"];
+      this.dataSource.data = this.listOrganization;
+      this.dataSource.paginator = this.paginator;
+      this.dataSource.sort = this.sort;
+    }, (err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informacion"
+      });
+    });
+    
+    //this.chartjs = true;
+    setTimeout(()=>{
+      Swal.close();
+    }, 1200);
+
+  }
+
+  delete_organization(id: string){
+    Swal.fire({
+      allowOutsideClick: false,
+      type: 'warning',
+      title: '¿Esta seguro?',
+      text: 'No podrá revertir esta acción',
+      showCancelButton: true,
+      confirmButtonColor: '#3085d6',
+      cancelButtonColor: '#d33',
+      confirmButtonText: 'Borrar registro',
+    })
+    .then((result) => {
+        if (result.value) {
+          this.delete_organization2(id);
+        }
+    });
+  }
+
+  delete_organization2(id: string){
+    this.orgService.deleteOrganization(id)
+    .subscribe(
+      data => {
+        if (data["success"] == true) {
+          //document.getElementById(id).remove();
+          Swal.fire({
+            allowOutsideClick: false,
+            type: 'success',
+            text: 'Registro eliminado con exito'
+          }).then((result) => {
+            if (result.value) {
+              this.reloadComponent();
+            }
+          });
+          //window.location.href="#/organizations";
+        }
+        else {
+          Swal.fire({
+            allowOutsideClick: false,
+            type: 'error',
+            text: "No tienes permiso para eliminar el registro"
+          });
+        }  
+      },
+      err => {
+        Swal.fire({
+          type: 'error',
+          title: 'Error en el servidor',
+          text: err.message
+        });
+      });
+  }
+
+
+  reloadComponent(){
+    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
+    this.router.onSameUrlNavigation = 'reload';
+    this.router.navigate(['/organizations']);
+  }
+
+  applyFilter(filterValue: string) {
+    this.dataSource.filter = filterValue.trim().toLowerCase();
+    if (this.dataSource.paginator) {
+      this.dataSource.paginator.firstPage();
+    }
+  }
+
+  allowedUser(){
+    let is_allowed: boolean;
+    if(+this.authService.getUserLevel() < 3){
+      is_allowed = false;
+    }
+    else {
+      is_allowed = true;
+    }
+    console.log(is_allowed);
+    return is_allowed
+  }
+
+}

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

@@ -0,0 +1,22 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item"><a [routerLink]="['/plants']">Plantas</a></li>
+              <li class="breadcrumb-item">Editar planta</li>
+            </ol>
+          </nav>
+        </div>
+
+      </div>
+
+    </div>          
+  </div>
+</div>

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


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

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

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

@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+  selector: 'app-edit-plant',
+  templateUrl: './edit-plant.component.html',
+  styleUrls: ['./edit-plant.component.scss']
+})
+export class EditPlantComponent implements OnInit {
+  title:string = "Editar planta"; 
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

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

@@ -0,0 +1,67 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+
+    <div class="row">
+      <div class="col-12">
+
+        <nav aria-label="breadcrumb">
+          <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+            <li class="breadcrumb-item"><a [routerLink]="['/plants']">Plantas</a></li>
+            <li class="breadcrumb-item">Nueva planta</li>
+          </ol>
+        </nav>
+      </div>
+    </div>
+    <br>
+    <div class="row justify-content-center">
+      
+      <div class="col-8">
+        <div class="align-container">
+
+          <div class="card">
+            <div class="card-header card-header-icon card-header-rose">
+              <div class="card-icon"><i class="material-icons">map</i></div>
+              <h4 class="card-title">Nueva planta - <small class="category">Complete la información básica</small></h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" >
+                  <div class="form-group">
+                    <label for="name">Nombre de la planta: </label>
+                    <input type="text" name="name" class="form-control" required />
+                  </div>
+
+                  <div class="form-group">
+                    <label for="country">País: </label>
+                    <input type="text" name="country" class="form-control" required />
+                  </div>
+                  
+                  <div class="form-group">
+                    <label for="city">Ciudad: </label>
+                    <input type="text" name="city" class="form-control" required />
+                  </div>      
+                  
+                  <div class="form-group">
+                    <label for="address">Dirección: </label>
+                    <input type="text" name="address" class="form-control" required />
+                  </div>
+
+                  <br>
+                  <button class="btn btn-primary">
+                    Crear organización
+                  </button>
+                  <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+
+    </div>          
+  </div>
+</div>

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


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

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

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

@@ -0,0 +1,17 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+  selector: 'app-new-plant',
+  templateUrl: './new-plant.component.html',
+  styleUrls: ['./new-plant.component.scss']
+})
+export class NewPlantComponent implements OnInit {
+  title:string = "Nueva planta"; 
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

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

@@ -0,0 +1,80 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item">Plantas</li>
+            </ol>
+          </nav>
+
+          <a class="btn btn-primary" [routerLink]="['/plants/new']" *ngIf="allowedUser()">
+            Nuevo registro
+          </a>
+        </div>
+      </div>
+
+      <div class="col-12">
+          <div class="align-container">
+
+            <h4><b>Listado de plantas</b></h4>
+            <div class="example-container mat-elevation-z8">
+              <div class="example-table-container">
+          
+                <table mat-table [dataSource]="dataSource" class="example-table">
+          
+                  <!-- Name Column -->
+                  <ng-container matColumnDef="name">
+                    <th mat-header-cell *matHeaderCellDef>Planta</th>
+                    <td mat-cell *matCellDef="let row">{{row.name}}</td>
+                  </ng-container>
+
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="country">
+                    <th mat-header-cell *matHeaderCellDef>País</th>
+                    <td mat-cell *matCellDef="let row">{{row.country}}</td>
+                  </ng-container>
+
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="city">
+                    <th mat-header-cell *matHeaderCellDef>Ciudad</th>
+                    <td mat-cell *matCellDef="let row">{{row.city}}</td>
+                  </ng-container>
+                  
+                  <!--  Column -->
+                  <ng-container matColumnDef="id">
+                    <th mat-header-cell *matHeaderCellDef>&nbsp;</th>
+                    <td mat-cell *matCellDef="let row">
+
+                      <div class="action-buttons">
+                        <a class="btn btn-primary btn-sm" [routerLink]="['/plant', row.id, 'edit']" *ngIf="allowedUser()" >
+                          Editar
+                        </a>
+                        <!--
+                        <a class="btn btn-danger btn-sm" (click)="delete_organization(row.id)" *ngIf="allowedUser()" >
+                          Eliminar
+                        </a>-->
+                      </div>
+                    </td>
+                  </ng-container>
+          
+                  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+                  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+                </table>
+                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+              </div>
+            </div>
+          </div>
+      
+      </div>
+      <br>
+      
+    </div>          
+  </div>
+</div>

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


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

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

+ 78 - 0
src/app/components/plants/plants.component.ts

@@ -0,0 +1,78 @@
+import { Component, ViewChild, OnInit } from '@angular/core';
+
+import { Plant } from 'src/app/models/plant';
+import { PlantsService } from 'src/app/services/plants.service';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+
+import {MatTableDataSource} from '@angular/material/table';
+import Swal from 'sweetalert2';
+import { UserService } from '@app/services/user.service';
+import { AuthService } from '@app/services/auth2.service';
+
+@Component({
+  selector: 'app-plants',
+  templateUrl: './plants.component.html',
+  styleUrls: ['./plants.component.scss']
+})
+export class PlantsComponent implements OnInit {
+
+  title:string = "Plantas";
+ 
+  displayedColumns: string[] = ['name', 'country', 'city', 'id'];
+  //displayedColumns: string[] = ['state'];
+
+  listData: Plant[] = [];
+  listPlant:any;
+  dataSource = new MatTableDataSource(this.listPlant);
+
+  resultsLength = 0;
+  isLoadingResults = true;
+  isRateLimitReached = false;
+
+  @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
+  @ViewChild(MatSort, {static: true}) sort: MatSort;
+
+
+  constructor(private orgService: PlantsService, private authService: AuthService) {
+    Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+  }
+
+  ngOnInit() {
+    this.orgService.getAllAssets().subscribe(ans => {
+      this.listPlant = ans["data"]["assets"];
+      this.dataSource.data = this.listPlant;
+      this.dataSource.paginator = this.paginator;
+      this.dataSource.sort = this.sort;
+    });
+
+    setTimeout(()=>{
+      Swal.close();
+    }, 1000);
+  }
+
+  applyFilter(filterValue: string) {
+    this.dataSource.filter = filterValue.trim().toLowerCase();
+    if (this.dataSource.paginator) {
+      this.dataSource.paginator.firstPage();
+    }
+  }
+
+  allowedUser(){
+    let is_allowed: boolean;
+    if(+this.authService.getUserLevel() < 3){
+      is_allowed = false;
+    }
+    else {
+      is_allowed = true;
+    }
+    console.log(is_allowed);
+    return is_allowed
+  }
+
+}

+ 1 - 1
src/app/components/plugins/maps/maps.component.ts

@@ -42,7 +42,7 @@ export class MapsComponent implements OnInit {
   };
 
   ngOnInit() {
-    this.plantsService.getAssets().subscribe(res => {
+    this.plantsService.getAllAssets().subscribe(res => {
       this.listData = res;
     });
     this.addMarkers();

+ 0 - 32
src/app/components/plugins/organization-cards/organization-cards.component.html

@@ -1,32 +0,0 @@
-<div class="row">
-  <div *ngIf="error; then thenBlock else elseBlock"></div>
-  <ng-template #thenBlock>
-    <div class="col-lg-12">
-      <div class= 'card border-danger'>
-        <div class="card-body">
-          Error en el servidor, no se pudo obtener el listado de organizaciones
-        </div>
-      </div>
-    </div>
-  </ng-template>
-  <ng-template #elseBlock>
-    <div *ngFor="let item of listOrganizations; index as i;" class="col-lg-4 col-md-4 col-sm-6">
-      <div class= 'card border-success'>
-        <div class="card-body">
-          <h5 class="card-title">{{item.name}}</h5>
-          <h6 class="card-subtitle mb-2 text-muted">{{item.city}}, {{item.country}}</h6>
-          <br>
-          <p class="card-text">
-            Nombre contacto: <b>{{item.contactName}}</b>
-            <br>
-            Numero contacto: <b>{{item.contactNumber}}</b>
-            <br>
-            Direccion: <b>{{item.address}}</b>
-          </p>
-          <a [routerLink]="['/assets']" [queryParams]="{ id: item.id }" href="#" class="btn btn-sm btn-primary float-right">Ir a plantas</a>
-        </div>
-      </div>
-    </div>
-  </ng-template>
-</div>
-<br>

+ 0 - 4
src/app/components/plugins/organization-cards/organization-cards.component.scss

@@ -1,4 +0,0 @@
-.card-title {
-  color: #ee964b;
-  font-weight: bold;
-}

+ 0 - 35
src/app/components/plugins/organization-cards/organization-cards.component.ts

@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { Organization } from 'src/app/models/organization';
-import { OrganizationsService } from 'src/app/services/organizations.service';
-import { RouterModule } from '@angular/router';
-
-@Component({
-  selector: 'app-organization-cards',
-  templateUrl: './organization-cards.component.html',
-  styleUrls: ['./organization-cards.component.scss']
-})
-export class OrganizationCardsComponent implements OnInit {
-  
-  listOrganizations:any;
-  error:boolean = false;
-  errorMessage:string;
-
-  constructor(private orgService: OrganizationsService) { }
-
-  ngOnInit() {
-
-    //});
-    this.orgService.getOrganizations().subscribe((res)=>{
-      this.listOrganizations = res["organizations"];
-    },
-    (err) => {
-      this.error = true;
-      this.errorMessage = err.message;
-    });
-      
-
-  }
-
-
-  
-}

+ 1 - 2
src/app/components/plugins/plugins.module.ts

@@ -77,8 +77,7 @@ export const MQTT_SERVICE_OPTIONS: IMqttServiceOptions = {
     WeatherCardComponent,
     BarChartComponent,
     PolarChartComponent,
-    OrganizationCardsComponent
-    
+    OrganizationCardsComponent,
   ]
 })
 export class PluginsModule {}

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

@@ -0,0 +1,21 @@
+import { FormGroup } from '@angular/forms';
+
+// custom validator to check that two fields match
+export function ValidatorComponent(controlName: string, matchingControlName: string) {
+    return (formGroup: FormGroup) => {
+        const control = formGroup.controls[controlName];
+        const matchingControl = formGroup.controls[matchingControlName];
+
+        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
+            // return if another validator has already found an error on the matchingControl
+            return;
+        }
+
+        // set error on matchingControl if validation fails
+        if (control.value !== matchingControl.value) {
+            matchingControl.setErrors({ mustMatch: true });
+        } else {
+            matchingControl.setErrors(null);
+        }
+    }
+}

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

@@ -1,6 +1,6 @@
 <div class="widget">
   <div class="row">
-    <div class="col-lg-7 col-md-6 col-xs-12">
+    <div class="col-lg-6 col-md-6 col-xs-12">
       <div class="date">
         {{currentDate}}
       </div>
@@ -11,7 +11,7 @@
         {{state}}
       </div>
     </div>
-    <div class="col-lg-5 col-md-6 col-xs-12">
+    <div class="col-lg-6 col-md-6 col-xs-12">
       <div class="temp">
          <img src="assets/img/{{icon}}.png" alt="" width="60" *ngIf="icon_exists">
          <span>{{temp}}°</span>

+ 4 - 3
src/app/components/shared/navbar/navbar.component.html

@@ -17,13 +17,14 @@
     <div class="collapse navbar-collapse justify-content-end" id="navigation">
       <ul class="navbar-nav">
         <li class="nav-item">
-          <a class="nav-link" href="javascript:void(0)">
+          <a class="nav-link" href="javascript:void(0)" title="Dashboard">
             <i class="material-icons">dashboard</i>
             <p>
               <span class="d-lg-none d-md-block">Stats</span>
             </p>
           </a>
         </li>
+        <!--
         <li class="nav-item">
           <a class="nav-link" title="Account" href="#/profile">
             <i class="material-icons">person</i>
@@ -31,9 +32,9 @@
               <span class="d-lg-none d-md-block">Account</span>
             </p>
           </a>
-        </li>
+        </li>-->
         <li class="nav-item">
-          <a class="nav-link" title="Logout" (click)="logout()" href="javascript:void(0)">
+          <a class="nav-link" title="Cerrar sesión" (click)="logout()" href="javascript:void(0)">
             <i class="material-icons">exit_to_app</i>
             <p>
               <span class="d-lg-none d-md-block">Logout</span>

+ 9 - 3
src/app/components/shared/navbar/navbar.component.ts

@@ -1,10 +1,12 @@
+import { Observable } from 'rxjs';
+
 import { Component, OnInit, ElementRef } from '@angular/core';
 import { ROUTES } from '../sidebar/sidebar.component';
 import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
 import { Router, ActivatedRoute } from '@angular/router';
 //import { AuthenticationService } from 'src/app/services/authentication.service';
 import { AdminLayoutRoutes } from 'src/app/layouts/admin/admin.routing';
-import { AuthService } from '../../../services/auth.service';
+import { AuthService } from '../../../services/auth2.service';
 
 @Component({
   selector: 'app-navbar',
@@ -144,8 +146,12 @@ export class NavbarComponent implements OnInit {
     };
 
     logout() {
-      this.auth.logout();
-      window.location.href="";
+      this.auth.logout().subscribe(success => {
+        if (success) {
+          window.location.href="";
+        }
+      });
+      //window.location.href="";
 
       //this.router.navigateByUrl("login");
     }

+ 14 - 38
src/app/components/shared/sidebar/sidebar.component.html

@@ -21,17 +21,6 @@
         </div>
       </span>
     </form> -->
-    <ul class="nav navbar-nav nav-mobile-menu">
-      <li class="nav-item">
-          <a class="nav-link" href="javascript:void(0)">
-              <i class="material-icons">person</i>
-              <p>
-                  <span class="d-lg-none d-md-block">Account</span>
-              </p>
-          </a>
-      </li>
-      <hr>
-    </ul>
   </div>
     <ul class="nav">
       <li routerLinkActive="active" *ngFor="let menuItem of menuItems" class="{{menuItem.class}} nav-item">
@@ -40,35 +29,22 @@
           <p>{{menuItem.title}}</p>
         </a>
       </li>
-      <!--
-      <li class="nav-item" routerlinkactive="active">
-        
-        <a class="nav-link collapsed" data-toggle="collapse" href="#components" aria-expanded="false">
-          <i class="material-icons">business</i>
-          <p>Plantas<b class="caret"></b></p>
+      
+      <hr>
+    </ul>
+    <ul *ngIf="adminMenu" class="nav">
+      <li class="nav-item">
+        <a class="nav-link" href="javascript:void(0)">
+          <i class="material-icons">settings_application</i>
+          <p>Administración</p>
+        </a>
+      </li>
+      <li routerLinkActive="active" *ngFor="let menuItem of adminMenuItems" class="{{menuItem.class}} nav-item">
+        <a class="nav-link" [routerLink]="[menuItem.path]" *ngIf="menuItem.allowed_roles.indexOf(+role_number)>-1"> 
+          <i class="material-icons">{{menuItem.icon}}</i>
+          <p>{{menuItem.title}}</p>
         </a>
-        <div class="collapse" id="components" style="">
-          <ul class="nav">
-            <li class="nav-item" routerlinkactive="active">
-              <a class="nav-link" href="#/components/buttons"><span class="sidebar-mini">B</span><span class="sidebar-normal">COCESNA ESTACION<br> EL SALVADOR</span></a>
-            </li>
-            <li class="nav-item" routerlinkactive="active">
-              <a class="nav-link" href="#/components/grid"><span class="sidebar-mini">GS</span><span class="sidebar-normal">COCESNA ICCAE</span></a>
-            </li>
-            <li class="nav-item" routerlinkactive="active">
-              <a class="nav-link" href="#/components/panels"><span class="sidebar-mini">P</span><span class="sidebar-normal">CONDUSAL</span></a>
-            </li>
-            <li class="nav-item" routerlinkactive="active">
-              <a class="nav-link" href="#/components/sweet-alert"><span class="sidebar-mini">SA</span><span class="sidebar-normal">NZEB</span></a>
-            </li>
-            <li class="nav-item" routerlinkactive="active">
-              <a class="nav-link" href="#/components/notifications"><span class="sidebar-mini">N</span><span class="sidebar-normal">SIGET GERENCIA<br> ELECTRICIDAD</span></a>
-            </li>
-          </ul>
-        </div>
       </li>
-      -->
-      <hr>
     </ul>
   <div *ngIf="isMobileMenu()">
     <ul class="nav navbar-nav nav-mobile-menu-bottom">

+ 0 - 1
src/app/components/shared/sidebar/sidebar.component.scss

@@ -1,5 +1,4 @@
 ul.nav.navbar-nav.nav-mobile-menu-bottom {
   bottom: 0;
-  position: fixed;
   width: 100%;
 }

+ 24 - 4
src/app/components/shared/sidebar/sidebar.component.ts

@@ -1,6 +1,7 @@
 import { Component, OnInit } from '@angular/core';
-import { AuthService } from '../../../services/auth.service';
+import { AuthService } from '../../../services/auth2.service';
 import { Router } from '@angular/router';
+import * as CryptoJS from 'crypto-js';
 
 declare const $: any;
 declare interface RouteInfo {
@@ -8,12 +9,11 @@ declare interface RouteInfo {
     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: '/reports', title: 'Reportes',  icon: 'assignment', class: '' },
-    { path: '/profile', title: 'Perfil',  icon: 'person', 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: '' },
@@ -23,6 +23,13 @@ export const ROUTES: RouteInfo[] = [
     { 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] },
+];
+
 @Component({
   selector: 'app-sidebar',
   templateUrl: './sidebar.component.html',
@@ -30,11 +37,24 @@ export const ROUTES: RouteInfo[] = [
 })
 export class SidebarComponent implements OnInit {
   menuItems: any[];
+  adminMenuItems: any[];
+  adminMenu:boolean = false;
+  role_number:any;
 
-  constructor(private auth: AuthService, private router: Router) { }
+  constructor(private auth: AuthService, private router: Router) {
+    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);
+
+    // 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() {

+ 113 - 0
src/app/components/users/new-user/new-user.component.html

@@ -0,0 +1,113 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+
+    <div class="row">
+      <div class="col-12">
+
+        <nav aria-label="breadcrumb">
+          <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+            <li class="breadcrumb-item"><a [routerLink]="['/users']">Usuarios</a></li>
+            <li class="breadcrumb-item">Nuevo Usuario</li>
+          </ol>
+        </nav>
+      </div>
+    </div>
+    <br>
+    <div class="row justify-content-center">
+      
+      <div class="col-8">
+        <div class="align-container">
+
+          <div class="card">
+            <div class="card-header card-header-icon card-header-rose">
+              <div class="card-icon"><i class="material-icons">map</i></div>
+              <h4 class="card-title">Nuevo Usuario - <small class="category">Complete la información básica</small></h4>
+            </div>
+            <div class="card-body">              
+              <div class="align-container">
+                <form class="form-auth-small ng-untouched ng-pristine ng-valid" [formGroup]="userForm" (ngSubmit)="createUser()">
+                  <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="role">Rol: </label>
+                    <br>
+                    
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" formControlName="role" id="roleRadios1" value="0" 
+                        [ngClass]="{ 'is-invalid': submitted && f.role.errors }">
+                        <label class="form-check-label" for="roleRadios1">
+                          Invitado
+                        </label>
+                      </div>
+                      <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" formControlName="role" id="roleRadios2" value="1" 
+                        [ngClass]="{ 'is-invalid': submitted && f.role.errors }">
+                        <label class="form-check-label" for="roleRadios2">
+                          Usuario
+                        </label>
+                      </div>
+                      <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" formControlName="role" id="roleRadios3" value="2" 
+                        [ngClass]="{ 'is-invalid': submitted && f.role.errors }">
+                        <label class="form-check-label" for="roleRadios3">
+                          Administrador
+                        </label>
+                      </div>
+                      <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" formControlName="role" id="roleRadios3" value="3" 
+                        [ngClass]="{ 'is-invalid': submitted && f.role.errors }">
+                        <label class="form-check-label" for="roleRadios3">
+                          Super Admin
+                        </label>
+                      </div>
+                      
+                    <!--<select class="form-control" formControlName="role" [ngClass]="{ 'is-invalid': submitted && f.role.errors }" >
+                      <option>Seleccione una opción</option>
+                      <option value="0">Invitado</option>
+                      <option value="1">Usuario</option>
+                      <option value="2">Administrador</option>
+                      <option value="3">Super Admin</option>
+                    </select> -->
+                    
+                  </div>
+                  <br>
+                  <button class="btn btn-primary">
+                    Crear usuario
+                  </button>
+                  <!--<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>-->
+                </form>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+
+    </div>          
+  </div>
+</div>

+ 0 - 0
src/app/components/users/new-user/new-user.component.scss


+ 25 - 0
src/app/components/users/new-user/new-user.component.spec.ts

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

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

@@ -0,0 +1,68 @@
+import { Component, OnInit } from '@angular/core';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import { UserService } from '@app/services/user.service';
+import Swal from 'sweetalert2';
+
+@Component({
+  selector: 'app-new-user',
+  templateUrl: './new-user.component.html',
+  styleUrls: ['./new-user.component.scss']
+})
+export class NewUserComponent implements OnInit {
+  title:string = "Nuevo usuario"; 
+  userForm: FormGroup;
+  submitted: boolean= false;
+
+
+  constructor(private userService: UserService, private formBuilder: FormBuilder) {
+  }
+
+  ngOnInit() {
+
+
+    this.userForm = this.formBuilder.group({
+      first_name: ['', Validators.required],
+      last_name: ['', Validators.required],
+      email: ['', Validators.required],
+      role: ['', Validators.required],
+    });
+  }
+
+  get f() { return this.userForm.controls; }
+
+  createUser() {
+    this.submitted = true;
+
+    // stop here if form is invalid
+    if (this.userForm.invalid) {
+      return;
+    }
+    
+      this.userService.createUser(
+        {
+          first_name: this.f.first_name.value,
+          last_name: this.f.last_name.value,
+          email: this.f.email.value,
+          role: +this.f.role.value,
+        }
+      )
+      .subscribe(success => {
+        if (success) {
+          Swal.fire({
+            allowOutsideClick: false,
+            type: 'success',
+            showCancelButton: false,
+            title: 'Exito',
+            confirmButtonText: 'El usuario ha sido creado'
+          }).then((result) => {
+            if (result.value) {
+              window.location.href="#/users";
+            }
+          });
+        }
+        
+      });
+  }
+
+
+}

+ 80 - 0
src/app/components/users/users.component.html

@@ -0,0 +1,80 @@
+<h2 class="floating-title">{{title}}</h2>
+
+<div class="main-content">
+  <div class="container-fluid">
+    <div class="row">
+      
+      <div class="col-12 align-right">
+        <div class="align-container">
+          
+          <nav aria-label="breadcrumb">
+            <ol class="breadcrumb">
+              <li class="breadcrumb-item"><a [routerLink]="['/']">Dashboard</a></li>
+              <li class="breadcrumb-item">Usuarios</li>
+            </ol>
+          </nav>
+
+          <a class="btn btn-primary" [routerLink]="['/users/new']">
+            Nuevo registro
+          </a>
+        </div>
+      </div>
+
+      <div class="col-12">
+          <div class="align-container">
+
+            <h4><b>Listado de usuarios</b></h4>
+            <div class="example-container mat-elevation-z8">
+              <div class="example-table-container">
+          
+                <table mat-table [dataSource]="dataSource" class="example-table">
+          
+                  <!-- Name Column -->
+                  <ng-container matColumnDef="email">
+                    <th mat-header-cell *matHeaderCellDef>Correo</th>
+                    <td mat-cell *matCellDef="let row">{{row.email}}</td>
+                  </ng-container>
+
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="first_name">
+                    <th mat-header-cell *matHeaderCellDef>Nombre</th>
+                    <td mat-cell *matCellDef="let row">{{row.first_name}}</td>
+                  </ng-container>
+
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="last_name">
+                    <th mat-header-cell *matHeaderCellDef>Apellido</th>
+                    <td mat-cell *matCellDef="let row">{{row.last_name}}</td>
+                  </ng-container>
+                  
+                  <!-- Country Column -->
+                  <ng-container matColumnDef="role">
+                    <th mat-header-cell *matHeaderCellDef>Rol</th>
+                    <td mat-cell *matCellDef="let row">{{row.role}}</td>
+                  </ng-container>
+
+                  <!--  Column 
+                  <ng-container matColumnDef="id">
+                    <th mat-header-cell *matHeaderCellDef>&nbsp;</th>
+                    <td mat-cell *matCellDef="let row">
+                      <a class="btn btn-primary btn-sm" [routerLink]="['/plant', row.id, 'edit']"  >
+                        Editar
+                      </a>
+                    </td>
+                  </ng-container>
+          -->
+          
+                  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+                  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+                </table>
+                <mat-paginator [pageSizeOptions]="[5, 10, 25]"></mat-paginator>
+              </div>
+            </div>
+          </div>
+      
+      </div>
+      <br>
+      
+    </div>          
+  </div>
+</div>

+ 0 - 0
src/app/components/users/users.component.scss


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

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

+ 74 - 0
src/app/components/users/users.component.ts

@@ -0,0 +1,74 @@
+import { Component, ViewChild, OnInit } from '@angular/core';
+
+import {HttpClient} from '@angular/common/http';
+import { User } from 'src/app/models/user';
+import { UserService } from 'src/app/services/user.service';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {MatTableDataSource} from '@angular/material/table';
+import Swal from 'sweetalert2';
+
+
+@Component({
+  selector: 'app-users',
+  templateUrl: './users.component.html',
+  styleUrls: ['./users.component.scss']
+})
+export class UsersComponent implements OnInit {
+
+  title:string = "Usuarios";
+ 
+  displayedColumns: string[] = ['email', 'first_name', 'last_name', 'role'];
+  //displayedColumns: string[] = ['state'];
+
+  listData: User[] = [];
+  listUsers:any;
+  dataSource = new MatTableDataSource(this.listUsers);
+
+  resultsLength = 0;
+  isLoadingResults = true;
+  isRateLimitReached = false;
+
+  @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
+  @ViewChild(MatSort, {static: true}) sort: MatSort;
+  role_number: any;
+
+
+  constructor(private userService: UserService) {
+
+    Swal.fire({
+      allowOutsideClick: false,
+      type: 'info',
+      text: 'Espere por favor...'
+    });
+    Swal.showLoading();
+  }
+
+  ngOnInit() {
+    this.userService.getAllUsers().subscribe(ans => {
+      this.listUsers = ans["data"]["users"];
+      this.dataSource.data = this.listUsers;
+      this.dataSource.paginator = this.paginator;
+      this.dataSource.sort = this.sort;
+    }, (err) => {
+      Swal.fire({
+        type: 'error',
+        title: 'Error en el servidor',
+        text: "No su pudo obtener la informaci&oacute;n"
+      });
+    });
+
+    setTimeout(()=>{
+      Swal.close();
+    }, 1200);
+
+  }
+
+  applyFilter(filterValue: string) {
+    this.dataSource.filter = filterValue.trim().toLowerCase();
+    if (this.dataSource.paginator) {
+      this.dataSource.paginator.firstPage();
+    }
+  }
+
+}

+ 94 - 0
src/app/data/organizations.data.ts

@@ -0,0 +1,94 @@
+import { Organization } from 'src/app/models/organization';
+    
+export const organizationsData: Organization[] = [
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Exozent',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'Zacatecoluca',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Norsup',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Miguel',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Digique',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Salvador',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Zeam',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'La Union',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Bitrex',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'Zacatecoluca',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Polaria',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Miguel',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Velity',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'La Union',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Farmex',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Vicente',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Wazzu',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Vicente',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  },
+  {
+    id: "5d70319fb288f25b679c683c",
+    name: 'Olympix',
+    contactName: "Rodrigo O'Byrne",
+    contactNumber: "+503 7555-1234",
+    country: 'El Salvador',
+    city: 'San Miguel',
+    address: 'KM 15 CARRETERA AL AEROPUERTO SANTO TOMAS, 11015'
+  }
+];

+ 28 - 1
src/app/layouts/admin/admin.module.ts

@@ -4,7 +4,10 @@ import { RouterModule } from '@angular/router';
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { LeafletModule } from "@asymmetrik/ngx-leaflet";
+
 import { ChartsModule } from 'ng2-charts';
+import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
+import { AngularMyDatePickerModule } from 'angular-mydatepicker';
 
 
 
@@ -12,6 +15,16 @@ import { AdminLayoutRoutes } from './admin.routing';
 import { DashboardComponent } from '../../components/dashboard/dashboard.component';
 import { ProfileComponent } from '../../components/profile/profile.component';
 import { AssetsComponent } from '../../components/assets/assets.component';
+import { OrganizationsComponent } from '../../components/organizations/organizations.component';
+import { OrganizationComponent } from '../../components/organizations/organization/organization.component';
+import { NewOrganizationComponent } from '../../components/organizations/new-organization/new-organization.component';
+import { EditOrganizationComponent } from '../../components/organizations/edit-organization/edit-organization.component';
+import { PlantsComponent } from '@app/components/plants/plants.component';
+import { EditPlantComponent } from '@app/components/plants/edit-plant/edit-plant.component';
+import { NewPlantComponent } from '@app/components/plants/new-plant/new-plant.component';
+import { UsersComponent } from '@app/components/users/users.component';
+import { NewUserComponent } from '@app/components/users/new-user/new-user.component';
+
 
 import { PluginsModule } from '../../components/plugins/plugins.module';
 
@@ -30,6 +43,9 @@ import {
 
 } from '@angular/material';
 
+import { BreadcrumbModule, IconsModule } from 'angular-bootstrap-md'
+
+
 @NgModule({
   imports: [
     CommonModule,
@@ -49,12 +65,23 @@ import {
     MatSortModule,
     PluginsModule,
     LeafletModule,
-    ChartsModule
+    ChartsModule,
+    NgbModule,
+    AngularMyDatePickerModule
   ],
   declarations: [
     DashboardComponent,
     ProfileComponent,
     AssetsComponent,
+    OrganizationsComponent,
+    OrganizationComponent,
+    NewOrganizationComponent,
+    EditOrganizationComponent,
+    PlantsComponent,
+    EditPlantComponent,
+    NewPlantComponent,
+    UsersComponent,
+    NewUserComponent
   ]
 })
 

+ 101 - 14
src/app/layouts/admin/admin.routing.ts

@@ -3,13 +3,18 @@ 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 { TableListComponent } from '../../table-list/table-list.component';
-import { TypographyComponent } from '../../typography/typography.component';
-import { IconsComponent } from '../../icons/icons.component';
-import { MapsComponent } from '../../maps/maps.component';
-import { NotificationsComponent } from '../../notifications/notifications.component';
-import { UpgradeComponent } from '../../upgrade/upgrade.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 { 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';
+
 
 export const AdminLayoutRoutes: Routes = [
   { path: 'dashboard',
@@ -24,12 +29,94 @@ export const AdminLayoutRoutes: Routes = [
     component: AssetsComponent,
     data: {title: "Listado de plantas"}
   },
+  // General management
+  { path: 'plants', 
+    component: PlantsComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Listado de plantas",
+      breadcrumb: "Plantas",
+      roles: [2,3]
+    },
+  },
+  { path: 'plants/new', 
+    component: NewPlantComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Nueva planta",
+      breadcrumb: "Nueva planta",
+      roles: [3]
+    },
+  },
+  { path: 'plant/:id/edit', 
+    component: EditPlantComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Editar planta",
+      breadcrumb: "Editar planta",
+      roles: [2,3]
+    },
+  },
+  
+  { path: 'users', 
+    component: UsersComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Listado de usuarios",
+      breadcrumb: "Usuarios",
+      roles: [3]
+    },
+  },
+    
+  { path: 'users/new', 
+    component: NewUserComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Nuevo usuario",
+      breadcrumb: "Nuevo usuario",
+      roles: [3]
+    },
+  },
+
+  // Organizations actions
+  { path: 'organizations', 
+    component: OrganizationsComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: "Listado de organizaciones",
+      breadcrumb: "Organizaciones",
+      roles: [2,3]
+    },
+  },
+  {
+    path: 'organizations/new',
+    component: NewOrganizationComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: 'Nueva organización',
+      breadcrumb:'Nueva organización',
+      roles: [3]
+    }
+  },
+  {
+    path: 'organization/:id',
+    component: OrganizationComponent,
+    canActivate: [AuthGuard], 
+    data: {
+      title: 'Organización',
+      breadcrumb: 'Detalle',
+      roles: [2,3]
+    }
+  },
+  {
+    path: 'organization/:id/edit',
+    canActivate: [AuthGuard], 
+    component: EditOrganizationComponent,
+    data: {
+      title: 'Editar organización',
+      breadcrumb: 'Editar organización',
+      roles: [3]
+    }
+  },
 
-  /*
-  { path: 'table-list',     component: TableListComponent },
-  { path: 'typography',     component: TypographyComponent },
-  { path: 'icons',          component: IconsComponent },
-  { path: 'maps',           component: MapsComponent },
-  { path: 'notifications',  component: NotificationsComponent },
-  { path: 'upgrade',        component: UpgradeComponent },*/
 ];

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

@@ -4,7 +4,7 @@ export interface Organization {
     id: string;
     name: string;
     contactName: string;
-    contactNumber: number;
+    contactNumber: string;
     country: string;
     city: string;
     address: string;

+ 4 - 0
src/app/models/token.ts

@@ -0,0 +1,4 @@
+export class Token {
+  jwt: string;
+  refreshToken: string;
+}

+ 28 - 0
src/app/services/auth.guard.ts

@@ -0,0 +1,28 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { AuthService } from '../services/auth2.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuthGuard implements CanActivate {
+
+  constructor(private authService: AuthService, private router: Router) { }
+
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
+    
+    if (this.authService.isLoggedIn()) {
+      const userLevel = +this.authService.getUserLevel();
+      if (route.data.roles && route.data.roles.indexOf(userLevel) === -1) {
+        // role not authorised so redirect to home page
+        this.router.navigate(['/']);
+        return false;
+      }
+      return true
+    }
+    else {
+      this.router.navigate(['login'], { queryParams: { returnUrl: state.url }});
+      return !this.authService.isLoggedIn();
+    }
+  }
+}

+ 96 - 0
src/app/services/auth2.service.ts

@@ -0,0 +1,96 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { of, Observable } from 'rxjs';
+import { catchError, mapTo, tap, map } from 'rxjs/operators';
+import { Token } from '@app/models/token';
+import { environment } from '@environments/environment';
+import * as CryptoJS from 'crypto-js';
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class AuthService {
+
+  private readonly JWT_TOKEN = 'JWT_TOKEN';
+  private readonly REFRESH_TOKEN = 'REFRESH_TOKEN';
+  private readonly USER_MENU = 'USER_MENU';
+  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)
+      .pipe(
+        tap(tokens => this.doLoginUser(user.email, tokens)),
+        mapTo(true),
+        catchError(error => {
+          return of(false);
+        }));
+  }
+
+  logout() {
+    return this.http.post<any>(`${environment.productionApiUrl}/auth/logout`, {
+    }).pipe(
+      tap(() => this.doLogoutUser()),
+      mapTo(true),
+      catchError(error => {
+        return of(false);
+      }));
+    }
+
+  isLoggedIn() {
+    return !!this.getJwtToken();
+  }
+
+  getUserLevel() {
+    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"]);
+    }));
+  }
+
+  getJwtToken() {
+    return localStorage.getItem(this.JWT_TOKEN);
+  }
+
+  private doLoginUser(email: string, tokens: Token) {
+    this.loggedUser = email;
+    this.storeTokens(tokens);
+  }
+
+  private doLogoutUser() {
+    this.loggedUser = null;
+    this.removeTokens();
+  }
+
+  getRefreshToken() {
+    return localStorage.getItem(this.REFRESH_TOKEN);
+  }
+
+  private storeJwtToken(jwt: string) {
+    localStorage.setItem(this.JWT_TOKEN, jwt);
+  }
+
+  private storeTokens(tokens: Token) {
+    localStorage.clear();
+    //console.log(CryptoJS.AES.encrypt(this.JWT_TOKEN, tokens["data"]["user"].role.toString(), 'soma-inverlec-2019').toString());
+    localStorage.setItem(this.USER_MENU, CryptoJS.AES.encrypt(tokens["data"]["user"].role.toString(), 'soma-inverlec-2019').toString())
+    localStorage.setItem(this.JWT_TOKEN, tokens["data"]["user"].token); 
+    localStorage.setItem(this.REFRESH_TOKEN, tokens["data"]["user"].refresh);
+  }
+
+  private removeTokens() {
+    localStorage.removeItem(this.USER_MENU);
+    localStorage.removeItem(this.JWT_TOKEN);
+    localStorage.removeItem(this.REFRESH_TOKEN);
+  }
+}

+ 28 - 5
src/app/services/logs.service.ts

@@ -18,6 +18,28 @@ export class LogsService {
   constructor(private http: HttpClient) {
   }
 
+  getEnergyProducedByParams(id:string, interval:string, date:string) {
+    return this.http.get(`${environment.productionApiUrl}/asset/${id}/energy-produced/${interval}/${date}`)
+    .pipe(
+      timeout(this.time),
+      map(response =>{
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+
+  getEnergySummaryByAsset(id:string){
+    return this.http.get(`${environment.productionApiUrl}/asset/${id}/energy-stats`)
+    .pipe(
+      timeout(this.time),
+      map(response =>{
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+
   getEnergyProducedByDay() {
     return this.http.get(`${environment.apiUrl}/logs/energyProduced/1D`)
     .pipe(
@@ -62,16 +84,17 @@ export class LogsService {
     )
   }
 
+  //if(error.error instanceof ErrorEvent) {
   errorHandl(error) {
     let errorMessage = '';
-    if(error.error instanceof ErrorEvent) {
+    if(error.error) {
       // Get client-side error
-      errorMessage = error.error.message;
+      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);
- }
-
+  }
+  
 }

+ 63 - 3
src/app/services/organizations.service.ts

@@ -5,6 +5,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { retry, catchError, map, timeout } from 'rxjs/operators';
 
 import { environment } from '@environments/environment';
+import { organizationsData } from '../data/organizations.data';
 
 
 @Injectable({
@@ -12,7 +13,7 @@ import { environment } from '@environments/environment';
 })
 export class OrganizationsService {
 
-  private listData: Organization[];
+  private listData: Organization[] = organizationsData;
 
   constructor(private http: HttpClient) {}
 
@@ -23,6 +24,10 @@ export class OrganizationsService {
     })
   }
  
+  getListOrganizations() {
+    return observableOf(this.listData);
+  }
+
   getOrganizations(): Observable<any> {
     return this.http.get<any>(`${environment.apiUrl}/organizations`)
     .pipe(
@@ -33,15 +38,70 @@ export class OrganizationsService {
       catchError(this.errorHandl)
     )
   }
+
+
+  getOrganization(id: string){
+    return this.http.get<any>(`${environment.productionApiUrl}/organization/${id}`)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
+
+  createOrganization(organization: { name :string, address :string, 
+    city :string, country :string, contactName :string,
+    contactNumber: string, assets:any}): Observable<boolean> {
+      return this.http.post<any>(`${environment.productionApiUrl}/organizations`, organization)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
+  
+  updateOrganization(id:string, organization: { name :string, address :string, 
+    city :string, country :string, contactName :string,
+    contactNumber: string, assets:any}): Observable<boolean> {
+      return this.http.put<any>(`${environment.productionApiUrl}/organization/${id}`, organization)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
+  
+  deleteOrganization(id:string): Observable<boolean> {
+    return this.http.delete<any>(`${environment.productionApiUrl}/organization/${id}`)
+    .pipe(
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )  
+  }
+
+  getAllOrganizations() {
+    return this.http.get(`${environment.productionApiUrl}/organizations`)
+    .pipe(
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )  }
   
   errorHandl(error) {
     let errorMessage = '';
-    if(error.error instanceof ErrorEvent) {
+    if(error.error) {
       // Get client-side error
-      errorMessage = error.error.message;
+      errorMessage = error.error;
     } else {
       // Get server-side error
       errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
+      
     }
     return throwError(errorMessage);
  }

+ 24 - 3
src/app/services/plants.service.ts

@@ -34,6 +34,27 @@ export class PlantsService extends PlantData {
     )
   }
 
+  getAllAssets(){
+    return this.http.get<any>(`${environment.productionApiUrl}/assets`, {})
+    .pipe(
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+
+  getAssetById(assetId:string){
+    return this.http.get<any>(`${environment.productionApiUrl}/asset/${assetId}`, {})
+    .pipe(
+      map(response => {
+        return response;
+      }),
+      catchError(this.errorHandl)
+    )
+  }
+
+
   getAssets() {
     return this.http.get(`${environment.apiUrl}/assets`)
     .pipe(
@@ -84,12 +105,12 @@ export class PlantsService extends PlantData {
   
   errorHandl(error) {
     let errorMessage = '';
-    if(error.error instanceof ErrorEvent) {
+    if(error.error) {
       // Get client-side error
-      errorMessage = error.error.message;
+      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);
  }

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

@@ -0,0 +1,67 @@
+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';
+
+@Injectable()
+export class TokenInterceptor implements HttpInterceptor {
+
+  private isRefreshing = false;
+  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
+
+  constructor(public authService: AuthService) { }
+
+  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+    if (request.url.indexOf("auth/refresh")!= -1){
+      request = this.addToken(request, this.authService.getRefreshToken());
+    }
+    else if (request.url.indexOf("openweather")!= -1){
+      request;
+    }
+    else {
+      if (this.authService.getJwtToken()) {
+        request = this.addToken(request, this.authService.getJwtToken());
+      }
+    }
+    
+    return next.handle(request).pipe(catchError(error => {
+      if (error instanceof HttpErrorResponse && error.status === 401) {
+        return this.handle401Error(request, next);
+      } else {
+        return throwError(error);
+      }
+    }));      
+
+  }
+
+  private addToken(request: HttpRequest<any>, token: string) {
+    return request.clone({
+      setHeaders: {
+        'Authorization': `Bearer ${token}`
+      }
+    });
+  }
+
+  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
+    if (!this.isRefreshing) {
+      this.isRefreshing = true;
+      this.refreshTokenSubject.next(null);
+
+      return this.authService.refreshToken().pipe(
+        switchMap((token: any) => {
+          this.isRefreshing = false;
+          this.refreshTokenSubject.next(token["data"]["access_token"]);
+          return next.handle(this.addToken(request, token["data"]["access_token"]));
+        }));
+
+    } else {
+      return this.refreshTokenSubject.pipe(
+        filter(token => token != null),
+        take(1),
+        switchMap(jwt => {
+          return next.handle(this.addToken(request, jwt));
+        }));
+    }
+  }
+}

+ 55 - 6
src/app/services/user.service.ts

@@ -3,16 +3,65 @@ 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';
 
 @Injectable({ providedIn: 'root' })
 export class UserService {
-    constructor(private http: HttpClient) { }
+  constructor(private http: HttpClient) { }
 
-    getAll() {
-        return this.http.get<User[]>(`${environment.apiUrl}/users`);
-    }
+  getAllUsers() {
+      return this.http.get<User[]>(`${environment.productionApiUrl}/users`);
+  }
+
+  getById(id: number) {
+      return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
+  }
+
+  createUser(user: { first_name :string, last_name :string, 
+    email :string, role :number }): Observable<boolean> {
+      return this.http.post<any>(`${environment.productionApiUrl}/users`, user)
+      .pipe(
+        map(response => {
+          return response;
+        }),
+        catchError(this.errorHandl)
+      )  
+  }
 
-    getById(id: number) {
-        return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
+  validateUserToken(user: {token :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)
+      )  
+  }
+
+
+  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);
+ }
+
 }

+ 1 - 1
src/assets/scss/core/_sidebar-and-main-panel.scss

@@ -410,7 +410,7 @@
     @include transition (0.33s, cubic-bezier(0.685, 0.0473, 0.346, 1));
 
      .main-content{
-        margin-top: 50px;
+        margin-top: 30px;
         padding: 20px 10px;
         min-height: calc(100vh - 123px);
     }

+ 94 - 0
src/assets/scss/material-dashboard.scss

@@ -99,4 +99,98 @@
 .floating-title {
   font-size: 1.2rem;
   margin: 21px 65px 10px;
+}
+
+.card-icon {
+  border-radius: 3px;
+  background-color: #999;
+  padding: 15px;
+  margin-top: -30px;
+  margin-right: 15px;
+  float: left;
+  width: 60px;
+  height: 60px;
+  line-height: 40px;
+  text-align: center;
+}
+
+.breadcrumb {
+  background-color: #fff;
+}
+
+table {
+  width: 100%;
+}
+
+.mat-form-field {
+  font-size: 14px;
+  width: 100%;
+}
+
+.example-loading-shade {
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 56px;
+  right: 0;
+  background: rgba(0, 0, 0, 0.15);
+  z-index: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.example-rate-limit-reached {
+  color: #980000;
+  max-width: 360px;
+  text-align: center;
+}
+
+/* Structure */
+.example-container {
+  position: relative;
+  min-height: 200px;
+}
+
+.example-table-container {
+  position: relative;
+  //max-height: 400px;
+  overflow: auto;
+}
+
+.align-right {
+  text-align: right;
+}
+
+
+.was-validated .form-control:invalid, .form-control.is-invalid {
+  border-bottom: 1px solid #f44336;
+}
+
+
+.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid, input.form-control:invalid, input.form-control.is-invalid {
+  background-size: 18px;
+}
+
+.action-buttons {
+  text-align: right;
+
+  .btn-danger {
+    color: $white !important;
+  }
+
+  a {
+    color: $white;
+  }
+}
+
+
+.form-check .form-check-input {
+  opacity: 1;
+  height: 20px;
+  width: 20px;
+  overflow: hidden;
+  background: black;
+  color: black;
+  z-index: 1;
 }

+ 1 - 0
src/environments/environment.prod.ts

@@ -1,5 +1,6 @@
 export const environment = {
   production: true,
   apiUrl: 'https://192.168.98.140:8000',
+  productionApiUrl: 'https://denmark.inverlec.solar/api/v1',
   appID: '55899b9ea53834f2736b65a3582b734b',
 };

+ 2 - 1
src/environments/environment.ts

@@ -4,7 +4,8 @@
 
 export const environment = {
   production: false,
-  apiUrl: 'https://192.168.98.140:8000',
+  apiUrl: 'https://api.inverlec.solar',
+  productionApiUrl: 'http://192.168.98.10:8888/api/v1',
   appID: '55899b9ea53834f2736b65a3582b734b',
   gKey: '',
   config: {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است