kamau1 commited on
Commit
2dab624
·
verified ·
1 Parent(s): 32aa8db

Improve dashboard UX: layout, visuals, functionality

Browse files
static/css/dashboard-simple.css CHANGED
@@ -131,6 +131,14 @@
131
  background-color: #f9fafb;
132
  }
133
 
 
 
 
 
 
 
 
 
134
  .upload-content {
135
  display: flex;
136
  flex-direction: column;
@@ -160,18 +168,56 @@
160
 
161
  /* Settings */
162
  .settings-section {
 
 
 
 
 
 
 
 
 
 
 
 
163
  margin-bottom: 20px;
164
  padding-top: 20px;
165
  border-top: 1px solid #e5e7eb;
166
  }
167
 
168
- .settings-section h4 {
169
  margin-bottom: 15px;
170
  color: #374151;
171
  font-size: 1rem;
172
  font-weight: 600;
173
  }
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  .setting-item {
176
  margin-bottom: 15px;
177
  }
@@ -240,6 +286,7 @@
240
 
241
  .btn-full {
242
  width: 100%;
 
243
  }
244
 
245
  /* Image Containers */
@@ -341,3 +388,58 @@
341
  font-weight: 600;
342
  color: #2563eb;
343
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  background-color: #f9fafb;
132
  }
133
 
134
+ .upload-area.has-image {
135
+ border-style: solid;
136
+ border-color: #059669;
137
+ background-color: #f9fafb;
138
+ padding: 0;
139
+ height: 200px;
140
+ }
141
+
142
  .upload-content {
143
  display: flex;
144
  flex-direction: column;
 
168
 
169
  /* Settings */
170
  .settings-section {
171
+ margin-bottom: 25px;
172
+ }
173
+
174
+ .settings-section h4 {
175
+ margin-bottom: 15px;
176
+ color: #374151;
177
+ font-size: 1rem;
178
+ font-weight: 600;
179
+ }
180
+
181
+ /* Model Details Section */
182
+ .model-details-section {
183
  margin-bottom: 20px;
184
  padding-top: 20px;
185
  border-top: 1px solid #e5e7eb;
186
  }
187
 
188
+ .model-details-section h4 {
189
  margin-bottom: 15px;
190
  color: #374151;
191
  font-size: 1rem;
192
  font-weight: 600;
193
  }
194
 
195
+ .model-details-grid {
196
+ display: grid;
197
+ gap: 10px;
198
+ }
199
+
200
+ .detail-item {
201
+ display: flex;
202
+ justify-content: space-between;
203
+ align-items: center;
204
+ padding: 8px 12px;
205
+ background: #f9fafb;
206
+ border-radius: 6px;
207
+ }
208
+
209
+ .detail-label {
210
+ font-size: 0.875rem;
211
+ color: #6b7280;
212
+ font-weight: 500;
213
+ }
214
+
215
+ .detail-value {
216
+ font-size: 0.875rem;
217
+ color: #111827;
218
+ font-weight: 600;
219
+ }
220
+
221
  .setting-item {
222
  margin-bottom: 15px;
223
  }
 
286
 
287
  .btn-full {
288
  width: 100%;
289
+ margin-bottom: 25px;
290
  }
291
 
292
  /* Image Containers */
 
388
  font-weight: 600;
389
  color: #2563eb;
390
  }
391
+
392
+ /* Sample Images Section */
393
+ .sample-images-section {
394
+ margin-top: 25px;
395
+ padding-top: 20px;
396
+ border-top: 1px solid #e5e7eb;
397
+ }
398
+
399
+ .sample-images-section h4 {
400
+ margin-bottom: 15px;
401
+ color: #374151;
402
+ font-size: 1rem;
403
+ font-weight: 600;
404
+ }
405
+
406
+ .sample-images-grid {
407
+ display: grid;
408
+ grid-template-columns: 1fr 1fr;
409
+ gap: 12px;
410
+ }
411
+
412
+ .sample-image-item {
413
+ position: relative;
414
+ aspect-ratio: 1;
415
+ border-radius: 8px;
416
+ overflow: hidden;
417
+ cursor: pointer;
418
+ transition: all 0.2s ease;
419
+ border: 2px solid transparent;
420
+ }
421
+
422
+ .sample-image-item:hover {
423
+ border-color: #2563eb;
424
+ transform: scale(1.02);
425
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
426
+ }
427
+
428
+ .sample-image-item img {
429
+ width: 100%;
430
+ height: 100%;
431
+ object-fit: cover;
432
+ }
433
+
434
+ .sample-image-overlay {
435
+ position: absolute;
436
+ bottom: 0;
437
+ left: 0;
438
+ right: 0;
439
+ background: linear-gradient(transparent, rgba(0,0,0,0.7));
440
+ color: white;
441
+ padding: 8px;
442
+ font-size: 0.75rem;
443
+ font-weight: 500;
444
+ text-align: center;
445
+ }
static/js/dashboard.js CHANGED
@@ -9,6 +9,7 @@ class MarineDashboard {
9
  this.initializeElements();
10
  this.bindEvents();
11
  this.checkAPIStatus();
 
12
  }
13
 
14
  initializeElements() {
@@ -24,7 +25,6 @@ class MarineDashboard {
24
  this.iouValue = document.getElementById('iouValue');
25
 
26
  // Display elements
27
- this.originalImageContainer = document.getElementById('originalImageContainer');
28
  this.annotatedImageContainer = document.getElementById('annotatedImageContainer');
29
 
30
  // Results elements
@@ -39,6 +39,13 @@ class MarineDashboard {
39
  this.statusDot = document.getElementById('statusDot');
40
  this.statusText = document.getElementById('statusText');
41
  this.modelInfo = document.getElementById('modelInfo');
 
 
 
 
 
 
 
42
  }
43
 
44
  bindEvents() {
@@ -71,6 +78,8 @@ class MarineDashboard {
71
 
72
  if (data.model_info) {
73
  this.modelInfo.textContent = `Model: ${data.model_info.model_name} (${data.model_info.total_classes} species)`;
 
 
74
  }
75
  } else {
76
  this.statusDot.className = 'status-dot error';
@@ -129,29 +138,20 @@ class MarineDashboard {
129
  }
130
 
131
  this.currentImageFile = file;
132
- this.displayOriginalImage(file);
133
  this.identifyBtn.disabled = false;
134
 
135
  // Update upload area
136
  this.uploadArea.classList.add('has-image');
137
- this.uploadArea.innerHTML = `
138
- <div class="upload-content">
139
- <div class="upload-icon">✓</div>
140
- <div class="upload-text">
141
- <p class="upload-primary">${file.name}</p>
142
- <p class="upload-secondary">${window.MarineAPI.utils.formatFileSize(file.size)}</p>
143
- </div>
144
- </div>
145
- `;
146
  }
147
 
148
- // Display Original Image
149
- displayOriginalImage(file) {
150
  const reader = new FileReader();
151
  reader.onload = (e) => {
152
  this.currentImage = e.target.result;
153
- this.originalImageContainer.innerHTML = `
154
- <img src="${e.target.result}" alt="Original image" />
155
  `;
156
  };
157
  reader.readAsDataURL(file);
@@ -242,9 +242,58 @@ class MarineDashboard {
242
  this.speciesSection.style.display = 'block';
243
  }
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
 
 
 
 
247
  // Initialize dashboard when DOM is loaded
248
  document.addEventListener('DOMContentLoaded', () => {
249
- new MarineDashboard();
250
  });
 
9
  this.initializeElements();
10
  this.bindEvents();
11
  this.checkAPIStatus();
12
+ this.loadSampleImages();
13
  }
14
 
15
  initializeElements() {
 
25
  this.iouValue = document.getElementById('iouValue');
26
 
27
  // Display elements
 
28
  this.annotatedImageContainer = document.getElementById('annotatedImageContainer');
29
 
30
  // Results elements
 
39
  this.statusDot = document.getElementById('statusDot');
40
  this.statusText = document.getElementById('statusText');
41
  this.modelInfo = document.getElementById('modelInfo');
42
+
43
+ // Model details elements
44
+ this.totalSpecies = document.getElementById('totalSpecies');
45
+ this.deviceInfo = document.getElementById('deviceInfo');
46
+
47
+ // Sample images
48
+ this.sampleImagesGrid = document.getElementById('sampleImagesGrid');
49
  }
50
 
51
  bindEvents() {
 
78
 
79
  if (data.model_info) {
80
  this.modelInfo.textContent = `Model: ${data.model_info.model_name} (${data.model_info.total_classes} species)`;
81
+ this.totalSpecies.textContent = data.model_info.total_classes;
82
+ this.deviceInfo.textContent = data.model_info.device;
83
  }
84
  } else {
85
  this.statusDot.className = 'status-dot error';
 
138
  }
139
 
140
  this.currentImageFile = file;
141
+ this.displayUploadedImage(file);
142
  this.identifyBtn.disabled = false;
143
 
144
  // Update upload area
145
  this.uploadArea.classList.add('has-image');
 
 
 
 
 
 
 
 
 
146
  }
147
 
148
+ // Display Uploaded Image in Upload Area
149
+ displayUploadedImage(file) {
150
  const reader = new FileReader();
151
  reader.onload = (e) => {
152
  this.currentImage = e.target.result;
153
+ this.uploadArea.innerHTML = `
154
+ <img src="${e.target.result}" alt="Uploaded image" style="width: 100%; height: 100%; object-fit: cover; border-radius: 8px;" />
155
  `;
156
  };
157
  reader.readAsDataURL(file);
 
242
  this.speciesSection.style.display = 'block';
243
  }
244
  }
245
+
246
+ // Load Sample Images
247
+ loadSampleImages() {
248
+ const sampleImages = [
249
+ {
250
+ name: 'red_fish.png',
251
+ url: 'https://huggingface.co/seamo-ai/marina-species-v1/resolve/main/red_fish.png',
252
+ description: 'Marine Fish Sample'
253
+ },
254
+ {
255
+ name: 'red_fish_2.png',
256
+ url: 'https://huggingface.co/seamo-ai/marina-species-v1/resolve/main/red_fish_2.png',
257
+ description: 'Deep Sea Sample'
258
+ }
259
+ ];
260
+
261
+ this.sampleImagesGrid.innerHTML = sampleImages.map(image => `
262
+ <div class="sample-image-item" onclick="dashboard.loadSampleImage('${image.url}', '${image.name}')">
263
+ <img src="${image.url}" alt="${image.description}" loading="lazy" />
264
+ <div class="sample-image-overlay">${image.description}</div>
265
+ </div>
266
+ `).join('');
267
+ }
268
+
269
+ // Load Sample Image
270
+ async loadSampleImage(imageUrl, imageName) {
271
+ try {
272
+ // Show loading state
273
+ window.MarineAPI.utils.showNotification('Loading sample image...', 'info');
274
+
275
+ // Fetch the image
276
+ const response = await fetch(imageUrl);
277
+ const blob = await response.blob();
278
+
279
+ // Convert to File object
280
+ const file = new File([blob], imageName, { type: blob.type });
281
+
282
+ // Process the file
283
+ this.processFile(file);
284
+
285
+ window.MarineAPI.utils.showNotification('Sample image loaded successfully!', 'success');
286
+ } catch (error) {
287
+ console.error('Failed to load sample image:', error);
288
+ window.MarineAPI.utils.showNotification('Failed to load sample image', 'error');
289
+ }
290
+ }
291
  }
292
 
293
+ // Make dashboard instance globally available for sample image clicks
294
+ let dashboard;
295
+
296
  // Initialize dashboard when DOM is loaded
297
  document.addEventListener('DOMContentLoaded', () => {
298
+ dashboard = new MarineDashboard();
299
  });
templates/dashboard.html CHANGED
@@ -12,10 +12,6 @@
12
  <!-- Page Header -->
13
  <div class="page-header">
14
  <h1>Species Identification</h1>
15
- <p class="page-description">
16
- Upload an image to identify marine species using our advanced YOLOv5 model.
17
- Supports 691+ species including fish, corals, sea stars, and more.
18
- </p>
19
  </div>
20
 
21
  <!-- API Status Banner -->
@@ -29,26 +25,14 @@
29
 
30
  <!-- Main Dashboard Grid -->
31
  <div class="dashboard-grid">
32
- <!-- Input Panel -->
33
- <div class="panel input-panel">
34
  <div class="card">
35
  <div class="card-header">
36
- <h3 class="card-title">Upload Image</h3>
37
  </div>
38
  <div class="card-body">
39
- <!-- Upload Area -->
40
- <div class="upload-area" id="uploadArea">
41
- <div class="upload-content">
42
- <div class="upload-icon">+</div>
43
- <div class="upload-text">
44
- <p class="upload-primary">Drag image here or click to browse</p>
45
- <p class="upload-secondary">PNG, JPG, JPEG up to 10MB</p>
46
- </div>
47
- </div>
48
- <input type="file" id="fileInput" accept="image/*" hidden>
49
- </div>
50
-
51
- <!-- Settings -->
52
  <div class="settings-section">
53
  <h4>Detection Settings</h4>
54
  <div class="settings-grid">
@@ -69,25 +53,61 @@
69
  </div>
70
  </div>
71
 
72
- <!-- Action Button -->
73
- <button class="btn btn-primary btn-full" id="identifyBtn" disabled>
74
- Identify Species
75
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  </div>
77
  </div>
78
  </div>
79
 
80
- <!-- Original Image Panel -->
81
- <div class="panel image-panel">
82
  <div class="card">
83
  <div class="card-header">
84
- <h3 class="card-title">Original Image</h3>
85
  </div>
86
  <div class="card-body">
87
- <div class="image-container" id="originalImageContainer">
88
- <div class="image-placeholder">
89
- <div class="placeholder-icon">□</div>
90
- <p>Upload an image to see it here</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </div>
92
  </div>
93
  </div>
 
12
  <!-- Page Header -->
13
  <div class="page-header">
14
  <h1>Species Identification</h1>
 
 
 
 
15
  </div>
16
 
17
  <!-- API Status Banner -->
 
25
 
26
  <!-- Main Dashboard Grid -->
27
  <div class="dashboard-grid">
28
+ <!-- Model Information Panel -->
29
+ <div class="panel model-panel">
30
  <div class="card">
31
  <div class="card-header">
32
+ <h3 class="card-title">Model Information</h3>
33
  </div>
34
  <div class="card-body">
35
+ <!-- Detection Settings -->
 
 
 
 
 
 
 
 
 
 
 
 
36
  <div class="settings-section">
37
  <h4>Detection Settings</h4>
38
  <div class="settings-grid">
 
53
  </div>
54
  </div>
55
 
56
+ <!-- Model Details -->
57
+ <div class="model-details-section">
58
+ <h4>Model Details</h4>
59
+ <div class="model-details-grid">
60
+ <div class="detail-item">
61
+ <span class="detail-label">Model Type</span>
62
+ <span class="detail-value" id="modelType">YOLOv5</span>
63
+ </div>
64
+ <div class="detail-item">
65
+ <span class="detail-label">Total Species</span>
66
+ <span class="detail-value" id="totalSpecies">-</span>
67
+ </div>
68
+ <div class="detail-item">
69
+ <span class="detail-label">Device</span>
70
+ <span class="detail-value" id="deviceInfo">-</span>
71
+ </div>
72
+ <div class="detail-item">
73
+ <span class="detail-label">Model Size</span>
74
+ <span class="detail-value" id="modelSize">33k Images</span>
75
+ </div>
76
+ </div>
77
+ </div>
78
  </div>
79
  </div>
80
  </div>
81
 
82
+ <!-- Upload & Test Panel -->
83
+ <div class="panel upload-panel">
84
  <div class="card">
85
  <div class="card-header">
86
+ <h3 class="card-title">Upload & Test</h3>
87
  </div>
88
  <div class="card-body">
89
+ <!-- Upload Area -->
90
+ <div class="upload-area" id="uploadArea">
91
+ <div class="upload-content">
92
+ <div class="upload-icon">+</div>
93
+ <div class="upload-text">
94
+ <p class="upload-primary">Drag image here or click to browse</p>
95
+ <p class="upload-secondary">PNG, JPG, JPEG up to 10MB</p>
96
+ </div>
97
+ </div>
98
+ <input type="file" id="fileInput" accept="image/*" hidden>
99
+ </div>
100
+
101
+ <!-- Action Button -->
102
+ <button class="btn btn-primary btn-full" id="identifyBtn" disabled>
103
+ Identify Species
104
+ </button>
105
+
106
+ <!-- Sample Images -->
107
+ <div class="sample-images-section">
108
+ <h4>Try Sample Images</h4>
109
+ <div class="sample-images-grid" id="sampleImagesGrid">
110
+ <!-- Sample images will be populated here -->
111
  </div>
112
  </div>
113
  </div>