Skip to content

Commit 9a5a118

Browse files
committed
move navbar component into general component
1 parent a161f58 commit 9a5a118

11 files changed

+526
-100
lines changed

web-app/admin/src/app/admin/admin-teams/admin-teams.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { MatButtonModule } from '@angular/material/button';
1313
import { TeamDashboardComponent } from './dashboard/team-dashboard.component';
1414
import { CreateTeamDialogComponent } from './create-team/create-team.component';
1515
import { TeamsService } from './teams-service';
16+
import { CoreModule } from '../../core/core.module';
1617

1718
const routes: Routes = [
1819
{
@@ -29,6 +30,7 @@ const routes: Routes = [
2930
imports: [
3031
CommonModule,
3132
FormsModule,
33+
CoreModule,
3234
ReactiveFormsModule,
3335
MatTableModule,
3436
MatPaginatorModule,

web-app/admin/src/app/admin/admin-teams/dashboard/team-dashboard.component.html

Lines changed: 23 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,31 @@
77
</div>
88

99
<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>
10+
<mage-card-navbar [title]="'Teams'" [isSearchable]="true" [searchPlaceholder]="'Search teams...'"
11+
[actionButtons]="actionButtons" (searchTermChanged)="onSearchTermChanged($event)"
12+
(searchCleared)="onSearchCleared()"></mage-card-navbar>
2013

21-
<button class="btn btn-default pull-right" (click)="reset()">Reset</button>
22-
</div>
14+
<div class="col-md-12 mat-elevation-z1 w-100">
15+
<table mat-table [class.mat-table]="true" [dataSource]="dataSource" class="mat-table-even-columns">
16+
<ng-container matColumnDef="name">
17+
<th mat-header-cell *matHeaderCellDef>Name</th>
18+
<td mat-cell *matCellDef="let team">
19+
<div class="strong">{{ team.name }}</div>
20+
</td>
21+
</ng-container>
2322

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>
23+
<ng-container matColumnDef="description">
24+
<th mat-header-cell *matHeaderCellDef>Description</th>
25+
<td mat-cell *matCellDef="let team">
26+
<div class="muted description-cell" [title]="team.description">{{ team.description }}</div>
27+
</td>
28+
</ng-container>
3029

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="col-md-12 mat-elevation-z1 w-100">
38-
<table mat-table [class.mat-table]="true" [dataSource]="dataSource" class="mat-table-even-columns">
39-
<ng-container matColumnDef="name">
40-
<th mat-header-cell *matHeaderCellDef>Name</th>
41-
<td mat-cell *matCellDef="let team">
42-
<div class="strong">{{ team.name }}</div>
43-
</td>
44-
</ng-container>
45-
46-
<ng-container matColumnDef="description">
47-
<th mat-header-cell *matHeaderCellDef>Description</th>
48-
<td mat-cell *matCellDef="let team">
49-
<div class="muted description-cell" [title]="team.description">{{ team.description }}</div>
50-
</td>
51-
</ng-container>
52-
53-
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
54-
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)"
55-
class="pointer"></tr>
56-
</table>
57-
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
58-
(page)="onPageChange($event)">
59-
</mat-paginator>
60-
</div>
61-
</div>
30+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
31+
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)" class="pointer"></tr>
32+
</table>
33+
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
34+
(page)="onPageChange($event)">
35+
</mat-paginator>
6236
</div>
6337
</div>

web-app/admin/src/app/admin/admin-teams/dashboard/team-dashboard.component.scss

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
overflow: hidden;
3232
text-overflow: ellipsis;
3333
white-space: normal;
34-
line-height: 1.2;
35-
max-height: calc(3 * 1.2em);
34+
line-height: 1.1em;
35+
height: calc(3 * 1.1em);
36+
align-content: center;
3637
}
3738
}
3839

web-app/admin/src/app/admin/admin-teams/dashboard/team-dashboard.component.spec.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,31 +76,11 @@ describe('TeamDashboardComponent', () => {
7676
expect(component.dataSource.data).toEqual(mockTeams);
7777
});
7878

79-
it('should perform search with debouncing', fakeAsync(() => {
80-
fixture.detectChanges();
81-
component.search('test');
82-
tick(100); // Less than debounce time
83-
expect(mockTeamsService.getTeams).not.toHaveBeenCalledWith({
84-
term: 'test',
85-
sort: { name: 1 },
86-
limit: 10,
87-
start: '0'
88-
});
89-
90-
tick(200); // Complete debounce time (250ms total)
91-
expect(mockTeamsService.getTeams).toHaveBeenCalledWith({
92-
term: 'test',
93-
sort: { name: 1 },
94-
limit: 10,
95-
start: '0'
96-
});
97-
}));
98-
9979
it('should reset page index when searching', fakeAsync(() => {
10080
fixture.detectChanges();
10181
component.pageIndex = 2;
10282

103-
component.search('test');
83+
component.onSearchTermChanged('test');
10484
tick(250);
10585

10686
expect(component.pageIndex).toBe(0);
@@ -129,7 +109,7 @@ describe('TeamDashboardComponent', () => {
129109
component.teamSearch = 'test';
130110
component.pageIndex = 2;
131111

132-
component.reset();
112+
component.onSearchCleared();
133113

134114
expect(component.teamSearch).toBe('');
135115
expect(component.pageIndex).toBe(0);

web-app/admin/src/app/admin/admin-teams/dashboard/team-dashboard.component.ts

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { PageEvent } from '@angular/material/paginator';
44
import { MatTableDataSource } from '@angular/material/table';
55
import { Team } from '../team';
66
import { Subject } from 'rxjs';
7-
import { debounceTime, distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
87
import { TeamsService } from '../teams-service';
98
import { CreateTeamDialogComponent } from '../create-team/create-team.component';
9+
import { CardActionButton } from '../../../core/card-navbar/card-navbar.component';
1010

1111
@Component({
1212
selector: 'mage-admin-teams',
@@ -24,7 +24,8 @@ export class TeamDashboardComponent implements OnInit, OnDestroy {
2424
dataSource = new MatTableDataSource<Team>();
2525
displayedColumns = ['name', 'description'];
2626

27-
private search$ = new Subject<string>();
27+
actionButtons: CardActionButton[] = [{ label: 'New Team', type: 'primary', action: () => this.newTeam() }];
28+
2829
private destroy$ = new Subject<void>();
2930

3031
constructor(
@@ -34,28 +35,6 @@ export class TeamDashboardComponent implements OnInit, OnDestroy {
3435

3536
ngOnInit(): void {
3637
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-
});
5938
}
6039

6140
ngOnDestroy(): void {
@@ -85,12 +64,13 @@ export class TeamDashboardComponent implements OnInit, OnDestroy {
8564
this.fetchTeams();
8665
}
8766

88-
search(term: string): void {
67+
onSearchTermChanged(term: string): void {
8968
this.teamSearch = term;
90-
this.search$.next(term);
69+
this.pageIndex = 0; // Reset to first page when searching
70+
this.fetchTeams();
9171
}
9272

93-
reset(): void {
73+
onSearchCleared(): void {
9474
this.teamSearch = '';
9575
this.pageIndex = 0;
9676
this.fetchTeams();

web-app/admin/src/app/app.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ import { AdminEventFormPreviewComponent } from './admin/admin-event/admin-event-
134134
import { AdminEventFormPreviewDialogComponent } from './admin/admin-event/admin-event-form/admin-event-form-preview/admin-event-form-preview-dialog.component';
135135
import { AdminMapComponent } from './admin/admin-map/admin-map.component';
136136
import { AdminTeamsModule } from './admin/admin-teams/admin-teams.module';
137+
import { CardNavbarComponent } from './core/card-navbar/card-navbar.component';
137138

138139
@NgModule({
139140
declarations: [
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<nav class="navbar navbar-default admin-dashboard-navbar">
2+
<div class="container-fluid">
3+
<div class="navbar-header">
4+
<h3 class="navbar-brand">{{ title }}</h3>
5+
</div>
6+
7+
<div class="navbar-form navbar-left search-container" *ngIf="isSearchable">
8+
<input type="text" class="form-control search-input" [placeholder]="searchPlaceholder"
9+
[(ngModel)]="searchTerm" (ngModelChange)="onSearchChange($event)">
10+
<button type="button" class="btn clear-search-btn" (click)="clearSearch()">
11+
<i class="fa fa-times"></i>
12+
</button>
13+
</div>
14+
15+
<form class="navbar-form navbar-right" role="search" *ngIf="actionButtons.length > 0">
16+
<ng-container *ngFor="let button of actionButtons">
17+
<button type="button" class="btn btn-default action-button {{ button.type }}"
18+
(click)="onActionButtonClick(button)" [disabled]="button.disabled">
19+
{{ button.label }}
20+
</button>
21+
</ng-container>
22+
</form>
23+
</div>
24+
</nav>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
.container-fluid {
2+
display: flex;
3+
align-items: center;
4+
justify-content: space-between;
5+
margin: 0;
6+
padding: 0;
7+
}
8+
9+
.navbar-header {
10+
flex-shrink: 0;
11+
align-items: center;
12+
display: flex;
13+
order: 1;
14+
}
15+
16+
.navbar-brand {
17+
margin: 0 !important;
18+
}
19+
20+
.navbar-form.navbar-left {
21+
order: 2;
22+
flex-shrink: 0;
23+
margin-left: 15px;
24+
}
25+
26+
.navbar-form.navbar-right {
27+
order: 3;
28+
flex-shrink: 0;
29+
margin-left: auto;
30+
}
31+
32+
.navbar-form {
33+
margin: 0;
34+
padding: 0;
35+
}
36+
37+
.search-container {
38+
position: relative;
39+
}
40+
41+
.search-input {
42+
padding-right: 35px;
43+
}
44+
45+
.clear-search-btn {
46+
position: absolute;
47+
right: 8px;
48+
top: 50%;
49+
transform: translateY(-50%);
50+
z-index: 10;
51+
border: none;
52+
background: none;
53+
padding: 5px;
54+
cursor: pointer;
55+
}
56+
57+
.action-button {
58+
margin-right: 0.35rem;
59+
}

0 commit comments

Comments
 (0)