Spaces:
Building
Building
Update flare-ui/src/app/components/projects/projects.component.html
Browse files
flare-ui/src/app/components/projects/projects.component.html
CHANGED
@@ -1,126 +1,179 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
</button>
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
>
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
</button>
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
-
|
38 |
-
<
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
<p>No projects found.</p>
|
44 |
-
<button class="btn btn-primary" (click)="createProject()">
|
45 |
-
Create your first project
|
46 |
-
</button>
|
47 |
-
</div>
|
48 |
-
} @else {
|
49 |
-
@if (viewMode === 'card') {
|
50 |
-
<div class="project-cards">
|
51 |
-
@for (project of filteredProjects; track project.id) {
|
52 |
-
<div class="project-card" [class.disabled]="!project.enabled" [class.deleted]="project.deleted">
|
53 |
-
<div class="project-icon">🛩️</div>
|
54 |
-
<h3>{{ project.name }}</h3>
|
55 |
-
<p>{{ project.caption || 'No description' }}</p>
|
56 |
-
<div class="project-meta">
|
57 |
-
<span>Versions: {{ project.versions.length || 0 }} ({{ getPublishedCount(project) }} published)</span>
|
58 |
-
<span>Status: {{ project.enabled ? '✓ Enabled' : '✗ Disabled' }}</span>
|
59 |
-
<span>Last update: {{ getRelativeTime(project.last_update_date) }}</span>
|
60 |
-
</div>
|
61 |
-
<div class="project-actions">
|
62 |
-
<button class="btn btn-secondary" (click)="editProject(project)">Edit</button>
|
63 |
-
<button class="btn btn-secondary" (click)="manageVersions(project)">Versions</button>
|
64 |
-
<button class="btn btn-secondary" (click)="exportProject(project)">Export</button>
|
65 |
-
<button class="btn btn-secondary" (click)="toggleProject(project)">
|
66 |
-
{{ project.enabled ? 'Disable' : 'Enable' }}
|
67 |
-
</button>
|
68 |
-
</div>
|
69 |
-
</div>
|
70 |
-
}
|
71 |
-
</div>
|
72 |
-
} @else {
|
73 |
-
<table class="table">
|
74 |
-
<thead>
|
75 |
-
<tr>
|
76 |
-
<th>Name</th>
|
77 |
-
<th>Caption</th>
|
78 |
-
<th>Versions</th>
|
79 |
-
<th>Enabled</th>
|
80 |
-
<th>Deleted</th>
|
81 |
-
<th>Last Update</th>
|
82 |
-
<th>Actions</th>
|
83 |
-
</tr>
|
84 |
-
</thead>
|
85 |
-
<tbody>
|
86 |
-
@for (project of filteredProjects; track project.id) {
|
87 |
-
<tr [class.deleted]="project.deleted">
|
88 |
-
<td>{{ project.name }}</td>
|
89 |
-
<td>{{ project.caption || '-' }}</td>
|
90 |
-
<td>{{ project.versions.length || 0 }} ({{ getPublishedCount(project) }} published)</td>
|
91 |
-
<td>
|
92 |
-
@if (project.enabled) {
|
93 |
-
<span class="status-badge enabled">✓</span>
|
94 |
-
} @else {
|
95 |
-
<span class="status-badge">✗</span>
|
96 |
-
}
|
97 |
-
</td>
|
98 |
-
<td>
|
99 |
-
@if (project.deleted) {
|
100 |
-
<span class="status-badge deleted">✓</span>
|
101 |
-
} @else {
|
102 |
-
<span class="status-badge">✗</span>
|
103 |
-
}
|
104 |
-
</td>
|
105 |
-
<td>{{ getRelativeTime(project.last_update_date) }}</td>
|
106 |
-
<td class="actions">
|
107 |
-
<button class="action-btn" title="Edit" (click)="editProject(project)">🖊️</button>
|
108 |
-
<button class="action-btn" title="Versions" (click)="manageVersions(project)">📋</button>
|
109 |
-
<button class="action-btn" title="Export" (click)="exportProject(project)">📤</button>
|
110 |
-
@if (!project.deleted) {
|
111 |
-
<button class="action-btn danger" title="Delete" (click)="deleteProject(project)">🗑️</button>
|
112 |
-
}
|
113 |
-
</td>
|
114 |
-
</tr>
|
115 |
-
}
|
116 |
-
</tbody>
|
117 |
-
</table>
|
118 |
-
}
|
119 |
-
}
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="projects-container">
|
2 |
+
<div class="toolbar">
|
3 |
+
<div class="toolbar-left">
|
4 |
+
<h2>Projects</h2>
|
5 |
+
</div>
|
6 |
+
<div class="toolbar-right">
|
7 |
+
<button class="btn btn-primary" (click)="createProject()">
|
8 |
+
New Project
|
9 |
+
</button>
|
10 |
+
<button class="btn btn-secondary" (click)="importProject()">
|
11 |
+
Import Project
|
12 |
+
</button>
|
13 |
+
<mat-form-field class="search-field" appearance="outline">
|
14 |
+
<mat-label>Search projects</mat-label>
|
15 |
+
<input matInput [(ngModel)]="searchTerm" (input)="filterProjects()" placeholder="Search...">
|
16 |
+
<mat-icon matSuffix>search</mat-icon>
|
17 |
+
</mat-form-field>
|
18 |
+
<mat-checkbox [(ngModel)]="showDeleted" (change)="loadProjects()">
|
19 |
+
Display Deleted
|
20 |
+
</mat-checkbox>
|
21 |
+
<mat-button-toggle-group class="view-toggle" [(ngModel)]="viewMode">
|
22 |
+
<mat-button-toggle value="card">
|
23 |
+
<mat-icon>view_module</mat-icon>
|
24 |
+
</mat-button-toggle>
|
25 |
+
<mat-button-toggle value="list">
|
26 |
+
<mat-icon>view_list</mat-icon>
|
27 |
+
</mat-button-toggle>
|
28 |
+
</mat-button-toggle-group>
|
29 |
+
</div>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<mat-progress-bar *ngIf="loading" mode="indeterminate"></mat-progress-bar>
|
33 |
+
|
34 |
+
<div class="content" *ngIf="!loading">
|
35 |
+
<!-- Empty State -->
|
36 |
+
<div class="no-data" *ngIf="filteredProjects.length === 0">
|
37 |
+
<mat-icon>folder_open</mat-icon>
|
38 |
+
<p>No projects found.</p>
|
39 |
+
<button mat-raised-button color="primary" (click)="createProject()">
|
40 |
+
Create your first project
|
41 |
+
</button>
|
42 |
+
</div>
|
43 |
+
|
44 |
+
<!-- Card View -->
|
45 |
+
<div class="projects-grid" *ngIf="viewMode === 'card' && filteredProjects.length > 0">
|
46 |
+
<mat-card *ngFor="let project of filteredProjects; trackBy: trackByProjectId"
|
47 |
+
class="project-card"
|
48 |
+
[class.disabled]="!project.enabled"
|
49 |
+
[class.deleted]="project.deleted">
|
50 |
+
<mat-card-header>
|
51 |
+
<div mat-card-avatar class="project-icon">
|
52 |
+
<mat-icon>flight_takeoff</mat-icon>
|
53 |
+
</div>
|
54 |
+
<mat-card-title>{{ project.name }}</mat-card-title>
|
55 |
+
<mat-card-subtitle>{{ project.caption || 'No description' }}</mat-card-subtitle>
|
56 |
+
</mat-card-header>
|
57 |
+
|
58 |
+
<mat-card-content>
|
59 |
+
<div class="project-info">
|
60 |
+
<div class="info-item">
|
61 |
+
<mat-icon>layers</mat-icon>
|
62 |
+
<span>{{ project.versions?.length || 0 }} versions ({{ getPublishedCount(project) }} published)</span>
|
63 |
+
</div>
|
64 |
+
<div class="info-item">
|
65 |
+
<mat-icon>{{ project.enabled ? 'check_circle' : 'cancel' }}</mat-icon>
|
66 |
+
<span>{{ project.enabled ? 'Enabled' : 'Disabled' }}</span>
|
67 |
+
</div>
|
68 |
+
<div class="info-item">
|
69 |
+
<mat-icon>update</mat-icon>
|
70 |
+
<span>{{ getRelativeTime(project.last_update_date) }}</span>
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
</mat-card-content>
|
74 |
+
|
75 |
+
<mat-card-actions>
|
76 |
+
<button mat-button (click)="editProject(project)">EDIT</button>
|
77 |
+
<button mat-button (click)="manageVersions(project)">VERSIONS</button>
|
78 |
+
<button mat-button (click)="exportProject(project)">EXPORT</button>
|
79 |
+
<button mat-button (click)="toggleProject(project)" color="warn">
|
80 |
+
{{ project.enabled ? 'DISABLE' : 'ENABLE' }}
|
81 |
</button>
|
82 |
+
</mat-card-actions>
|
83 |
+
</mat-card>
|
84 |
+
</div>
|
85 |
+
|
86 |
+
<!-- Table View -->
|
87 |
+
<div class="table-container" *ngIf="viewMode === 'list' && filteredProjects.length > 0">
|
88 |
+
<table mat-table [dataSource]="filteredProjects" class="projects-table">
|
89 |
+
<!-- Name Column -->
|
90 |
+
<ng-container matColumnDef="name">
|
91 |
+
<th mat-header-cell *matHeaderCellDef>Name</th>
|
92 |
+
<td mat-cell *matCellDef="let project">
|
93 |
+
{{ project.name }}
|
94 |
+
<mat-icon class="deleted-icon" *ngIf="project.deleted">delete</mat-icon>
|
95 |
+
</td>
|
96 |
+
</ng-container>
|
97 |
+
|
98 |
+
<!-- Caption Column -->
|
99 |
+
<ng-container matColumnDef="caption">
|
100 |
+
<th mat-header-cell *matHeaderCellDef>Caption</th>
|
101 |
+
<td mat-cell *matCellDef="let project">{{ project.caption || '-' }}</td>
|
102 |
+
</ng-container>
|
103 |
+
|
104 |
+
<!-- Versions Column -->
|
105 |
+
<ng-container matColumnDef="versions">
|
106 |
+
<th mat-header-cell *matHeaderCellDef>Versions</th>
|
107 |
+
<td mat-cell *matCellDef="let project">
|
108 |
+
<mat-chip-list>
|
109 |
+
<mat-chip>{{ project.versions?.length || 0 }} total</mat-chip>
|
110 |
+
<mat-chip color="primary" selected>{{ getPublishedCount(project) }} published</mat-chip>
|
111 |
+
</mat-chip-list>
|
112 |
+
</td>
|
113 |
+
</ng-container>
|
114 |
+
|
115 |
+
<!-- Status Column -->
|
116 |
+
<ng-container matColumnDef="status">
|
117 |
+
<th mat-header-cell *matHeaderCellDef>Status</th>
|
118 |
+
<td mat-cell *matCellDef="let project">
|
119 |
+
<mat-icon *ngIf="project.enabled" color="primary">check_circle</mat-icon>
|
120 |
+
<mat-icon *ngIf="!project.enabled" color="warn">cancel</mat-icon>
|
121 |
+
</td>
|
122 |
+
</ng-container>
|
123 |
+
|
124 |
+
<!-- Last Update Column -->
|
125 |
+
<ng-container matColumnDef="lastUpdate">
|
126 |
+
<th mat-header-cell *matHeaderCellDef>Last Update</th>
|
127 |
+
<td mat-cell *matCellDef="let project">{{ getRelativeTime(project.last_update_date) }}</td>
|
128 |
+
</ng-container>
|
129 |
+
|
130 |
+
<!-- Actions Column -->
|
131 |
+
<ng-container matColumnDef="actions">
|
132 |
+
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
133 |
+
<td mat-cell *matCellDef="let project">
|
134 |
+
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Project actions">
|
135 |
+
<mat-icon>more_vert</mat-icon>
|
136 |
</button>
|
137 |
+
<mat-menu #menu="matMenu">
|
138 |
+
<button mat-menu-item (click)="editProject(project)">
|
139 |
+
<mat-icon>edit</mat-icon>
|
140 |
+
<span>Edit</span>
|
141 |
+
</button>
|
142 |
+
<button mat-menu-item (click)="manageVersions(project)">
|
143 |
+
<mat-icon>layers</mat-icon>
|
144 |
+
<span>Manage Versions</span>
|
145 |
+
</button>
|
146 |
+
<button mat-menu-item (click)="exportProject(project)">
|
147 |
+
<mat-icon>download</mat-icon>
|
148 |
+
<span>Export</span>
|
149 |
+
</button>
|
150 |
+
<button mat-menu-item (click)="toggleProject(project)">
|
151 |
+
<mat-icon>{{ project.enabled ? 'block' : 'check_circle' }}</mat-icon>
|
152 |
+
<span>{{ project.enabled ? 'Disable' : 'Enable' }}</span>
|
153 |
+
</button>
|
154 |
+
<mat-divider *ngIf="!project.deleted"></mat-divider>
|
155 |
+
<button mat-menu-item (click)="deleteProject(project)" *ngIf="!project.deleted">
|
156 |
+
<mat-icon color="warn">delete</mat-icon>
|
157 |
+
<span>Delete</span>
|
158 |
+
</button>
|
159 |
+
</mat-menu>
|
160 |
+
</td>
|
161 |
+
</ng-container>
|
162 |
|
163 |
+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
164 |
+
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
165 |
+
[class.deleted-row]="row.deleted"></tr>
|
166 |
+
</table>
|
167 |
+
</div>
|
168 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
|
170 |
+
<!-- Message Snackbar -->
|
171 |
+
<div class="message-container" *ngIf="message">
|
172 |
+
<mat-card [class.success]="!isError" [class.error]="isError">
|
173 |
+
<mat-card-content>
|
174 |
+
<mat-icon>{{ isError ? 'error' : 'check_circle' }}</mat-icon>
|
175 |
+
{{ message }}
|
176 |
+
</mat-card-content>
|
177 |
+
</mat-card>
|
178 |
+
</div>
|
179 |
+
</div>
|