A production-ready admin dashboard skeleton — the kind of UI you'd build for a CMS, analytics tool, or back-office application.
ng new admin-dashboard --routing --style=scss
ng add @angular/material
npm install ng2-charts chart.js
<!-- shell.component.html -->
<mat-sidenav-container>
<mat-sidenav #sidenav [mode]="isHandset ? 'over' : 'side'"
[opened]="!isHandset">
<mat-nav-list>
@for (item of navItems; track item.label) {
<mat-list-item [routerLink]="item.path" routerLinkActive="active">
<mat-icon matListItemIcon>{{ item.icon }}</mat-icon>
<span matListItemTitle>{{ item.label }}</span>
</mat-list-item>
}
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button mat-icon-button (click)="sidenav.toggle()">
<mat-icon>menu</mat-icon>
</button>
<span>Admin Dashboard</span>
<span class="spacer"></span>
<button mat-icon-button (click)="toggleTheme()">
<mat-icon>{{ isDark() ? 'light_mode' : 'dark_mode' }}</mat-icon>
</button>
</mat-toolbar>
<router-outlet />
</mat-sidenav-content>
</mat-sidenav-container>
@Component({
standalone: true,
imports: [MatTableModule, MatSortModule, MatPaginatorModule, MatInputModule],
template: `
<mat-form-field>
<input matInput (keyup)="applyFilter($event)" placeholder="Search" />
</mat-form-field>
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let row">{{ row.name }}</td>
</ng-container>
<!-- more columns... -->
<tr mat-header-row *matHeaderRowDef="columns"></tr>
<tr mat-row *matRowDef="let row; columns: columns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 25, 50]" />
`,
})
export class UsersTableComponent implements AfterViewInit {
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatPaginator) paginator!: MatPaginator;
columns = ['name', 'email', 'role', 'actions'];
dataSource = new MatTableDataSource<User>();
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
applyFilter(event: Event): void {
this.dataSource.filter = (event.target as HTMLInputElement).value.trim().toLowerCase();
}
}
All Comments