浏览代码

update on vault history in the frontend

Dr-Swopt 1 周之前
父节点
当前提交
1d641b6408

+ 10 - 8
frontend/src/app/components/analyzer/analyzer.component.ts

@@ -122,14 +122,16 @@ export class AnalyzerComponent implements OnInit {
             original_dimensions: img
           };
 
-          // Persist to local vault
-          this.localHistory.saveRecord(
-            this.results, 
-            this.selectedFile!.name, 
-            this.inferenceService.mode(),
-            this.previewUrl!,
-            img
-          );
+          // Persist to local vault ONLY if we are in Local Edge AI mode
+          if (this.inferenceService.mode() === 'local') {
+            this.localHistory.saveRecord(
+              this.results,
+              this.selectedFile!.name,
+              this.inferenceService.mode(),
+              this.previewUrl!,
+              img
+            );
+          }
 
           this.loading = false;
           setTimeout(() => this.drawDetections(), 100);

+ 5 - 3
frontend/src/app/components/history/history.component.html

@@ -35,7 +35,9 @@
           <span class="engine-badge">{{ record.engine }}</span>
         </div>
         <div class="summary-mini">
-          <span *ngFor="let badge of getSummaryBadge(record.summary)" class="badge">{{ badge }}</span>
+          <span *ngFor="let badge of getSummaryBadge(record.industrial_summary || record.summary)" class="badge">
+            {{ badge }}
+          </span>
         </div>
         <div class="expand-icon">{{ isExpanded(record.timestamp, record.filename, record.archive_id) ? '▾' : '▸' }}
         </div>
@@ -45,8 +47,8 @@
         <hr>
         <div class="details-grid">
           <div class="preview-side">
-            <div *ngIf="record.imageData" class="image-wrapper">
-              <img [src]="record.imageData" alt="Analysis Preview">
+            <div class="image-wrapper" *ngIf="record.imageData">
+              <img [src]="record.imageData" (error)="record.imageData = null">
               <div *ngFor="let det of record.detections" class="detection-box"
                 [ngStyle]="getBoxStyles(det.box, record)">
               </div>

+ 44 - 0
frontend/src/app/components/history/history.component.scss

@@ -4,6 +4,50 @@
   gap: 16px;
 }
 
+.vault-tabs {
+  display: flex;
+  gap: 1rem;
+  margin-top: 1.5rem;
+  margin-bottom: 2rem;
+
+  .tab-btn {
+    background: rgba(255, 255, 255, 0.05);
+    border: 1px solid rgba(255, 255, 255, 0.1);
+    color: var(--text-secondary);
+    padding: 0.8rem 1.5rem;
+    border-radius: 12px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    display: flex;
+    align-items: center;
+    gap: 0.5rem;
+    font-weight: 600;
+
+    &:hover {
+      background: rgba(255, 255, 255, 0.1);
+      transform: translateY(-2px);
+    }
+
+    &.active {
+      background: var(--accent-green);
+      color: white;
+      border-color: var(--accent-green);
+      box-shadow: 0 4px 15px rgba(0, 166, 81, 0.3);
+    }
+  }
+}
+
+.badge {
+  background: rgba(0, 166, 81, 0.1);
+  color: #00A651;
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 0.75rem;
+  border: 1px solid rgba(0, 166, 81, 0.2);
+  margin-right: 5px;
+  font-weight: 600;
+}
+
 .history-item {
   background: var(--input-bg);
   border-radius: 12px;

+ 11 - 23
frontend/src/app/components/history/history.component.ts

@@ -36,18 +36,14 @@ export class HistoryComponent implements OnInit {
     this.loading = true;
     this.remoteInference.getHistory().subscribe({
       next: (data) => {
-        // Normalize remote data if necessary
         this.remoteHistoryRecords = data.map(record => ({
+          ...record,
           timestamp: new Date(record.created_at).toLocaleString(),
-          filename: record.filename,
-          summary: record.industrial_summary,
-          inference_ms: record.inference_ms,
           engine: 'API AI',
-          detections: record.detections,
-          archive_id: record.archive_id,
-          isNormalized: true,
-          // Remote records usually don't have base64 image data for privacy/storage reasons
-          imageData: null 
+          // CRITICAL: Set to false because NestJS now sends absolute pixels
+          isNormalized: false,
+          // Sync with NestJS Streaming Endpoint
+          imageData: `http://localhost:3000/palm-oil/archive/${record.archive_id}`
         }));
         this.loading = false;
       },
@@ -98,25 +94,17 @@ export class HistoryComponent implements OnInit {
   }
 
   getBoxStyles(box: number[], record: any): any {
-    // If the record is from the API, coordinates are already 0.0-1.0
-    if (record.isNormalized) {
-      return {
-        'left': (box[0] * 100) + '%',
-        'top': (box[1] * 100) + '%',
-        'width': ((box[2] - box[0]) * 100) + '%',
-        'height': ((box[3] - box[1]) * 100) + '%'
-      };
-    }
-
-    // Fallback for local records or records with dimensions
-    const dims = record.original_dimensions || record.dimensions;
+    // Use absolute dimensions from the record if available
+    // We calculate percentage for CSS 'top/left' based on original image size
+    const dims = record.industrial_summary ? { width: 640, height: 640 } : (record.original_dimensions || record.dimensions);
     if (!dims) return {};
-    
+
     return {
       'left': (box[0] / dims.width * 100) + '%',
       'top': (box[1] / dims.height * 100) + '%',
       'width': ((box[2] - box[0]) / dims.width * 100) + '%',
-      'height': ((box[3] - box[1]) / dims.height * 100) + '%'
+      'height': ((box[3] - box[1]) / dims.height * 100) + '%',
+      'border-color': record.is_health_alert ? '#DC3545' : '#00A651'
     };
   }
 }