Skip to content

Commit 8d25189

Browse files
committed
initial commit
1 parent cdfacd4 commit 8d25189

17 files changed

+472
-4
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<div class="admin-nav-gap">
2+
<div class="container-fluid">
3+
<ol class="breadcrumb">
4+
<li class="active"><i class="fa fa-users"></i> Teams</li>
5+
</ol>
6+
</div>
7+
</div>
8+
9+
<div class="container bottom-gap-l">
10+
<div class="row">
11+
<div class="col-md-3">
12+
<form class="form">
13+
<div class="top-gap">
14+
<label>Search</label>
15+
<input type="text" class="form-control" placeholder="team name, description" name="teamSearch"
16+
[(ngModel)]="teamSearch" (ngModelChange)="search($event)">
17+
</div>
18+
</form>
19+
<hr>
20+
21+
<button class="btn btn-default pull-right" (click)="reset()">Reset</button>
22+
</div>
23+
24+
<div class="col-md-9">
25+
<nav class="navbar navbar-default admin-dashboard-navbar">
26+
<div class="container-fluid">
27+
<div class="navbar-header">
28+
<a class="navbar-brand">Teams</a>
29+
</div>
30+
31+
<form class="navbar-form navbar-right" role="search">
32+
<button type="submit" class="btn btn-default" (click)="newTeam()">New Team</button>
33+
</form>
34+
</div>
35+
</nav>
36+
37+
<div class="row">
38+
<div class="col-md-12">
39+
<div class="mat-elevation-z1">
40+
<div class="table-container" [style.min-height.px]="pageSize * 48">
41+
<table mat-table [class.mat-table]="true" [dataSource]="dataSource"
42+
class="mat-table-even-columns">
43+
<ng-container matColumnDef="name">
44+
<th mat-header-cell *matHeaderCellDef>Name</th>
45+
<td mat-cell *matCellDef="let team">
46+
<div class="strong">{{ team.name }}</div>
47+
</td>
48+
</ng-container>
49+
50+
<ng-container matColumnDef="description">
51+
<th mat-header-cell *matHeaderCellDef>Description</th>
52+
<td mat-cell *matCellDef="let team">
53+
<div class="muted">{{ team.description }}</div>
54+
</td>
55+
</ng-container>
56+
57+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
58+
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)"
59+
class="pointer"></tr>
60+
</table>
61+
</div>
62+
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
63+
(page)="onPageChange($event)">
64+
</mat-paginator>
65+
</div>
66+
</div>
67+
</div>
68+
</div>
69+
</div>
70+
</div>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.mat-table-even-columns {
2+
table-layout: fixed;
3+
width: 100%;
4+
5+
.mat-cell {
6+
text-overflow: ellipsis;
7+
white-space: wrap;
8+
}
9+
10+
.mat-header-cell {
11+
font-size: 1em;
12+
}
13+
14+
.mat-cell {
15+
font-size: 0.875em;
16+
}
17+
18+
.mat-header-cell,
19+
.mat-cell {
20+
padding: 0 0.75rem;
21+
}
22+
}
23+
24+
.table-container {
25+
position: relative;
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { AdminTeamsComponent } from './admin-teams.component';
4+
5+
describe('AdminTeamsComponent', () => {
6+
let component: AdminTeamsComponent;
7+
let fixture: ComponentFixture<AdminTeamsComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ AdminTeamsComponent ]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(AdminTeamsComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
2+
import { MatDialog } from '@angular/material/dialog';
3+
import { PageEvent } from '@angular/material/paginator';
4+
import { MatTableDataSource } from '@angular/material/table';
5+
import { Team } from '../team';
6+
import { Subject } from 'rxjs';
7+
import { debounceTime, distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
8+
import { TeamsService } from '../teams-service';
9+
import { AdminTeamCreateComponent } from '../admin-team-create/admin-team-create.component';
10+
11+
@Component({
12+
selector: 'mage-admin-teams',
13+
templateUrl: './admin-teams.component.html',
14+
styleUrls: ['./admin-teams.component.scss']
15+
})
16+
export class AdminTeamsComponent implements OnInit, OnDestroy {
17+
teamSearch = '';
18+
teams: Team[] = [];
19+
totalTeams = 0;
20+
pageSize = 10;
21+
pageIndex = 0;
22+
pageSizeOptions = [5, 10, 25];
23+
24+
dataSource = new MatTableDataSource<Team>();
25+
displayedColumns = ['name', 'description'];
26+
27+
private search$ = new Subject<string>();
28+
private destroy$ = new Subject<void>();
29+
30+
constructor(
31+
private modal: MatDialog,
32+
private teamService: TeamsService
33+
) { }
34+
35+
ngOnInit(): void {
36+
this.fetchTeams();
37+
38+
this.search$.pipe(
39+
debounceTime(250),
40+
distinctUntilChanged(),
41+
switchMap(searchTerm => {
42+
this.pageIndex = 0;
43+
return this.teamService.getTeams({
44+
term: searchTerm,
45+
sort: { name: 1 },
46+
limit: this.pageSize,
47+
start: String(this.pageIndex * this.pageSize)
48+
});
49+
}),
50+
takeUntil(this.destroy$)
51+
).subscribe((results) => {
52+
if (results?.length > 0) {
53+
const teams = results[0];
54+
this.teams = teams.items;
55+
this.totalTeams = teams.totalCount;
56+
this.dataSource.data = this.teams;
57+
}
58+
});
59+
}
60+
61+
ngOnDestroy(): void {
62+
this.destroy$.next();
63+
this.destroy$.complete();
64+
}
65+
66+
fetchTeams(): void {
67+
this.teamService.getTeams({
68+
term: this.teamSearch,
69+
sort: { name: 1 },
70+
limit: this.pageSize,
71+
start: String(this.pageIndex * this.pageSize)
72+
}).subscribe((results) => {
73+
if (results?.length > 0) {
74+
const teams = results[0];
75+
this.teams = teams.items;
76+
this.totalTeams = teams.totalCount;
77+
this.dataSource.data = this.teams;
78+
}
79+
});
80+
}
81+
82+
onPageChange(event: PageEvent): void {
83+
this.pageSize = event.pageSize;
84+
this.pageIndex = event.pageIndex;
85+
this.fetchTeams();
86+
}
87+
88+
search(term: string): void {
89+
this.teamSearch = term;
90+
this.search$.next(term);
91+
}
92+
93+
reset(): void {
94+
this.teamSearch = '';
95+
this.pageIndex = 0;
96+
this.fetchTeams();
97+
}
98+
99+
newTeam(): void {
100+
const dialogRef = this.modal.open(AdminTeamCreateComponent, {
101+
width: '50rem',
102+
height: '25rem',
103+
data: { team: {} }
104+
});
105+
106+
dialogRef.afterClosed().subscribe(newTeam => {
107+
if (newTeam) {
108+
this.fetchTeams();
109+
}
110+
});
111+
}
112+
113+
gotoTeam(team: Team): void {
114+
// TODO: convert to this to using a router once upgrade is complete
115+
window.location.href = `/#/home/teams/${team.id}`;
116+
}
117+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<div class="admin-team-create">
2+
<h1 mat-dialog-title>New Team</h1>
3+
<div mat-dialog-content>
4+
<form [formGroup]="teamForm">
5+
<mat-form-field class="full-width">
6+
<mat-label>Name</mat-label>
7+
<input matInput formControlName="name" required>
8+
<mat-error *ngIf="teamForm.get('name').hasError('required')">
9+
Name is required
10+
</mat-error>
11+
</mat-form-field>
12+
<mat-form-field class="full-width">
13+
<mat-label>Description</mat-label>
14+
<textarea matInput formControlName="description"></textarea>
15+
</mat-form-field>
16+
</form>
17+
</div>
18+
<div mat-dialog-actions>
19+
<button mat-button (click)="cancel()">Cancel</button>
20+
<button mat-button color="primary" (click)="save()" [disabled]="teamForm.invalid">Save</button>
21+
</div>
22+
</div>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.full-width {
2+
width: 100%;
3+
}
4+
5+
.admin-team-create {
6+
display: flex;
7+
flex-direction: column;
8+
height: 100%;
9+
justify-content: space-around;
10+
}
11+
12+
.mat-dialog-actions {
13+
display: flex;
14+
justify-content: flex-end;
15+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Component, Inject } from '@angular/core';
2+
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
3+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
4+
import { TeamsService } from '../teams-service';
5+
import { Team } from '../team';
6+
7+
@Component({
8+
selector: 'mage-admin-team-create',
9+
templateUrl: './admin-team-create.component.html',
10+
styleUrls: ['./admin-team-create.component.scss']
11+
})
12+
export class AdminTeamCreateComponent {
13+
teamForm: FormGroup;
14+
15+
constructor(
16+
public dialogRef: MatDialogRef<AdminTeamCreateComponent>,
17+
@Inject(MAT_DIALOG_DATA) public data: { team: Partial<Team> },
18+
private fb: FormBuilder,
19+
private teamsService: TeamsService
20+
) {
21+
this.teamForm = this.fb.group({
22+
name: [data.team.name || '', [Validators.required]],
23+
description: [data.team.description || '']
24+
});
25+
}
26+
27+
save(): void {
28+
if (this.teamForm.invalid) {
29+
return;
30+
}
31+
32+
const teamData = this.teamForm.value;
33+
this.teamsService.createTeam(teamData).subscribe(newTeam => {
34+
this.dialogRef.close(newTeam);
35+
});
36+
}
37+
38+
cancel(): void {
39+
this.dialogRef.close();
40+
}
41+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { NgModule } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { RouterModule, Routes } from '@angular/router';
4+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5+
import { MatTableModule } from '@angular/material/table';
6+
import { MatPaginatorModule } from '@angular/material/paginator';
7+
import { MatSortModule } from '@angular/material/sort';
8+
import { MatDialogModule } from '@angular/material/dialog';
9+
import { MatFormFieldModule } from '@angular/material/form-field';
10+
import { MatInputModule } from '@angular/material/input';
11+
import { MatButtonModule } from '@angular/material/button';
12+
13+
import { AdminTeamsComponent } from './admin-dashboard/admin-teams.component';
14+
import { AdminTeamCreateComponent } from './admin-team-create/admin-team-create.component';
15+
import { TeamsService } from './teams-service';
16+
17+
const routes: Routes = [
18+
{
19+
path: '',
20+
component: AdminTeamsComponent
21+
}
22+
];
23+
24+
@NgModule({
25+
declarations: [
26+
AdminTeamsComponent,
27+
AdminTeamCreateComponent
28+
],
29+
imports: [
30+
CommonModule,
31+
FormsModule,
32+
ReactiveFormsModule,
33+
MatTableModule,
34+
MatPaginatorModule,
35+
MatSortModule,
36+
MatDialogModule,
37+
MatFormFieldModule,
38+
MatInputModule,
39+
MatButtonModule,
40+
RouterModule.forChild(routes)
41+
],
42+
providers: [
43+
TeamsService
44+
],
45+
entryComponents: [
46+
AdminTeamCreateComponent
47+
]
48+
})
49+
export class AdminTeamsModule { }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ObjectId } from 'mongodb';
2+
3+
export interface Team {
4+
id: ObjectId;
5+
name: string;
6+
description: string;
7+
teamEventId: number;
8+
userIds: ObjectId[];
9+
acl: Records<ObjectId, string>;
10+
permissions: string[];
11+
}

0 commit comments

Comments
 (0)