浏览代码

SidebarComponent

Alex Ondoa 2 年前
父节点
当前提交
c955763a4c

+ 4
- 0
Angular/src/app/app-routing.module.ts 查看文件

@@ -1,7 +1,9 @@
1 1
 import { NgModule } from '@angular/core';
2 2
 import { RouterModule, Routes } from '@angular/router';
3 3
 import { AppMainComponent } from './app.main.component';
4
+import { CandidatDetailComponent } from './pages/candidat/candidat-detail/candidat-detail.component';
4 5
 import { CandidatListComponent } from './pages/candidat/candidat-list/candidat-list.component';
6
+import { CandidatSidebarComponent } from './pages/candidat/candidat-sidebar/candidat-sidebar.component';
5 7
 import { DashboardComponent } from './pages/dashboard/dashboard/dashboard.component';
6 8
 
7 9
 export const appRouteList: Routes = [
@@ -17,7 +19,9 @@ export const appRouteList: Routes = [
17 19
             {
18 20
                 path: '', component: AppMainComponent,
19 21
                 children: [
22
+                    {path: 'candidat/candidat-sidebar', component: CandidatSidebarComponent},
20 23
                     {path: 'candidat/candidat-list', component: CandidatListComponent},
24
+                    {path: 'candidat/candidat-detail/:candidatId', component: CandidatDetailComponent},
21 25
                     {path: 'dashboard', component: DashboardComponent},
22 26
                 ]
23 27
             },

+ 3
- 2
Angular/src/app/app.module.ts 查看文件

@@ -1,5 +1,5 @@
1 1
 import {NgModule} from '@angular/core';
2
-import {FormsModule} from '@angular/forms';
2
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
3 3
 import {HttpClientModule} from '@angular/common/http';
4 4
 import {BrowserModule} from '@angular/platform-browser';
5 5
 import {HashLocationStrategy, LocationStrategy} from '@angular/common';
@@ -200,7 +200,8 @@ FullCalendarModule.registerPlugins([
200 200
         TreeModule,
201 201
         TreeTableModule,
202 202
         VirtualScrollerModule,
203
-        AppCodeModule
203
+        AppCodeModule,
204
+        ReactiveFormsModule
204 205
     ],
205 206
     declarations: [
206 207
         AppComponent,

+ 30
- 16
Angular/src/app/pages/candidat/candidat-detail/candidat-detail.component.html 查看文件

@@ -1,37 +1,51 @@
1 1
 <div>
2 2
     <div>
3 3
         <div class="modal-header">
4
-          <h4 class="modal-title" id="modal-basic-title">Candidate Detail</h4>
4
+          <h4 class="modal-title" id="modal-basic-title">Candidat Detail</h4>
5 5
           <button type="button" class="close" aria-label="Close">
6 6
             <span aria-hidden="true">×</span>
7 7
           </button>
8 8
         </div>
9 9
         <div class="modal-body">
10
-            <form>
10
+            <form [formGroup]="detailForm">
11 11
                 <div class="form-group">
12 12
                     <label for="firstName">First Name</label>
13
-                    <input type="text" class="form-control" id="firstName" value="">
13
+                    <input type="text" class="form-control" id="firstName" formControlName="firstName" value="{{_candidat.firstName}}">
14 14
                 </div>
15 15
                 <div class="form-group">
16 16
                     <label for="LastName">Last Name</label>
17
-                    <input type="text" class="form-control" id="lastName" value="">
17
+                    <input type="text" class="form-control" id="lastName" formControlName="lastName" value="{{_candidat.lastName}}">
18 18
                 </div>
19
-                <div class="form-group">
20
-                    <label for="type">Emails Type's</label>
21
-                    <select class="form-control" id="type">
22
-                      <option>Professional</option>
23
-                      <option>Personal</option>
24
-                    </select>
19
+                <div *ngFor="let ml of _candidat.emails">
20
+                  <div *ngIf="_candidat.emails != null && ml.type ==='profesionnal'" class="form-group">
21
+                    <label for="emailPro">Professional email</label>
22
+                    <input type="emails" class="form-control" id="emailPro" value="{{ml.email}}">
25 23
                   </div>
26
-                <div class="form-group">
27
-                  <label for="emails">Emails address</label>
28
-                  <input type="emails" class="form-control" id="emails">
24
+                  <div *ngIf="_candidat.emails != null && ml.type ==='personal'" class="form-group">
25
+                    <div class="form-group">
26
+                      <label for="emailPerso">Personal email</label>
27
+                      <input type="emails" class="form-control" id="emailPerso" value="{{ml.email}}">
28
+                    </div>
29
+                  </div>
30
+                </div>
31
+                <div *ngIf="_candidat.emails.length <= 0">
32
+                  <div class="form-group">
33
+                    <label for="emailPro">Professional email</label>
34
+                    <input type="emails" class="form-control" id="emailPro">
35
+                  </div>
36
+                  <div class="form-group">
37
+                    <div class="form-group">
38
+                      <label for="emailPerso">Personal email</label>
39
+                      <input type="emails" class="form-control" id="emailPerso">
40
+                    </div>
41
+                  </div>
42
+                </div>
43
+                <div class="modal-footer">
44
+                  <button class="btn btn-primary mr-1" (click)="onSubmit()">Update</button>
29 45
                 </div>
30
-                
31 46
               </form>
32 47
         </div>
33 48
         <div class="modal-footer">
34
-          <button type="button" class="btn btn-outline-dark">Ok</button>
35 49
         </div>
36 50
     </div>
37
-</div>>
51
+</div>

+ 42
- 6
Angular/src/app/pages/candidat/candidat-detail/candidat-detail.component.ts 查看文件

@@ -1,5 +1,6 @@
1 1
 import { HttpClient } from '@angular/common/http';
2 2
 import { Component, OnInit } from '@angular/core';
3
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3 4
 import { ActivatedRoute } from '@angular/router';
4 5
 import { Observable } from 'rxjs';
5 6
 import { AppBreadcrumbService } from 'src/app/app.breadcrumb.service';
@@ -13,23 +14,58 @@ import { CandidateService } from 'src/app/shared/services/candidate.service';
13 14
 })
14 15
 export class CandidatDetailComponent implements OnInit {
15 16
 
16
-  private _candidat: Observable<Candidate> = new Observable<Candidate>() ;
17
+  _candidat: Candidate = new Candidate() ;
17 18
   paramId: string;
18
-  constructor(private breadcrumbService: AppBreadcrumbService,private http: HttpClient, private candidateService: CandidateService, private route: ActivatedRoute) {
19
+  detailForm: FormGroup;
20
+  submitted = false;
21
+
22
+  constructor(private breadcrumbService: AppBreadcrumbService,private http: HttpClient, private candidateService: CandidateService, 
23
+    private route: ActivatedRoute,private formBuilder: FormBuilder) {
19 24
     this.breadcrumbService.setItems([
20
-      { label: 'Candidats' },
21
-      { label: 'Detail', routerLink: ['/candidat/:candidatId/candidat-list'] }
25
+      { label: 'Candidat' },
26
+      { label: 'Detail' }
22 27
     ]);
23 28
   }
24 29
 
25 30
   ngOnInit(): void {
26
-  //  this.route.data.subscribe((data: { candidat: Observable<Candidate> }) => this._candidat = data.candidat);
31
+    this.getParamId();
32
+    this.getCandidatById();
33
+    this.currentDataForm();
34
+  }
35
+
36
+  private getCandidatById() {
37
+    this.candidateService.candidatGetById(this.paramId)
38
+      .subscribe(
39
+        data => {
40
+          this._candidat = data;
41
+          console.log(data);
42
+        },
43
+        error => {
44
+          console.log(error);
45
+        });
46
+  }
47
+
48
+  private getParamId() {
27 49
     this.route.paramMap.subscribe(params => {
28 50
       if (params.get('candidatId')) {
29 51
         this.paramId = params.get('candidatId');
30 52
       }
31 53
     });
32
-    this._candidat = this.candidateService.candidatGetById(this.paramId);
54
+  }
55
+
56
+  private currentDataForm() {
57
+    this.detailForm = this.formBuilder.group({
58
+      firstName: [this._candidat.firstName, Validators.required],
59
+      lastName: [this._candidat.lastName, Validators.required]
60
+    });
61
+  }
62
+  
63
+  onSubmit() {
64
+    this.submitted = true;
65
+    if (this.detailForm.invalid) {
66
+      alert('Data Error!! :\n\n' + JSON.stringify(this.detailForm.value, null,4));
67
+      return;
68
+    }
33 69
   }
34 70
 
35 71
 }

+ 3
- 7
Angular/src/app/pages/candidat/candidat-list/candidat-list.component.html 查看文件

@@ -1,6 +1,6 @@
1 1
 <div class="card">
2 2
     <h5>Candidates List</h5>
3
-    <div><button [routerLink]="'/dasboard/dasboard'" type="button" class="primary">Add</button><br></div>
3
+    <div><button (click)="showNew()" type="button" class="primary">Add</button><br></div>
4 4
     <table class="table table-bordered">
5 5
         <thead>
6 6
           <tr>
@@ -11,7 +11,7 @@
11 11
           </tr>
12 12
         </thead>
13 13
         <tbody>
14
-          <tr  *ngFor="let candidate of candidates | async">
14
+          <tr (click)="showDetail(candidate.id)"  *ngFor="let candidate of candidates | async">
15 15
                 <td>{{candidate.id}}</td>
16 16
                 <td>{{candidate.firstName}}</td>
17 17
                 <td>{{candidate.lastName}}</td>
@@ -22,7 +22,7 @@
22 22
                     </tr>
23 23
                 </td>
24 24
                 <td>
25
-                  <button  icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" (click)="show(candidate)">edit</button>
25
+                  <button icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" (click)="showDetail(candidate.id)">edit</button>
26 26
                   <button  icon="pi pi-trash" class="p-button-rounded p-button-warning">delete</button>
27 27
                </td>
28 28
           </tr>
@@ -33,10 +33,6 @@
33 33
             <button>Search</button>
34 34
         </form>
35 35
       </div>
36
-
37
-      <div *ngIf="isEdit">
38
-        <app-candidat-detail></app-candidat-detail>
39
-    </div>
40 36
    <!-- <button [routerLink]="'/candidate/candidate-sidebar'" pButton pRipple type="button" label="Add" class="mr-2 mb-2"></button> -->
41 37
    <!--  <p-table #dt  [value]="theCandidates" [paginator]="true" [rows]="5" [showCurrentPageReport]="true" responsiveLayout="scroll"
42 38
         currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" [rowsPerPageOptions]="[5,10,25,50]">

+ 14
- 11
Angular/src/app/pages/candidat/candidat-list/candidat-list.component.ts 查看文件

@@ -1,12 +1,13 @@
1 1
 import { HttpClient } from '@angular/common/http';
2 2
 import { Component, OnInit, ViewChild } from '@angular/core';
3 3
 import { FormBuilder } from '@angular/forms';
4
-import { ActivatedRoute } from '@angular/router';
4
+import { ActivatedRoute, Router } from '@angular/router';
5 5
 import { Table } from 'primeng/table';
6 6
 import { BehaviorSubject, Observable } from 'rxjs';
7 7
 import { tap } from 'rxjs/operators';
8 8
 import { AppBreadcrumbService } from 'src/app/app.breadcrumb.service';
9 9
 import { Candidate } from 'src/app/shared/models/candidate';
10
+import { CandidateService } from 'src/app/shared/services/candidate.service';
10 11
 import { candidatListService } from './candidat-list.service';
11 12
 
12 13
 @Component({
@@ -15,10 +16,10 @@ import { candidatListService } from './candidat-list.service';
15 16
   styleUrls: ['./candidat-list.component.scss']
16 17
 })
17 18
 export class CandidatListComponent implements OnInit {
18
-    isEdit: boolean = false;
19
+
19 20
     candidates: Observable<Candidate[]>;
20 21
     candidate: Candidate;
21
-    constructor(private breadcrumbService: AppBreadcrumbService,private http: HttpClient, private candidatListService: candidatListService, private route: ActivatedRoute) {
22
+    constructor(private breadcrumbService: AppBreadcrumbService,private http: HttpClient, private candidatListService: candidatListService, private route: ActivatedRoute,private router: Router, private candidatService: CandidateService) {
22 23
       this.breadcrumbService.setItems([
23 24
         { label: 'Candidats' },
24 25
         { label: 'List', routerLink: ['/candidat/candidat-list'] }
@@ -29,14 +30,16 @@ export class CandidatListComponent implements OnInit {
29 30
     this.route.data.subscribe((data: { candidats: Observable<Candidate[]> }) => this.candidates = data.candidats);
30 31
     this.candidates = this.candidatListService.getCandidats();
31 32
   }
32
-  show(candidateUI: Candidate){
33
-   // this.route.navigate(); // navigate to other page
34
-    this.isEdit = true;
35
-    this.candidate = new Candidate()
36
-    this.candidate.id = candidateUI.id;
37
-    this.candidate.firstName = candidateUI.firstName;
38
-    this.candidate.lastName = candidateUI.lastName;
39
-    this.candidate.emails = candidateUI.emails;
33
+  showDetail(candidateId: string){
34
+    var myurl = `${"/candidat/candidat-detail"}/${candidateId}`;
35
+    this.router.navigateByUrl(myurl);
40 36
     
41 37
   }
38
+  showNew(){
39
+    this.router.navigateByUrl("/candidat/candidat-sidebar");  
40
+  }
41
+
42
+  search(){
43
+    this.candidates = this.candidatService.search("","","");
44
+  }
42 45
 }

+ 36
- 0
Angular/src/app/pages/candidat/candidat-sidebar/candidat-sidebar.component.html 查看文件

@@ -0,0 +1,36 @@
1
+<div>
2
+    <div>
3
+        <div class="modal-header">
4
+          <h4 class="modal-title" id="modal-basic-title">New Candidat</h4>
5
+        </div>
6
+        <div class="modal-body">
7
+            <form [formGroup]="createForm">
8
+                <div class="form-group">
9
+                    <label for="firstName">First Name</label>
10
+                    <input type="text" class="form-control" id="firstName" formControlName="firstName">
11
+                </div>
12
+                <div class="form-group">
13
+                    <label for="LastName">Last Name</label>
14
+                    <input type="text" class="form-control" id="lastName" formControlName="lastName">
15
+                </div>
16
+                <div>
17
+                  <div class="form-group">
18
+                    <label for="emailPro">Professional email</label>
19
+                    <input type="emails" class="form-control" id="emailPro" formControlName="emailPro">
20
+                  </div>
21
+                  <div class="form-group">
22
+                    <div class="form-group">
23
+                      <label for="emailPerso">Personal email</label>
24
+                      <input type="emails" class="form-control" id="emailPerso" formControlName="emailPerso">
25
+                    </div>
26
+                  </div>
27
+                </div>
28
+                <div class="modal-footer">
29
+                  <button class="btn btn-primary mr-1" (click)="onSubmit()">Create</button>
30
+                </div>
31
+              </form>
32
+        </div>
33
+        <div class="modal-footer">
34
+        </div>
35
+    </div>
36
+</div>

+ 0
- 0
Angular/src/app/pages/candidat/candidat-sidebar/candidat-sidebar.component.scss 查看文件


+ 25
- 0
Angular/src/app/pages/candidat/candidat-sidebar/candidat-sidebar.component.spec.ts 查看文件

@@ -0,0 +1,25 @@
1
+import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+import { CandidatSidebarComponent } from './candidat-sidebar.component';
4
+
5
+describe('CandidatSidebarComponent', () => {
6
+  let component: CandidatSidebarComponent;
7
+  let fixture: ComponentFixture<CandidatSidebarComponent>;
8
+
9
+  beforeEach(async () => {
10
+    await TestBed.configureTestingModule({
11
+      declarations: [ CandidatSidebarComponent ]
12
+    })
13
+    .compileComponents();
14
+  });
15
+
16
+  beforeEach(() => {
17
+    fixture = TestBed.createComponent(CandidatSidebarComponent);
18
+    component = fixture.componentInstance;
19
+    fixture.detectChanges();
20
+  });
21
+
22
+  it('should create', () => {
23
+    expect(component).toBeTruthy();
24
+  });
25
+});

+ 41
- 0
Angular/src/app/pages/candidat/candidat-sidebar/candidat-sidebar.component.ts 查看文件

@@ -0,0 +1,41 @@
1
+import { HttpClient } from '@angular/common/http';
2
+import { Component, OnInit } from '@angular/core';
3
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
4
+import { AppBreadcrumbService } from 'src/app/app.breadcrumb.service';
5
+
6
+@Component({
7
+  selector: 'app-candidat-sidebar',
8
+  templateUrl: './candidat-sidebar.component.html',
9
+  styleUrls: ['./candidat-sidebar.component.scss']
10
+})
11
+export class CandidatSidebarComponent implements OnInit {
12
+  createForm: FormGroup;
13
+  submitted = false;
14
+  constructor(private breadcrumbService: AppBreadcrumbService,private http: HttpClient, private formBuilder: FormBuilder) {
15
+    this.breadcrumbService.setItems([
16
+      { label: 'Candidat' },
17
+      { label: 'New' }
18
+    ]);
19
+  }
20
+
21
+  ngOnInit(): void {
22
+    this.Form();
23
+  }
24
+  private Form() {
25
+    this.createForm = this.formBuilder.group({
26
+      firstName: ["", Validators.required],
27
+      lastName: ["", Validators.required],
28
+      emailPro: ["", Validators.email],
29
+      emailPerso: ["", Validators.email]
30
+    });
31
+  }
32
+  
33
+  onSubmit() {
34
+    this.submitted = true;
35
+    if (this.createForm.invalid) {
36
+      alert('Data Error!! :\n\n' + JSON.stringify(this.createForm.value, null,4));
37
+      return;
38
+    }
39
+    alert('SUCCESS!! :\n\n' + JSON.stringify(this.createForm.value, null,4));
40
+  }
41
+}

+ 15
- 6
Angular/src/app/pages/candidat/candidat.module.ts 查看文件

@@ -6,6 +6,8 @@ import { CandidatSearchComponent } from './candidat-search/candidat-search.compo
6 6
 import { CandidatsResolver } from 'src/app/shared/resolver/candidats.resolver';
7 7
 import { CandidatDetailComponent } from './candidat-detail/candidat-detail.component';
8 8
 import { CandidatDetailResolver } from 'src/app/shared/resolver/candidat-detail.resolver';
9
+import { ReactiveFormsModule } from '@angular/forms';
10
+import { CandidatSidebarComponent } from './candidat-sidebar/candidat-sidebar.component';
9 11
 
10 12
 
11 13
 export const routes: Routes = [
@@ -17,23 +19,30 @@ export const routes: Routes = [
17 19
       },
18 20
   },
19 21
   {
20
-    path: 'candidat/:candidatId/candidat-detail',
22
+    path: '/candidat/candidat-detail/:candidatId',
21 23
     component: CandidatDetailComponent,
22 24
     resolve: {
23
-      Candidat: CandidatDetailResolver 
25
+      candidat: CandidatDetailResolver 
24 26
     },
25
-}
27
+  },
28
+  {
29
+    path: '/candidat/candidat-sidebar',
30
+    component: CandidatSidebarComponent
31
+  }
26 32
 ];
27 33
 
28 34
 @NgModule({
29 35
   declarations: [
30 36
     CandidatListComponent,
31 37
     CandidatSearchComponent,
32
-    CandidatDetailComponent
38
+    CandidatDetailComponent,
39
+    CandidatSidebarComponent
33 40
   ],
34 41
   imports: [
35 42
     CommonModule,
36
-    RouterModule.forChild(routes)
37
-  ]
43
+    RouterModule.forChild(routes),
44
+    ReactiveFormsModule
45
+  ],
46
+  exports: [RouterModule]
38 47
 })
39 48
 export class CandidatModule { }

+ 2
- 1
Angular/src/app/pages/dashboard/dashboard.module.ts 查看文件

@@ -20,6 +20,7 @@ export const routes: Routes = [
20 20
     CommonModule,
21 21
     RouterModule.forChild(routes),
22 22
     TreeModule
23
-  ]
23
+  ],
24
+  exports: [RouterModule]
24 25
 })
25 26
 export class DashboardModule { }

+ 2
- 1
Angular/src/app/pages/pages.module.ts 查看文件

@@ -1,10 +1,11 @@
1 1
 import { NgModule } from '@angular/core';
2
+import { ReactiveFormsModule } from '@angular/forms';
2 3
 import { PagesRoutingModule } from './pages.routing.module';
3 4
 
4 5
 @NgModule({
5 6
     declarations: [],
6 7
     imports: [
7
-        PagesRoutingModule,
8
+        PagesRoutingModule
8 9
     ],
9 10
     providers: []
10 11
 })

+ 28
- 21
Angular/src/app/shared/services/candidate.service.ts 查看文件

@@ -1,6 +1,6 @@
1 1
 import { HttpClient } from '@angular/common/http';
2 2
 import { Injectable } from '@angular/core';
3
-import { Observable } from 'rxjs';
3
+import { BehaviorSubject, Observable } from 'rxjs';
4 4
 import { Candidate } from '../models/candidate';
5 5
 
6 6
 @Injectable({
@@ -8,28 +8,35 @@ import { Candidate } from '../models/candidate';
8 8
 })
9 9
 export class CandidateService {
10 10
 
11
+  private _candidat = new BehaviorSubject<Candidate>(new Candidate());
12
+  readonly candidats = this._candidat.asObservable();
11 13
   constructor(private http: HttpClient) { }
12
-
13
-  listOfCandidates() {
14
-    return this.http.get<any>('assets/demo/data/candidates.json')
15
-            .toPromise()
16
-            .then(res => res.data as Candidate[])
17
-            .then(data => data);
18
-  }
19 14
   
20
-  candidatGetById(id: string): Observable<Candidate> {
21
-     return null;
15
+ candidatGetById(id: string): Observable<Candidate> {
16
+   this.http.get<Candidate[]>('assets/demo/data/candidates.json').subscribe(
17
+           e => { 
18
+             var candidatById = e.filter(i => i.id == id); 
19
+             this._candidat.next(Object.assign({}, candidatById[0]));
20
+           });
21
+    return this.candidats;
22 22
   }
23
-  async Search(query: string): Promise<Candidate[]> {
24
-    let candidates = this.listOfCandidates();
25
-    let candidatesFilter: Candidate[] ;
26
-    for (let item of await candidates) 
27
-    {
28
-      if (item.firstName == query || item.lastName == query) 
29
-      {
30
-        candidatesFilter.push(item);
31
-      }
32
-    }  
33
-    return candidatesFilter
23
+
24
+  search(queryFN: string, queryLN: string, queryEML: string): Observable<Candidate[]> {
25
+    let _candidats = new BehaviorSubject<Candidate[]>([]);
26
+    let dataStore:  Candidate[];
27
+    let candidats = _candidats.asObservable();
28
+    
29
+    this.http.get<Candidate[]>('assets/demo/data/candidates.json').subscribe(
30
+      e => { 
31
+        if(queryFN){
32
+          dataStore = e.filter(i => i.firstName == queryFN); 
33
+        }
34
+        if(queryLN){
35
+          dataStore = e.filter(i => i.lastName == queryLN); 
36
+        }
37
+        _candidats.next(Object.assign({}, dataStore));
38
+      });
39
+     
40
+    return candidats;
34 41
   }
35 42
 }

+ 1
- 1
Angular/src/index.html 查看文件

@@ -13,7 +13,7 @@
13 13
 
14 14
     <link id="theme-css" rel="stylesheet" type="text/css" href="assets/theme/indigo/theme-light.css">
15 15
     <link id="layout-css" rel="stylesheet" type="text/css" href="assets/layout/css/layout-light.css">
16
-    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
16
+    <!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> -->
17 17
 </head>
18 18
 <body>
19 19
     <app-root>

Powered by TurnKey Linux.