Spaces:
Building
Building
<div class="projects-container"> | |
<div class="toolbar"> | |
<div class="toolbar-left"> | |
<h2>Projects</h2> | |
</div> | |
<div class="toolbar-right"> | |
<button mat-raised-button color="primary" (click)="createProject()"> | |
<mat-icon>add</mat-icon> | |
New Project | |
</button> | |
<button mat-button (click)="importProject()"> | |
<mat-icon>upload</mat-icon> | |
Import Project | |
</button> | |
<mat-form-field appearance="outline" class="search-field"> | |
<mat-label>Search projects</mat-label> | |
<input matInput [(ngModel)]="searchTerm" (input)="filterProjects()"> | |
<mat-icon matSuffix>search</mat-icon> | |
</mat-form-field> | |
<mat-checkbox [(ngModel)]="showDeleted" (change)="loadProjects()"> | |
Display Deleted | |
</mat-checkbox> | |
<mat-button-toggle-group [(ngModel)]="viewMode" class="view-toggle"> | |
<mat-button-toggle value="card"> | |
<mat-icon>view_module</mat-icon> | |
</mat-button-toggle> | |
<mat-button-toggle value="list"> | |
<mat-icon>view_list</mat-icon> | |
</mat-button-toggle> | |
</mat-button-toggle-group> | |
</div> | |
</div> | |
<mat-progress-bar *ngIf="loading" mode="indeterminate"></mat-progress-bar> | |
<div class="content" *ngIf="!loading"> | |
<!-- Empty State --> | |
<div class="no-data" *ngIf="filteredProjects.length === 0"> | |
<mat-icon>folder_open</mat-icon> | |
<p>No projects found.</p> | |
<button mat-raised-button color="primary" (click)="createProject()"> | |
Create your first project | |
</button> | |
</div> | |
<!-- Card View --> | |
<div class="projects-grid" *ngIf="viewMode === 'card' && filteredProjects.length > 0"> | |
<mat-card *ngFor="let project of filteredProjects; trackBy: trackByProjectId" | |
class="project-card" | |
[class.disabled]="!project.enabled" | |
[class.deleted]="project.deleted"> | |
<mat-card-header> | |
<div mat-card-avatar class="project-icon"> | |
<mat-icon>{{ project.icon || 'flight_takeoff' }}</mat-icon> | |
</div> | |
<mat-card-title>{{ project.name }}</mat-card-title> | |
<mat-card-subtitle>{{ project.caption || 'No description' }}</mat-card-subtitle> | |
</mat-card-header> | |
<mat-card-content> | |
<div class="project-info"> | |
<div class="info-item"> | |
<mat-icon>layers</mat-icon> | |
<span>{{ project.versions.length || 0 }} versions ({{ getPublishedCount(project) }} published)</span> | |
</div> | |
<div class="info-item"> | |
<mat-icon>{{ project.enabled ? 'check_circle' : 'cancel' }}</mat-icon> | |
<span>{{ project.enabled ? 'Enabled' : 'Disabled' }}</span> | |
</div> | |
<div class="info-item"> | |
<mat-icon>update</mat-icon> | |
<span>{{ getRelativeTime(project.last_update_date) }}</span> | |
</div> | |
</div> | |
</mat-card-content> | |
<mat-card-actions> | |
<button mat-button (click)="editProject(project)">EDIT</button> | |
<button mat-button (click)="manageVersions(project)">VERSIONS</button> | |
<button mat-button (click)="exportProject(project)">EXPORT</button> | |
<button mat-button (click)="toggleProject(project)" color="warn"> | |
{{ project.enabled ? 'DISABLE' : 'ENABLE' }} | |
</button> | |
</mat-card-actions> | |
</mat-card> | |
</div> | |
<!-- Table View --> | |
<div class="table-container" *ngIf="viewMode === 'list' && filteredProjects.length > 0"> | |
<table mat-table [dataSource]="filteredProjects" class="projects-table"> | |
<!-- Name Column --> | |
<ng-container matColumnDef="name"> | |
<th mat-header-cell *matHeaderCellDef>Name</th> | |
<td mat-cell *matCellDef="let project"> | |
<div class="name-with-icon"> | |
<mat-icon class="project-table-icon">{{ project.icon || 'flight_takeoff' }}</mat-icon> | |
{{ project.name }} | |
<mat-icon class="deleted-icon" *ngIf="project.deleted">delete</mat-icon> | |
</div> | |
</td> | |
</ng-container> | |
<!-- Caption Column --> | |
<ng-container matColumnDef="caption"> | |
<th mat-header-cell *matHeaderCellDef>Caption</th> | |
<td mat-cell *matCellDef="let project">{{ project.caption || '-' }}</td> | |
</ng-container> | |
<!-- Versions Column --> | |
<ng-container matColumnDef="versions"> | |
<th mat-header-cell *matHeaderCellDef>Versions</th> | |
<td mat-cell *matCellDef="let project"> | |
<mat-chip-listbox> | |
<mat-chip-option>{{ project.versions?.length || 0 }} total</mat-chip-option> | |
<mat-chip-option selected>{{ getPublishedCount(project) }} published</mat-chip-option> | |
</mat-chip-listbox> | |
</td> | |
</ng-container> | |
<!-- Status Column --> | |
<ng-container matColumnDef="status"> | |
<th mat-header-cell *matHeaderCellDef>Status</th> | |
<td mat-cell *matCellDef="let project"> | |
<mat-icon *ngIf="project.enabled" color="primary">check_circle</mat-icon> | |
<mat-icon *ngIf="!project.enabled" color="warn">cancel</mat-icon> | |
</td> | |
</ng-container> | |
<!-- Last Update Column --> | |
<ng-container matColumnDef="lastUpdate"> | |
<th mat-header-cell *matHeaderCellDef>Last Update</th> | |
<td mat-cell *matCellDef="let project">{{ getRelativeTime(project.last_update_date) }}</td> | |
</ng-container> | |
<!-- Actions Column --> | |
<ng-container matColumnDef="actions"> | |
<th mat-header-cell *matHeaderCellDef>Actions</th> | |
<td mat-cell *matCellDef="let project"> | |
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Project actions"> | |
<mat-icon>more_vert</mat-icon> | |
</button> | |
<mat-menu #menu="matMenu"> | |
<button mat-menu-item (click)="editProject(project)"> | |
<mat-icon>edit</mat-icon> | |
<span>Edit</span> | |
</button> | |
<button mat-menu-item (click)="manageVersions(project)"> | |
<mat-icon>layers</mat-icon> | |
<span>Manage Versions</span> | |
</button> | |
<button mat-menu-item (click)="exportProject(project)"> | |
<mat-icon>download</mat-icon> | |
<span>Export</span> | |
</button> | |
<button mat-menu-item (click)="toggleProject(project)"> | |
<mat-icon>{{ project.enabled ? 'block' : 'check_circle' }}</mat-icon> | |
<span>{{ project.enabled ? 'Disable' : 'Enable' }}</span> | |
</button> | |
<mat-divider *ngIf="!project.deleted"></mat-divider> | |
<button mat-menu-item (click)="deleteProject(project)" *ngIf="!project.deleted"> | |
<mat-icon color="warn">delete</mat-icon> | |
<span>Delete</span> | |
</button> | |
</mat-menu> | |
</td> | |
</ng-container> | |
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |
<tr mat-row *matRowDef="let row; columns: displayedColumns;" | |
[class.deleted-row]="row.deleted"></tr> | |
</table> | |
</div> | |
</div> | |
<!-- Message Snackbar --> | |
<div class="message-container" *ngIf="message"> | |
<mat-card [class.success]="!isError" [class.error]="isError"> | |
<mat-card-content> | |
<mat-icon>{{ isError ? 'error' : 'check_circle' }}</mat-icon> | |
{{ message }} | |
</mat-card-content> | |
</mat-card> | |
</div> | |
</div> |