@extends('layouts.app') @push('styles') @endpush @section('content') @php $metrics = $attempt->training->metric_settings ?? []; $wordCount = (int) ($attempt->review_data['speech_metrics']['word_count'] ?? 0); $hasDialogue = !empty($attempt->conversation_history); $hasSpeech = $wordCount >= 3 || $hasDialogue; $eyeScore = round(($attempt->review_data['video_metrics']['overall_eye_contact_score'] ?? 0) * 100); $wpm = round($attempt->review_data['speech_metrics']['avg_wpm'] ?? 0); $fillers = (int) ($attempt->review_data['speech_metrics']['filler_count'] ?? 0); $lexicalDensity = round(($attempt->review_data['speech_metrics']['lexical_density'] ?? 0.45) * 100); $fleschGrade = $attempt->review_data['speech_metrics']['flesch_kincaid_grade'] ?? 0; $powerWords = array_values(array_unique($attempt->review_data['nlp_analysis']['power_word_hits'] ?? [])); $topKeywords = array_values(array_unique($attempt->review_data['nlp_analysis']['top_keywords'] ?? [])); $salesConcepts = collect($attempt->review_data['nlp_analysis']['sales_concepts'] ?? []) ->unique(fn ($c) => strtolower($c['concept'] ?? '')) ->values() ->all(); if (!function_exists('renderFeedbackMarkdown')) { function renderFeedbackMarkdown($text) { // Clean up bold $text = preg_replace('/\*\*(.+?)\*\*/', '$1', $text); // Clean up H3 headers $text = preg_replace('/^###\s+(.+)$/m', '

$1

', $text); // Clean up H2 headers $text = preg_replace('/^##\s+(.+)$/m', '

$1

', $text); // Clean up bullet lists (* or -) $text = preg_replace('/^[\-\*]\s+(.+)$/m', '
$1
', $text); // Clean up ordered list items (e.g. 1.) $text = preg_replace('/^\d+\.\s+(.+)$/m', '
$1
', $text); // Replace double newlines with line breaks $text = str_replace("\n\n", "
", $text); $text = nl2br($text); return $text; } } @endphp @if($wordCount < 3 && !$hasDialogue)

NO SPEECH DETECTED

It looks like you didn't speak or say anything during this training session. Please ensure your microphone is configured properly and that you pitch clearly to generate an analysis.

TRY AGAIN
@endif @if($hasSpeech)
{{ $attempt->overall_score ?: 'N/A' }}/100
Overall Pitch Score
@if(($metrics['eye_contact']['enabled'] ?? '1') == '1')
{{ $eyeScore }}%
Eye Contact (Target ≥ {{ $metrics['eye_contact']['min_percentage'] ?? 70 }}%)
@endif @if(($metrics['wpm']['enabled'] ?? '1') == '1')
{{ $wpm }}
WPM (Target: {{ $metrics['wpm']['min'] ?? 120 }}-{{ $metrics['wpm']['max'] ?? 165 }})
@endif @if(($metrics['fillers']['enabled'] ?? '1') == '1')
{{ $fillers }}
Vocal Fillers (Max ≤ {{ $metrics['fillers']['max_count'] ?? 5 }})
@endif
@endif @if($attempt->training->isAiAvatar() && isset($attempt->review_data['avatar_status'])) @php $avatarStatus = $attempt->review_data['avatar_status']; @endphp
@if($avatarStatus === 'deal_closed') DEAL CLOSED @elseif($avatarStatus === 'demo_scheduled') DEMO SCHEDULED @elseif($avatarStatus === 'call_terminated') CALL TERMINATED @else CONVERSATION COMPLETE @endif
@if($avatarStatus === 'deal_closed') You won over the Cooperative buyer! They agreed to the deal immediately. @elseif($avatarStatus === 'demo_scheduled') You successfully framed the value of the platform and scheduled a demo with the buyer! @elseif($avatarStatus === 'call_terminated') The buyer terminated the call early. Practice overcoming objections without fumbling or using filler words. @else The conversation finished. Review your performance breakdown below. @endif
@endif @if(Auth::user()->isAdmin())

Admin Evaluation Studio

@if($attempt->grade) Current: {{ $attempt->grade }} @if($attempt->graded_at) · {{ $attempt->graded_at->diffForHumans() }} @endif @endif
@csrf
@elseif($attempt->grade)
Assigned Grade: {{ $attempt->grade }} @if($attempt->admin_feedback)
Feedback: "{{ $attempt->admin_feedback }}"
@endif
Graded: {{ $attempt->graded_at ? $attempt->graded_at->diffForHumans() : 'Recently' }}
@endif @if($hasSpeech)

SECTION A — Body Language & Visual Confidence

{{ number_format($attempt->review_data['video_metrics']['avg_blink_rate_per_min'] ?? 0, 1) }}
Blink Rate / min
{{ $attempt->review_data['video_metrics']['head_pose_stability'] ?? 'High' }}
Posture & Stability
@if(!empty($attempt->review_data['video_metrics']['gaze_windows']))
Eye Contact Over Time
@foreach($attempt->review_data['video_metrics']['gaze_windows'] as $index => $gaze) @php $val = (float)($gaze['eye_contact_score'] ?? 0); $label = ''; if (isset($gaze['window_start_s'])) { $m = floor($gaze['window_start_s'] / 60); $s = $gaze['window_start_s'] % 60; $label = "{$m}:" . str_pad($s, 2, '0', STR_PAD_LEFT); } @endphp
@if($label) {{ $label }} @endif
@endforeach
@endif @if(!empty($attempt->review_data['video_metrics']['expression_timeline']))
Expressions Timeline
@foreach($attempt->review_data['video_metrics']['expression_timeline'] as $frame)
@endforeach
Neutral Smile Raised Brows Frown
@endif

SECTION B — Pitch Delivery & Pacing

{{ round($attempt->duration_sec) }}s
Total Duration
{{ $wordCount }}
Word Count
{{ $lexicalDensity }}%
Vocab Richness
Grade {{ $fleschGrade }}
Complexity
@if(!empty($attempt->review_data['speech_metrics']['pause_events']))
Detected Hesitation Pauses (> 1.5s)
@foreach($attempt->review_data['speech_metrics']['pause_events'] as $pause) @ {{ round(($pause['start_ms'] ?? 0) / 1000) }}s — {{ number_format(($pause['duration_ms'] ?? 0) / 1000, 1) }}s pause @endforeach
@endif @if(!empty($attempt->review_data['speech_metrics']['wpm_by_window']))
Speaking Speed (WPM) Over Time
@foreach($attempt->review_data['speech_metrics']['wpm_by_window'] as $index => $wpmVal) @php $maxVal = 200; $percent = min(100, ($wpmVal / $maxVal) * 100); $label = ($index * 30) . 's'; @endphp
{{ $label }}
@endforeach
@endif

SECTION C — Message Quality & Vocabulary

@if(!empty($attempt->review_data['nlp_analysis']['sentiment_arc'])) @php $sentimentData = $attempt->review_data['nlp_analysis']['sentiment_arc']; $width = 600; $height = 160; $paddingTop = 20; $paddingRight = 20; $paddingBottom = 30; $paddingLeft = 40; $chartW = $width - $paddingLeft - $paddingRight; $chartH = $height - $paddingTop - $paddingBottom; // Reconstruct sentences from transcript for older reviews that lack `sentence` $transcriptSentences = []; $rawTranscript = trim((string) ($attempt->transcript ?? '')); if ($rawTranscript !== '') { $current = ''; $chars = preg_split('//u', $rawTranscript, -1, PREG_SPLIT_NO_EMPTY) ?: []; $length = count($chars); for ($i = 0; $i < $length; $i++) { $current .= $chars[$i]; if ($chars[$i] === '!' || $chars[$i] === '?') { $trimmed = trim(rtrim(trim($current), '.!?')); if ($trimmed !== '') { $transcriptSentences[] = $trimmed; } $current = ''; } elseif ($chars[$i] === '.') { $pre = rtrim($current, '.'); $parts = preg_split('/\s+/', trim($pre)) ?: []; $lastWord = $parts ? (string) end($parts) : ''; $beforeIsSingleUpper = mb_strlen($lastWord) === 1 && preg_match('/^[A-Z]$/u', $lastWord); $afterIsDigit = ($i + 1 < $length) && ctype_digit($chars[$i + 1]); if (! $beforeIsSingleUpper && ! $afterIsDigit) { $trimmed = trim(rtrim(trim($current), '.')); if ($trimmed !== '') { $transcriptSentences[] = $trimmed; } $current = ''; } } } $remainder = trim(rtrim(trim($current), '.!?')); if ($remainder !== '') { $transcriptSentences[] = $remainder; } $allWords = preg_split('/\s+/', $rawTranscript) ?: []; if (count($transcriptSentences) <= 1 && count($allWords) > 15) { $transcriptSentences = []; for ($j = 0; $j < count($allWords); $j += 12) { $transcriptSentences[] = implode(' ', array_slice($allWords, $j, 12)); } } } $points = []; $path = ''; $positiveStatements = []; $negativeStatements = []; foreach ($sentimentData as $index => $point) { $x = $paddingLeft + ($index / max(count($sentimentData) - 1, 1)) * $chartW; $score = (float)($point['score'] ?? 0.5); $label = strtoupper($point['label'] ?? 'NEUTRAL'); $sentenceIndex = (int) ($point['sentence_index'] ?? $index); $sentenceText = trim((string) ($point['sentence'] ?? ($transcriptSentences[$sentenceIndex] ?? ''))); $val = 0; if ($label === 'POSITIVE') { $val = max(0.0, $score - 0.5) * 2; if ($sentenceText !== '') { $positiveStatements[] = $sentenceText; } } elseif ($label === 'NEGATIVE') { $val = -max(0.0, $score - 0.5) * 2; if ($sentenceText !== '') { $negativeStatements[] = $sentenceText; } } $y = $paddingTop + $chartH / 2 - ($val * $chartH / 2); $points[] = [ 'x' => $x, 'y' => $y, 'label' => $label, 'score' => $score ]; $path .= ($index === 0 ? 'M' : 'L') . " {$x} {$y} "; } $positiveStatements = array_values(array_unique($positiveStatements)); $negativeStatements = array_values(array_unique($negativeStatements)); @endphp
Sentiment Arc / Pitch Tone Trajectory POS NEU NEG @if($path) @endif @foreach($points as $p) @php $color = 'var(--text-dim)'; if ($p['label'] === 'POSITIVE') $color = 'var(--accent)'; elseif ($p['label'] === 'NEGATIVE') $color = 'var(--red)'; @endphp @endforeach @foreach($points as $index => $p) @if($index % 4 === 0) S{{ $index + 1 }} @endif @endforeach
Positive Statements ({{ count($positiveStatements) }})
@forelse($positiveStatements as $statement)
"{{ $statement }}"
@empty [ No positive statements detected ] @endforelse
Negative Statements ({{ count($negativeStatements) }})
@forelse($negativeStatements as $statement)
"{{ $statement }}"
@empty [ No negative statements detected ] @endforelse
@endif
Detected Power Words
@forelse($powerWords as $powerWord) {{ $powerWord }} @empty [ No power words detected ] @endforelse
Top Informative Keywords
@forelse($topKeywords as $kw) {{ $kw }} @empty [ No keywords detected ] @endforelse
@if(!empty($salesConcepts))
Sales Concept Coverage Details
@foreach($salesConcepts as $concept)
{{ !empty($concept['detected']) ? '[X]' : '[ ]' }} {{ $concept['concept'] ?? 'Concept' }} @if(!empty($concept['detected']) && isset($concept['confidence'])) {{ round($concept['confidence'] * 100) }}% confidence @endif
@if(!empty($concept['detected']) && !empty($concept['evidence_sentence']))

"{{ $concept['evidence_sentence'] }}"

@endif
@endforeach
@endif

SECTION D — {{ !empty($attempt->conversation_history) ? 'Dialogue History' : 'Full Pitch Transcript' }}

@if(!empty($attempt->conversation_history))
@foreach($attempt->conversation_history as $msg)
{{ ($msg['speaker'] ?? '') === 'avatar' ? 'AI BUYER' : 'SALES REP' }}
{{ $msg['text'] ?? '' }}
@endforeach
@else
{{ $attempt->transcript ?: 'No transcript captured.' }}
@endif
@if($attempt->llm_report || ($attempt->training->isAiAvatar() && $attempt->conversation_analysis))
@if($attempt->llm_report)

AI Pitch Coaching Report

{!! renderFeedbackMarkdown(e($attempt->llm_report)) !!}
@endif @if($attempt->training->isAiAvatar() && $attempt->conversation_analysis) @php $analysisData = null; try { $analysisData = json_decode($attempt->conversation_analysis, true); } catch (\Exception $e) { $analysisData = null; } @endphp

AI Dialogue Analysis & Feedback

@if(is_array($analysisData) && isset($analysisData['good'], $analysisData['bad'], $analysisData['better']))

What was Good

    @foreach($analysisData['good'] as $item)
  • {{ $item }}
  • @endforeach

What was Bad

    @foreach($analysisData['bad'] as $item)
  • {{ $item }}
  • @endforeach

What can be done better

    @foreach($analysisData['better'] as $item)
  • {{ $item }}
  • @endforeach
@else
{!! renderFeedbackMarkdown(e($attempt->conversation_analysis)) !!}
@endif
@endif
@endif @endif @endsection