Oviya commited on
Commit
24822a7
·
1 Parent(s): 6f093ab

pydetect-updated

Browse files
src/app/case-details-page/case-details-page.component.html CHANGED
@@ -146,7 +146,7 @@
146
  </button>
147
  <button type="button" class="detect-btn"
148
  [disabled]="!c.caseId"
149
- (click)="c.caseId && goToDetect(c.caseId)"
150
  title="Go Detect" aria-label="Go Detect">
151
  Go Detect
152
  </button>
 
146
  </button>
147
  <button type="button" class="detect-btn"
148
  [disabled]="!c.caseId"
149
+ (click)="c.caseId && goToDetectWithMetadata(c)"
150
  title="Go Detect" aria-label="Go Detect">
151
  Go Detect
152
  </button>
src/app/case-details-page/case-details-page.component.ts CHANGED
@@ -166,6 +166,25 @@ export class CaseDetailsPageComponent implements OnInit {
166
  this.router.navigate(['/py-detect'], { state: { caseId } });
167
  }
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  navigateHome(): void {
170
  this.router.navigate(['/']);
171
  }
 
166
  this.router.navigate(['/py-detect'], { state: { caseId } });
167
  }
168
 
169
+ goToDetectWithMetadata(c: PoliceCase): void {
170
+ const metadata = {
171
+ caseId: c.caseId || '',
172
+ crimeType: c.crime || '',
173
+ dateTime: c.dateTime || '',
174
+ location: c.police?.address || '',
175
+ suspectName: c.accused?.name || '',
176
+ status: c.status || '',
177
+ investigationOfficer: c.police?.name || '',
178
+ progress: this.getProgressValue(c),
179
+ progressStage: 'Interrogation',
180
+ sessionTime: '00:00:00'
181
+ };
182
+ localStorage.setItem('pyDetectMetadata', JSON.stringify(metadata)); // Save to localStorage
183
+ this.router.navigate(['/py-detect'], {
184
+ state: { metadata }
185
+ });
186
+ }
187
+
188
  navigateHome(): void {
189
  this.router.navigate(['/']);
190
  }
src/app/py-detect/py-detect.component.css CHANGED
@@ -1,10 +1,8 @@
1
- /* Custom Styles */
2
  body, html {
3
- background: url('/assets/background.jpg') no-repeat center center fixed;
4
- background-color: #1a202c;
5
- margin: 0;
6
- padding: 0;
7
- font-family: 'Inter', Arial, sans-serif;
8
  }
9
 
10
  .h-full {
@@ -234,173 +232,411 @@ body, html {
234
  }
235
 
236
  .main-flex-layout {
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  display: flex;
238
- flex-direction: row;
239
- justify-content: space-between;
 
240
  align-items: flex-start;
241
- width: 100%;
242
- height: 80vh;
243
- gap: 48px; /* Increased gap for more separation */
244
  }
245
 
246
- .question-section {
247
- flex: 1;
248
  display: flex;
249
  flex-direction: column;
250
- align-items: flex-start;
251
- justify-content: flex-start;
252
- min-width: 420px;
253
- max-width: 700px;
254
- padding: 32px 24px 32px 32px;
255
- position: relative;
256
  }
257
 
258
- .big-question {
259
- background: #f0f4ff;
 
260
  border-radius: 18px;
261
- box-shadow: 0 2px 16px #2563eb22;
262
- padding: 32px 28px;
263
- color: #23272b;
264
- font-size: 2.2rem;
265
- font-weight: 700;
266
- margin-bottom: 24px;
267
- min-height: 100px;
268
- max-width: 700px;
269
- word-break: break-word;
270
- text-align: left;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  }
272
 
273
- .big-question-animated {
274
- animation: questionFadeIn 0.7s, pulseText 1.2s infinite alternate;
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
 
277
- @keyframes pulseText {
278
- 0% { text-shadow: 0 0 8px #38bdf8, 0 2px 8px #22222244; }
279
- 100% { text-shadow: 0 0 32px #38bdf8, 0 2px 24px #22222288; }
 
 
 
 
 
 
 
280
  }
281
 
282
- @keyframes questionFadeIn {
283
- from { opacity: 0; transform: translateY(24px); }
284
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
 
 
285
  }
286
 
287
- .video-section {
288
- flex: 1;
 
 
 
 
 
 
 
 
 
 
289
  display: flex;
290
  flex-direction: column;
291
  align-items: center;
292
- justify-content: flex-start;
293
- min-width: 400px;
294
- max-width: 100vw;
295
- padding: 32px 32px 32px 0;
296
  }
297
-
298
  .camera-video {
299
  border-radius: 18px;
300
- border: 4px solid #38bdf8;
301
- box-shadow: 0 0 24px #38bdf844, 0 0 8px #1e293b88;
302
  background: #222;
303
  object-fit: cover;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  margin-bottom: 18px;
305
- transition: box-shadow 0.4s, border 0.4s;
 
 
 
 
 
 
 
 
 
 
 
306
  }
307
 
308
- .camera-video-large {
309
- width: 60vw;
310
- max-width: 800px;
311
- height: 40vh;
312
- max-height: 60vh;
 
 
 
 
 
313
  }
314
 
315
- /* Animated buttons */
316
- .home-btn, .btn-start, .btn-stop, .btn-resume, .btn-submit, .back-btn {
 
 
 
 
 
 
317
  font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
318
- font-size: 1.05rem;
319
  font-weight: 700;
320
- letter-spacing: 2px;
321
- background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
322
- color: #e3f6ff;
323
- box-shadow: 0 2px 16px #38bdf888;
324
  border: none;
325
- border-radius: 12px;
326
- padding: 0.55rem 1.3rem;
327
- margin: 0 0.3rem;
328
  cursor: pointer;
329
- transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
330
- position: relative;
331
- overflow: hidden;
332
  animation: buttonPulse 1.2s infinite alternate;
333
  }
334
-
335
- .btn-start {
336
- background: linear-gradient(90deg, #2563eb 0%, #6366f1 100%);
 
 
 
 
 
 
 
 
 
337
  color: #fff;
338
- font-size: 1.25rem;
 
339
  font-weight: 700;
340
- padding: 0.7rem 2.2rem;
341
- border-radius: 999px;
342
- box-shadow: 0 4px 24px #6366f188, 0 2px 8px #2563eb44;
343
- transition: background 0.2s, color 0.2s, transform 0.2s, box-shadow 0.2s;
344
  border: none;
345
- outline: none;
346
- letter-spacing: 2px;
347
- position: relative;
348
- overflow: hidden;
 
349
  animation: buttonPulse 1.2s infinite alternate;
350
  }
351
-
352
- .btn-start:hover {
353
- background: linear-gradient(90deg, #6366f1 0%, #2563eb 100%);
354
- color: #e0e7ff;
355
- transform: scale(1.05);
356
  box-shadow: 0 6px 32px #6366f1cc;
 
357
  }
358
-
359
- .btn-submit {
360
  position: fixed;
361
- right: 32px;
362
- bottom: 32px;
363
- z-index: 1000;
364
- font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  font-size: 1.05rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  font-weight: 700;
367
- letter-spacing: 2px;
368
- background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
369
- color: #e3f6ff;
370
- box-shadow: 0 2px 16px #38bdf888;
371
  border: none;
372
  border-radius: 12px;
373
  padding: 0.55rem 1.3rem;
374
- margin: 0 0.3rem;
375
  cursor: pointer;
376
  transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
377
- overflow: hidden;
378
- animation: buttonPulse 1.2s infinite alternate;
379
- }
380
-
381
- @keyframes buttonPulse {
382
- 0% { box-shadow: 0 2px 16px #38bdf888; transform: scale(1); }
383
- 100% { box-shadow: 0 2px 32px #bae6fd88; transform: scale(1.07); }
384
  }
385
-
386
- .home-btn:hover, .btn-start:hover, .btn-stop:hover, .btn-resume:hover, .btn-submit:hover, .back-btn:hover {
387
- background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
388
  color: #bae6fd;
389
- box-shadow: 0 2px 24px #bae6fd88;
390
- animation: buttonPulse 0.7s;
391
  }
392
 
393
- .fade-out {
394
- opacity: 0.4;
395
- pointer-events: none;
396
- transition: opacity 0.5s;
397
  }
398
-
399
- .global-bottom-left-btn {
400
- position: fixed;
401
- left: 24px;
402
- bottom: 24px;
403
- z-index: 1000;
404
  }
405
 
406
  /* Modern UI header styles from infopage */
@@ -503,422 +739,500 @@ body, html {
503
  text-shadow: 0 0 6px #38bdf8;
504
  }
505
 
506
- /* Footer */
507
- footer {
508
- background: linear-gradient(to right, #011022, #01030a);
509
- color: #fff;
510
- text-align: center;
511
- padding: 10px 0px;
512
- position: fixed;
513
- bottom: 0;
514
- left: 0;
515
  width: 100%;
 
 
 
 
 
 
516
  }
517
-
518
-
519
- /* Left Panel */
520
- .left-panel {
521
- flex: 1;
522
  display: flex;
523
- flex-direction: column;
524
- align-items: flex-start;
525
- padding: 32px 24px 32px 32px;
526
- min-width: 420px;
527
- max-width: 700px;
528
- }
529
- .case-meta {
530
- margin-bottom: 24px;
531
- font-size: 1.1rem;
532
- color: #6366f1;
533
- }
534
- .case-id-value {
535
- color: #fbbf24;
536
- font-weight: bold;
537
  }
538
- .officer-value {
539
- color: #38bdf8;
540
- font-weight: bold;
 
 
 
 
 
 
541
  }
542
- .progress-label {
543
- color: #38bdf8;
544
- }
545
- .progress-value {
546
- color: #fff;
547
- background: #2563eb;
548
- border-radius: 6px;
549
- padding: 2px 8px;
550
- font-weight: 700;
551
- }
552
-
553
- /* Question Card */
554
- .question-card {
555
- background: #f0f4ff;
556
- border-radius: 18px;
557
- box-shadow: 0 2px 16px #2563eb22;
558
- padding: 32px 28px;
559
- margin-bottom: 18px;
560
- min-height: 120px;
561
- width: 100%;
562
- position: relative;
563
- display: flex;
564
- flex-direction: column;
565
- align-items: flex-start;
566
- }
567
- .question-typewriter {
568
- font-size: 2.1rem;
569
  font-weight: 700;
570
- color: #23272b;
571
- min-height: 48px;
572
- width: 100%;
573
- animation: typewriter 2.2s steps(40, end) 1;
574
- white-space: pre-line;
575
- overflow: hidden;
576
- border-right: 2px solid #2563eb;
577
- }
578
- @keyframes typewriter {
579
- from { width: 0; }
580
- to { width: 100%; }
581
- }
582
-
583
- /* Audio Status & Waveform */
584
- .audio-status {
585
- margin-top: 18px;
586
- width: 100%;
587
  display: flex;
588
  align-items: center;
589
- gap: 18px;
590
- }
591
- .recording-text {
592
- color: #38bdf8;
593
- font-weight: 700;
594
- font-size: 1.1rem;
595
- letter-spacing: 1px;
596
- animation: pulseRecording 1.2s infinite alternate;
597
  }
598
- .processing-text {
599
- color: #fbbf24;
600
- font-weight: 700;
601
- font-size: 1.1rem;
602
- letter-spacing: 1px;
603
- animation: pulseProcessing 1.2s infinite alternate;
604
  }
605
- @keyframes pulseRecording {
606
- 0% { opacity: 0.7; }
607
- 100% { opacity: 1; }
608
  }
609
- @keyframes pulseProcessing {
610
- 0% { opacity: 0.7; }
611
- 100% { opacity: 1; }
612
  }
613
- .waveform-animation {
614
- width: 48px;
615
- height: 24px;
616
- display: inline-block;
617
- background: none;
 
 
 
 
 
 
 
 
 
 
618
  position: relative;
619
- }
620
- .waveform-animation::before {
621
- content: '';
622
- display: block;
623
- width: 100%;
624
- height: 100%;
625
- background: url('data:image/svg+xml;utf8,<svg width="48" height="24" viewBox="0 0 48 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="10" width="4" height="4" rx="2" fill="%2338bdf8"><animate attributeName="height" values="4;16;4" dur="1s" repeatCount="indefinite"/></rect><rect x="10" y="6" width="4" height="12" rx="2" fill="%2338bdf8"><animate attributeName="height" values="12;4;12" dur="1s" repeatCount="indefinite"/></rect><rect x="18" y="10" width="4" height="4" rx="2" fill="%2338bdf8"><animate attributeName="height" values="4;16;4" dur="1s" repeatCount="indefinite"/></rect><rect x="26" y="6" width="4" height="12" rx="2" fill="%2338bdf8"><animate attributeName="height" values="12;4;12" dur="1s" repeatCount="indefinite"/></rect><rect x="34" y="10" width="4" height="4" rx="2" fill="%2338bdf8"><animate attributeName="height" values="4;16;4" dur="1s" repeatCount="indefinite"/></rect><rect x="42" y="6" width="4" height="12" rx="2" fill="%2338bdf8"><animate attributeName="height" values="12;4;12" dur="1s" repeatCount="indefinite"/></rect></svg>') no-repeat center center;
626
  }
627
 
628
- /* Controls Row */
629
- .controls-row {
630
- display: flex;
631
- gap: 18px;
632
- margin-top: 18px;
633
- }
634
- .btn-start-gradient {
635
- background: linear-gradient(90deg, #2563eb 0%, #6366f1 100%);
636
- color: #fff;
637
- font-size: 1.25rem;
638
- font-weight: 700;
639
- padding: 0.7rem 2.2rem;
640
- border-radius: 999px;
641
- box-shadow: 0 4px 24px #6366f188, 0 2px 8px #2563eb44;
642
- transition: background 0.2s, color 0.2s, transform 0.2s, box-shadow 0.2s;
643
- border: none;
644
- outline: none;
645
- letter-spacing: 2px;
646
- position: relative;
647
- overflow: hidden;
648
- animation: buttonPulse 1.2s infinite alternate;
649
- }
650
- .btn-start-gradient:hover {
651
- background: linear-gradient(90deg, #6366f1 0%, #2563eb 100%);
652
- color: #e0e7ff;
653
- transform: scale(1.05);
654
- box-shadow: 0 6px 32px #6366f1cc;
655
- }
656
- .btn-pause {
657
- background: #fbbf24;
658
- color: #23272b;
659
- font-weight: 700;
660
- border-radius: 999px;
661
- padding: 0.7rem 1.5rem;
662
- border: none;
663
- font-size: 1.1rem;
664
- box-shadow: 0 2px 8px #fbbf2444;
665
- transition: background 0.2s, color 0.2s;
666
- }
667
- .btn-skip {
668
- background: #e0e7ff;
669
- color: #6366f1;
670
- font-weight: 700;
671
- border-radius: 999px;
672
- padding: 0.7rem 1.5rem;
673
- border: none;
674
- font-size: 1.1rem;
675
- box-shadow: 0 2px 8px #6366f144;
676
- transition: background 0.2s, color 0.2s;
677
  }
678
 
679
- /* Right Panel */
680
- .right-panel {
681
- flex: 1;
 
 
 
682
  display: flex;
683
  flex-direction: column;
684
  align-items: flex-start;
685
- padding: 32px 32px 32px 0;
686
- min-width: 400px;
687
- max-width: 100vw;
688
- }
689
- .video-preview {
690
- margin-bottom: 18px;
691
- margin-left: 48px; /* Ensure this matches transcript-panel */
692
- display: flex;
693
- flex-direction: column;
694
- align-items: center;
695
- justify-content: center;
696
- min-height: 285px;
697
- background: #374151;
698
- border: 2px dashed #64748b;
699
- border-radius: 16px;
700
- box-shadow: 0 2px 16px #23272b44;
701
- padding: 32px 0;
702
  width: 100%;
703
- max-width: 995px;
704
- }
705
- .camera-video {
706
- border-radius: 18px;
707
- border: 4px solid #38bdf8;
708
- box-shadow: 0 0 24px #38bdf844, 0 0 8px #1e293b88;
709
- background: #222;
710
- object-fit: cover;
711
- margin-bottom: 18px;
712
- transition: box-shadow 0.4s, border 0.4s;
713
  }
714
- .camera-video-large {
715
- width: 60vw;
716
- max-width: 800px;
717
- height: 40vh;
718
- max-height: 60vh;
719
- }
720
- .camera-inactive-block {
721
- display: flex;
722
- flex-direction: column;
723
- align-items: center;
724
- justify-content: center;
725
- min-height: 260px;
726
- color: #a0aec0;
727
- }
728
- .camera-inactive-icon {
729
- font-size: 4rem;
730
- color: #64748b;
731
- margin-bottom: 18px;
732
  }
733
- .camera-inactive-title {
734
- font-size: 1.4rem;
 
735
  font-weight: 600;
736
- color: #cbd5e1;
737
- margin-bottom: 6px;
738
  }
739
- .camera-inactive-sub {
740
  font-size: 1.05rem;
741
- color: #94a3b8;
 
 
 
 
 
 
 
 
 
 
 
 
742
  }
743
 
744
  /* Transcript Panel */
745
  .transcript-panel {
746
- margin-left: 48px; /* Match video-preview for alignment */
747
  background: #fff;
748
  border-radius: 14px;
749
  box-shadow: 0 2px 12px #6366f122;
750
  padding: 18px 18px 18px 18px;
751
  width: 100%;
752
- max-width: 962px;
753
  overflow: hidden;
754
  display: flex;
755
  flex-direction: column;
756
  }
 
757
  .transcript-title {
758
  font-weight: 700;
759
  color: #6366f1;
760
  margin-bottom: 8px;
761
  }
 
762
  .transcript-scrollable {
763
  overflow-y: auto;
764
  max-height: 180px;
765
  padding-right: 8px;
766
  }
 
767
  .transcript-line {
768
  font-size: 1.05rem;
769
  color: #23272b;
770
  margin-bottom: 4px;
771
  }
772
 
773
- /* Completion Summary Modal */
774
- .summary-modal {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  position: fixed;
776
- top: 0; left: 0; right: 0; bottom: 0;
777
- background: rgba(30,41,59,0.85);
778
- z-index: 9999;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  display: flex;
780
  align-items: center;
781
- justify-content: center;
 
 
782
  }
783
- .summary-modal-content {
784
- background: #fff;
785
- border-radius: 18px;
786
- box-shadow: 0 2px 32px #6366f1cc;
787
- padding: 32px 40px;
788
- min-width: 420px;
789
- max-width: 700px;
790
  }
791
- .summary-qa-row {
792
- margin-bottom: 18px;
 
 
 
 
 
 
 
 
793
  }
794
- .summary-question {
795
- font-weight: 700;
796
- color: #2563eb;
 
 
797
  }
798
- .summary-answer {
799
- color: #23272b;
800
- margin-bottom: 4px;
 
 
 
801
  }
802
- .summary-duration {
 
 
 
 
 
 
803
  color: #6366f1;
804
- font-size: 0.98em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
805
  }
806
- .btn-close-summary {
807
- background: linear-gradient(90deg, #2563eb 0%, #6366f1 100%);
 
 
 
 
 
 
 
 
 
 
 
 
808
  color: #fff;
809
- font-size: 1.1rem;
810
- font-weight: 700;
811
- padding: 0.7rem 2.2rem;
812
- border-radius: 999px;
813
- box-shadow: 0 4px 24px #6366f188, 0 2px 8px #2563eb44;
 
 
 
 
 
 
814
  border: none;
815
- outline: none;
816
- letter-spacing: 2px;
817
- margin-top: 18px;
818
  cursor: pointer;
 
 
819
  }
820
- .btn-close-summary:hover {
821
- background: linear-gradient(90deg, #6366f1 0%, #2563eb 100%);
822
- color: #e0e7ff;
823
- transform: scale(1.05);
824
- box-shadow: 0 6px 32px #6366f1cc;
 
 
 
 
 
 
 
 
825
  }
826
 
827
- /* Info Card Styles */
828
- .info-card {
829
- background: linear-gradient(135deg, #232b3e 60%, #232b3e 100%);
830
- border: 2px solid #38bdf8;
831
- border-radius: 14px;
832
- box-shadow: 0 2px 16px #38bdf822;
833
- padding: 32px 24px;
834
- margin-bottom: 32px;
835
- max-width: 520px;
836
- text-align: center;
 
 
837
  color: #fff;
 
 
 
 
 
 
 
 
 
 
838
  }
839
- .info-title {
840
- font-size: 2rem;
 
 
 
 
 
 
 
841
  font-weight: 700;
842
- margin-bottom: 12px;
 
 
 
 
 
 
 
 
 
 
 
843
  }
844
- .info-desc {
845
- font-size: 1.15rem;
846
- color: #e2e8f0;
 
 
 
 
 
847
  }
848
 
849
- /* Camera Inactive Card Styles */
850
- .camera-inactive-card {
851
- background: linear-gradient(135deg, #232b3e 60%, #232b3e 100%);
852
- border: 2px dashed #a0aec0;
853
- border-radius: 14px;
854
- box-shadow: 0 2px 16px #38bdf822;
855
- padding: 48px 24px 32px 24px;
856
- margin-bottom: 32px;
857
- max-width: 600px;
858
- min-height: 320px;
859
- text-align: center;
860
- color: #e2e8f0;
861
  display: flex;
862
- flex-direction: column;
863
  align-items: center;
864
  justify-content: center;
 
 
 
 
 
 
 
865
  }
866
- .camera-icon {
867
- margin-bottom: 18px;
868
- color: #a0aec0;
 
 
869
  }
870
 
871
- .button-bar {
 
 
 
 
 
 
 
 
 
 
872
  display: flex;
873
  flex-direction: row;
874
- justify-content: flex-end;
875
  align-items: center;
876
- gap: 18px;
877
- padding: 18px 32px 18px 32px;
878
- box-shadow: 0 2px 12px #38bdf844;
879
- border-bottom: 1px solid #23272b;
880
  }
881
- .header-btn {
882
- font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
883
- font-size: 1.05rem;
884
- font-weight: 700;
885
- letter-spacing: 2px;
886
- background: #fff;
887
  color: #2563eb;
888
- border: 2px solid #2563eb;
 
889
  border-radius: 12px;
890
- padding: 0.55rem 1.3rem;
891
- margin: 0 0.3rem;
892
- cursor: pointer;
893
- box-shadow: 0 2px 16px #38bdf888;
894
- transition: background 0.2s, color 0.2s, box-shadow 0.2s, border 0.2s;
895
- }
896
- .header-btn.active, .header-btn:focus, .header-btn:hover {
897
- background: #2563eb;
898
- color: #fff;
899
- border-color: #38bdf8;
900
- box-shadow: 0 2px 24px #bae6fd88;
901
- }
902
- .home-btn {
903
- background: #2563eb;
904
- color: #fff;
905
- border-color: #38bdf8;
906
- box-shadow: 0 2px 24px #bae6fd88;
907
- }
908
- .home-btn:focus, .home-btn:hover {
909
- background: #38bdf8;
 
 
910
  color: #fff;
 
 
 
 
 
 
 
 
 
 
 
911
  }
912
- .start-btn {
913
- background: #fff;
914
- color: #2563eb;
915
- }
916
- .camera-btn, .voice-btn, .tts-btn {
917
- background: #e0e7ff;
918
- color: #2563eb;
919
- border-color: #2563eb;
920
- }
921
- .tts-btn.active {
922
- background: #2563eb;
923
- color: #fff;
924
  }
 
 
 
1
+ /* --- Crispy Modern Redesign --- */
2
  body, html {
3
+ font-family: 'Poppins', 'Inter', Arial, sans-serif;
4
+ background: #f6f8fa;
5
+ color: #23272b;
 
 
6
  }
7
 
8
  .h-full {
 
232
  }
233
 
234
  .main-flex-layout {
235
+ display: grid;
236
+ grid-template-columns: 1fr 1.5fr;
237
+ gap: 32px;
238
+ margin: 32px auto 0 auto;
239
+ max-width: 1400px;
240
+ min-height: 80vh;
241
+ background: linear-gradient(120deg, #f6f8fa 60%, #e0f2fe 100%);
242
+ border-radius: 24px;
243
+ box-shadow: 0 8px 32px #38bdf822;
244
+ padding: 32px 32px 48px 32px;
245
+ }
246
+
247
+ .left-panel {
248
  display: flex;
249
+ flex-direction: column;
250
+ gap: 18px;
251
+ justify-content: flex-start;
252
  align-items: flex-start;
253
+ min-width: 555px;
254
+ max-width: 480px;
 
255
  }
256
 
257
+ .right-panel {
 
258
  display: flex;
259
  flex-direction: column;
260
+ gap: 24px;
261
+ align-items: stretch;
262
+ min-width: 400px;
 
 
 
263
  }
264
 
265
+ /* Card style for summary/case info */
266
+ .case-meta {
267
+ background: #fff;
268
  border-radius: 18px;
269
+ box-shadow: 0 2px 16px #38bdf822;
270
+ padding: 18px 24px;
271
+ margin-bottom: 12px;
272
+ display: flex;
273
+ flex-direction: column;
274
+ gap: 8px;
275
+ position: relative;
276
+ }
277
+ .case-meta::before {
278
+ content: '';
279
+ position: absolute;
280
+ top: 0; left: 0; right: 0; height: 6px;
281
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
282
+ border-radius: 18px 18px 0 0;
283
+ }
284
+
285
+ /* Add avatar/icon for suspect/officer */
286
+ .suspect-avatar {
287
+ width: 54px;
288
+ height: 54px;
289
+ border-radius: 50%;
290
+ object-fit: cover;
291
+ box-shadow: 0 2px 8px #38bdf844;
292
+ margin-right: 12px;
293
+ }
294
+
295
+ .case-row {
296
+ display: flex;
297
+ align-items: center;
298
+ gap: 10px;
299
+ margin-bottom: 2px;
300
+ border-radius: 8px;
301
+ padding: 2px 0;
302
+ font-size: 1.02rem;
303
+ }
304
+ .case-label {
305
+ color: #2563eb;
306
+ font-weight: 600;
307
+ min-width: 120px;
308
+ }
309
+ .case-value {
310
+ color: #e3f6ff;
311
+ font-weight: 500;
312
+ }
313
+ .divider-line {
314
+ border-top: 1.5px solid #e5e7eb;
315
+ margin: 10px 0 14px 0;
316
+ }
317
+
318
+ /* Button group for actions */
319
+ .button-group {
320
+ display: flex;
321
+ gap: 12px;
322
+ margin: 18px 0 0 0;
323
+ justify-content: flex-end;
324
  }
325
 
326
+ .summary-toggle-btn,
327
+ .back-btn,
328
+ .evidence-toggle-btn,
329
+ .btn-start-gradient,
330
+ .evidence-submit-btn,
331
+ .submit-evaluate-btn {
332
+ font-size: 0.92rem;
333
+ padding: 0.38rem 0.85rem;
334
+ border-radius: 8px;
335
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
336
+ font-weight: 700;
337
+ letter-spacing: 1px;
338
+ box-shadow: 0 2px 12px #38bdf844;
339
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s, transform 0.2s;
340
  }
341
 
342
+ .summary-toggle-btn:hover,
343
+ .back-btn:hover,
344
+ .evidence-toggle-btn:hover,
345
+ .btn-start-gradient:hover,
346
+ .evidence-submit-btn:hover,
347
+ .submit-evaluate-btn:hover {
348
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
349
+ color: #bae6fd;
350
+ box-shadow: 0 6px 32px #6366f1cc;
351
+ transform: scale(1.04);
352
  }
353
 
354
+ /* Animated divider */
355
+ .animated-divider {
356
+ width: 100%;
357
+ height: 4px;
358
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
359
+ border-radius: 2px;
360
+ margin: 18px 0;
361
+ animation: dividerPulse 2s infinite alternate;
362
+ }
363
+ @keyframes dividerPulse {
364
+ 0% { box-shadow: 0 0 8px #38bdf844; }
365
+ 100% { box-shadow: 0 0 24px #6366f144; }
366
  }
367
 
368
+ /* Camera/video preview card */
369
+ .video-preview {
370
+ background: #374151;
371
+ border: 2px dashed #64748b;
372
+ border-radius: 18px;
373
+ box-shadow: 0 2px 16px #23272b44;
374
+ padding: 0;
375
+ width: 100%;
376
+ max-width: 921px;
377
+ min-height: 368px;
378
+ height: 368px;
379
+ box-sizing: border-box;
380
  display: flex;
381
  flex-direction: column;
382
  align-items: center;
383
+ justify-content: center;
384
+ position: relative;
385
+ overflow: hidden;
 
386
  }
 
387
  .camera-video {
388
  border-radius: 18px;
389
+ border: none;
 
390
  background: #222;
391
  object-fit: cover;
392
+ width: 100%;
393
+ height: 100%;
394
+ max-width: 100%;
395
+ max-height: 100%;
396
+ display: block;
397
+ position: absolute;
398
+ top: 0;
399
+ left: 0;
400
+ }
401
+
402
+ /* Camera inactive styles */
403
+ .camera-inactive-block {
404
+ width: 100%;
405
+ height: 100%;
406
+ min-height: 368px;
407
+ display: flex;
408
+ flex-direction: column;
409
+ align-items: center;
410
+ justify-content: center;
411
+ position: relative;
412
+ }
413
+ .camera-placeholder-img {
414
+ width: 96px;
415
+ height: 96px;
416
+ object-fit: contain;
417
+ margin-bottom: 18px;
418
+ opacity: 0.7;
419
+ }
420
+ .camera-placeholder-icon {
421
+ font-size: 4rem;
422
+ color: #38bdf8;
423
  margin-bottom: 18px;
424
+ opacity: 0.7;
425
+ }
426
+ .camera-inactive-title {
427
+ font-size: 1.2rem;
428
+ color: #bae6fd;
429
+ font-weight: 700;
430
+ margin-bottom: 8px;
431
+ }
432
+ .camera-inactive-sub {
433
+ font-size: 1rem;
434
+ color: #fff;
435
+ opacity: 0.8;
436
  }
437
 
438
+ /* Footer */
439
+ footer {
440
+ background: linear-gradient(to right, #011022, #01030a);
441
+ color: #fff;
442
+ text-align: center;
443
+ padding: 10px 0px;
444
+ position: fixed;
445
+ bottom: 0;
446
+ left: 0;
447
+ width: 100%;
448
  }
449
 
450
+ /* Evidence and summary sidebar styles restoration */
451
+ .evidence-toggle-btn {
452
+ position: fixed;
453
+ top: 110px;
454
+ right: 32px;
455
+ z-index: 1202;
456
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
457
+ color: #fff;
458
  font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
459
+ font-size: 1rem;
460
  font-weight: 700;
 
 
 
 
461
  border: none;
462
+ border-radius: 16px;
463
+ padding: 12px 28px;
464
+ box-shadow: 0 2px 16px #38bdf888;
465
  cursor: pointer;
466
+ transition: background 0.3s, box-shadow 0.3s, color 0.2s, transform 0.2s;
 
 
467
  animation: buttonPulse 1.2s infinite alternate;
468
  }
469
+ .evidence-toggle-btn:hover {
470
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
471
+ color: #bae6fd;
472
+ box-shadow: 0 6px 32px #6366f1cc;
473
+ transform: scale(1.04);
474
+ }
475
+ .summary-toggle-btn {
476
+ position: fixed;
477
+ top: 160px;
478
+ right: 32px;
479
+ z-index: 1202;
480
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
481
  color: #fff;
482
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
483
+ font-size: 1rem;
484
  font-weight: 700;
 
 
 
 
485
  border: none;
486
+ border-radius: 16px;
487
+ padding: 12px 28px;
488
+ box-shadow: 0 2px 16px #38bdf888;
489
+ cursor: pointer;
490
+ transition: background 0.3s, box-shadow 0.3s, color 0.2s, transform 0.2s;
491
  animation: buttonPulse 1.2s infinite alternate;
492
  }
493
+ .summary-toggle-btn:hover {
494
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
495
+ color: #bae6fd;
 
 
496
  box-shadow: 0 6px 32px #6366f1cc;
497
+ transform: scale(1.04);
498
  }
499
+ .evidence-sidebar, .summary-sidebar {
 
500
  position: fixed;
501
+ top: 90px;
502
+ right: -35vw;
503
+ width: 35vw;
504
+ max-width: 562px;
505
+ height: 60vh;
506
+ min-height: 340px;
507
+ padding: 0;
508
+ display: flex;
509
+ flex-direction: column;
510
+ justify-content: flex-start;
511
+ align-items: stretch;
512
+ background: rgba(30, 41, 59, 0.97);
513
+ box-shadow: 0 8px 32px #38bdf844, 0 2px 16px #6366f144;
514
+ border-radius: 24px 0 0 24px;
515
+ z-index: 1201;
516
+ transition: right 0.35s cubic-bezier(.23,1,.32,1);
517
+ color: #fff;
518
+ overflow-y: auto;
519
+ backdrop-filter: blur(18px);
520
+ -webkit-backdrop-filter: blur(18px);
521
+ border: 2.5px solid #38bdf8;
522
+ animation: fadeInPanel 0.7s cubic-bezier(.23,1,.32,1);
523
+ }
524
+ .evidence-sidebar.open, .summary-sidebar.open {
525
+ right: 0;
526
+ }
527
+ .evidence-title-bar, .summary-title-bar {
528
+ display: flex;
529
+ align-items: center;
530
+ justify-content: space-between;
531
+ gap: 18px;
532
+ padding: 24px 32px 0 32px;
533
+ }
534
+ .evidence-title, .summary-title {
535
+ font-size: 1.3rem;
536
+ font-weight: 700;
537
+ color: #38bdf8;
538
+ letter-spacing: 1px;
539
+ }
540
+ .evidence-close-btn, .summary-close-btn {
541
+ background: none;
542
+ border: none;
543
+ font-size: 1.5rem;
544
+ color: #38bdf8;
545
+ cursor: pointer;
546
+ }
547
+ .evidence-form-card, .summary-content-card {
548
+ background: rgba(44, 54, 74, 0.98);
549
+ border-radius: 18px;
550
+ box-shadow: 0 2px 16px #38bdf822;
551
+ margin: 24px 24px 24px 24px;
552
+ padding: 32px 36px 24px 36px;
553
+ display: flex;
554
+ flex-direction: column;
555
+ align-items: flex-start;
556
+ color: #fff;
557
+ }
558
+ .evidence-upload-form {
559
+ display: flex;
560
+ flex-direction: column;
561
+ gap: 18px;
562
+ width: 100%;
563
+ }
564
+ .evidence-upload-row {
565
+ display: flex;
566
+ align-items: center;
567
+ gap: 14px;
568
+ margin-bottom: 8px;
569
+ color: #fff;
570
+ }
571
+ .evidence-upload-label {
572
+ display: flex;
573
+ align-items: center;
574
+ gap: 8px;
575
+ font-size: 1.08rem;
576
+ font-weight: 600;
577
+ color: #fff;
578
+ min-width: 160px;
579
+ }
580
+ .evidence-field-icon {
581
+ font-size: 1.3rem;
582
+ color: #38bdf8;
583
+ }
584
+ .evidence-summary-row {
585
+ display: flex;
586
+ flex-direction: column;
587
+ gap: 6px;
588
+ margin-top: 12px;
589
+ width: 100%;
590
+ }
591
+ .evidence-summary-label {
592
  font-size: 1.05rem;
593
+ font-weight: 600;
594
+ color: #fff;
595
+ margin-bottom: 2px;
596
+ }
597
+ .evidence-summary-textarea {
598
+ border-radius: 8px;
599
+ border: 1.5px solid #38bdf8;
600
+ padding: 8px 12px;
601
+ font-size: 1rem;
602
+ background: #232b3e;
603
+ color: #fff;
604
+ resize: vertical;
605
+ min-height: 48px;
606
+ width: 100%;
607
+ }
608
+ .evidence-submit-btn {
609
+ margin-top: 18px;
610
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
611
+ color: #fff;
612
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
613
+ font-size: 1.08rem;
614
  font-weight: 700;
 
 
 
 
615
  border: none;
616
  border-radius: 12px;
617
  padding: 0.55rem 1.3rem;
618
+ box-shadow: 0 2px 16px #38bdf888;
619
  cursor: pointer;
620
  transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
621
+ letter-spacing: 1px;
622
+ width: 140px;
623
+ display: block;
 
 
 
 
624
  }
625
+ .evidence-submit-btn:hover {
626
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
 
627
  color: #bae6fd;
628
+ box-shadow: 0 6px 32px #6366f1cc;
629
+ transform: scale(1.04);
630
  }
631
 
632
+ /* Prevent page scroll */
633
+ body, html {
634
+ overflow: hidden !important;
635
+ height: 100vh;
636
  }
637
+ .main-flex-layout {
638
+ max-height: calc(100vh - 60px);
639
+ overflow: hidden;
 
 
 
640
  }
641
 
642
  /* Modern UI header styles from infopage */
 
739
  text-shadow: 0 0 6px #38bdf8;
740
  }
741
 
742
+ .header-action-bar {
743
+ display: flex;
744
+ justify-content: space-between;
745
+ align-items: center;
 
 
 
 
 
746
  width: 100%;
747
+ position: relative;
748
+ margin-top: 0;
749
+ margin-bottom: 0;
750
+ padding: 0 32px 18px 32px;
751
+ min-height: 96px;
752
+ box-sizing: border-box;
753
  }
754
+ .header-action-left {
 
 
 
 
755
  display: flex;
756
+ align-items: center;
757
+ gap: 12px;
758
+ height: 40px;
 
 
 
 
 
 
 
 
 
 
 
759
  }
760
+ .header-action-right {
761
+ display: flex;
762
+ align-items: center;
763
+ gap: 12px;
764
+ height: 40px;
765
+ position: absolute;
766
+ top: 18px;
767
+ right: 32px;
768
+ z-index: 100;
769
  }
770
+ .header-action-bar .small-btn,
771
+ .header-action-bar .summary-toggle-btn,
772
+ .header-action-bar .evidence-toggle-btn {
773
+ font-size: 1.08rem;
774
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  font-weight: 700;
776
+ letter-spacing: 1px;
777
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
778
+ color: #fff;
779
+ border: none;
780
+ border-radius: 12px;
781
+ padding: 0.55rem 1.3rem;
782
+ box-shadow: 0 2px 16px #38bdf888;
 
 
 
 
 
 
 
 
 
 
783
  display: flex;
784
  align-items: center;
785
+ gap: 8px;
786
+ cursor: pointer;
787
+ transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
788
+ overflow: hidden;
789
+ height: 40px;
790
+ line-height: 1.2;
 
 
791
  }
792
+ .header-action-bar .small-btn {
793
+ margin-left: 8px;
 
 
 
 
794
  }
795
+ .header-action-bar .summary-toggle-btn {
796
+ margin-right: 8px;
 
797
  }
798
+ .header-action-bar .evidence-toggle-btn {
799
+ margin-right: 8px;
 
800
  }
801
+ .header-action-bar .small-btn:hover,
802
+ .header-action-bar .summary-toggle-btn:hover,
803
+ .header-action-bar .evidence-toggle-btn:hover {
804
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
805
+ color: #bae6fd;
806
+ box-shadow: 0 6px 32px #6366f1cc;
807
+ transform: scale(1.04);
808
+ }
809
+ .header-divider-line {
810
+ width: calc(100% - 64px); /* 32px gap on both sides */
811
+ height: 2px;
812
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
813
+ border-radius: 2px;
814
+ margin: 0 auto 18px auto;
815
+ box-shadow: 0 2px 8px #38bdf844;
816
  position: relative;
817
+ top: -19px;
 
 
 
 
 
 
818
  }
819
 
820
+ .blur-bg {
821
+ position: fixed;
822
+ top: 0;
823
+ left: 0;
824
+ width: 100vw;
825
+ height: 100vh;
826
+ z-index: 1200;
827
+ background: rgba(30,41,59,0.18);
828
+ backdrop-filter: blur(8px);
829
+ -webkit-backdrop-filter: blur(8px);
830
+ transition: opacity 0.3s;
831
+ opacity: 1;
832
+ pointer-events: auto;
833
+ }
834
+ .blur-bg.hide {
835
+ opacity: 0;
836
+ pointer-events: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  }
838
 
839
+ .tts-question-card {
840
+ background: linear-gradient(120deg, #f6f8fa 60%, #bae6fd 100%);
841
+ border-radius: 18px;
842
+ box-shadow: 0 8px 32px #38bdf822, 0 2px 16px #6366f144;
843
+ padding: 32px 36px 24px 36px;
844
+ margin: 0 0 18px 0;
845
  display: flex;
846
  flex-direction: column;
847
  align-items: flex-start;
848
+ position: relative;
849
+ animation: fadeInUp 0.7s cubic-bezier(.39,.58,.57,1);
850
+ min-height: 180px;
851
+ max-width: 480px;
 
 
 
 
 
 
 
 
 
 
 
 
 
852
  width: 100%;
853
+ z-index: 1;
 
 
 
 
 
 
 
 
 
854
  }
855
+ .tts-question-title {
856
+ font-size: 1.25rem;
857
+ font-weight: 700;
858
+ color: #2563eb;
859
+ margin-bottom: 10px;
860
+ letter-spacing: 1px;
861
+ text-shadow: 0 2px 12px #38bdf844;
 
 
 
 
 
 
 
 
 
 
 
862
  }
863
+ .tts-question-text {
864
+ font-size: 1.12rem;
865
+ color: #23272b;
866
  font-weight: 600;
867
+ margin-bottom: 8px;
868
+ text-shadow: 0 1px 8px #bae6fd44;
869
  }
870
+ .tts-status-row {
871
  font-size: 1.05rem;
872
+ color: #6366f1;
873
+ font-weight: 500;
874
+ margin-bottom: 4px;
875
+ background: rgba(56,189,248,0.08);
876
+ border-radius: 8px;
877
+ padding: 6px 14px;
878
+ box-shadow: 0 2px 8px #38bdf822;
879
+ display: inline-block;
880
+ animation: statusPulse 1.2s infinite alternate;
881
+ }
882
+ @keyframes statusPulse {
883
+ 0% { box-shadow: 0 2px 8px #38bdf822; }
884
+ 100% { box-shadow: 0 4px 16px #6366f144; }
885
  }
886
 
887
  /* Transcript Panel */
888
  .transcript-panel {
 
889
  background: #fff;
890
  border-radius: 14px;
891
  box-shadow: 0 2px 12px #6366f122;
892
  padding: 18px 18px 18px 18px;
893
  width: 100%;
894
+ max-width: 889px;
895
  overflow: hidden;
896
  display: flex;
897
  flex-direction: column;
898
  }
899
+
900
  .transcript-title {
901
  font-weight: 700;
902
  color: #6366f1;
903
  margin-bottom: 8px;
904
  }
905
+
906
  .transcript-scrollable {
907
  overflow-y: auto;
908
  max-height: 180px;
909
  padding-right: 8px;
910
  }
911
+
912
  .transcript-line {
913
  font-size: 1.05rem;
914
  color: #23272b;
915
  margin-bottom: 4px;
916
  }
917
 
918
+
919
+
920
+ /* Evidence Panel Sidebar Styles */
921
+ .evidence-toggle-btn {
922
+ display: block;
923
+ position: relative;
924
+ margin: 2px 0 0 auto;
925
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
926
+ color: #fff;
927
+ font-family: 'Poppins', 'Inter', Arial, sans-serif;
928
+ font-size: 1rem;
929
+ font-weight: 600;
930
+ border: none;
931
+ border-radius: 8px 8px 0 0;
932
+ padding: 10px 22px;
933
+ box-shadow: 0 2px 12px #38bdf844;
934
+ cursor: pointer;
935
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
936
+ z-index: 1200;
937
+ }
938
+
939
+ .evidence-toggle-btn:hover {
940
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
941
+ color: #bae6fd;
942
+ }
943
+
944
+ .evidence-sidebar {
945
  position: fixed;
946
+ top: 90px;
947
+ right: -35vw;
948
+ width: 35vw;
949
+ max-width: 562px;
950
+ height: 60vh;
951
+ min-height: 340px;
952
+ padding: 0;
953
+ gap: 0;
954
+ display: flex;
955
+ flex-direction: column;
956
+ justify-content: flex-start;
957
+ align-items: stretch;
958
+ background: rgba(30, 41, 59, 0.97);
959
+ box-shadow: 0 8px 32px #38bdf844, 0 2px 16px #6366f144;
960
+ border-radius: 24px 0 0 24px;
961
+ z-index: 1201;
962
+ transition: right 0.35s cubic-bezier(.23,1,.32,1);
963
+ color: #fff;
964
+ overflow-y: auto;
965
+ backdrop-filter: blur(18px);
966
+ -webkit-backdrop-filter: blur(18px);
967
+ border: 2.5px solid #38bdf8;
968
+ animation: fadeInPanel 0.7s cubic-bezier(.23,1,.32,1);
969
+ }
970
+
971
+ .evidence-sidebar.open {
972
+ right: 0;
973
+ }
974
+
975
+ .evidence-title-bar {
976
  display: flex;
977
  align-items: center;
978
+ justify-content: space-between;
979
+ gap: 18px;
980
+ padding: 24px 32px 0 32px;
981
  }
982
+
983
+ .evidence-title {
984
+ font-size: 1.3rem;
985
+ font-weight: 700;
986
+ color: #38bdf8;
987
+ letter-spacing: 1px;
 
988
  }
989
+
990
+ .evidence-form-card {
991
+ background: rgba(44, 54, 74, 0.98);
992
+ border-radius: 18px;
993
+ box-shadow: 0 2px 16px #38bdf822;
994
+ margin: 24px 24px 24px 24px;
995
+ padding: 24px 28px 18px 28px;
996
+ display: flex;
997
+ flex-direction: column;
998
+ align-items: stretch;
999
  }
1000
+
1001
+ .evidence-upload-form {
1002
+ display: flex;
1003
+ flex-direction: column;
1004
+ gap: 18px;
1005
  }
1006
+
1007
+ .evidence-upload-row {
1008
+ display: flex;
1009
+ align-items: center;
1010
+ gap: 14px;
1011
+ margin-bottom: 0;
1012
  }
1013
+
1014
+ .evidence-upload-label {
1015
+ display: flex;
1016
+ align-items: center;
1017
+ gap: 8px;
1018
+ font-size: 1.08rem;
1019
+ font-weight: 600;
1020
  color: #6366f1;
1021
+ min-width: 160px;
1022
+ }
1023
+
1024
+ .evidence-field-icon {
1025
+ font-size: 1.3rem;
1026
+ color: #38bdf8;
1027
+ }
1028
+
1029
+ .evidence-divider {
1030
+ border: none;
1031
+ border-top: 1.5px solid #38bdf8;
1032
+ margin: 12px 0 8px 0;
1033
+ }
1034
+
1035
+ .evidence-summary-row {
1036
+ display: flex;
1037
+ flex-direction: column;
1038
+ gap: 6px;
1039
  }
1040
+
1041
+ .evidence-summary-label {
1042
+ font-size: 1.05rem;
1043
+ font-weight: 600;
1044
+ color: #38bdf8;
1045
+ margin-bottom: 2px;
1046
+ }
1047
+
1048
+ .evidence-summary-textarea {
1049
+ border-radius: 8px;
1050
+ border: 1.5px solid #6366f1;
1051
+ padding: 8px 12px;
1052
+ font-size: 1rem;
1053
+ background: #232b3e;
1054
  color: #fff;
1055
+ resize: vertical;
1056
+ min-height: 48px;
1057
+ }
1058
+
1059
+ .evidence-submit-btn {
1060
+ margin-top: 12px;
1061
+ background: linear-gradient(90deg, #2563eb 0%, #38bdf8 100%);
1062
+ color: #fff;
1063
+ font-family: 'Poppins', 'Inter', Arial, sans-serif;
1064
+ font-size: 1rem;
1065
+ font-weight: 600;
1066
  border: none;
1067
+ border-radius: 8px;
1068
+ padding: 10px 22px;
1069
+ box-shadow: 0 2px 12px #38bdf844;
1070
  cursor: pointer;
1071
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
1072
+ letter-spacing: 1px;
1073
  }
1074
+
1075
+ .evidence-submit-btn:hover {
1076
+ background: linear-gradient(90deg, #38bdf8 0%, #2563eb 100%);
1077
+ color: #bae6fd;
1078
+ }
1079
+
1080
+ .evidence-upload-form input[type="file"] {
1081
+ background: #232b3e;
1082
+ color: #fff;
1083
+ border-radius: 6px;
1084
+ border: 1px solid #6366f1;
1085
+ padding: 4px 8px;
1086
+ font-size: 0.98rem;
1087
  }
1088
 
1089
+ .evidence-upload-icon {
1090
+ font-size: 1.15em;
1091
+ margin-right: 6px;
1092
+ vertical-align: middle;
1093
+ }
1094
+
1095
+ .back-btn {
1096
+ font-size: 1.08rem;
1097
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
1098
+ font-weight: 700;
1099
+ letter-spacing: 1px;
1100
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
1101
  color: #fff;
1102
+ border: none;
1103
+ border-radius: 12px;
1104
+ padding: 0.55rem 1.3rem;
1105
+ box-shadow: 0 2px 16px #38bdf888;
1106
+ display: flex;
1107
+ align-items: center;
1108
+ gap: 8px;
1109
+ cursor: pointer;
1110
+ transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
1111
+ overflow: hidden;
1112
  }
1113
+
1114
+ .recording-status-bar {
1115
+ display: flex;
1116
+ align-items: center;
1117
+ gap: 12px;
1118
+ justify-content: flex-start;
1119
+ background: linear-gradient(90deg, #e0f2fe 0%, #bae6fd 100%);
1120
+ color: #23272b;
1121
+ font-size: 1.08rem;
1122
  font-weight: 700;
1123
+ border-radius: 12px;
1124
+ box-shadow: 0 2px 8px #38bdf822;
1125
+ padding: 8px 22px;
1126
+ margin: 18px 0 0 0;
1127
+ min-width: 220px;
1128
+ max-width: 340px;
1129
+ position: relative;
1130
+ z-index: 2;
1131
+ border: none;
1132
+ outline: none;
1133
+ cursor: default;
1134
+ transition: none;
1135
  }
1136
+ .recording-status-bar .status-icon {
1137
+ font-size: 1.3em;
1138
+ margin-right: 6px;
1139
+ }
1140
+ .recording-status-bar .status-timer {
1141
+ font-size: 1.08em;
1142
+ font-weight: 600;
1143
+ color: #2563eb;
1144
  }
1145
 
1146
+ .icon-btn {
1147
+ background: linear-gradient(90deg, #38bdf8 0%, #6366f1 100%);
1148
+ color: #fff;
1149
+ border: none;
1150
+ border-radius: 50%;
1151
+ width: 44px;
1152
+ height: 44px;
 
 
 
 
 
1153
  display: flex;
 
1154
  align-items: center;
1155
  justify-content: center;
1156
+ font-size: 1.5rem;
1157
+ box-shadow: 0 2px 12px #38bdf844;
1158
+ margin-bottom: 0;
1159
+ margin-top: 0;
1160
+ margin-right: 8px;
1161
+ cursor: pointer;
1162
+ transition: background 0.3s, box-shadow 0.3s, color 0.2s, transform 0.2s;
1163
  }
1164
+ .icon-btn:hover {
1165
+ background: linear-gradient(90deg, #6366f1 0%, #38bdf8 100%);
1166
+ color: #bae6fd;
1167
+ box-shadow: 0 6px 32px #6366f1cc;
1168
+ transform: scale(1.08);
1169
  }
1170
 
1171
+ .header-actions-right-group {
1172
+ position: absolute;
1173
+ top: 18px;
1174
+ right: 32px;
1175
+ z-index: 100;
1176
+ display: flex;
1177
+ flex-direction: column;
1178
+ align-items: flex-end;
1179
+ gap: 10px;
1180
+ }
1181
+ .header-actions-icons {
1182
  display: flex;
1183
  flex-direction: row;
 
1184
  align-items: center;
1185
+ gap: 10px;
1186
+ margin-top: 37px;
 
 
1187
  }
1188
+
1189
+ .guidance-tooltip {
1190
+ display: flex;
1191
+ align-items: center;
1192
+ gap: 8px;
1193
+ background: linear-gradient(90deg, #e0f2fe 0%, #bae6fd 100%);
1194
  color: #2563eb;
1195
+ font-size: 1.08rem;
1196
+ font-weight: 600;
1197
  border-radius: 12px;
1198
+ box-shadow: 0 2px 12px #38bdf844;
1199
+ padding: 6px 14px;
1200
+ margin-top: 8px;
1201
+ margin-left: 0;
1202
+ position: absolute;
1203
+ left: 32px;
1204
+ top: 60px;
1205
+ z-index: 101;
1206
+ animation: fadeInTooltip 0.7s cubic-bezier(.39,.58,.57,1);
1207
+ white-space: nowrap;
1208
+ }
1209
+ @keyframes fadeInTooltip {
1210
+ 0% { opacity: 0; transform: translateY(-12px); }
1211
+ 100% { opacity: 1; transform: translateY(0); }
1212
+ }
1213
+
1214
+ .recording-indicator {
1215
+ position: absolute;
1216
+ top: 18px;
1217
+ left: 50%;
1218
+ transform: translateX(-50%);
1219
+ background: #ef4444;
1220
  color: #fff;
1221
+ font-size: 1.15rem;
1222
+ font-weight: 700;
1223
+ padding: 8px 22px;
1224
+ border-radius: 16px;
1225
+ box-shadow: 0 2px 12px #ef444488;
1226
+ z-index: 10;
1227
+ letter-spacing: 1px;
1228
+ display: flex;
1229
+ align-items: center;
1230
+ gap: 8px;
1231
+ animation: fadeInRecording 0.5s cubic-bezier(.39,.58,.57,1);
1232
  }
1233
+ @keyframes fadeInRecording {
1234
+ 0% { opacity: 0; transform: translateX(-50%) scale(0.8); }
1235
+ 100% { opacity: 1; transform: translateX(-50%) scale(1); }
 
 
 
 
 
 
 
 
 
1236
  }
1237
+
1238
+
src/app/py-detect/py-detect.component.html CHANGED
@@ -17,98 +17,162 @@
17
  <span class="py-letter t2">T</span>
18
  </div>
19
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  </div>
21
  </div>
22
-
23
- <!-- Button Bar Below Header -->
24
- <div class="button-bar">
25
- <button class="header-btn home-btn" (click)="navigateHome()">Home</button>
26
- <button class="header-btn camera-btn" (click)="startCamera()">Start Camera</button>
27
- <button class="header-btn voice-btn" (click)="startVoice()">Start Voice</button>
28
- <button class="header-btn tts-btn" [class.active]="ttsEnabled()"> <i class="fas fa-volume-up"></i> TTS On</button>
29
- </div>
30
-
 
 
 
 
 
31
  <main class="main-flex-layout">
32
- <!-- Left Panel: Controls & Question -->
33
  <section class="left-panel">
34
- <div class="case-meta">
35
- <div class="case-id">Case ID: <span class="case-id-value">{{ caseId }}</span></div>
36
- <div class="officer">Officer: <span class="officer-value">{{ officer }}</span></div>
37
- <div class="progress-label">Progress: <span class="progress-value">{{ progress }}%</span></div>
38
- </div>
39
- <div class="question-card">
40
- <!-- Show Start button if no question is active -->
41
- <div *ngIf="!currentQuestionText">
42
- <button class="btn-start-gradient" (click)="onStartRecording()">
43
- <i class="fas fa-search"></i> Start Investigation
44
- </button>
45
  </div>
46
- <!-- Show question if active -->
47
- <div *ngIf="currentQuestionText">
48
- <div class="question-number" style="font-size:1.1em;color:#6366f1;font-weight:600;margin-bottom:8px;">
49
- Question {{ currentQuestionIndex }} of {{ totalQuestions }}
50
- </div>
51
- <div class="question-typewriter">
52
- {{ currentQuestionText }}
53
- </div>
54
- <!-- Asking popup -->
55
- <div *ngIf="status() === 'asking'" class="popup asking-popup">
56
- <span>Asking...</span>
57
- </div>
58
- <!-- Idle popup -->
59
- <div *ngIf="status() === 'idle-wait'" class="popup idle-popup">
60
- <span>Waiting for suspect reply...</span>
61
- </div>
62
- <div *ngIf="status() === 'recording'" class="recording-indicator" style="color:#38bdf8;font-weight:600;margin-top:12px;">Recording...</div>
63
- </div>
64
- <div class="audio-status">
65
- <span *ngIf="isRecording" class="recording-text">Recording...</span>
66
- <span *ngIf="isProcessing" class="processing-text">Processing Answer...</span>
67
- <div class="waveform-animation" *ngIf="isRecording"></div>
68
  </div>
69
- </div>
70
- <div class="controls-row" *ngIf="currentQuestionText">
71
- <button class="btn-pause" (click)="pauseRecording()"><i class="fas fa-pause"></i> Pause</button>
72
- <button class="btn-skip" (click)="skipQuestion()"><i class="fas fa-forward"></i> Skip Question</button>
73
  </div>
74
  </section>
75
-
76
  <!-- Right Panel: Video + Transcript -->
77
  <section class="right-panel">
78
  <div class="video-preview">
79
  <ng-container *ngIf="!videoStream">
80
  <div class="camera-inactive-block">
81
- <i class="fas fa-video camera-inactive-icon"></i>
82
  <div class="camera-inactive-title">Camera Inactive</div>
83
- <div class="camera-inactive-sub">Click "Start Camera" to begin video recording</div>
84
  </div>
85
  </ng-container>
86
  <ng-container *ngIf="videoStream">
87
- <video #videoElement autoplay muted playsinline class="camera-video camera-video-large"></video>
 
 
 
88
  <div class="video-status stylish-font">{{ videoStatus }}</div>
 
 
 
89
  </ng-container>
90
  </div>
 
91
  <div class="transcript-panel">
92
  <div class="transcript-title">Transcription (Live):</div>
93
  <div class="transcript-scrollable">
94
  <div *ngFor="let line of transcriptLines" class="transcript-line">{{ line }}</div>
95
  </div>
96
  </div>
 
 
 
97
  </section>
98
  </main>
99
-
100
- <!-- Completion Summary Modal -->
101
- <div class="summary-modal" *ngIf="showSummary">
102
- <div class="summary-modal-content">
103
- <h2>Interview Summary</h2>
104
- <div *ngFor="let qa of summaryData" class="summary-qa-row">
105
- <div class="summary-question">Q: {{ qa.question }}</div>
106
- <div class="summary-answer">A: {{ qa.answer }}</div>
107
- <div class="summary-duration">Audio: {{ qa.duration }}s</div>
108
- </div>
109
- <button class="btn-close-summary" (click)="closeSummary()">Close</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
111
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  <footer>
114
  <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
 
17
  <span class="py-letter t2">T</span>
18
  </div>
19
  </div>
20
+ <div class="header-actions-right-group">
21
+ <button class="back-btn" (click)="navigateBackToCaseDetails()">
22
+ <span class="back-icon">←</span> Back to Case Details
23
+ </button>
24
+ <div class="header-actions-icons">
25
+ <button class="icon-btn" (click)="showEvidencePanel = !showEvidencePanel" title="Upload Evidence">
26
+ <i class="fas fa-file-upload"></i>
27
+ </button>
28
+ <button class="icon-btn" (click)="showSummaryPanel = !showSummaryPanel" title="Investigation Summary">
29
+ <i class="fas fa-clipboard-list"></i>
30
+ </button>
31
+ </div>
32
+ </div>
33
  </div>
34
  </div>
35
+ <!-- Header action bar with buttons and divider line -->
36
+ <div class="header-action-bar">
37
+ <div class="header-action-left" style="position:relative;">
38
+ <button class="small-btn" (click)="onStartRecording()">
39
+ <i class="fas fa-search"></i> Start Investigation
40
+ </button>
41
+ <div *ngIf="currentQuestionIndex < 0" class="guidance-tooltip">
42
+ <span style="font-size:1.3em;">💡</span>
43
+ Click Start Investigation to activate camera and begin questioning.
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <div class="header-divider-line"></div>
48
+ <!-- Investigation Summary Toggle Button (moved below header, above camera box) -->
49
  <main class="main-flex-layout">
50
+ <!-- Left Panel: Case Info + Question -->
51
  <section class="left-panel">
52
+ <div class="animated-divider"></div>
53
+ <!-- Remove old question/status block, keep only the new decorated card -->
54
+ <div class="tts-question-card">
55
+ <div class="tts-question-title">Question {{ currentQuestionIndex + 1 }} of {{ totalQuestions }}</div>
56
+ <div class="tts-question-text">{{ currentQuestionText }}</div>
57
+ <div *ngIf="infoText" class="tts-status-row">{{ infoText }}</div>
58
+ <div *ngIf="status() === 'idle-wait'" class="tts-status-row">
59
+ Waiting for suspect reply...
 
 
 
60
  </div>
61
+ <div *ngIf="isRecording" class="tts-status-row">
62
+ Voice is being recognized...
63
+ </div>
64
+ <!-- Recording Status Bar inside question card -->
65
+ <div class="recording-status-bar">
66
+ <span class="status-icon">
67
+ <ng-container *ngIf="isRecording; else pausedIcon">🔴</ng-container>
68
+ <ng-template #pausedIcon>⏸️</ng-template>
69
+ </span>
70
+ <span>{{ isRecording ? 'Recording' : 'Paused' }}</span>
71
+ <span class="status-timer">— {{ isRecording ? elapsedTime : remainingTime }}</span>
 
 
 
 
 
 
 
 
 
 
 
72
  </div>
 
 
 
 
73
  </div>
74
  </section>
 
75
  <!-- Right Panel: Video + Transcript -->
76
  <section class="right-panel">
77
  <div class="video-preview">
78
  <ng-container *ngIf="!videoStream">
79
  <div class="camera-inactive-block">
80
+ <i class="fas fa-camera camera-placeholder-icon"></i>
81
  <div class="camera-inactive-title">Camera Inactive</div>
82
+ <div class="camera-inactive-sub">Click "Start Investigation" to begin video recording</div>
83
  </div>
84
  </ng-container>
85
  <ng-container *ngIf="videoStream">
86
+ <video #videoElement autoplay muted playsinline class="camera-video"></video>
87
+ <div *ngIf="isRecording && currentQuestionIndex >= 0 && currentQuestionIndex < totalQuestions" class="recording-indicator">
88
+ <span style="font-size:1.3em;">🔴</span> Recording...
89
+ </div>
90
  <div class="video-status stylish-font">{{ videoStatus }}</div>
91
+ <div class="waveform-animation" *ngIf="isRecording">
92
+ <span></span><span></span><span></span><span></span><span></span>
93
+ </div>
94
  </ng-container>
95
  </div>
96
+ <div class="section-line"></div>
97
  <div class="transcript-panel">
98
  <div class="transcript-title">Transcription (Live):</div>
99
  <div class="transcript-scrollable">
100
  <div *ngFor="let line of transcriptLines" class="transcript-line">{{ line }}</div>
101
  </div>
102
  </div>
103
+ <button class="submit-evaluate-btn" (click)="navigateToValidationPage()">
104
+ Submit & Evaluate
105
+ </button>
106
  </section>
107
  </main>
108
+ <!-- Evidence Panel Sidebar (directly below button bar) -->
109
+ <div class="evidence-sidebar" [class.open]="showEvidencePanel">
110
+ <div class="evidence-title-bar">
111
+ <span class="evidence-title">Evidence Panel</span>
112
+ <button class="evidence-close-btn" (click)="showEvidencePanel = false" title="Close">
113
+ <i class="fas fa-times"></i>
114
+ </button>
115
+ </div>
116
+ <div class="evidence-form-card">
117
+ <form class="evidence-upload-form" (submit)="$event.preventDefault(); uploadDocument()">
118
+ <div class="evidence-upload-row">
119
+ <label for="evidenceFileDoc" class="evidence-upload-label">
120
+ <i class="fas fa-file-upload evidence-field-icon"></i>
121
+ <span>Upload Document</span>
122
+ </label>
123
+ <input id="evidenceFileDoc" type="file" (change)="onEvidenceFileSelect($event, 'document')" />
124
+ </div>
125
+ <div class="evidence-upload-row">
126
+ <label for="evidenceFilePhoto" class="evidence-upload-label">
127
+ <i class="fas fa-image evidence-field-icon"></i>
128
+ <span>Upload Photo</span>
129
+ </label>
130
+ <input id="evidenceFilePhoto" type="file" accept="image/*" (change)="onEvidenceFileSelect($event, 'photo')" />
131
+ </div>
132
+ <div class="evidence-upload-row">
133
+ <label for="evidenceFileRec" class="evidence-upload-label">
134
+ <i class="fas fa-microphone evidence-field-icon"></i>
135
+ <span>Upload Recording</span>
136
+ </label>
137
+ <input id="evidenceFileRec" type="file" accept="audio/*,video/*" (change)="onEvidenceFileSelect($event, 'recording')" />
138
+ </div>
139
+ <hr class="evidence-divider" />
140
+ <div class="evidence-summary-row">
141
+ <label for="evidenceSummary" class="evidence-summary-label">Remark</label>
142
+ <textarea id="evidenceSummary" [(ngModel)]="evidenceSummary" rows="3" placeholder="Enter Remark..." class="evidence-summary-textarea"></textarea>
143
+ </div>
144
+ <button type="submit" class="evidence-submit-btn">Submit</button>
145
+ </form>
146
  </div>
147
  </div>
148
+ <!-- Summary Sidebar -->
149
+ <div class="summary-sidebar" [class.open]="showSummaryPanel">
150
+ <div class="summary-title-bar">
151
+ <span class="summary-title">Investigation Summary</span>
152
+ <button class="summary-close-btn" (click)="showSummaryPanel = false" title="Close">
153
+ <i class="fas fa-times"></i>
154
+ </button>
155
+ </div>
156
+ <div class="summary-content-card">
157
+ <div class="case-section-title">Case Details</div>
158
+ <div class="case-row"><span class="case-label">Case ID:</span> <span class="case-value">{{ caseId }}</span></div>
159
+ <div class="case-row"><span class="case-label">Crime Type:</span> <span class="case-value">{{ crimeType }}</span></div>
160
+ <div class="case-row"><span class="case-label">Date & Time:</span> <span class="case-value">{{ dateTime }}</span></div>
161
+ <div class="case-row"><span class="case-label">Location:</span> <span class="case-value">{{ location }}</span></div>
162
+ <div class="divider-line"></div>
163
+ <div class="case-section-title">Person Details</div>
164
+ <div class="case-row"><span class="case-label">Suspect Name:</span> <span class="case-value">{{ suspectName }}</span></div>
165
+ <div class="case-row"><span class="case-label">Investigation Officer:</span> <span class="case-value">{{ investigationOfficer }}</span></div>
166
+ <div class="divider-line"></div>
167
+ <div class="case-section-title">Progress Details</div>
168
+ <div class="case-row"><span class="case-label">Status:</span> <span class="case-value">{{ statusText }}</span></div>
169
+ <div class="case-row"><span class="case-label">Progress:</span> <span class="case-value">{{progress}}% (Stage: {{ progressStage }})</span></div>
170
+ <div class="case-row"><span class="case-label">Session Time:</span> <span class="case-value">{{ sessionTime }}</span></div>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Blur background when evidence or summary panel is open -->
175
+ <div class="blur-bg" [class.hide]="!showEvidencePanel && !showSummaryPanel"></div>
176
 
177
  <footer>
178
  <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
src/app/py-detect/py-detect.component.ts CHANGED
@@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit, signal, ViewChild, ElementRef, ChangeDete
2
  import { CommonModule } from '@angular/common';
3
  import { Router, NavigationStart } from '@angular/router';
4
  import { Subscription } from 'rxjs';
 
5
 
6
  declare global {
7
  interface Window {
@@ -19,16 +20,19 @@ type QAResult = {
19
  audioUrl: string;
20
  startedAt: number;
21
  endedAt: number;
 
22
  };
23
 
24
  @Component({
25
  standalone: true,
26
  selector: 'app-py-detect',
27
- imports: [CommonModule],
28
  templateUrl: './py-detect.component.html',
29
  styleUrls: ['./py-detect.component.css']
30
  })
31
  export class PyDetectComponent implements OnInit, OnDestroy {
 
 
32
 
33
  // ---- UI state ----
34
  status = signal<'idle' | 'asking' | 'idle-wait' | 'recording' | 'processing'>('idle');
@@ -86,7 +90,7 @@ export class PyDetectComponent implements OnInit, OnDestroy {
86
  private analyserWindowMs = 100; // Declare analyserWindowMs property
87
 
88
  // Example question source (replace with API call when ready)
89
- private seedQuestions = [
90
  'Please introduce yourself in two sentences.',
91
  'What motivates you to take on challenging tasks?',
92
  'Describe a situation where you solved a tough problem.',
@@ -109,35 +113,118 @@ export class PyDetectComponent implements OnInit, OnDestroy {
109
  public videoAnswers: Blob[] = [];
110
 
111
  // UI properties for template
 
 
 
 
 
 
 
112
  progress: number = 0;
113
- caseId: string = 'CASE-007';
114
- officer: string = 'Ganesh';
115
- currentQuestionText: string = '';
116
  isRecording: boolean = false;
117
  isProcessing: boolean = false;
118
  transcriptLines: string[] = [];
119
  showSummary: boolean = false;
120
  summaryData: { question: string; answer: string; duration: number }[] = [];
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  // Navigate back to the homepage
 
123
  navigateHome() {
124
  if (window.speechSynthesis) {
125
- window.speechSynthesis.cancel(); // Stop any TTS audio immediately on navigation
126
  }
127
- this.router.navigate(['/']); // Navigates to the root (homepage)
128
  }
129
 
130
- // Navigate to the info page
131
  goToInfoPage() {
132
  if (window.speechSynthesis) {
133
- window.speechSynthesis.cancel(); // Stop any TTS audio immediately on navigation
134
  }
135
  this.router.navigate(['/infopage']);
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  // ======== Lifecycle ========
139
  ngOnInit(): void {
140
  this.isActive = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  }
142
 
143
  ngOnDestroy(): void {
@@ -231,22 +318,6 @@ export class PyDetectComponent implements OnInit, OnDestroy {
231
  }
232
  }
233
 
234
- public submitAll() {
235
- this.stopAll();
236
- this.stopVideoRecording();
237
- this.videoStatus = 'Submitted!';
238
- this.startDisabled.set(true);
239
- this.stopDisabled.set(true);
240
- this.resumeDisabled.set(true);
241
- this.submitDisabled.set(true);
242
- // TODO: Upload all videoAnswers to backend
243
-
244
- // Example: Calculate dummy percentages (replace with real logic)
245
- const truePercentage = 70;
246
- const falsePercentage = 30;
247
- this.router.navigate(['/validationpage'], { state: { truePercentage, falsePercentage } });
248
- }
249
-
250
  private nextQuestionLoopRunning = false;
251
 
252
  private async nextQuestionLoop(): Promise<void> {
@@ -254,53 +325,47 @@ export class PyDetectComponent implements OnInit, OnDestroy {
254
  // 1) Get next question
255
  const q = await this.fetchNextQuestion();
256
  this.currentQuestion.set(q);
257
- this.status.set('asking'); // Show asking popup
258
  if (this.ttsEnabled()) {
259
  await this.speak(q);
260
  }
261
- // After question finishes playing, show idle popup for 5 seconds
262
- this.status.set('idle-wait'); // Show idle popup
263
- await this.sleep(5000); // Wait for suspect reply
264
- // Now start speech recognition only
265
- this.status.set('recording');
266
- let userSpoke = false;
267
  this.transcriptSoFar = '';
268
  this.startRecognition('en-IN');
269
- // Wait for user to start speaking
 
 
 
 
270
  await new Promise<void>((resolve) => {
271
- const checkSpeech = () => {
272
- if (this.transcriptSoFar.trim().length > 0 && !userSpoke) {
273
- userSpoke = true;
274
- // Start video recording and analysis when user speaks
275
- this.startVideoRecording();
276
- resolve();
277
- } else {
278
- setTimeout(checkSpeech, 200);
 
 
 
 
 
 
279
  }
 
280
  };
281
- checkSpeech();
282
  });
283
- // Stop recognition and video recording after answer window
284
- await this.sleep(this.maxAnswerMs);
285
  this.stopRecognition();
286
  this.stopVideoRecording();
287
- const startedAt = Date.now();
288
- const { audioUrl, avgPitchHz, avgVolume, transcript, language } =
289
- await this.captureAnswerWithAnalysis(this.maxAnswerMs);
290
- const endedAt = Date.now();
291
- this.log.push({
292
- question: q,
293
- transcript,
294
- language,
295
- avgPitchHz,
296
- avgVolume,
297
- audioUrl,
298
- startedAt,
299
- endedAt
300
- });
301
- this.questionIndex.set(this.questionIndex() + 1);
302
  this.status.set('processing');
303
  await this.sleep(700);
 
304
  }
305
  this.status.set('idle');
306
  this.stopVideoRecording();
@@ -545,7 +610,7 @@ export class PyDetectComponent implements OnInit, OnDestroy {
545
  };
546
  }
547
 
548
- private removeFillerWords(text: string): string {
549
  const fillerWords = ['um', 'ah', 'like', 'you know', 'so', 'actually', 'basically'];
550
  const regex = new RegExp(`\\b(${fillerWords.join('|')})\\b`, 'gi');
551
  return text.replace(regex, '').replace(/\s+/g, ' ').trim(); // Clean extra spaces after removal
@@ -621,152 +686,243 @@ export class PyDetectComponent implements OnInit, OnDestroy {
621
  }
622
  }
623
 
 
 
 
 
 
 
 
 
624
  // UI methods for template
625
  onStartInterview() {
626
- this.currentQuestionText = this.seedQuestions[0];
627
  this.progress = 0;
628
  this.isRecording = false;
629
  this.isProcessing = false;
630
  }
631
- pauseRecording() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  this.isRecording = false;
633
- // Add logic to pause recording
 
 
 
 
 
634
  }
635
- skipQuestion() {
636
- const idx = this.seedQuestions.indexOf(this.currentQuestionText);
637
- if (idx >= 0 && idx < this.seedQuestions.length - 1) {
638
- this.currentQuestionText = this.seedQuestions[idx + 1];
639
- this.progress = Math.round(((idx + 2) / this.seedQuestions.length) * 100);
 
640
  } else {
641
- this.currentQuestionText = '';
642
- this.progress = 100;
643
- this.showSummary = true;
644
  }
645
  }
646
- closeSummary() {
647
- this.showSummary = false;
 
 
 
 
 
 
 
 
648
  }
649
- onStartRecording() {
650
- this.progress = 0;
651
- this.transcriptLines = [];
652
- this.showSummary = false;
653
- this.startQuestionFlow(0);
654
- // Play the question using TTS, then wait 2s, then start recognition
655
- this.speakQuestion(this.seedQuestions[0], () => {
656
- setTimeout(() => {
657
- this.startRecognitionWithRecording(0);
658
- }, 2000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  });
660
  }
661
 
662
- speakQuestion(text: string, onEnd?: () => void) {
663
- const synth = window.speechSynthesis;
664
- if (!synth) {
665
- if (onEnd) onEnd();
 
666
  return;
667
  }
668
- const utter = new SpeechSynthesisUtterance(text);
669
- utter.lang = 'en-IN';
670
- synth.cancel(); // Stop any previous speech
671
- utter.onend = () => { if (onEnd) onEnd(); };
672
- synth.speak(utter);
673
- }
674
-
675
- startRecognitionWithRecording(idx: number) {
676
- const Ctor = window.webkitSpeechRecognition || window.SpeechRecognition;
677
- if (!Ctor) return;
678
  this.recognition = new Ctor();
679
  this.recognition.lang = 'en-IN';
680
- this.recognition.continuous = false;
681
- this.recognition.interimResults = false;
682
- let recordingStarted = false;
683
- const startTime = Date.now();
684
- // Timeout logic for 5 seconds if no answer
685
- const silenceTimeout = setTimeout(() => {
686
- if (!recordingStarted) {
687
- this.recognition.stop();
688
- // Do NOT set isRecording = false here
689
- this.status.set('idle');
690
- setTimeout(() => this.playNextQuestion(idx + 1), 5000);
691
- }
692
- }, 5000);
693
  this.recognition.onresult = (event: any) => {
694
- let finalText = '';
695
- for (let i = event.resultIndex; i < event.results.length; i++) {
696
- const result = event.results[i];
697
- if (result.isFinal) {
698
- finalText += result[0].transcript.trim();
699
- }
700
- }
701
- if (!recordingStarted) {
702
- recordingStarted = true;
703
- clearTimeout(silenceTimeout); // Cancel timeout if answer detected
704
  }
705
- this.transcriptLines.push(finalText);
 
 
 
 
 
 
 
706
  };
707
  this.recognition.onend = () => {
708
- this.isRecording = false;
 
709
  this.status.set('processing');
710
- setTimeout(() => this.playNextQuestion(idx + 1), 5000);
711
  };
712
- this.recognition.onerror = () => {
713
- this.isRecording = false;
714
  this.status.set('idle');
715
  };
716
  this.recognition.start();
717
  }
718
 
719
- playNextQuestion(idx: number) {
720
- if (idx >= this.seedQuestions.length) {
721
- this.currentQuestionText = '';
722
- this.progress = 100;
723
- this.showSummary = true;
724
- this.status.set('idle');
725
- this.cdr.detectChanges();
726
- this.isRecording = false;
727
- return;
728
- }
729
- this.currentQuestionText = this.seedQuestions[idx];
730
- this.cdr.detectChanges();
731
- this.status.set('asking'); // Set status to asking
732
- this.isRecording = false; // Not recording yet
733
- this.speakQuestion(this.seedQuestions[idx], () => {
734
- // After TTS, wait 5 seconds, then start recording
735
- setTimeout(() => {
736
- this.status.set('recording');
737
- this.isRecording = true;
738
- this.startRecognitionWithRecording(idx);
739
- }, 5000);
740
- });
741
  }
742
 
743
- startQuestionFlow(idx: number) {
744
- if (idx >= this.seedQuestions.length) {
745
- this.currentQuestionText = '';
746
- this.progress = 100;
747
- this.showSummary = true;
748
- this.status.set('idle');
749
- return;
750
- }
751
- this.currentQuestionText = this.seedQuestions[idx];
752
- this.status.set('asking');
753
- setTimeout(() => {
754
- this.status.set('recording');
755
- this.isRecording = true;
756
- this.isProcessing = false;
757
- this.transcriptLines = [];
758
- this.startRecognition('en-IN');
759
- }, 1200); // 1.2s delay for "Asking..."
 
 
 
 
 
 
760
  }
761
 
762
- get currentQuestionIndex(): number {
763
- return this.seedQuestions.indexOf(this.currentQuestionText) + 1;
764
- }
765
- get totalQuestions(): number {
766
- return this.seedQuestions.length;
767
  }
768
- startVoice() {
769
- // Add logic to start voice recording or recognition here
770
- this.status.set('recording');
 
 
771
  }
772
  }
 
2
  import { CommonModule } from '@angular/common';
3
  import { Router, NavigationStart } from '@angular/router';
4
  import { Subscription } from 'rxjs';
5
+ import { FormsModule } from '@angular/forms';
6
 
7
  declare global {
8
  interface Window {
 
20
  audioUrl: string;
21
  startedAt: number;
22
  endedAt: number;
23
+ skipped?: boolean;
24
  };
25
 
26
  @Component({
27
  standalone: true,
28
  selector: 'app-py-detect',
29
+ imports: [CommonModule, FormsModule],
30
  templateUrl: './py-detect.component.html',
31
  styleUrls: ['./py-detect.component.css']
32
  })
33
  export class PyDetectComponent implements OnInit, OnDestroy {
34
+ public showDetailsPanel: boolean = false;
35
+ public metadata: any = null;
36
 
37
  // ---- UI state ----
38
  status = signal<'idle' | 'asking' | 'idle-wait' | 'recording' | 'processing'>('idle');
 
90
  private analyserWindowMs = 100; // Declare analyserWindowMs property
91
 
92
  // Example question source (replace with API call when ready)
93
+ public seedQuestions = [
94
  'Please introduce yourself in two sentences.',
95
  'What motivates you to take on challenging tasks?',
96
  'Describe a situation where you solved a tough problem.',
 
113
  public videoAnswers: Blob[] = [];
114
 
115
  // UI properties for template
116
+ caseId: string = '';
117
+ crimeType: string = '';
118
+ dateTime: string = '';
119
+ location: string = '';
120
+ suspectName: string = '';
121
+ statusText: string = '';
122
+ investigationOfficer: string = '';
123
  progress: number = 0;
124
+ progressStage: string = '';
125
+ sessionTime: string = '';
 
126
  isRecording: boolean = false;
127
  isProcessing: boolean = false;
128
  transcriptLines: string[] = [];
129
  showSummary: boolean = false;
130
  summaryData: { question: string; answer: string; duration: number }[] = [];
131
 
132
+ // Evidence panel state and placeholder data
133
+ public showEvidencePanel: boolean = false;
134
+ public showSummaryPanel: boolean = false;
135
+ public uploadedDocuments: string[] = ['Report.pdf', 'Statement.docx'];
136
+ public capturedPhotos: string[] = ['Photo1.jpg', 'Photo2.png'];
137
+ public previousRecordings: string[] = ['Recording1.webm', 'Recording2.mp3'];
138
+
139
+ // Info text for HUD below question
140
+ public floatingInfoText: string | null = null;
141
+ public infoText: string | null = null;
142
+
143
+ // Elapsed and remaining time for recording status
144
+ public elapsedTime: string = '00:00';
145
+ public remainingTime: string = '00:00';
146
+ private recordingTimerInterval: any;
147
+
148
+ // Repeat current question using TTS
149
+ public async repeatQuestion() {
150
+ await this.speakQuestion(this.currentQuestionText);
151
+ await this.sleep(1000);
152
+ this.infoText = 'Recording in progress – Listening to answer.';
153
+ }
154
+
155
  // Navigate back to the homepage
156
+
157
  navigateHome() {
158
  if (window.speechSynthesis) {
159
+ window.speechSynthesis.cancel();
160
  }
161
+ this.router.navigate(['/']);
162
  }
163
 
 
164
  goToInfoPage() {
165
  if (window.speechSynthesis) {
166
+ window.speechSynthesis.cancel();
167
  }
168
  this.router.navigate(['/infopage']);
169
  }
170
 
171
+ navigateBackToCaseDetails() {
172
+ if (window.speechSynthesis) {
173
+ window.speechSynthesis.cancel();
174
+ }
175
+ // Stop any <audio> elements
176
+ const audioElems = document.querySelectorAll('audio');
177
+ audioElems.forEach((audioElem) => {
178
+ (audioElem as HTMLAudioElement).pause();
179
+ (audioElem as HTMLAudioElement).currentTime = 0;
180
+ });
181
+ // Stop custom Audio object if present
182
+ if ((window as any).pyDetectAudioObj && typeof (window as any).pyDetectAudioObj.pause === 'function') {
183
+ (window as any).pyDetectAudioObj.pause();
184
+ (window as any).pyDetectAudioObj.currentTime = 0;
185
+ }
186
+ // Stop any media streams
187
+ if (this.mediaStream) {
188
+ this.mediaStream.getTracks().forEach(t => t.stop());
189
+ this.mediaStream = undefined;
190
+ }
191
+ this.router.navigate(['/case-details']);
192
+ }
193
+
194
+
195
  // ======== Lifecycle ========
196
  ngOnInit(): void {
197
  this.isActive = true;
198
+ // Do NOT set isRecording = true here; only set when question starts
199
+
200
+ let metadata: any = null;
201
+ const nav = this.router.getCurrentNavigation();
202
+ if (nav?.extras?.state && nav.extras.state['metadata']) {
203
+ metadata = nav.extras.state['metadata'];
204
+ localStorage.setItem('pyDetectMetadata', JSON.stringify(metadata)); // Update localStorage on navigation
205
+ console.log('Received metadata from navigation:', metadata);
206
+ } else {
207
+ // Fallback: read from localStorage
208
+ const stored = localStorage.getItem('pyDetectMetadata');
209
+ if (stored) {
210
+ metadata = JSON.parse(stored);
211
+ console.log('Loaded metadata from localStorage:', metadata);
212
+ } else {
213
+ console.warn('No metadata found in navigation state or localStorage');
214
+ }
215
+ }
216
+ if (metadata) {
217
+ this.caseId = metadata.caseId || '';
218
+ this.crimeType = metadata.crimeType || '';
219
+ this.dateTime = metadata.dateTime || '';
220
+ this.location = metadata.location || '';
221
+ this.suspectName = metadata.suspectName || '';
222
+ this.statusText = metadata.status || '';
223
+ this.investigationOfficer = metadata.investigationOfficer || '';
224
+ this.progress = metadata.progress || 0;
225
+ this.progressStage = metadata.progressStage || '';
226
+ this.sessionTime = metadata.sessionTime || '';
227
+ }
228
  }
229
 
230
  ngOnDestroy(): void {
 
318
  }
319
  }
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  private nextQuestionLoopRunning = false;
322
 
323
  private async nextQuestionLoop(): Promise<void> {
 
325
  // 1) Get next question
326
  const q = await this.fetchNextQuestion();
327
  this.currentQuestion.set(q);
328
+ this.status.set('asking'); // Show listening HUD
329
  if (this.ttsEnabled()) {
330
  await this.speak(q);
331
  }
332
+ // Start camera and audio recording
333
+ await this.startCamera();
334
+ this.status.set('recording'); // Show recording HUD
335
+ await this.startVideoRecording();
 
 
336
  this.transcriptSoFar = '';
337
  this.startRecognition('en-IN');
338
+ let silenceTimer: any;
339
+ let lastTranscript = '';
340
+ let silenceStart: number | null = null;
341
+ let answerDone = false;
342
+ // Wait for user to start speaking, then monitor for silence
343
  await new Promise<void>((resolve) => {
344
+ const poll = () => {
345
+ const currentTranscript = this.transcriptSoFar.trim();
346
+ if (currentTranscript.length > 0) {
347
+ if (lastTranscript !== currentTranscript) {
348
+ lastTranscript = currentTranscript;
349
+ silenceStart = null;
350
+ } else {
351
+ if (!silenceStart) silenceStart = Date.now();
352
+ if (Date.now() - silenceStart > 10000) {
353
+ answerDone = true;
354
+ resolve();
355
+ return;
356
+ }
357
+ }
358
  }
359
+ setTimeout(poll, 500);
360
  };
361
+ poll();
362
  });
363
+ // Stop recognition and video recording
 
364
  this.stopRecognition();
365
  this.stopVideoRecording();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  this.status.set('processing');
367
  await this.sleep(700);
368
+ this.questionIndex.set(this.questionIndex() + 1);
369
  }
370
  this.status.set('idle');
371
  this.stopVideoRecording();
 
610
  };
611
  }
612
 
613
+ private removeFillerWords(text: string): string {
614
  const fillerWords = ['um', 'ah', 'like', 'you know', 'so', 'actually', 'basically'];
615
  const regex = new RegExp(`\\b(${fillerWords.join('|')})\\b`, 'gi');
616
  return text.replace(regex, '').replace(/\s+/g, ' ').trim(); // Clean extra spaces after removal
 
686
  }
687
  }
688
 
689
+ public stopCamera() {
690
+ if (this.videoStream) {
691
+ this.videoStream.getTracks().forEach((track: any) => track.stop());
692
+ this.videoStream = undefined;
693
+ }
694
+ this.videoStatus = 'Camera stopped';
695
+ }
696
+
697
  // UI methods for template
698
  onStartInterview() {
699
+ this.currentQuestionIndex = 0;
700
  this.progress = 0;
701
  this.isRecording = false;
702
  this.isProcessing = false;
703
  }
704
+ public submitAll() {
705
+ // Do NOT stop audio or video playback here
706
+ this.videoStatus = 'Submitted!';
707
+ this.startDisabled.set(true);
708
+ this.stopDisabled.set(true);
709
+ this.resumeDisabled.set(true);
710
+ this.submitDisabled.set(true);
711
+ // TODO: Upload all videoAnswers to backend
712
+
713
+ // Example: Calculate dummy percentages (replace with real logic)
714
+ const truePercentage = 70;
715
+ const falsePercentage = 30;
716
+ this.router.navigate(['/validationpage'], { state: { truePercentage, falsePercentage } });
717
+ }
718
+
719
+ // Start Investigation workflow
720
+ public async onStartRecording() {
721
+ this.floatingInfoText = 'Starting camera and microphone...';
722
+ this.infoText = null;
723
+ await this.sleep(1200); // Wait for UI effect
724
+ await this.startCamera();
725
+ await this.startVideoRecording();
726
+ this.floatingInfoText = 'Camera and microphone ready.';
727
+ await this.sleep(800);
728
+ this.floatingInfoText = null;
729
+ this.currentQuestionIndex = 0; // Ensure first question is selected
730
+ this.askCurrentQuestion(); // Automatically ask the first question
731
+ }
732
+
733
+ // Refactor askCurrentQuestion to allow time for user to answer after silence
734
+ public async askCurrentQuestion(): Promise<void> {
735
+ if (this.currentQuestionIndex >= this.seedQuestions.length) {
736
+ // All questions done
737
+ this.floatingInfoText = 'Investigation complete. Camera stopped.';
738
+ this.stopVideoRecording();
739
+ this.stopCamera();
740
+ setTimeout(() => {
741
+ this.floatingInfoText = null;
742
+ this.showSummary = true;
743
+ }, 2000);
744
+ return;
745
+ }
746
+
747
+ // Start camera and video recording for each question
748
+ await this.startCamera();
749
+ await this.startVideoRecording();
750
+
751
+ // Show "Recording..." only during active questions
752
+ this.isRecording = (this.currentQuestionIndex >= 0 && this.currentQuestionIndex < this.totalQuestions);
753
+ this.infoText = 'Recording in progress – Asking question.';
754
+
755
+ // Start audio recording in parallel
756
+ this.startRecognitionWithRecording(this.currentQuestionIndex);
757
+
758
+ await this.speakQuestion(this.currentQuestionText);
759
+ await this.sleep(1000);
760
+
761
+ this.infoText = 'Recording in progress – Listening to answer.';
762
+
763
+ await this.sleep(10000); // Give user time to answer
764
+
765
+ // Hide recording status bar and indicator before processing
766
+ this.isRecording = false;
767
+ // Stop audio and video recording after answer
768
+ this.stopRecognition();
769
+ this.stopVideoRecording();
770
+ this.infoText = 'Processing your question and answer...';
771
+ await this.sleep(5000); // Simulate backend save
772
+
773
+ // Hide recording status bar and indicator before showing saved text
774
  this.isRecording = false;
775
+ this.infoText = 'Saved successfully.';
776
+ await this.sleep(1000);
777
+ this.infoText = null;
778
+
779
+ this.currentQuestionIndex++;
780
+ await this.askCurrentQuestion();
781
  }
782
+
783
+ // Move to next question
784
+ public nextQuestion() {
785
+ this.currentQuestionIndex++;
786
+ if (this.currentQuestionIndex < this.seedQuestions.length) {
787
+ this.askCurrentQuestion();
788
  } else {
789
+ this.infoText = null;
790
+ this.floatingInfoText = 'Session complete.';
791
+ setTimeout(() => this.floatingInfoText = null, 2000);
792
  }
793
  }
794
+
795
+ // Add evidence summary and file handling
796
+ public evidenceSummary: string = '';
797
+ public evidenceFiles: { document?: File, photo?: File, recording?: File } = {};
798
+
799
+ public onEvidenceFileSelect(event: any, type: 'document' | 'photo' | 'recording') {
800
+ const file = event.target.files && event.target.files[0];
801
+ if (file) {
802
+ this.evidenceFiles[type] = file;
803
+ }
804
  }
805
+
806
+ public uploadDocument() {
807
+ if (this.evidenceFiles.document) {
808
+ this.uploadedDocuments.push(this.evidenceFiles.document.name + (this.evidenceSummary ? ' - ' + this.evidenceSummary : ''));
809
+ this.evidenceFiles.document = undefined;
810
+ this.evidenceSummary = '';
811
+ }
812
+ if (this.evidenceFiles.photo) {
813
+ this.capturedPhotos.push(this.evidenceFiles.photo.name);
814
+ this.evidenceFiles.photo = undefined;
815
+ }
816
+ if (this.evidenceFiles.recording) {
817
+ this.previousRecordings.push(this.evidenceFiles.recording.name);
818
+ this.evidenceFiles.recording = undefined;
819
+ }
820
+ this.showEvidencePanel = false; // Automatically close the panel after submit
821
+ }
822
+
823
+ // Add missing properties and methods for workflow
824
+ public currentQuestionIndex: number = -1;
825
+ public get totalQuestions(): number { return this.seedQuestions.length; }
826
+ public liveTranscription: string = '';
827
+ public get currentQuestionText(): string {
828
+ return this.currentQuestionIndex >= 0 ? this.seedQuestions[this.currentQuestionIndex] : '';
829
+ }
830
+
831
+ // TTS method
832
+ public async speakQuestion(text: string): Promise<void> {
833
+ return new Promise<void>((resolve) => {
834
+ const utterance = new SpeechSynthesisUtterance(text);
835
+ utterance.lang = 'en-IN';
836
+ utterance.pitch = 1;
837
+ utterance.rate = 1;
838
+ utterance.volume = 1;
839
+ utterance.onend = () => resolve();
840
+ window.speechSynthesis.cancel();
841
+ window.speechSynthesis.speak(utterance);
842
  });
843
  }
844
 
845
+ // Speech recognition stub
846
+ public startRecognitionWithRecording(idx: number) {
847
+ const Ctor = (window as any).webkitSpeechRecognition || (window as any).SpeechRecognition;
848
+ if (!Ctor) {
849
+ alert('Speech Recognition not supported on this browser.');
850
  return;
851
  }
 
 
 
 
 
 
 
 
 
 
852
  this.recognition = new Ctor();
853
  this.recognition.lang = 'en-IN';
854
+ this.recognition.continuous = true;
855
+ this.recognition.interimResults = true;
856
+ let hasStartedSpeaking = false;
857
+ let silenceTimer: any = null;
 
 
 
 
 
 
 
 
 
858
  this.recognition.onresult = (event: any) => {
859
+ const transcript = Array.from(event.results)
860
+ .map((res: any) => res[0].transcript)
861
+ .join(' ')
862
+ .trim();
863
+ this.liveTranscription = transcript; // <-- This is critical!
864
+ if (transcript.length > 0 && !hasStartedSpeaking) {
865
+ hasStartedSpeaking = true;
866
+ this.isRecording = true;
 
 
867
  }
868
+ // Reset silence detection timer
869
+ clearTimeout(silenceTimer);
870
+ silenceTimer = setTimeout(() => {
871
+ if (hasStartedSpeaking) {
872
+ // Only stop recognition, do not set isRecording = false here
873
+ this.recognition.stop();
874
+ }
875
+ }, 2000); // Stop after 2 seconds of silence
876
  };
877
  this.recognition.onend = () => {
878
+ clearTimeout(silenceTimer);
879
+ // Do NOT set isRecording = false here
880
  this.status.set('processing');
881
+ // Do NOT auto-advance to next question here. Wait for user to click Next Question.
882
  };
883
+ this.recognition.onerror = (err: any) => {
884
+ // Do NOT set isRecording = false here
885
  this.status.set('idle');
886
  };
887
  this.recognition.start();
888
  }
889
 
890
+ public closeSummary() {
891
+ this.showSummary = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
892
  }
893
 
894
+ // Navigate to the validation page
895
+ navigateToValidationPage() {
896
+ this.router.navigate(['/validationpage']);
897
+ }
898
+
899
+ // Start timer when recording starts
900
+ public startRecordingTimer(durationSeconds: number) {
901
+ let elapsed = 0;
902
+ let remaining = durationSeconds;
903
+ this.elapsedTime = '00:00';
904
+ this.remainingTime = this.formatTime(durationSeconds);
905
+ clearInterval(this.recordingTimerInterval);
906
+ this.recordingTimerInterval = setInterval(() => {
907
+ if (this.isRecording) {
908
+ elapsed++;
909
+ remaining--;
910
+ this.elapsedTime = this.formatTime(elapsed);
911
+ this.remainingTime = this.formatTime(remaining);
912
+ if (remaining <= 0) {
913
+ clearInterval(this.recordingTimerInterval);
914
+ }
915
+ }
916
+ }, 1000);
917
  }
918
 
919
+ public stopRecordingTimer() {
920
+ clearInterval(this.recordingTimerInterval);
 
 
 
921
  }
922
+
923
+ private formatTime(seconds: number): string {
924
+ const m = Math.floor(seconds / 60).toString().padStart(2, '0');
925
+ const s = (seconds % 60).toString().padStart(2, '0');
926
+ return `${m}:${s}`;
927
  }
928
  }
src/app/summary-toggle-button/summary-toggle-button.component.css ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .summary-toggle-bar {
2
+ display: flex;
3
+ align-items: center;
4
+ width: 100%;
5
+ margin-bottom: 18px;
6
+ }
7
+ .summary-toggle-btn {
8
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
9
+ font-size: 1.05rem;
10
+ font-weight: 700;
11
+ letter-spacing: 2px;
12
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
13
+ color: #e3f6ff;
14
+ box-shadow: 0 2px 16px #38bdf888;
15
+ border: none;
16
+ border-radius: 12px;
17
+ padding: 0.55rem 1.3rem;
18
+ margin: 0;
19
+ cursor: pointer;
20
+ transition: background 0.4s, box-shadow 0.4s, color 0.3s, transform 0.2s;
21
+ overflow: hidden;
22
+ }
23
+ .summary-toggle-btn:hover {
24
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
25
+ color: #bae6fd;
26
+ box-shadow: 0 2px 24px #bae6fd88;
27
+ }
28
+ .summary-icon {
29
+ font-size: 1.2em;
30
+ margin-right: 6px;
31
+ }
src/app/validationpage/validationpage.component.css CHANGED
@@ -1,21 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /* Professional, modern look for validation results */
2
  .validation-result-container {
3
  display: flex;
4
  flex-direction: column;
5
  align-items: center;
6
- justify-content: center;
7
- min-height: 100vh;
8
- background: linear-gradient(120deg, #23272b 0%, #38bdf8 100%);
9
  position: relative;
 
 
10
  }
11
 
12
  h2 {
13
- font-size: 2.6rem;
14
- font-weight: 900;
15
- color: #e3f6ff;
16
- margin-bottom: 32px;
 
17
  letter-spacing: 2px;
18
- text-shadow: 0 2px 16px #1e293b88;
 
 
 
 
 
 
19
  }
20
 
21
  .percentage-box {
@@ -106,3 +344,558 @@ h2 {
106
  0% { box-shadow: 0 2px 16px #38bdf888; transform: scale(1); }
107
  100% { box-shadow: 0 2px 32px #bae6fd88; transform: scale(1.07); }
108
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Modern UI header styles from infopage */
2
+ .site-header {
3
+ background: #011329;
4
+ box-shadow: 0 2px 12px #38bdf844;
5
+ margin-bottom: 0;
6
+ position: relative;
7
+ z-index: 10;
8
+ padding-bottom: 0;
9
+ }
10
+
11
+ /* Validation Report Template Styles */
12
+ .validation-template-main {
13
+ margin-top: 32px;
14
+ width: 100%;
15
+ display: flex;
16
+ justify-content: center;
17
+ }
18
+ .validation-template-flex {
19
+ display: flex;
20
+ flex-direction: row;
21
+ max-width: 1200px;
22
+ width: 100%;
23
+ gap: 32px;
24
+ }
25
+ .validation-template-left {
26
+ display: flex;
27
+ flex-direction: column;
28
+ align-items: center;
29
+ justify-content: flex-start;
30
+ gap: 32px;
31
+ min-width: 180px;
32
+ }
33
+ .validation-circle {
34
+ width: 140px;
35
+ height: 140px;
36
+ border-radius: 50%;
37
+ display: flex;
38
+ flex-direction: column;
39
+ align-items: center;
40
+ justify-content: center;
41
+ box-shadow: 0 2px 16px #38bdf844;
42
+ margin-bottom: 8px;
43
+ }
44
+ .validation-circle.truth {
45
+ background: linear-gradient(135deg, #bae6fd 0%, #38bdf8 100%);
46
+ }
47
+ .validation-circle.inconsistency {
48
+ background: linear-gradient(135deg, #fee2e2 0%, #991b1b 100%);
49
+ }
50
+ .circle-value {
51
+ font-size: 2.4rem;
52
+ font-weight: 900;
53
+ color: #2563eb;
54
+ margin-bottom: 8px;
55
+ }
56
+ .validation-circle.inconsistency .circle-value {
57
+ color: #991b1b;
58
+ }
59
+ .circle-label {
60
+ font-size: 1rem;
61
+ color: #6366f1;
62
+ text-align: center;
63
+ }
64
+ .validation-template-right {
65
+ flex: 1;
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: 24px;
69
+ }
70
+ .validation-section {
71
+ background: #fff;
72
+ border-radius: 12px;
73
+ box-shadow: 0 2px 16px #2563eb22;
74
+ margin-bottom: 0;
75
+ padding-bottom: 0;
76
+ }
77
+ .section-header {
78
+ font-size: 1.15rem;
79
+ font-weight: 700;
80
+ padding: 12px 24px;
81
+ border-radius: 12px 12px 0 0;
82
+ letter-spacing: 1px;
83
+ }
84
+ .details-header {
85
+ background: #fbbf24;
86
+ color: #23272b;
87
+ }
88
+ .incident-header {
89
+ background: #ef4444;
90
+ color: #fff;
91
+ }
92
+ .followup-header {
93
+ background: #14b8a6;
94
+ color: #fff;
95
+ }
96
+ .section-table {
97
+ width: 100%;
98
+ display: flex;
99
+ flex-direction: column;
100
+ padding: 0 24px 18px 24px;
101
+ }
102
+ .section-row {
103
+ display: flex;
104
+ border-bottom: 1px solid #e5e7eb;
105
+ padding: 8px 0;
106
+ }
107
+ .section-row:last-child {
108
+ border-bottom: none;
109
+ }
110
+ .section-cell {
111
+ flex: 1;
112
+ font-size: 1rem;
113
+ color: #23272b;
114
+ padding-right: 16px;
115
+ word-break: break-word;
116
+ }
117
+ .section-cell[colspan="3"] {
118
+ flex: 3;
119
+ }
120
+ @media (max-width: 900px) {
121
+ .validation-template-flex {
122
+ flex-direction: column;
123
+ align-items: center;
124
+ }
125
+ .validation-template-right {
126
+ width: 100%;
127
+ }
128
+ }
129
+
130
+ .header-inner {
131
+ display: flex;
132
+ align-items: center;
133
+ justify-content: flex-start;
134
+ padding: 18px 32px 0 32px;
135
+ position: relative;
136
+ }
137
+
138
+ .logo-cluster {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 18px;
142
+ }
143
+
144
+ .logo-img-header {
145
+ width: 54px;
146
+ height: 54px;
147
+ border-radius: 50%;
148
+ background: #fff;
149
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
150
+ padding: 4px;
151
+ margin-top: -6px;
152
+ margin-bottom: 1vh;
153
+ }
154
+
155
+ .py-detect-title-header {
156
+ font-size: 2.1rem;
157
+ font-family: 'Segoe UI', 'Arial', 'Roboto', sans-serif;
158
+ font-weight: 900;
159
+ letter-spacing: 6px;
160
+ color: #38bdf8;
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 2px;
164
+ margin-bottom: 1.5vh;
165
+ }
166
+
167
+ .py-detect-title-header .py-letter.p {
168
+ color: #e3f6ff;
169
+ text-shadow: 0 0 6px #38bdf8;
170
+ }
171
+
172
+ .py-detect-title-header .py-letter.y {
173
+ color: #38bdf8;
174
+ text-shadow: 0 0 6px #38bdf8;
175
+ }
176
+
177
+ .py-detect-title-header .py-shape {
178
+ color: #e3f6ff;
179
+ background: #e3f6ff;
180
+ text-shadow: 0 0 6px #38bdf8;
181
+ box-shadow: 0 0 6px #38bdf8, 0 0 2px #fff;
182
+ border: 2px solid #23272b;
183
+ width: 18px;
184
+ height: 4px;
185
+ display: inline-block;
186
+ margin: 0 8px;
187
+ border-radius: 2px;
188
+ }
189
+
190
+ .py-detect-title-header .py-letter.d {
191
+ color: #e3f6ff;
192
+ text-shadow: 0 0 6px #38bdf8;
193
+ }
194
+
195
+ .py-detect-title-header .py-letter.e {
196
+ color: #38bdf8;
197
+ text-shadow: 0 0 6px #38bdf8;
198
+ }
199
+
200
+ .py-detect-title-header .py-letter.t {
201
+ color: #e3f6ff;
202
+ text-shadow: 0 0 6px #38bdf8;
203
+ }
204
+
205
+ .py-detect-title-header .py-letter.e2 {
206
+ color: #38bdf8;
207
+ text-shadow: 0 0 6px #38bdf8;
208
+ }
209
+
210
+ .py-detect-title-header .py-letter.c {
211
+ color: #e3f6ff;
212
+ text-shadow: 0 0 6px #38bdf8;
213
+ }
214
+
215
+ .py-detect-title-header .py-letter.t2 {
216
+ color: #38bdf8;
217
+ text-shadow: 0 0 6px #38bdf8;
218
+ }
219
+
220
+
221
+ /* Footer */
222
+ footer {
223
+ background: linear-gradient(to right, #011022, #01030a);
224
+ color: #fff;
225
+ text-align: center;
226
+ padding: 10px 0px;
227
+ position: static;
228
+ width: 100%;
229
+ }
230
+
231
  /* Professional, modern look for validation results */
232
  .validation-result-container {
233
  display: flex;
234
  flex-direction: column;
235
  align-items: center;
236
+ justify-content: flex-start;
237
+ background: #f6f8fa;
 
238
  position: relative;
239
+ padding-bottom: 64px; /* Ensure space for footer */
240
+ min-height: 0;
241
  }
242
 
243
  h2 {
244
+ font-size: 2.4rem;
245
+ font-weight: 800;
246
+ color: #2563eb;
247
+ margin-top: 40px;
248
+ margin-bottom: 28px;
249
  letter-spacing: 2px;
250
+ text-align: center;
251
+ background: linear-gradient(90deg, #2563eb 0%, #38bdf8 100%);
252
+ -webkit-background-clip: text;
253
+ -webkit-text-fill-color: transparent;
254
+ text-shadow: 0 2px 12px #38bdf844;
255
+ border-bottom: 2px solid #38bdf8;
256
+ padding-bottom: 12px;
257
  }
258
 
259
  .percentage-box {
 
344
  0% { box-shadow: 0 2px 16px #38bdf888; transform: scale(1); }
345
  100% { box-shadow: 0 2px 32px #bae6fd88; transform: scale(1.07); }
346
  }
347
+
348
+ /* Summary card for investigation evaluation */
349
+ .summary-card {
350
+ margin-top: 32px;
351
+ background: rgba(56, 189, 248, 0.08);
352
+ border-radius: 14px;
353
+ border: 2px solid rgba(99, 102, 241, 0.15);
354
+ box-shadow: 0 0 24px rgba(99, 102, 241, 0.12);
355
+ padding: 24px 32px;
356
+ max-width: 420px;
357
+ text-align: left;
358
+ display: flex;
359
+ flex-direction: column;
360
+ align-items: flex-start;
361
+ }
362
+ .summary-title {
363
+ font-size: 1.25rem;
364
+ font-weight: 700;
365
+ color: #2563eb;
366
+ margin-bottom: 10px;
367
+ letter-spacing: 1px;
368
+ }
369
+ .summary-text {
370
+ font-size: 1.08rem;
371
+ color: #23272b;
372
+ font-weight: 500;
373
+ margin: 0;
374
+ }
375
+
376
+ /* Status chips/badges for result */
377
+ .status-chip {
378
+ display: inline-block;
379
+ font-size: 0.85rem;
380
+ padding: 4px 10px;
381
+ border-radius: 8px;
382
+ font-weight: 600;
383
+ }
384
+ .status-chip.active { background: #dcfce7; color: #15803d; }
385
+ .status-chip.archived { background: #fee2e2; color: #991b1b; }
386
+ /* Modal styles for report summary */
387
+ .modal-overlay {
388
+ position: fixed;
389
+ top: 0; left: 0; right: 0; bottom: 0;
390
+ background: rgba(30,41,59,0.65);
391
+ z-index: 2000;
392
+ display: flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ animation: fadeInUp 0.4s;
396
+ }
397
+ .modal-content {
398
+ background: #fff;
399
+ border-radius: 18px;
400
+ box-shadow: 0 8px 32px #38bdf844, 0 2px 16px #6366f144;
401
+ padding: 36px 44px 28px 44px;
402
+ min-width: 340px;
403
+ max-width: 480px;
404
+ color: #23272b;
405
+ position: relative;
406
+ outline: none;
407
+ }
408
+ .modal-title {
409
+ font-size: 2rem;
410
+ font-weight: 800;
411
+ color: #2563eb;
412
+ margin-bottom: 18px;
413
+ letter-spacing: 1px;
414
+ }
415
+ .modal-section {
416
+ margin-bottom: 18px;
417
+ }
418
+ .modal-close {
419
+ position: absolute;
420
+ top: 18px;
421
+ right: 24px;
422
+ background: none;
423
+ border: none;
424
+ font-size: 2rem;
425
+ color: #2563eb;
426
+ cursor: pointer;
427
+ z-index: 10;
428
+ }
429
+ .report-actions, .modal-section h3 {
430
+ margin-top: 18px;
431
+ }
432
+ .report-btn {
433
+ background: linear-gradient(90deg, #2563eb 0%, #38bdf8 100%);
434
+ color: #fff;
435
+ font-weight: 700;
436
+ border: none;
437
+ border-radius: 999px;
438
+ padding: 0.5rem 1.4rem;
439
+ margin-right: 12px;
440
+ margin-bottom: 8px;
441
+ cursor: pointer;
442
+ box-shadow: 0 2px 16px #38bdf888;
443
+ display: inline-flex;
444
+ align-items: center;
445
+ gap: 8px;
446
+ font-size: 1rem;
447
+ transition: background 0.2s, box-shadow 0.2s;
448
+ }
449
+ .report-btn:hover {
450
+ background: linear-gradient(90deg, #38bdf8 0%, #2563eb 100%);
451
+ color: #bae6fd;
452
+ box-shadow: 0 2px 24px #bae6fd88;
453
+ }
454
+ .icon-report::before {
455
+ content: "\1F4C4";
456
+ font-size: 1.2em;
457
+ margin-right: 4px;
458
+ }
459
+ .icon-download::before {
460
+ content: "\1F4BE";
461
+ font-size: 1.2em;
462
+ margin-right: 4px;
463
+ }
464
+ .icon-email::before {
465
+ content: "\2709";
466
+ font-size: 1.2em;
467
+ margin-right: 4px;
468
+ }
469
+ .modal-content h3 {
470
+ font-size: 1.1rem;
471
+ color: #2563eb;
472
+ font-weight: 700;
473
+ margin-bottom: 6px;
474
+ }
475
+ .modal-content p {
476
+ font-size: 1rem;
477
+ color: #23272b;
478
+ margin-bottom: 0;
479
+ }
480
+
481
+ /* Dashboard header styles */
482
+ .dashboard-header {
483
+ background: linear-gradient(90deg, rgba(30,41,59,0.92) 0%, #38bdf8 100%);
484
+ padding: 32px 0 24px 0;
485
+ color: #fff;
486
+ box-shadow: 0 2px 16px #2563eb44;
487
+ position: relative;
488
+ }
489
+ .dashboard-header-content {
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ max-width: 1200px;
494
+ margin: 0 auto;
495
+ padding: 0 32px;
496
+ }
497
+ .dashboard-logo {
498
+ width: 64px;
499
+ height: 64px;
500
+ border-radius: 50%;
501
+ background: #fff;
502
+ box-shadow: 0 2px 8px #38bdf844;
503
+ margin-right: 24px;
504
+ }
505
+ .dashboard-title-block {
506
+ display: flex;
507
+ flex-direction: column;
508
+ align-items: flex-start;
509
+ }
510
+ .dashboard-title {
511
+ font-size: 2.2rem;
512
+ font-weight: 900;
513
+ letter-spacing: 2px;
514
+ color: #fff;
515
+ }
516
+ .dashboard-date {
517
+ font-size: 1.1rem;
518
+ color: #bae6fd;
519
+ margin-top: 4px;
520
+ }
521
+ .header-btns-right {
522
+ display: flex;
523
+ align-items: center;
524
+ gap: 16px;
525
+ }
526
+
527
+ /* Main dashboard content */
528
+ .dashboard-main {
529
+ background: #f6f8fa;
530
+ min-height: 100vh;
531
+ padding: 32px 0 0 0;
532
+ }
533
+ .dashboard-cards {
534
+ display: flex;
535
+ flex-wrap: wrap;
536
+ gap: 32px;
537
+ max-width: 1200px;
538
+ margin: 0 auto;
539
+ justify-content: center;
540
+ }
541
+ .dashboard-card {
542
+ background: #fff;
543
+ border-radius: 18px;
544
+ box-shadow: 0 4px 24px #2563eb22;
545
+ padding: 32px 28px;
546
+ min-width: 280px;
547
+ max-width: 340px;
548
+ flex: 1 1 320px;
549
+ display: flex;
550
+ flex-direction: column;
551
+ align-items: center;
552
+ }
553
+ .dashboard-card-title {
554
+ font-size: 1.25rem;
555
+ font-weight: 700;
556
+ color: #2563eb;
557
+ margin-bottom: 18px;
558
+ letter-spacing: 1px;
559
+ }
560
+ .dashboard-chart-card {
561
+ align-items: center;
562
+ justify-content: center;
563
+ }
564
+ .dashboard-chart-circle {
565
+ width: 120px;
566
+ height: 120px;
567
+ border-radius: 50%;
568
+ background: linear-gradient(135deg, #bae6fd 0%, #38bdf8 100%);
569
+ display: flex;
570
+ align-items: center;
571
+ justify-content: center;
572
+ margin-bottom: 12px;
573
+ box-shadow: 0 2px 16px #38bdf844;
574
+ }
575
+ .dashboard-circle-value {
576
+ font-size: 2.2rem;
577
+ font-weight: 900;
578
+ color: #2563eb;
579
+ text-shadow: 0 2px 12px #38bdf844;
580
+ }
581
+ .dashboard-card-label {
582
+ font-size: 1rem;
583
+ color: #6366f1;
584
+ margin-top: 8px;
585
+ }
586
+ .dashboard-details-card {
587
+ align-items: flex-start;
588
+ }
589
+ .dashboard-details-list {
590
+ width: 100%;
591
+ }
592
+ .dashboard-details-row {
593
+ display: flex;
594
+ justify-content: space-between;
595
+ align-items: center;
596
+ margin-bottom: 14px;
597
+ }
598
+ .dashboard-details-label {
599
+ font-size: 1rem;
600
+ color: #23272b;
601
+ font-weight: 600;
602
+ }
603
+ .dashboard-details-value {
604
+ font-size: 1rem;
605
+ color: #2563eb;
606
+ font-weight: 700;
607
+ }
608
+ .dashboard-actions-card {
609
+ align-items: flex-start;
610
+ }
611
+
612
+ /* Session Overview Card */
613
+ .session-overview-card {
614
+ background: #fbbf24;
615
+ border-radius: 12px;
616
+ box-shadow: 0 2px 12px #fbbf2444;
617
+ margin: 32px auto 18px auto;
618
+ max-width: 700px;
619
+ padding: 18px 32px 12px 32px;
620
+ }
621
+ .session-header {
622
+ font-size: 1.15rem;
623
+ font-weight: 700;
624
+ color: #23272b;
625
+ margin-bottom: 10px;
626
+ }
627
+ .session-fields {
628
+ display: flex;
629
+ flex-wrap: wrap;
630
+ gap: 18px 32px;
631
+ }
632
+ .session-field {
633
+ min-width: 220px;
634
+ font-size: 1rem;
635
+ color: #23272b;
636
+ }
637
+ .field-label {
638
+ font-weight: 600;
639
+ color: #23272b;
640
+ }
641
+ .field-value {
642
+ font-weight: 500;
643
+ color: #2563eb;
644
+ }
645
+
646
+ /* AI Analysis Summary */
647
+ .ai-summary-section {
648
+ background: #fff;
649
+ border-radius: 12px;
650
+ box-shadow: 0 2px 16px #2563eb22;
651
+ margin: 0 auto 24px auto;
652
+ max-width: 900px;
653
+ padding: 24px 32px;
654
+ }
655
+ .ai-summary-header {
656
+ font-size: 1.2rem;
657
+ font-weight: 700;
658
+ color: #2563eb;
659
+ margin-bottom: 18px;
660
+ }
661
+ .ai-metrics-grid {
662
+ display: grid;
663
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
664
+ gap: 18px 32px;
665
+ }
666
+ .ai-metric {
667
+ display: flex;
668
+ flex-direction: column;
669
+ align-items: flex-start;
670
+ background: #f6f8fa;
671
+ border-radius: 10px;
672
+ padding: 16px 18px;
673
+ box-shadow: 0 2px 8px #2563eb11;
674
+ }
675
+ .metric-label {
676
+ font-size: 1rem;
677
+ font-weight: 600;
678
+ color: #23272b;
679
+ margin-bottom: 8px;
680
+ }
681
+ .metric-ring {
682
+ font-size: 2rem;
683
+ font-weight: 900;
684
+ border-radius: 50%;
685
+ padding: 18px 0;
686
+ width: 90px;
687
+ text-align: center;
688
+ margin-bottom: 6px;
689
+ }
690
+ .metric-blue {
691
+ background: linear-gradient(135deg, #bae6fd 0%, #38bdf8 100%);
692
+ color: #2563eb;
693
+ box-shadow: 0 2px 8px #38bdf844;
694
+ }
695
+ .metric-cyan {
696
+ background: linear-gradient(135deg, #a7f3d0 0%, #38bdf8 100%);
697
+ color: #0e7490;
698
+ }
699
+ .metric-red {
700
+ background: linear-gradient(135deg, #fee2e2 0%, #991b1b 100%);
701
+ color: #991b1b;
702
+ }
703
+ .metric-bar {
704
+ font-size: 1rem;
705
+ font-weight: 700;
706
+ color: #2563eb;
707
+ margin-bottom: 6px;
708
+ }
709
+ .metric-verdict-box {
710
+ background: #fef9c3;
711
+ color: #f59e42;
712
+ font-weight: 700;
713
+ border-radius: 8px;
714
+ padding: 8px 12px;
715
+ margin-top: 6px;
716
+ box-shadow: 0 2px 8px #fbbf2444;
717
+ }
718
+
719
+ /* AI Observations Section */
720
+ .ai-observations-section {
721
+ background: #14b8a6;
722
+ border-radius: 12px;
723
+ box-shadow: 0 2px 16px #14b8a644;
724
+ margin: 0 auto 24px auto;
725
+ max-width: 900px;
726
+ padding: 24px 32px;
727
+ color: #fff;
728
+ }
729
+ .observations-header {
730
+ font-size: 1.15rem;
731
+ font-weight: 700;
732
+ margin-bottom: 12px;
733
+ display: flex;
734
+ align-items: center;
735
+ gap: 8px;
736
+ }
737
+ .ai-icon {
738
+ font-size: 1.3rem;
739
+ }
740
+ .observations-note {
741
+ background: #fff;
742
+ color: #134e4a;
743
+ border-radius: 8px;
744
+ padding: 14px 18px;
745
+ font-size: 1rem;
746
+ font-weight: 500;
747
+ box-shadow: 0 2px 8px #14b8a644;
748
+ }
749
+
750
+ /* Audio–Video Metrics Section */
751
+ .audio-video-metrics-section {
752
+ background: #fff;
753
+ border-radius: 12px;
754
+ box-shadow: 0 2px 16px #2563eb22;
755
+ margin: 0 auto 24px auto;
756
+ max-width: 900px;
757
+ padding: 24px 32px;
758
+ }
759
+ .metrics-header {
760
+ font-size: 1.15rem;
761
+ font-weight: 700;
762
+ color: #2563eb;
763
+ margin-bottom: 12px;
764
+ }
765
+ .metrics-graphs {
766
+ display: flex;
767
+ flex-wrap: wrap;
768
+ gap: 18px;
769
+ }
770
+ .graph-placeholder {
771
+ background: #f6f8fa;
772
+ border-radius: 8px;
773
+ color: #6366f1;
774
+ font-size: 1rem;
775
+ font-weight: 600;
776
+ padding: 18px 24px;
777
+ min-width: 220px;
778
+ flex: 1;
779
+ text-align: center;
780
+ box-shadow: 0 2px 8px #2563eb11;
781
+ }
782
+
783
+ /* Final Outcome Section */
784
+ .final-outcome-section {
785
+ background: linear-gradient(90deg, #fbbf24 0%, #fef9c3 100%);
786
+ border-radius: 12px;
787
+ box-shadow: 0 2px 16px #fbbf2444;
788
+ margin: 0 auto 32px auto;
789
+ max-width: 900px;
790
+ padding: 24px 32px;
791
+ }
792
+ .outcome-header {
793
+ font-size: 1.15rem;
794
+ font-weight: 700;
795
+ color: #f59e42;
796
+ margin-bottom: 12px;
797
+ }
798
+ .outcome-fields {
799
+ display: flex;
800
+ flex-wrap: wrap;
801
+ gap: 18px 32px;
802
+ margin-bottom: 18px;
803
+ }
804
+ .outcome-field {
805
+ min-width: 220px;
806
+ font-size: 1rem;
807
+ color: #23272b;
808
+ }
809
+ .outcome-field .field-label {
810
+ font-weight: 600;
811
+ color: #23272b;
812
+ }
813
+ .outcome-field .field-value {
814
+ font-weight: 500;
815
+ color: #2563eb;
816
+ }
817
+ .outcome-field .field-value.warning {
818
+ color: #991b1b;
819
+ font-weight: 700;
820
+ }
821
+ .outcome-actions {
822
+ display: flex;
823
+ gap: 18px;
824
+ }
825
+ .report-btn {
826
+ background: linear-gradient(90deg, #2563eb 0%, #38bdf8 100%);
827
+ color: #fff;
828
+ font-weight: 700;
829
+ border: none;
830
+ border-radius: 999px;
831
+ padding: 0.5rem 1.4rem;
832
+ margin-bottom: 8px;
833
+ cursor: pointer;
834
+ box-shadow: 0 2px 16px #38bdf888;
835
+ font-size: 1rem;
836
+ transition: background 0.2s, box-shadow 0.2s;
837
+ }
838
+ .report-btn:hover {
839
+ background: linear-gradient(90deg, #38bdf8 0%, #2563eb 100%);
840
+ color: #bae6fd;
841
+ box-shadow: 0 2px 24px #bae6fd88;
842
+ }
843
+ .investigation-outcome-card {
844
+ width: 100%;
845
+ max-width: 420px;
846
+ margin: 0 auto;
847
+ background: #fff;
848
+ border-radius: 18px;
849
+ box-shadow: 0 4px 24px #2563eb22;
850
+ padding: 32px 28px;
851
+ display: flex;
852
+ flex-direction: column;
853
+ align-items: center;
854
+ }
855
+ .outcome-fields-grid {
856
+ display: grid;
857
+ grid-template-columns: 1fr 1.2fr;
858
+ gap: 18px 12px;
859
+ width: 100%;
860
+ margin-top: 8px;
861
+ }
862
+ .outcome-field-row {
863
+ display: contents;
864
+ }
865
+ .outcome-label {
866
+ font-size: 1rem;
867
+ color: #23272b;
868
+ font-weight: 600;
869
+ text-align: right;
870
+ padding-right: 12px;
871
+ }
872
+ .outcome-value {
873
+ font-size: 1rem;
874
+ color: #2563eb;
875
+ font-weight: 700;
876
+ text-align: left;
877
+ }
878
+ .status-chip.active {
879
+ background: #dcfce7;
880
+ color: #15803d;
881
+ border-radius: 8px;
882
+ padding: 4px 10px;
883
+ font-weight: 700;
884
+ }
885
+ .status-chip.archived {
886
+ background: #fee2e2;
887
+ color: #991b1b;
888
+ border-radius: 8px;
889
+ padding: 4px 10px;
890
+ font-weight: 700;
891
+ }
892
+ .summary-value {
893
+ grid-column: span 2;
894
+ background: #f6f8fa;
895
+ color: #991b1b;
896
+ border-radius: 8px;
897
+ padding: 8px 12px;
898
+ font-size: 0.98rem;
899
+ font-weight: 600;
900
+ margin-top: 6px;
901
+ }
src/app/validationpage/validationpage.component.html CHANGED
@@ -1,8 +1,203 @@
1
- <div class="validation-result-container">
2
- <h2>Validation Results</h2>
3
- <div class="percentage-box">
4
- <p><strong>Pass Percentage:</strong> {{ truePercentage }}%</p>
5
- <p><strong>Fail Percentage:</strong> {{ falsePercentage }}%</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  </div>
7
- <button class="back-btn global-bottom-left-btn" (click)="goBack()">Back</button>
8
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- Back to Case Details button at the top -->
2
+ <button class="back-btn" (click)="navigateBackToCaseDetails()">
3
+ <span class="back-icon">←</span> Back to Case Details
4
+ </button>
5
+
6
+ <!-- Modern UI header with logo and PyDetect title -->
7
+ <div class="site-header">
8
+ <div class="header-inner">
9
+ <div class="logo-cluster">
10
+ <span (click)="navigateHome()" style="cursor:pointer;display:flex;align-items:center;">
11
+ <img src="/assets/pykara-logo.png" alt="PyDetect Logo" class="logo-img-header" />
12
+ </span>
13
+ <div class="py-detect-title-header">
14
+ <span class="py-letter p">P</span>
15
+ <span class="py-letter y">Y</span>
16
+ <span class="py-shape"></span>
17
+ <span class="py-letter d">D</span>
18
+ <span class="py-letter e">E</span>
19
+ <span class="py-letter t">T</span>
20
+ <span class="py-letter e2">E</span>
21
+ <span class="py-letter c">C</span>
22
+ <span class="py-letter t2">T</span>
23
+ </div>
24
+ </div>
25
  </div>
 
26
  </div>
27
+
28
+ <!-- Dashboard-style header -->
29
+ <div class="dashboard-header">
30
+ <div class="dashboard-header-content">
31
+
32
+ <div class="dashboard-title-block">
33
+ <div class="dashboard-title">Investigation Report</div>
34
+ <div class="dashboard-date">{{ reportDate || 'June 2024' }}</div>
35
+ </div>
36
+ <div class="header-btns-right">
37
+ <button class="back-btn" (click)="navigateBackToPyDetect()">
38
+ <span class="back-icon">←</span> Back to Investigation
39
+ </button>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <!-- Main dashboard content -->
45
+ <div class="dashboard-main">
46
+ <div class="dashboard-cards">
47
+ <!-- Circular chart card -->
48
+ <div class="dashboard-card dashboard-chart-card">
49
+ <div class="dashboard-card-title">Truth Consistency</div>
50
+ <div class="dashboard-chart-circle">
51
+ <!-- Placeholder for chart, can use SVG or library later -->
52
+ <div class="dashboard-circle-value">{{ truePercentage }}%</div>
53
+ </div>
54
+ <div class="dashboard-card-label">Consistency Index</div>
55
+ </div>
56
+ <!-- Circular chart card -->
57
+ <div class="dashboard-card dashboard-chart-card">
58
+ <div class="dashboard-card-title">Inconsistency Index</div>
59
+ <div class="dashboard-chart-circle">
60
+ <!-- Placeholder for chart, can use SVG or library later -->
61
+ <div class="dashboard-circle-value">{{ truePercentage }}%</div>
62
+ </div>
63
+ <div class="dashboard-card-label">InConsistency Index</div>
64
+ </div>
65
+
66
+ <!-- Actions card -->
67
+ <div class="dashboard-card dashboard-actions-card">
68
+ <div class="dashboard-card-title">Actions</div>
69
+ <button class="report-btn" (click)="downloadPDF()">
70
+ <span class="icon-download"></span> Download PDF
71
+ </button>
72
+ <button class="report-btn" (click)="emailReport()">
73
+ <span class="icon-email"></span> Email Report
74
+ </button>
75
+ <button class="report-btn" (click)="reAnalyze()">Re-Analyze Audio/Video</button>
76
+ </div>
77
+ <!-- Details card -->
78
+ <div class="dashboard-card dashboard-details-card investigation-outcome-card">
79
+ <div class="dashboard-card-title">Investigation Outcome</div>
80
+ <div class="outcome-fields-grid">
81
+ <div class="outcome-field-row">
82
+ <span class="outcome-label">Status:</span>
83
+ <span class="outcome-value">
84
+ <span *ngIf="truePercentage >= 80" class="status-chip active">🟩 Consistent</span>
85
+ <span *ngIf="truePercentage < 80" class="status-chip archived">🟥 Inconsistent</span>
86
+ </span>
87
+ </div>
88
+ <div class="outcome-field-row">
89
+ <span class="outcome-label">Investigation Confidence Score:</span>
90
+ <span class="outcome-value">92%</span>
91
+ </div>
92
+ <div class="outcome-field-row">
93
+ <span class="outcome-label">Dominant Emotion Detected:</span>
94
+ <span class="outcome-value">Calm</span>
95
+ </div>
96
+ <div class="outcome-field-row">
97
+ <span class="outcome-label">Response Clarity:</span>
98
+ <span class="outcome-value">84% Clear</span>
99
+ </div>
100
+ <div class="outcome-field-row">
101
+ <span class="outcome-label">Speech Tone Analysis:</span>
102
+ <span class="outcome-value">Neutral: 60%, Tense: 40%</span>
103
+ </div>
104
+ <div class="outcome-field-row">
105
+ <span class="outcome-label">Summary:</span>
106
+ <span class="outcome-value summary-value">
107
+ The investigation did not meet validation criteria. Further review advised.
108
+ </span>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <!-- Session Overview -->
116
+ <div class="session-overview-card">
117
+ <div class="session-header">Session Overview</div>
118
+ <div class="session-fields">
119
+ <div class="session-field"><span class="field-label">Session ID:</span> <span class="field-value">INT-2025-007</span></div>
120
+ <div class="session-field"><span class="field-label">Suspect Name:</span> <span class="field-value">Ajay Kumar</span></div>
121
+ <div class="session-field"><span class="field-label">Investigation Officer:</span> <span class="field-value">Ganesh R.</span></div>
122
+ <div class="session-field"><span class="field-label">Session Duration:</span> <span class="field-value">00:42:18</span></div>
123
+ <div class="session-field"><span class="field-label">AI Model Version:</span> <span class="field-value">Py-Detect AI 2.0</span></div>
124
+ <div class="session-field"><span class="field-label">Date Analyzed:</span> <span class="field-value">2025-10-15</span></div>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- AI Analysis Summary -->
129
+ <div class="ai-summary-section">
130
+ <div class="ai-summary-header">AI Analysis Summary</div>
131
+ <div class="ai-metrics-grid">
132
+ <div class="ai-metric metric-consistency">
133
+ <div class="metric-label">Truth Consistency Index</div>
134
+ <div class="metric-ring metric-blue">72%</div>
135
+ </div>
136
+ <div class="ai-metric metric-inconsistency">
137
+ <div class="metric-label">Inconsistency Index</div>
138
+ <div class="metric-ring metric-red">28%</div>
139
+ </div>
140
+ <div class="ai-metric metric-emotion">
141
+ <div class="metric-label">Emotional State Distribution</div>
142
+ <div class="metric-bar">Calm – 60%, Nervous – 30%, Angry – 10%</div>
143
+ </div>
144
+ <div class="ai-metric metric-clarity">
145
+ <div class="metric-label">Response Clarity</div>
146
+ <div class="metric-ring metric-cyan">85% clear</div>
147
+ </div>
148
+ <div class="ai-metric metric-eyecontact">
149
+ <div class="metric-label">Eye-Contact Frequency</div>
150
+ <div class="metric-ring metric-blue">68% consistent</div>
151
+ </div>
152
+ <div class="ai-metric metric-confidence">
153
+ <div class="metric-label">Speech Confidence Level</div>
154
+ <div class="metric-ring metric-cyan">75%</div>
155
+ </div>
156
+ <div class="ai-metric metric-verdict">
157
+ <div class="metric-label">Overall AI Verdict</div>
158
+ <div class="metric-verdict-box">Partially Consistent / Needs Review</div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+
163
+ <!-- AI Observations and Insights -->
164
+ <div class="ai-observations-section">
165
+ <div class="observations-header"><span class="ai-icon">🤖</span> AI Observations & Insights</div>
166
+ <div class="observations-note">
167
+ <strong>Observation Summary:</strong><br>
168
+ During questioning, the suspect showed hesitation when discussing the time of the incident.<br>
169
+ Eye movement frequency decreased by 25% during key questions.<br>
170
+ Speech tone remained steady, indicating partial honesty.<br>
171
+ <strong>Recommendation:</strong> Further questioning advised for financial motive discussion.
172
+ </div>
173
+ </div>
174
+
175
+ <!-- Audio–Video Metrics (Graphs Placeholder) -->
176
+ <div class="audio-video-metrics-section">
177
+ <div class="metrics-header">Audio–Video Metrics</div>
178
+ <div class="metrics-graphs">
179
+ <div class="graph-placeholder">Waveform & Emotion Timeline (Graph)</div>
180
+ <div class="graph-placeholder">Confidence vs Time (Graph)</div>
181
+ <div class="graph-placeholder">Facial Micro-Expression (Graph)</div>
182
+ </div>
183
+ </div>
184
+
185
+ <!-- Final Outcome Summary -->
186
+ <div class="final-outcome-section">
187
+ <div class="outcome-header">Final Outcome Summary</div>
188
+ <div class="outcome-fields">
189
+ <div class="outcome-field"><span class="field-label">AI Verdict:</span> <span class="field-value warning">⚠️ Requires Further Verification</span></div>
190
+ <div class="outcome-field"><span class="field-label">Confidence Score:</span> <span class="field-value">82%</span></div>
191
+ <div class="outcome-field"><span class="field-label">Recommended Action:</span> <span class="field-value">Conduct follow-up questioning with corroborating evidence C-2025-A.</span></div>
192
+ <div class="outcome-field"><span class="field-label">Report Generated By:</span> <span class="field-value">Py-Detect AI System</span></div>
193
+ </div>
194
+ <div class="outcome-actions">
195
+ <button class="report-btn" (click)="downloadPDF()">Download Report (PDF)</button>
196
+ <button class="report-btn" (click)="emailReport()">Email Summary</button>
197
+ <button class="report-btn" (click)="reAnalyze()">Re-Analyze Audio/Video</button>
198
+ </div>
199
+ </div>
200
+
201
+ <footer>
202
+ <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
203
+ </footer>
src/app/validationpage/validationpage.component.ts CHANGED
@@ -1,5 +1,7 @@
1
  import { Component } from '@angular/core';
2
  import { Router } from '@angular/router';
 
 
3
 
4
  @Component({
5
  selector: 'app-validationpage',
@@ -9,6 +11,8 @@ import { Router } from '@angular/router';
9
  export class ValidationpageComponent {
10
  truePercentage: number = 0;
11
  falsePercentage: number = 0;
 
 
12
 
13
  constructor(private router: Router) {
14
  const nav = this.router.getCurrentNavigation();
@@ -17,7 +21,44 @@ export class ValidationpageComponent {
17
  this.falsePercentage = state?.falsePercentage ?? 0;
18
  }
19
 
20
- goBack() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  this.router.navigate(['/py-detect']);
22
  }
 
 
 
 
 
23
  }
 
1
  import { Component } from '@angular/core';
2
  import { Router } from '@angular/router';
3
+ // For PDF generation
4
+ // import jsPDF from 'jspdf';
5
 
6
  @Component({
7
  selector: 'app-validationpage',
 
11
  export class ValidationpageComponent {
12
  truePercentage: number = 0;
13
  falsePercentage: number = 0;
14
+ modalOpen: boolean = false;
15
+ reportDate: string = new Date().toLocaleString('default', { month: 'long', year: 'numeric' });
16
 
17
  constructor(private router: Router) {
18
  const nav = this.router.getCurrentNavigation();
 
21
  this.falsePercentage = state?.falsePercentage ?? 0;
22
  }
23
 
24
+
25
+ navigateBackToCaseDetails() {
26
+ this.router.navigate(['/case-details']);
27
+ }
28
+
29
+ openModal() {
30
+ this.modalOpen = true;
31
+ }
32
+
33
+ closeModal() {
34
+ this.modalOpen = false;
35
+ }
36
+
37
+ downloadPDF() {
38
+ // Example: Use jsPDF to generate PDF
39
+ // const doc = new jsPDF();
40
+ // doc.text('Investigation Report', 10, 10);
41
+ // doc.text(`Pass Percentage: ${this.truePercentage}%`, 10, 30);
42
+ // doc.text(`Fail Percentage: ${this.falsePercentage}%`, 10, 40);
43
+ // doc.save('investigation-report.pdf');
44
+ alert('PDF download functionality to be implemented.');
45
+ }
46
+
47
+ emailReport() {
48
+ // Example: Use mailto or backend API
49
+ window.location.href = `mailto:?subject=Investigation Report&body=Pass Percentage: ${this.truePercentage}%\nFail Percentage: ${this.falsePercentage}%`;
50
+ }
51
+
52
+ navigateHome() {
53
+ this.router.navigate(['/']);
54
+ }
55
+
56
+ navigateBackToPyDetect() {
57
  this.router.navigate(['/py-detect']);
58
  }
59
+
60
+ reAnalyze() {
61
+ // TODO: Implement re-analysis logic
62
+ alert('Re-Analyze Audio/Video functionality to be implemented.');
63
+ }
64
  }