@extends('layouts.dashboard') @section('title', $course->title) @section('page_title', __('Learning')) @php $total = $course->sections->sum(fn ($s) => $s->lessons->count()); $doneCount = count($completedIds); $media = $current ? \App\Support\Video::resolve($current, true) : ['kind' => 'none', 'url' => null, 'hls' => false, 'subtitles' => [], 'poster' => null, 'downloadable' => false, 'provider' => null]; // YouTube's own control bar keeps a clickable logo/title/related-videos // overlay that opens youtube.com — no embed param removes it. Instead we // render the raw player with native controls off and skin it with our // own play/seek/mute/fullscreen bar, so nothing inside the iframe is // reachable by a click. $ytId = null; if ($media['kind'] === 'iframe' && \Illuminate\Support\Str::contains((string) $media['url'], ['youtube.com', 'youtube-nocookie.com', 'youtu.be'])) { preg_match('#/embed/([A-Za-z0-9_-]{6,})#', (string) $media['url'], $ytMatch); $ytId = $ytMatch[1] ?? null; } @endphp @section('content')
{{ $course->title }}
@if($total === 0 && $course->quizzes->isNotEmpty()) @php $passedTests = $course->quizzes->filter(fn ($q) => $q->resultsPublished() && $quizAttempts->get($q->id, collect())->where('is_passed', true)->isNotEmpty())->count(); @endphp {{ __(':done/:total tests passed', ['done' => $passedTests, 'total' => $course->quizzes->count()]) }} @else {{ __(':done/:total lessons', ['done' => $doneCount, 'total' => $total]) }} @endif
@if ($current)
@include('partials.video-watermark')
{{ __('Playback paused') }}
{{ __('Return to this window to continue protected content.') }}
@if ($current->lesson_type === 'scorm' && $current->scorm_launch) @elseif ($media['kind'] === 'iframe' && $ytId)
0:00 / 0:00
@elseif ($media['kind'] === 'iframe') @elseif ($media['kind'] === 'video') @elseif ($media['kind'] === 'audio')
@elseif ($media['kind'] === 'pdf') @else
@endif

@push('scripts') @endpush
{{-- Previous / Next: keyboard ← → also work (see script below). --}}
@if($prevLesson) {{ __('Previous') }} @else {{ __('Previous') }} @endif {{ __('Lesson :n of :total', ['n' => $lessonPosition, 'total' => $lessonTotal]) }} @if($nextLesson && empty($locks[$nextLesson->id])) {{ __('Next') }} @elseif($nextLesson) {{ __('Next') }} @else {{ __('Last lesson') }} @endif
{{ $current->title }}
@if ($current->content)
{!! nl2br(e($current->content)) !!}
@endif {{-- Primary action + downloads on one line, the same size, vertically centred. --}}
@if (in_array($current->id, $completedIds)) @else
@csrf
@endif @if($media['downloadable'] && $media['url'] && ($canDownloadResources ?? true)) {{ __('Download lesson file') }} @endif @if($canDownloadResources ?? true) {{ __('Download all resources (.zip)') }} @endif
@csrf
{{ __('Discussion') }} {{ __('Lesson questions') }}

{{ __('My Notes') }}
@csrf @error('note')
{{ $message }}
@enderror
@forelse ($lessonNotes as $note)
{{ $note->note }}
{{ $note->created_at->diffForHumans() }}
@csrf @method('DELETE')
@empty

{{ __('No notes for this lesson yet.') }}

@endforelse
@elseif ($course->quizzes->isNotEmpty()) {{-- Quiz-only course (test series): the tests are the content. --}}
{{ $course->isTestSeries() ? __('Your tests') : __('Quizzes') }}

{{ $course->isTestSeries() ? __('Pass every test to complete this test series.') : __('Pass every test to complete this course.') }}

@if($course->quizzes->contains(fn ($q) => $q->isScheduled())) {{ __('Exam Calendar') }} @endif
@foreach($course->quizzes as $quiz) @php $attempts = $quizAttempts->get($quiz->id, collect()); $published = $quiz->resultsPublished(); $best = $published ? $attempts->max('percentage') : null; $passed = $published && $attempts->where('is_passed', true)->isNotEmpty(); $outOfAttempts = (int) $quiz->max_attempts > 0 && $attempts->count() >= (int) $quiz->max_attempts; $status = $quiz->examStatus(); @endphp
{{ $quiz->title }}
{{ __('Passing score') }}: {{ (float) $quiz->passing_score }}% @if($quiz->duration_minutes) · {{ __(':count min', ['count' => $quiz->duration_minutes]) }} @endif · {{ (int) $quiz->max_attempts > 0 ? __(':used/:max attempts', ['used' => $attempts->count(), 'max' => (int) $quiz->max_attempts]) : trans_choice(':count attempt|:count attempts', $attempts->count(), ['count' => $attempts->count()]) }} @if($best !== null) · {{ __('best') }} {{ round((float) $best) }}%@endif @if($attempts->isNotEmpty() && ! $published) · {{ __('Result pending') }}@endif
@include('partials.quiz.schedule', ['quiz' => $quiz])
@if($attempts->isNotEmpty()) {{ $published ? __('View results') : __('Submitted') }} @endif @if($quiz->meritListVisible() && $attempts->isNotEmpty()) {{ __('Merit list') }} @endif @if($status === 'upcoming') {{ __('Add to calendar') }} @elseif($status !== 'ended' && ! $outOfAttempts) {{ $attempts->isEmpty() ? __('Start test') : __('Retake') }} @endif
@endforeach
@if(isset($seriesSubjects) && $seriesSubjects->isNotEmpty())
@include('partials.quiz.subject-analysis', ['subjects' => $seriesSubjects, 'title' => __('Your performance by subject')])
@endif @else
{{ __('This course has no lessons yet.') }}
@endif
@if($course->sections->isNotEmpty())
{{ __('Course Content') }}
@foreach ($course->sections as $section)

@foreach ($section->lessons as $lesson) @php $isDone = in_array($lesson->id, $completedIds); $isActive = $current && $current->id === $lesson->id; $lock = $locks[$lesson->id] ?? null; @endphp @if ($lock) {{-- Dripped or sequenced: shown, but not openable yet. --}}
{{ $lesson->title }} {{ $lock }}
@else @if ($isDone) @else @endif {{ $lesson->title }} @if ($lesson->duration) {{ __(':count min', ['count' => $lesson->duration]) }} @endif @endif @endforeach
@endforeach
@endif
{{ __('More') }}
@if($certificate)
{{ __('Certificate earned!') }}
{{ __('No.') }} {{ $certificate->certificate_no }}
{{ __('View') }}
@endif @if($course->quizzes->isNotEmpty())
{{ __('Quizzes') }}
@foreach($course->quizzes as $quiz) @php $attempts = $quizAttempts->get($quiz->id, collect()); $best = $quiz->resultsPublished() ? $attempts->max('percentage') : null; $passed = $quiz->resultsPublished() && $attempts->where('is_passed', true)->isNotEmpty(); @endphp {{ $quiz->title }} {{ (int) $quiz->max_attempts > 0 ? __(':used/:max attempts', ['used' => $attempts->count(), 'max' => (int) $quiz->max_attempts]) : trans_choice(':count attempt|:count attempts', $attempts->count(), ['count' => $attempts->count()]) }} · {{ __('pass') }} {{ (float) $quiz->passing_score }}% @if($best !== null) · {{ __('best') }} {{ round((float) $best) }}%@endif @endforeach
@endif @if($course->assignments->isNotEmpty())
{{ __('Assignments') }}
@foreach($course->assignments as $assignment) @php $submission = $assignmentSubmissions->get($assignment->id); $status = $submission?->status; $icon = $status === 'reviewed' ? 'bi-clipboard2-check-fill text-success' : ($status === 'submitted' ? 'bi-send-check text-info' : 'bi-clipboard2 text-primary'); @endphp {{ $assignment->title }} @if($status === 'reviewed') {{ __('Graded') }}: {{ $submission->marks_obtained ?? '-' }}/{{ $assignment->max_marks }} @elseif($status === 'submitted') {{ __('Submitted · awaiting review') }} @elseif($assignment->due_date) {{ __('Due') }} {{ \Carbon\Carbon::parse($assignment->due_date)->format('d M Y') }} @else {{ __('Not submitted') }} @endif @endforeach
@endif
{{ __('Community') }}
{{ __('Discussion (Q&A)') }} @if(module_enabled('liveclass') && \Illuminate\Support\Facades\Route::has('student.liveclasses.index')) {{ __('Live classes') }} @endif
{{ __('Write a review') }}
@csrf
@if($myReview)
@csrf @method('DELETE')
@endif
{{-- Shown only when the tutor can actually answer: enabled, and a key is configured. Otherwise a learner would type a question and get an administrator's error message back. --}} @if(module_enabled('ai') && tenant_setting('ai_tutor_enabled', '1') === '1' && app(\Modules\Ai\Services\AiClient::class)->isConfigured()) @include('ai::partials.tutor') @endif
@endsection @if($ytId) @push('scripts') @endpush @endif @push('scripts') @if(($media['kind'] ?? '') === 'video' && (($media['hls'] ?? false) || ($media['drm'] ?? false))) @endif @endpush @push('scripts') @endpush