|
1 | 1 | <div class="app-container"> |
2 | 2 | <!-- Header --> |
3 | | - <div class="header"> |
4 | | - <div class="header-left"> |
5 | | - <span class="logo">🔒</span> |
6 | | - <span class="title">GitHub Alerts</span> |
7 | | - </div> |
8 | | - <div class="header-right" *ngIf="authenticated"> |
9 | | - <button |
10 | | - class="icon-btn" |
11 | | - (click)="fetchAlerts()" |
12 | | - [disabled]="alertsLoading" |
13 | | - title="Refresh" |
14 | | - > |
15 | | - <span [class.spinning]="alertsLoading">🔄</span> |
16 | | - </button> |
17 | | - <button |
18 | | - class="icon-btn" |
19 | | - [class.active]="currentView === 'repos'" |
20 | | - (click)="showRepos()" |
21 | | - title="Repositories" |
22 | | - > |
23 | | - 📦 |
24 | | - </button> |
25 | | - <button |
26 | | - class="icon-btn" |
27 | | - [class.active]="currentView === 'settings'" |
28 | | - (click)="showSettings()" |
29 | | - title="Settings" |
30 | | - > |
31 | | - ⚙️ |
32 | | - </button> |
33 | | - </div> |
34 | | - </div> |
| 3 | + <app-header |
| 4 | + [authenticated]="authenticated" |
| 5 | + [alertsLoading]="alertsLoading" |
| 6 | + [currentView]="currentView" |
| 7 | + (refresh)="fetchAlerts()" |
| 8 | + (viewChange)="onViewChange($event)" |
| 9 | + ></app-header> |
35 | 10 |
|
36 | 11 | <!-- Not Authenticated --> |
37 | | - <div class="login-panel" *ngIf="!authenticated"> |
38 | | - <div class="login-content"> |
39 | | - <div class="login-icon">🔐</div> |
40 | | - <h2>Welcome to GitHub Security Alerts</h2> |
41 | | - <p>Enter your GitHub Personal Access Token to monitor security alerts.</p> |
42 | | - <p class="hint"> |
43 | | - You need a GitHub Personal Access Token with <strong>repo</strong> and |
44 | | - <strong>read:org</strong> scopes |
45 | | - </p> |
46 | | - <button |
47 | | - class="token-btn" |
48 | | - (click)="openCreateTokenPage()" |
49 | | - title="Open GitHub token creation page" |
50 | | - > |
51 | | - 🔑 Create Token on GitHub |
52 | | - </button> |
53 | | - <div class="token-input-group"> |
54 | | - <input |
55 | | - type="password" |
56 | | - [(ngModel)]="tokenInput" |
57 | | - placeholder="ghp_xxxxxxxxxxxx" |
58 | | - (keyup.enter)="login()" |
59 | | - [disabled]="authLoading" |
60 | | - /> |
61 | | - <button |
62 | | - class="primary-btn" |
63 | | - (click)="login()" |
64 | | - [disabled]="authLoading || !tokenInput.trim()" |
65 | | - > |
66 | | - <span *ngIf="!authLoading">Connect</span> |
67 | | - <span *ngIf="authLoading">⏳ Connecting...</span> |
68 | | - </button> |
69 | | - </div> |
70 | | - <p class="error-text" *ngIf="error">{{ error }}</p> |
71 | | - </div> |
72 | | - </div> |
| 12 | + <app-login-panel |
| 13 | + *ngIf="!authenticated" |
| 14 | + [authLoading]="authLoading" |
| 15 | + [error]="error" |
| 16 | + (login)="onLogin($event)" |
| 17 | + (openTokenPage)="openCreateTokenPage()" |
| 18 | + ></app-login-panel> |
73 | 19 |
|
74 | 20 | <!-- Settings View --> |
75 | | - <div |
76 | | - class="settings-panel" |
| 21 | + <app-settings-panel |
77 | 22 | *ngIf="authenticated && currentView === 'settings'" |
78 | | - > |
79 | | - <div class="settings-content"> |
80 | | - <div class="user-info"> |
81 | | - <span class="user-icon">👤</span> |
82 | | - <span class="username">{{ username }}</span> |
83 | | - </div> |
84 | | - <div class="settings-actions"> |
85 | | - <button class="danger-btn" (click)="logout()">🚪 Sign Out</button> |
86 | | - </div> |
87 | | - </div> |
88 | | - </div> |
| 23 | + [username]="username" |
| 24 | + (logout)="logout()" |
| 25 | + ></app-settings-panel> |
89 | 26 |
|
90 | 27 | <!-- Repos Selection View --> |
91 | | - <div class="repos-panel" *ngIf="authenticated && currentView === 'repos'"> |
92 | | - <div class="repos-header"> |
93 | | - <h3>Select Repositories to Monitor</h3> |
94 | | - </div> |
95 | | - |
96 | | - <div class="search-box"> |
97 | | - <input |
98 | | - type="text" |
99 | | - [(ngModel)]="searchQuery" |
100 | | - placeholder="🔍 Search repositories..." |
101 | | - /> |
102 | | - </div> |
103 | | - |
104 | | - <div class="repos-count">{{ selectedCount }} repositories selected</div> |
105 | | - |
106 | | - <div class="loading-spinner" *ngIf="ownersLoading"> |
107 | | - <span class="spinning">🌀</span> Loading... |
108 | | - </div> |
109 | | - |
110 | | - <div class="owners-list" *ngIf="!ownersLoading"> |
111 | | - <div class="owner-accordion" *ngFor="let ownerAcc of owners"> |
112 | | - <!-- Accordion Header --> |
113 | | - <div |
114 | | - class="accordion-header" |
115 | | - (click)="toggleOwner(ownerAcc)" |
116 | | - [class.expanded]="ownerAcc.expanded" |
117 | | - > |
118 | | - <span class="accordion-icon">{{ |
119 | | - ownerAcc.expanded ? "▼" : "▶" |
120 | | - }}</span> |
121 | | - <span class="owner-icon">{{ |
122 | | - ownerAcc.owner.is_user ? "👤" : "🏢" |
123 | | - }}</span> |
124 | | - <span class="owner-name">{{ ownerAcc.owner.name }}</span> |
125 | | - <span class="owner-badge" *ngIf="ownerAcc.loaded"> |
126 | | - {{ getSelectedCountForOwner(ownerAcc) }}/{{ ownerAcc.repos.length }} |
127 | | - </span> |
128 | | - <span class="owner-badge loading" *ngIf="ownerAcc.loading"> |
129 | | - <span class="spinning">⏳</span> |
130 | | - </span> |
131 | | - </div> |
132 | | - |
133 | | - <!-- Accordion Content --> |
134 | | - <div class="accordion-content" *ngIf="ownerAcc.expanded"> |
135 | | - <div |
136 | | - class="accordion-actions" |
137 | | - *ngIf="ownerAcc.loaded && ownerAcc.repos.length > 0" |
138 | | - > |
139 | | - <button |
140 | | - class="small-btn" |
141 | | - (click)="selectAllForOwner(ownerAcc); $event.stopPropagation()" |
142 | | - > |
143 | | - Select All |
144 | | - </button> |
145 | | - <button |
146 | | - class="small-btn" |
147 | | - (click)="selectNoneForOwner(ownerAcc); $event.stopPropagation()" |
148 | | - > |
149 | | - Select None |
150 | | - </button> |
151 | | - </div> |
152 | | - |
153 | | - <div class="loading-spinner small" *ngIf="ownerAcc.loading"> |
154 | | - <span class="spinning">🔄</span> Loading repos... |
155 | | - </div> |
156 | | - |
157 | | - <div class="repos-list" *ngIf="ownerAcc.loaded"> |
158 | | - <div |
159 | | - class="empty-repos" |
160 | | - *ngIf="getFilteredRepos(ownerAcc).length === 0" |
161 | | - > |
162 | | - No repositories found |
163 | | - </div> |
164 | | - <div |
165 | | - class="repo-select-item" |
166 | | - *ngFor="let repo of getFilteredRepos(ownerAcc)" |
167 | | - (click)="toggleRepo(repo); $event.stopPropagation()" |
168 | | - [class.selected]="repo.selected" |
169 | | - > |
170 | | - <div class="checkbox"> |
171 | | - <span *ngIf="repo.selected">✓</span> |
172 | | - </div> |
173 | | - <div class="repo-details"> |
174 | | - <span class="repo-name">{{ repo.name }}</span> |
175 | | - </div> |
176 | | - </div> |
177 | | - </div> |
178 | | - </div> |
179 | | - </div> |
180 | | - </div> |
181 | | - |
182 | | - <button class="primary-btn" (click)="showAlerts()"> |
183 | | - ✓ Done - View Alerts |
184 | | - </button> |
185 | | - </div> |
186 | | - |
187 | | - <!-- Error Message --> |
188 | | - <div |
189 | | - class="error-banner" |
190 | | - *ngIf="error && authenticated && currentView === 'alerts'" |
191 | | - > |
192 | | - {{ error }} |
193 | | - </div> |
| 28 | + <app-repos-panel |
| 29 | + *ngIf="authenticated && currentView === 'repos'" |
| 30 | + [owners]="owners" |
| 31 | + [ownersLoading]="ownersLoading" |
| 32 | + [searchQuery]="searchQuery" |
| 33 | + (searchQueryChange)="searchQuery = $event" |
| 34 | + (toggleOwner)="toggleOwner($event)" |
| 35 | + (toggleRepo)="toggleRepo($event)" |
| 36 | + (selectAllForOwner)="selectAllForOwner($event)" |
| 37 | + (selectNoneForOwner)="selectNoneForOwner($event)" |
| 38 | + (done)="showAlerts()" |
| 39 | + ></app-repos-panel> |
194 | 40 |
|
195 | 41 | <!-- Alerts List View --> |
196 | | - <div class="alerts-list" *ngIf="authenticated && currentView === 'alerts'"> |
197 | | - <!-- Loading spinner --> |
198 | | - <div class="loading-spinner" *ngIf="alertsLoading"> |
199 | | - <span class="spinning">🔄</span> Loading alerts... |
200 | | - </div> |
201 | | - |
202 | | - <!-- No repos selected --> |
203 | | - <div class="empty-state" *ngIf="!alertsLoading && (!alerts || alerts.repos.length === 0)"> |
204 | | - <div class="empty-icon">📦</div> |
205 | | - <p>No repositories selected</p> |
206 | | - <button class="primary-btn" (click)="showRepos()"> |
207 | | - Select Repositories |
208 | | - </button> |
209 | | - </div> |
210 | | - |
211 | | - <!-- Summary --> |
212 | | - <div |
213 | | - class="summary" |
214 | | - *ngIf="alerts && alerts.repos.length > 0" |
215 | | - [class.safe]="alerts.total_alerts === 0" |
216 | | - [class.danger]="alerts.total_alerts > 0" |
217 | | - > |
218 | | - <span class="summary-icon">{{ getAlertIcon() }}</span> |
219 | | - <span class="summary-text" *ngIf="alerts.total_alerts === 0" |
220 | | - >All repositories are secure</span |
221 | | - > |
222 | | - <span class="summary-text" *ngIf="alerts.total_alerts > 0" |
223 | | - >{{ alerts.total_alerts }} security alert(s) found</span |
224 | | - > |
225 | | - </div> |
226 | | - |
227 | | - <!-- Repository List --> |
228 | | - <div class="repo-list" *ngIf="alerts && alerts.repos.length > 0"> |
229 | | - <div |
230 | | - class="repo-item" |
231 | | - *ngFor="let repo of alerts.repos" |
232 | | - [class.has-alerts]="repo.alerts > 0" |
233 | | - > |
234 | | - <div class="repo-info"> |
235 | | - <span class="repo-icon">📦</span> |
236 | | - <span class="repo-name">{{ repo.name.split("/")[1] }}</span> |
237 | | - <span class="repo-org">{{ repo.name.split("/")[0] }}</span> |
238 | | - </div> |
239 | | - <div |
240 | | - class="repo-badge" |
241 | | - [class.safe]="repo.alerts === 0" |
242 | | - [class.danger]="repo.alerts > 0" |
243 | | - > |
244 | | - {{ repo.alerts === 0 ? "✓" : repo.alerts }} |
245 | | - </div> |
246 | | - </div> |
247 | | - </div> |
248 | | - </div> |
| 42 | + <app-alerts-list |
| 43 | + *ngIf="authenticated && currentView === 'alerts'" |
| 44 | + [alerts]="alerts" |
| 45 | + [alertsLoading]="alertsLoading" |
| 46 | + [error]="error" |
| 47 | + (showRepos)="showRepos()" |
| 48 | + ></app-alerts-list> |
249 | 49 |
|
250 | 50 | <!-- Footer --> |
251 | | - <div class="footer"> |
252 | | - <span class="update-time" *ngIf="alerts && authenticated" |
253 | | - >Last update: {{ getLastUpdate() }}</span |
254 | | - > |
255 | | - <span class="version">v1.0.0</span> |
256 | | - </div> |
| 51 | + <app-footer |
| 52 | + [alerts]="alerts" |
| 53 | + [authenticated]="authenticated" |
| 54 | + ></app-footer> |
257 | 55 | </div> |
0 commit comments