Initial commit

This commit is contained in:
Richard Jolley committed 2026-01-14 00:54:08 +00:00
commit c909d539bc
7178 files changed
+756624

No files matched your search

@@ -0,0 +1,46 @@
<x-app-layout>
<div class="container mx-auto my-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-2xl font-semibold">Cancel booking</h2>
<a href="{{ route('student.bookings.index') }}" class="text-blue-600 hover:underline">← Back to My bookings</a>
</div>
@if ($errors->any())
<div class="mb-4 p-3 rounded bg-red-50 text-red-700 text-sm">
<ul class="list-disc pl-5">
@foreach ($errors->all() as $err)
<li>{{ $err }}</li>
@endforeach
</ul>
</div>
@endif
<div class="border rounded p-4 bg-white shadow-sm space-y-2">
<p><span class="font-semibold">When:</span> {{ $weekdayName }} {{ $prettyDate }} at {{ $prettyTime }}</p>
<p><span class="font-semibold">Refund:</span> {{ $refundPercent }}%</p>
<p class="text-sm text-gray-600">
Cancellations made 24 hours or more in advance receive a full (100%) refund.
Cancellations within 24 hours receive a 50% refund.
</p>
</div>
{{-- Confirm cancel (POST) --}}
<form action="{{ route('student.bookings.cancel.store', ['booking' => $booking->id]) }}"
method="POST"
class="mt-6 border rounded p-4 bg-white shadow-sm flex items-center gap-3">
@csrf
<button type="submit" class="px-4 py-2 rounded bg-red-600 text-white hover:bg-red-700">
Confirm cancellation
</button>
<a href="{{ route('student.bookings.index') }}" class="text-blue-600 hover:underline">
Keep this booking
</a>
</form>
@if ($minutesUntil < 24*60)
<div class="mt-3 text-sm text-amber-700 bg-amber-50 border border-amber-200 rounded p-3">
Heads up: this session starts in less than 24 hours, so the refund will be 50%.
</div>
@endif
</div>
</x-app-layout>
@@ -0,0 +1,51 @@
<x-app-layout>
<div class="container mx-auto my-6">
<h2 class="text-2xl font-semibold mb-4">Book a Session</h2>
<div class="mb-4">
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">← Back to calendar</a>
</div>
{{-- Prefilled details from controller (no Carbon in Blade) --}}
<div class="border rounded p-4 bg-white shadow-sm space-y-1">
<p><span class="font-semibold">When:</span> {{ $weekdayName }} {{ $prettyDate }} at {{ $prettyTime }}</p>
<p><span class="font-semibold">Date (value to save):</span> {{ $date }}</p>
<p><span class="font-semibold">Slot ID:</span> {{ $slot->id }}</p>
</div>
<div class="mt-6 text-sm text-gray-600">
Next step: we’ll add a confirmation step (and later, payment) before creating the booking.
</div>
<form action="{{ route('student.bookings.store') }}" method="POST" class="mt-6 border rounded p-4 bg-white shadow-sm space-y-4">
@csrf
{{-- keep the selected date/slot --}}
<input type="hidden" name="date" value="{{ $date }}">
<input type="hidden" name="slot_id" value="{{ $slot->id }}">
@if ($errors->any())
<div class="p-3 rounded bg-red-50 text-red-700 text-sm">
<ul class="list-disc pl-5">
@foreach ($errors->all() as $err)
<li>{{ $err }}</li>
@endforeach
</ul>
</div>
@endif
<div>
<label class="block text-sm font-medium mb-1" for="message">Message (optional)</label>
<textarea id="message" name="message" rows="3" class="w-full border rounded px-3 py-2">{{ old('message') }}</textarea>
@error('message') <p class="text-sm text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
<div class="flex items-center gap-3">
<button type="submit" class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-700">
Confirm booking
</button>
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">Cancel</a>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,31 @@
@include('partials.flash')
<x-app-layout>
<div class="container mx-auto my-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-2xl font-semibold">My bookings</h2>
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">← Back to calendar</a>
</div>
@if ($bookings->isEmpty())
<div class="border rounded p-4 bg-white shadow-sm text-gray-700">
You don’t have any upcoming bookings yet.
</div>
@else
<div class="space-y-3">
@foreach ($bookings as $b)
<div class="border rounded p-4 bg-white shadow-sm flex items-center justify-between">
<div>
<div class="font-semibold">{{ $b['weekday'] }} {{ $b['prettyDate'] }} at {{ $b['prettyTime'] }}</div>
<div class="text-sm text-gray-600">Status: {{ ucfirst($b['status']) }}</div>
</div>
{{-- Cancel action will be added in the next step --}}
<a href="{{ route('student.bookings.cancel.confirm', ['booking' => $b['id']]) }}"
class="px-3 py-2 rounded bg-red-600 text-white hover:bg-red-700">
Cancel
</a>
</div>
@endforeach
</div>
@endif
</div>
</x-app-layout>
@@ -0,0 +1,71 @@
<x-app-layout>
<div class="container mx-auto my-6">
<h2 class="text-2xl font-semibold mb-4">Book a Weekly Series</h2>
<div class="mb-4">
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">← Back to calendar</a>
</div>
{{-- Prefilled details from controller (no Carbon in Blade) --}}
<div class="border rounded p-4 bg-white shadow-sm space-y-1 mb-6">
<p><span class="font-semibold">When:</span> {{ $weekdayName }} {{ $prettyDate }} at {{ $prettyTime }}</p>
<p><span class="font-semibold">Start date (value to save):</span> {{ $date }}</p>
<p><span class="font-semibold">Slot ID:</span> {{ $slot->id }}</p>
</div>
{{-- Adjust weeks via GET (keeps things simple for now) --}}
<form action="{{ route('student.bookings.series.create') }}" method="GET" class="border rounded p-4 bg-white shadow-sm space-y-4">
<input type="hidden" name="date" value="{{ $date }}">
<input type="hidden" name="slot_id" value="{{ $slot->id }}">
<div>
<label for="weeks" class="block text-sm font-medium mb-1">How many weeks?</label>
<input id="weeks" name="weeks" type="number" min="1" max="52" value="{{ $weeks }}" class="w-32 border rounded px-3 py-2">
<p class="text-xs text-gray-600 mt-1">We’ll repeat this weekday/time for the next N weeks.</p>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300">
Update weeks
</button>
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">Cancel</a>
</div>
</form>
<div class="mt-6 text-sm text-gray-600">
Next: we’ll add a <em>Confirm series</em> step (and later, a payment step) before creating the bookings.
</div>
<form action="{{ route('student.bookings.series.store') }}" method="POST" class="mt-6 border rounded p-4 bg-white shadow-sm space-y-4">
@csrf
{{-- keep selected values --}}
<input type="hidden" name="date" value="{{ $date }}">
<input type="hidden" name="slot_id" value="{{ $slot->id }}">
<input type="hidden" name="weeks" value="{{ $weeks }}">
@if ($errors->any())
<div class="p-3 rounded bg-red-50 text-red-700 text-sm">
<ul class="list-disc pl-5">
@foreach ($errors->all() as $err)
<li>{{ $err }}</li>
@endforeach
</ul>
</div>
@endif
<p class="text-sm text-gray-700">
This will create <strong>{{ $weeks }}</strong> weekly bookings starting
{{ $weekdayName }} {{ $prettyDate }} at {{ $prettyTime }}.
</p>
<div class="flex items-center gap-3">
<button type="submit" class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-700">
Confirm series ({{ $weeks }} weeks)
</button>
<a href="{{ route('student.calendar') }}" class="text-blue-600 hover:underline">Cancel</a>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,32 @@
<x-app-layout>
<div class="container my-5">
<h2>{{ $course->subject->name }} ({{ $course->level->name }})</h2>
<p>{{ $course->description }}</p>
<h4 class="mt-4">Modules</h4>
@foreach($course->modules as $module)
<div class="mb-3">
<h5>{{ $module->name }}</h5>
@foreach($module->lessonSections as $section)
<h6 class="mt-2">{{ $section->title }}</h6>
<ul>
@foreach($section->chapters as $chapter)
<li>
<strong>{{ $chapter->title }}</strong>
<ul>
@foreach($chapter->lessons as $lesson)
<li>
<a href="{{ route('student.lessons.show', $lesson->id) }}">
{{ $lesson->title }}
</a>
</li>
@endforeach
</ul>
</li>
@endforeach
</ul>
@endforeach
</div>
@endforeach
</div>
</x-app-layout>
+136
View File
@@ -0,0 +1,136 @@
<x-app-layout>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-10">
{{-- Greeting --}}
<div>
<h2 class="text-2xl font-semibold text-gray-800">
Welcome back, {{ auth()->user()->name }}!
</h2>
</div>
{{-- Continue Learning (enrolled & in-progress) --}}
<div class="bg-white rounded-xl shadow p-6">
<h3 class="text-lg font-medium text-gray-900 border-b pb-3 mb-4">Continue Learning</h3>
@if($inProgressCourses->isNotEmpty())
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($inProgressCourses as $course)
@php
// Find the most recent lesson viewed in this course
$lastLesson = $lastLessonsByCourse
->firstWhere('chapter.lessonSection.module.course.id', $course->id);
@endphp
<div class="border rounded-lg p-4 bg-gray-50 hover:bg-gray-100 transition">
<h4 class="text-indigo-600 font-semibold text-lg">{{ $course->name }}</h4>
<p class="text-gray-600 text-sm">
{{ $course->subject->name }} ({{ $course->level->name }})
</p>
@if($lastLesson)
<p class="text-gray-700 text-sm mt-2">Last lesson: {{ $lastLesson->title }}</p>
<a href="{{ route('student.course.lesson', [
'course' => $course->slug,
'lesson' => $lastLesson->slug
]) }}"
class="inline-block mt-3 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 text-sm">
▶ Resume Lesson
</a>
@else
@php
$firstLesson = $course->modules
->flatMap(fn($m) => $m->lessonSections)
->flatMap(fn($s) => $s->chapters)
->flatMap(fn($c) => $c->lessons)
->sortBy('order')
->first();
@endphp
@if($firstLesson)
<a href="{{ route('student.course.lesson', [
'course' => $course->slug,
'lesson' => $firstLesson->slug
]) }}"
class="inline-block mt-3 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 text-sm">
▶ Go to Course
</a>
@else
<span class="text-gray-500 text-sm mt-3 inline-block">No lessons yet</span>
@endif
@endif
</div>
@endforeach
</div>
@else
<p class="text-gray-600">You haven’t started any courses yet. Pick one below!</p>
@endif
</div>
{{-- Enrolled but not yet started --}}
<div class="bg-white rounded-xl shadow p-6">
<h3 class="text-lg font-medium text-gray-900 border-b pb-3 mb-4">Courses You’ve Enrolled In (Not Started)</h3>
@if($notStartedCourses->isNotEmpty())
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($notStartedCourses as $course)
<div class="border rounded-lg p-4 bg-white">
<h4 class="font-semibold text-gray-900">{{ $course->name }}</h4>
<p class="text-gray-600 text-sm">
{{ $course->subject->name }} ({{ $course->level->name }})
</p>
@php
$firstLesson = $course->modules
->flatMap(fn($m) => $m->lessonSections)
->flatMap(fn($s) => $s->chapters)
->flatMap(fn($c) => $c->lessons)
->sortBy('order')
->first();
@endphp
@if($firstLesson)
<a href="{{ route('student.course.lesson', [
'course' => $course->slug,
'lesson' => $firstLesson->slug
]) }}"
class="inline-block mt-3 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 text-sm">
▶ Start Course
</a>
@else
<span class="text-gray-500 text-sm">No lessons yet</span>
@endif
</div>
@endforeach
</div>
@else
<p class="text-gray-600">No enrolled courses awaiting a start right now.</p>
@endif
</div>
{{-- Available to enrol (not enrolled) --}}
<div class="bg-white rounded-xl shadow p-6">
<h3 class="text-lg font-medium text-gray-900 border-b pb-3 mb-4">Available Courses</h3>
@if($availableCourses->isNotEmpty())
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($availableCourses as $course)
<div class="border rounded-lg p-4 bg-white">
<h4 class="font-semibold text-gray-900">{{ $course->name }}</h4>
<p class="text-gray-600 text-sm">
{{ $course->subject->name }} ({{ $course->level->name }})
</p>
{{-- If you have a course details page, update this route accordingly --}}
<a href="#"
class="inline-block mt-3 px-4 py-2 bg-gray-900 text-white rounded-lg opacity-70 cursor-not-allowed text-sm">
Enrollment coming soon
</a>
</div>
@endforeach
</div>
@else
<p class="text-gray-600">You’re enrolled in all available courses. 🎉</p>
@endif
</div>
</div>
</x-app-layout>
@@ -0,0 +1,136 @@
<x-app-layout>
<div x-data="{ sidebarOpen: false }"
class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="flex flex-col lg:flex-row lg:space-x-8">
{{-- Mobile toggle (outside the aside) --}}
<div class="flex items-center justify-between lg:hidden mb-3">
<h3 class="text-lg font-semibold text-gray-800">Course Content</h3>
<button @click="sidebarOpen = !sidebarOpen"
class="text-gray-600 hover:text-indigo-600">
<svg :class="{ 'rotate-180': sidebarOpen }"
class="w-5 h-5 transform transition-transform"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
{{-- Sidebar --}}
<aside
x-cloak
:class="{ 'hidden': !sidebarOpen }" {{-- hide on mobile when closed --}}
class="hidden lg:block lg:w-64 bg-white rounded-xl shadow p-4 overflow-y-auto max-h-screen mb-6 lg:mb-0"
style="margin-top:20px">
<div class="space-y-3">
@foreach($chapters as $chapter)
<div x-data="{ open: {{ $chapter->id === $lesson->chapter?->id ? 'true' : 'false' }} }">
<button @click="open = !open"
class="w-full flex justify-between items-center text-left font-medium text-gray-700 hover:text-indigo-600">
<span>{{ $chapter->title }}</span>
<svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7" />
</svg>
</button>
<div x-show="open" class="mt-2 ml-3 space-y-1">
@foreach($chapter->lessons as $chLesson)
<a href="{{ route('student.course.lesson', [
'course' => $lesson->chapter->lessonSection->module->course->slug,
'lesson' => $chLesson->slug
]) }}"
class="block px-2 py-1 rounded-lg text-sm
{{ $chLesson->id === $lesson->id
? 'bg-indigo-100 text-indigo-700 font-medium'
: 'text-gray-600 hover:bg-gray-100' }}">
{{ $chLesson->title }}
</a>
@endforeach
</div>
</div>
@endforeach
</div>
</aside>
{{-- Main lesson content --}}
<main class="flex-1">
<h2 class="hidden lg:block text-2xl font-semibold text-gray-800 mb-2">
{{ $lesson->chapter?->title }} — {{ $lesson->title }}
</h2>
<p class="text-gray-500 mb-6">
{{ $lesson->chapter->lessonSection->module->course->subject->name }}
({{ $lesson->chapter->lessonSection->module->course->level->name }})
</p>
@forelse($lesson->videos as $video)
<x-video-player :url="$video->video_url" :title="$video->title" />
@empty
<p>No videos available for this lesson yet.</p>
@endforelse
{{-- ============================= --}}
{{-- Lesson Questions Section --}}
{{-- ============================= --}}
<div class="mt-10">
<h2 class="text-xl font-semibold mb-4">Questions about this lesson</h2>
{{-- Flash message --}}
@if (session('success'))
<div class="mb-4 p-3 bg-green-100 text-green-700 rounded">
{{ session('success') }}
</div>
@endif
{{-- Question submission form --}}
<form action="{{ route('student.course.lesson.questions.store', [$lesson->chapter->lessonSection->module->course->slug, $lesson->slug]) }}" method="POST">
@csrf
<textarea name="question" rows="3" class="w-full rounded-lg border-gray-300 focus:ring focus:ring-blue-200" placeholder="Ask your question here...">{{ old('question') }}</textarea>
@error('question')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
<div class="text-right mt-2">
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Submit Question
</button>
</div>
</form>
{{-- List of existing questions --}}
@if($lesson->questions->isNotEmpty())
<ul class="space-y-4">
@foreach ($lesson->questions as $question)
<li class="p-4 border rounded-lg bg-gray-50">
<p class="font-medium text-gray-800">
{{ $question->student->name }} asked:
</p>
<p class="mt-1 text-gray-700">{{ $question->question }}</p>
@if($question->answer)
<div class="mt-3 pl-4 border-l-4 border-blue-400">
<p class="text-sm text-gray-600">
<strong>Teacher’s reply:</strong> {{ $question->answer }}
</p>
</div>
@else
<p class="mt-2 text-sm text-gray-500 italic">Awaiting a reply...</p>
@endif
</li>
@endforeach
</ul>
@else
<p class="text-gray-500">No questions have been asked yet — be the first!</p>
@endif
</div>
<a href="{{ url()->previous() }}"
class="inline-block mt-4 px-4 py-2 border border-gray-300 text-gray-600 rounded-lg hover:bg-gray-100">
← Back
</a>
</main>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,22 @@
{{-- Continue learning --}}
@if($lastLesson)
<section>
<h2 class="text-xl font-semibold mb-4">Continue Learning</h2>
<a href="{{ route('student.lessons.show', $lastLesson->id) }}" class="btn-primary">
Resume "{{ $lastLesson->title }}"
</a>
</section>
@endif
{{-- My Courses --}}
<section class="mt-8">
<h2 class="text-xl font-semibold mb-4">My Courses</h2>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($otherCourses as $course)
<x-course-card :course="$course" />
@endforeach
</div>
</section>
{{-- Offered Courses (modular partial) --}}
@include('student.partials._offered_courses', ['otherCourses' => $otherCourses])