Initial commit
This commit is contained in:
commit
c909d539bc
7178 files changed
+756624
No files matched your search
@@ -0,0 +1,71 @@
|
||||
<link rel="stylesheet" href="{{ asset('css/custom.css') }}">
|
||||
|
||||
<x-app-layout>
|
||||
<div class="container mx-auto my-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-2xl font-semibold">
|
||||
ACalendar: {{ $start->toFormattedDateString() }} – {{ $end->toFormattedDateString() }}
|
||||
</h2>
|
||||
<div class="space-x-2">
|
||||
<a href="{{ route($viewer.'.calendar', ['week' => $weekOffset - 1]) }}"
|
||||
class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">← Previous</a>
|
||||
<a href="{{ route($viewer.'.calendar') }}"
|
||||
class="px-3 py-1 bg-blue-200 rounded hover:bg-blue-300">This Week</a>
|
||||
<a href="{{ route($viewer.'.calendar', ['week' => $weekOffset + 1]) }}"
|
||||
class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">Next →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 text-sm">
|
||||
<span class="inline-block px-2 py-1 rounded bg-green-100 border">Free</span>
|
||||
<span class="inline-block px-2 py-1 rounded bg-red-200 border ml-2">Booked</span>
|
||||
<span class="inline-block px-2 py-1 rounded bg-yellow-200 border ml-2">Blocked</span>
|
||||
<span class="inline-block px-2 py-1 rounded bg-gray-100 border ml-2">Closed / Not seeded</span>
|
||||
</div>
|
||||
|
||||
<table class="w-full border-collapse border border-gray-300">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="border p-2">Time</th>
|
||||
@foreach ($grid['headers'] as $head)
|
||||
<th class="border p-2">{{ $head['label'] }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($grid['rows'] as $row)
|
||||
<tr>
|
||||
<td class="border p-2 font-medium">{{ $row['label'] }}</td>
|
||||
@foreach ($row['cells'] as $cell)
|
||||
<td class="border p-2 text-center {{ $cell['class'] }}">
|
||||
@switch($cell['status'])
|
||||
@case('free')
|
||||
Free
|
||||
@break
|
||||
|
||||
@case('booked')
|
||||
Booked
|
||||
@if ($canSeeNames && !empty($cell['name']))
|
||||
<br><span class="text-xs">{{ $cell['name'] }}</span>
|
||||
@endif
|
||||
@break
|
||||
|
||||
@case('blocked')
|
||||
Blocked
|
||||
@break
|
||||
|
||||
@case('not_seeded')
|
||||
Not seeded
|
||||
@break
|
||||
|
||||
@default
|
||||
Closed
|
||||
@endswitch
|
||||
</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,113 @@
|
||||
<link rel="stylesheet" href="{{ asset('css/custom.css') }}">
|
||||
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">
|
||||
Chapter: {{ $chapter->title }}
|
||||
</h1>
|
||||
|
||||
{{-- Reorder Lessons --}}
|
||||
<form action="{{ route('admin.lessons.reorder') }}"
|
||||
method="POST"
|
||||
id="reorderLessonsForm"
|
||||
class="mb-6">
|
||||
@csrf
|
||||
|
||||
<ul id="lesson-list"
|
||||
class="border rounded divide-y divide-gray-200 sortable-item">
|
||||
@foreach($chapter->lessons->sortBy('order') as $lesson)
|
||||
<li class="flex items-center justify-between px-3 py-2 hover:bg-gray-50"
|
||||
data-id="{{ $lesson->id }}">
|
||||
|
||||
{{-- Drag + title --}}
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="cursor-move text-gray-400 select-none">⋮⋮</span>
|
||||
|
||||
<a href="{{ route('admin.lessons.show', [
|
||||
'course' => $course->slug,
|
||||
'chapter' => $chapter->slug,
|
||||
'lesson' => $lesson->slug
|
||||
]) }}"
|
||||
class="text-gray-800 hover:underline">
|
||||
{{ $lesson->title }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Delete button (now triggers external form) --}}
|
||||
<button type="button"
|
||||
onclick="deleteLesson('{{ $lesson->id }}', '{{ addslashes($lesson->title) }}')"
|
||||
class="text-red-600 hover:text-red-800">
|
||||
✖
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="order" id="lessonOrderInput">
|
||||
|
||||
<button type="submit"
|
||||
class="mt-3 px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-sm font-medium">
|
||||
💾 Save Order
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Hidden Delete Forms --}}
|
||||
@foreach($chapter->lessons as $lesson)
|
||||
<form id="delete-form-{{ $lesson->id }}"
|
||||
action="{{ route('admin.lessons.destroy', [
|
||||
'course' => $course->slug,
|
||||
'chapter' => $chapter->slug,
|
||||
'lesson' => $lesson->slug
|
||||
]) }}"
|
||||
method="POST"
|
||||
class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
@endforeach
|
||||
|
||||
{{-- Add New Lesson --}}
|
||||
<form action="{{ route('admin.lessons.store') }}"
|
||||
method="POST"
|
||||
class="mt-4 flex gap-2">
|
||||
@csrf
|
||||
<input type="hidden" name="chapter_id" value="{{ $chapter->id }}">
|
||||
|
||||
<input type="text"
|
||||
name="title"
|
||||
class="flex-1 border-gray-300 rounded-md shadow-sm focus:ring focus:ring-blue-200"
|
||||
placeholder="New Topic Title"
|
||||
required>
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-green-100 text-green-700 rounded hover:bg-green-200 text-sm font-medium">
|
||||
➕ Add
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
const lessonList = document.getElementById('lesson-list');
|
||||
new Sortable(lessonList, { animation: 150 });
|
||||
|
||||
document.getElementById('reorderLessonsForm').addEventListener('submit', function () {
|
||||
const ids = Array.from(lessonList.querySelectorAll('li')).map(li => li.dataset.id);
|
||||
document.getElementById('lessonOrderInput').value = JSON.stringify(ids);
|
||||
});
|
||||
|
||||
function deleteLesson(id, title) {
|
||||
if (confirm(`Are you sure you want to delete the topic "${title}"?\n\n⚠️ This will also remove any videos inside it.`)) {
|
||||
document.getElementById(`delete-form-${id}`).submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,95 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
|
||||
|
||||
<div class="bg-white shadow-sm sm:rounded-lg overflow-hidden">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">Add New Course</h1>
|
||||
|
||||
{{-- Error Messages --}}
|
||||
@if ($errors->any())
|
||||
<div class="bg-red-100 text-red-800 p-4 rounded mb-6">
|
||||
<ul class="list-disc pl-5">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.courses.store') }}" method="POST" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
{{-- Subject --}}
|
||||
<div>
|
||||
<label for="subject_id" class="block text-sm font-medium text-gray-700 mb-1">
|
||||
Subject
|
||||
</label>
|
||||
<select id="subject_id" name="subject_id"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
required>
|
||||
<option value="">-- Choose a subject --</option>
|
||||
@foreach ($subjects as $subject)
|
||||
<option value="{{ $subject->id }}"
|
||||
{{ old('subject_id') == $subject->id ? 'selected' : '' }}>
|
||||
{{ $subject->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Level --}}
|
||||
<div>
|
||||
<label for="level_id" class="block text-sm font-medium text-gray-700 mb-1">
|
||||
Level
|
||||
</label>
|
||||
<select id="level_id" name="level_id"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
required>
|
||||
<option value="">-- Choose a level --</option>
|
||||
@foreach ($levels as $level)
|
||||
<option value="{{ $level->id }}"
|
||||
{{ old('level_id') == $level->id ? 'selected' : '' }}>
|
||||
{{ $level->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Lead Teacher --}}
|
||||
<div>
|
||||
<label for="lead_teacher" class="block text-sm font-medium text-gray-700 mb-1">
|
||||
Lead Teacher
|
||||
</label>
|
||||
<input id="lead_teacher" type="text" name="lead_teacher"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
value="{{ old('lead_teacher') }}" required>
|
||||
</div>
|
||||
|
||||
{{-- Description --}}
|
||||
<div>
|
||||
<label for="description" class="block text-sm font-medium text-gray-700 mb-1">
|
||||
Description
|
||||
</label>
|
||||
<textarea id="description" name="description"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
rows="4">{{ old('description') }}</textarea>
|
||||
</div>
|
||||
|
||||
{{-- Save Button --}}
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700
|
||||
font-medium text-sm shadow-sm transition">
|
||||
Save Course
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,62 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-4">Courses</h1>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($courses->isEmpty())
|
||||
<p>No courses have been added yet.</p>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full border border-gray-200 bg-white">
|
||||
<thead class="bg-gray-100 border-b">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left">ID</th>
|
||||
<th class="px-4 py-2 text-left">Subject</th>
|
||||
<th class="px-4 py-2 text-left">Level</th>
|
||||
<th class="px-4 py-2 text-left">Lead Teacher</th>
|
||||
<th class="px-4 py-2 text-left">Description</th>
|
||||
<th class="px-4 py-2 text-left">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($courses as $course)
|
||||
<tr class="border-b hover:bg-gray-50">
|
||||
<td class="px-4 py-2">{{ $course->id }}</td>
|
||||
<td class="px-4 py-2">
|
||||
<a href="{{ route('admin.courses.show', $course) }}">
|
||||
{{ $course->subject->name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $course->level ? $course->level->name : '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-2">{{ $course->lead_teacher }}</td>
|
||||
<td class="px-4 py-2">{{ $course->description }}</td>
|
||||
<td class="px-4 py-2">{{ $course->created_at->format('Y-m-d') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
<div class="mt-6">
|
||||
<a href="{{ route('admin.courses.create') }}"
|
||||
class="px-4 py-2 bg-gray-200 border border-gray-300 rounded-md
|
||||
font-semibold text-sm text-black hover:bg-gray-300
|
||||
focus:outline-none focus:ring focus:ring-gray-400 transition">
|
||||
Add New Course
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,192 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
|
||||
<div class="bg-white shadow-sm sm:rounded-lg overflow-hidden">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">
|
||||
Manage Course: {{ $course->subject->name }} ({{ $course->level->name }})
|
||||
</h1>
|
||||
|
||||
@forelse($course->modules as $module)
|
||||
<div class="mb-10">
|
||||
<!-- Module Name -->
|
||||
<h2 class="text-xl font-semibold mb-4">
|
||||
{{ $module->name }}
|
||||
</h2>
|
||||
|
||||
<div class="bg-gray-50 border rounded-lg p-5">
|
||||
|
||||
<!-- SECTION HEADER + ADD FORM -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold">
|
||||
Lesson Sections
|
||||
</h3>
|
||||
|
||||
<form action="{{ route('admin.sections.store', ['course' => $course->slug]) }}"
|
||||
method="POST"
|
||||
class="flex items-center gap-2">
|
||||
@csrf
|
||||
<input type="hidden" name="module_id" value="{{ $module->id }}">
|
||||
|
||||
<input type="text"
|
||||
name="title"
|
||||
placeholder="New Section Title"
|
||||
required
|
||||
class="border rounded px-2 py-1 text-sm w-48 focus:ring-blue-200 focus:border-blue-300">
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-green-100 text-green-700 rounded hover:bg-green-200 text-sm font-medium">
|
||||
➕ Add
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- SECTION LIST -->
|
||||
<ul id="section-list-{{ $module->id }}"
|
||||
class="border rounded divide-y divide-gray-200 sortable-item bg-white">
|
||||
|
||||
@foreach($module->lessonSections->sortBy('order') as $section)
|
||||
<li class="flex items-center justify-between px-4 py-2 hover:bg-gray-50"
|
||||
data-id="{{ $section->id }}">
|
||||
|
||||
<!-- Drag handle + link -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="cursor-move text-gray-400 select-none">⋮⋮</span>
|
||||
|
||||
<a href="{{ route('admin.sections.show', [
|
||||
'course' => $course->slug,
|
||||
'section' => $section->slug
|
||||
]) }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
{{ $section->title }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Rename + Delete -->
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
<!-- RENAME -->
|
||||
<form action="{{ route('admin.sections.update', [
|
||||
'course' => $course->slug,
|
||||
'lessonSection' => $section->slug
|
||||
]) }}"
|
||||
method="POST"
|
||||
class="flex items-center gap-1">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<input type="text"
|
||||
name="title"
|
||||
value="{{ $section->title }}"
|
||||
class="border border-gray-300 rounded px-2 py-0.5 text-sm w-40 focus:ring-blue-200 focus:border-blue-300">
|
||||
|
||||
<button type="submit"
|
||||
class="text-blue-600 hover:text-blue-800 text-sm">
|
||||
✔
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- DELETE -->
|
||||
<button onclick="if (confirmDelete('{{ $section->title }}')) document.getElementById('delete-section-{{ $section->id }}').submit();"
|
||||
class="text-red-600 hover:text-red-800 text-sm">
|
||||
✖
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- SINGLE DELETE FORM (correct route, no duplicates) -->
|
||||
<form id="delete-section-{{ $section->id }}"
|
||||
action="{{ route('admin.sections.destroy', [
|
||||
'course' => $course->slug,
|
||||
'section' => $section->slug
|
||||
]) }}"
|
||||
method="POST"
|
||||
class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<!-- Hidden reorder field + button -->
|
||||
<button form="reorderForm-{{ $module->id }}"
|
||||
class="mt-3 px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-sm font-medium">
|
||||
💾 Save Section Order
|
||||
</button>
|
||||
|
||||
<form action="{{ route('admin.sections.reorder', ['course' => $course->slug]) }}"
|
||||
method="POST"
|
||||
id="reorderForm-{{ $module->id }}"
|
||||
class="hidden">
|
||||
@csrf
|
||||
<input type="hidden" name="order" id="reorderInput-{{ $module->id }}">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-600">
|
||||
No modules yet.
|
||||
<a href="{{ route('admin.modules.create') }}" class="text-blue-600 hover:underline">
|
||||
Add a module
|
||||
</a>
|
||||
</p>
|
||||
@endforelse
|
||||
|
||||
<!-- Add New Module -->
|
||||
<div class="mt-8 bg-gray-50 border rounded-lg p-5">
|
||||
<h3 class="text-lg font-semibold mb-4">Add a New Module</h3>
|
||||
|
||||
<form action="{{ route('admin.modules.store') }}" method="POST" class="flex gap-3">
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="course_id" value="{{ $course->id }}">
|
||||
|
||||
<input type="text"
|
||||
name="name"
|
||||
class="flex-1 border-gray-300 rounded-md shadow-sm focus:ring focus:ring-blue-200"
|
||||
placeholder="Module Name"
|
||||
required>
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-green-100 text-green-700 rounded hover:bg-green-200 text-sm font-medium">
|
||||
➕ Add Module
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<!-- Sortable -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
|
||||
<script>
|
||||
// Reorder handling
|
||||
document.querySelectorAll('[id^="section-list-"]').forEach(list => {
|
||||
const moduleId = list.id.replace('section-list-', '');
|
||||
const reorderForm = document.getElementById('reorderForm-' + moduleId);
|
||||
const reorderInput = document.getElementById('reorderInput-' + moduleId);
|
||||
|
||||
new Sortable(list, { animation: 150 });
|
||||
|
||||
reorderForm.addEventListener('submit', function () {
|
||||
const ids = [...list.querySelectorAll('li')].map(li => li.dataset.id);
|
||||
reorderInput.value = JSON.stringify(ids);
|
||||
});
|
||||
});
|
||||
|
||||
function confirmDelete(name) {
|
||||
return confirm(
|
||||
`Are you sure you want to delete the lesson section "${name}"?\n\n` +
|
||||
`⚠️ All chapters and lessons inside it will also be deleted.`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,104 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<h1 class="text-3xl font-extrabold text-gray-900 mb-6">
|
||||
Admin Dashboard
|
||||
</h1>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
|
||||
{{-- Students Card --}}
|
||||
<div class="bg-white shadow-sm rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Students</h2>
|
||||
<p class="text-gray-600 mb-4">Manage your student accounts and records.</p>
|
||||
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ route('admin.students.index') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
View Students
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- Parents Card --}}
|
||||
<div class="bg-white shadow-sm rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Parents</h2>
|
||||
<p class="text-gray-600 mb-4">Manage your parent accounts and records.</p>
|
||||
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ route('admin.parents.index') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
View Parents
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{-- Teachers Card --}}
|
||||
<div class="bg-white shadow-sm rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Teachers</h2>
|
||||
<p class="text-gray-600 mb-4">Manage teachers and related tasks.</p>
|
||||
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ route('admin.teachers.index') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
View Teachers
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ route('admin.teachers.create') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
Add Teacher
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ route('admin.users.index') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
Manage Users
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ route('admin.calendar') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
Calendar
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{-- Courses Card --}}
|
||||
<div class="bg-white shadow-sm rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Courses</h2>
|
||||
<p class="text-gray-600 mb-4">Add, edit, and manage course content.</p>
|
||||
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ route('admin.courses.index') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
Manage Courses
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ route('admin.subjects.store') }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
Add Subject
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,109 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white shadow-sm sm:rounded-lg overflow-hidden">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">
|
||||
Topic: {{ $lesson->title }}
|
||||
</h1>
|
||||
|
||||
{{-- Add Video --}}
|
||||
<div class="mb-8">
|
||||
<h2 class="text-lg font-semibold mb-2">Add New Video</h2>
|
||||
|
||||
<form action="{{ route('admin.videos.store') }}" method="POST" class="space-y-4">
|
||||
@csrf
|
||||
<input type="hidden" name="lesson_id" value="{{ $lesson->id }}">
|
||||
|
||||
{{-- Title --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Video Title</label>
|
||||
<input type="text" name="title"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
required>
|
||||
</div>
|
||||
|
||||
{{-- URL --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Video URL</label>
|
||||
<input type="url" name="video_url"
|
||||
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-200 focus:border-blue-300"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 text-sm font-medium">
|
||||
➕ Add Video
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- Videos List --}}
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold mb-2">Videos</h2>
|
||||
|
||||
{{-- Reorder form --}}
|
||||
<form action="{{ route('admin.videos.reorder') }}" method="POST" id="reorderVideosForm">
|
||||
@csrf
|
||||
|
||||
<ul id="video-list" class="border rounded divide-y divide-gray-200 sortable-item mb-4">
|
||||
@foreach($lesson->videos as $video)
|
||||
<li class="flex items-center justify-between px-3 py-2 hover:bg-gray-50"
|
||||
data-id="{{ $video->id }}">
|
||||
|
||||
<div class="flex items-center gap-3 flex-grow">
|
||||
<span class="cursor-move text-gray-400 select-none text-lg">⋮⋮</span>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">{{ $video->title }}</span>
|
||||
<span class="text-xs text-gray-500 break-all">{{ $video->video_url }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Delete button (now triggers external hidden form) --}}
|
||||
<button type="button"
|
||||
onclick="if (confirm('Delete this video?')) { document.getElementById('delete-video-{{ $video->id }}').submit(); }"
|
||||
class="text-red-600 hover:text-red-800 text-sm font-medium px-2">
|
||||
✖
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="order" id="videoOrderInput">
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-sm font-medium">
|
||||
💾 Save Order
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Hidden delete forms (must be outside reorder form) --}}
|
||||
@foreach($lesson->videos as $video)
|
||||
<form id="delete-video-{{ $video->id }}"
|
||||
action="{{ route('admin.videos.destroy', $video->id) }}"
|
||||
method="POST"
|
||||
class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
const videoList = document.getElementById('video-list');
|
||||
new Sortable(videoList, { animation: 150 });
|
||||
|
||||
document.getElementById('reorderVideosForm').addEventListener('submit', () => {
|
||||
const ids = [...videoList.querySelectorAll('li')].map(li => li.dataset.id);
|
||||
document.getElementById('videoOrderInput').value = JSON.stringify(ids);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<x-app-layout>
|
||||
<div class="container mx-auto">
|
||||
<h1 class="text-2xl font-bold mb-4">Add Level</h1>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="bg-red-100 text-red-800 p-3 rounded mb-4">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.levels.store') }}" method="POST" class="space-y-6">
|
||||
@csrf
|
||||
<div>
|
||||
<label for="name" class="block font-medium text-sm text-gray-700">Level Name</label>
|
||||
<input id="name" type="text" name="name" class="form-input mt-1 block w-full"
|
||||
value="{{ old('name') }}" required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 border border-transparent rounded-md
|
||||
font-semibold text-xs text-white uppercase tracking-widest hover:bg-blue-700
|
||||
active:bg-blue-900 focus:outline-none focus:border-blue-900 focus:ring
|
||||
focus:ring-blue-300 disabled:opacity-25 transition">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,36 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-4">Levels</h1>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($levels->isEmpty())
|
||||
<p>No levels found.</p>
|
||||
@else
|
||||
<ul class="list-disc pl-5">
|
||||
@foreach ($levels as $level)
|
||||
<li>{{ $level->name }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
<div class="mt-6">
|
||||
<a href="{{ route('admin.levels.create') }}"
|
||||
class="px-4 py-2 bg-gray-200 border border-gray-300 rounded-md
|
||||
font-semibold text-sm text-black hover:bg-gray-300
|
||||
focus:outline-none focus:ring focus:ring-gray-400 transition">
|
||||
Add New Level
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,54 @@
|
||||
<x-app-layout>
|
||||
<div class="container my-5">
|
||||
<h2 class="mb-4">Add Module</h2>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success mb-3">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.modules.create') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="course_id" class="form-label">Select Course</label>
|
||||
<select name="course_id" id="course_id"
|
||||
class="form-select @error('course_id') is-invalid @enderror"
|
||||
required>
|
||||
<option value="">-- Choose a Course --</option>
|
||||
@foreach($courses as $course)
|
||||
<option value="{{ $course->id }}">
|
||||
{{ $course->subject->name }} ({{ $course->level->name }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('course_id')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Module Name</label>
|
||||
<input type="text" name="name" id="name"
|
||||
class="form-control @error('name') is-invalid @enderror"
|
||||
value="{{ old('name') }}" required>
|
||||
@error('name')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="order" class="form-label">Order (optional)</label>
|
||||
<input type="number" name="order" id="order"
|
||||
class="form-control @error('order') is-invalid @enderror"
|
||||
value="{{ old('order') }}">
|
||||
@error('order')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Add Module</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,31 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
Add parent
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="rounded-lg border bg-white p-6 shadow-sm">
|
||||
|
||||
<form method="POST" action="{{ route('admin.parents.store') }}" class="space-y-4">
|
||||
@csrf
|
||||
|
||||
@include('admin.parents.partials.form', ['parent' => null])
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<a href="{{ route('admin.parents.index') }}"
|
||||
class="rounded-md border px-4 py-2 text-sm">
|
||||
Cancel
|
||||
</a>
|
||||
<button class="rounded-md bg-gray-900 px-4 py-2 text-white text-sm">
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,49 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
Edit parent: {{ $parent->name }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8 space-y-4">
|
||||
|
||||
@if(session('success'))
|
||||
<div class="rounded-md border border-green-200 bg-green-50 p-4 text-green-800">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="rounded-lg border bg-white p-6 shadow-sm">
|
||||
<form method="POST" action="{{ route('admin.parents.update', $parent) }}" class="space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
@include('admin.parents.partials.form', ['parent' => $parent])
|
||||
|
||||
<div class="flex justify-between">
|
||||
<form method="POST" action="{{ route('admin.parents.destroy', $parent) }}"
|
||||
onsubmit="return confirm('Delete this parent?');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="rounded-md border border-red-300 px-4 py-2 text-sm text-red-700 hover:bg-red-50">
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<a href="{{ route('admin.parents.index') }}"
|
||||
class="rounded-md border px-4 py-2 text-sm">
|
||||
Back
|
||||
</a>
|
||||
<button class="rounded-md bg-gray-900 px-4 py-2 text-white text-sm">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,75 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
Parents
|
||||
</h2>
|
||||
|
||||
<a href="{{ route('admin.parents.create') }}"
|
||||
class="rounded-md bg-gray-900 px-4 py-2 text-white text-sm hover:bg-gray-800">
|
||||
Add parent
|
||||
</a>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-4">
|
||||
|
||||
@if(session('success'))
|
||||
<div class="rounded-md border border-green-200 bg-green-50 p-4 text-green-800">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<form method="GET" class="flex gap-2">
|
||||
<input type="text" name="q" value="{{ $q }}"
|
||||
class="w-full rounded-md border-gray-300"
|
||||
placeholder="Search name or email...">
|
||||
<button class="rounded-md bg-gray-900 px-4 py-2 text-white text-sm">
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border bg-white shadow-sm overflow-hidden">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-gray-50">
|
||||
<tr class="text-left">
|
||||
<th class="px-4 py-3">Name</th>
|
||||
<th class="px-4 py-3">Email</th>
|
||||
<th class="px-4 py-3">Phone</th>
|
||||
<th class="px-4 py-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($parents as $parent)
|
||||
<tr class="border-t">
|
||||
<td class="px-4 py-3 font-medium">{{ $parent->user->first_name }} {{ $parent->user->last_name }}</td>
|
||||
<td class="px-4 py-3">{{ $parent->user->email }}</td>
|
||||
<td class="px-4 py-3">{{ $parent->phone ?? '—' }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<a href="{{ route('admin.parents.edit', $parent) }}"
|
||||
class="text-gray-900 hover:underline">
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr class="border-t">
|
||||
<td colspan="4" class="px-4 py-6 text-center text-gray-500">
|
||||
No parents found.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ $parents->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,34 @@
|
||||
@php
|
||||
$isEdit = !is_null($parent);
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Name</label>
|
||||
<input type="text" name="name"
|
||||
value="{{ old('name', $parent->name ?? '') }}"
|
||||
class="mt-1 w-full rounded-md border-gray-300">
|
||||
@error('name') <div class="mt-1 text-sm text-red-600">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email" name="email"
|
||||
value="{{ old('email', $parent->email ?? '') }}"
|
||||
class="mt-1 w-full rounded-md border-gray-300">
|
||||
@error('email') <div class="mt-1 text-sm text-red-600">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Phone</label>
|
||||
<input type="text" name="phone"
|
||||
value="{{ old('phone', $parent->phone ?? '') }}"
|
||||
class="mt-1 w-full rounded-md border-gray-300">
|
||||
@error('phone') <div class="mt-1 text-sm text-red-600">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Notes</label>
|
||||
<textarea name="notes" rows="4"
|
||||
class="mt-1 w-full rounded-md border-gray-300">{{ old('notes', $parent->notes ?? '') }}</textarea>
|
||||
@error('notes') <div class="mt-1 text-sm text-red-600">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
@@ -0,0 +1,126 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-6">
|
||||
Manage Course: {{ $course->subject->name }} ({{ $course->level->name }})
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
{{-- Show ONLY the module this section belongs to --}}
|
||||
<div class="mb-6">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ $section->module->name }}
|
||||
</h2>
|
||||
|
||||
<div class="ml-4 mb-6">
|
||||
<h3 class="text-lg font-semibold mb-2">
|
||||
Chapters ({{ $section->title }})
|
||||
</h3>
|
||||
|
||||
{{-- Reorder form --}}
|
||||
<form action="{{ route('admin.chapters.reorder') }}"
|
||||
method="POST"
|
||||
id="reorderForm-{{ $section->id }}">
|
||||
@csrf
|
||||
|
||||
<ul id="chapter-list-{{ $section->id }}"
|
||||
class="border rounded mb-3 divide-y divide-gray-200 sortable-item">
|
||||
|
||||
@foreach($section->chapters->sortBy('order') as $chapter)
|
||||
<li class="flex items-center justify-between px-3 py-2 hover:bg-gray-50"
|
||||
data-id="{{ $chapter->id }}">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="cursor-move text-gray-400 select-none">⋮⋮</span>
|
||||
|
||||
<a href="{{ route('admin.chapters.show', [
|
||||
'course' => $course->slug,
|
||||
'chapter' => $chapter->slug
|
||||
]) }}"
|
||||
class="text-blue-600 hover:underline">
|
||||
{{ $chapter->title }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button type="button"
|
||||
onclick="if (confirmDelete('{{ $chapter->title }}')) {
|
||||
document.getElementById('delete-form-{{ $chapter->id }}').submit();
|
||||
}"
|
||||
class="text-red-600 hover:text-red-800">
|
||||
✖
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="order" id="orderInput-{{ $section->id }}">
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-sm font-medium">
|
||||
💾 Save Order
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Delete forms --}}
|
||||
@foreach($section->chapters as $chapter)
|
||||
<form id="delete-form-{{ $chapter->id }}"
|
||||
action="{{ route('admin.chapters.destroy', $chapter->id) }}"
|
||||
method="POST"
|
||||
class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
@endforeach
|
||||
|
||||
{{-- Add Chapter --}}
|
||||
<form action="{{ route('admin.chapters.store') }}" method="POST" class="mt-4 flex gap-2">
|
||||
@csrf
|
||||
<input type="hidden" name="lesson_section_id" value="{{ $section->id }}">
|
||||
|
||||
<input type="text"
|
||||
name="title"
|
||||
class="flex-1 border-gray-300 rounded-md shadow-sm focus:ring-blue-200"
|
||||
placeholder="New Chapter Title"
|
||||
required>
|
||||
|
||||
<button type="submit"
|
||||
class="px-3 py-1 bg-green-100 text-green-700 rounded hover:bg-green-200 text-sm font-medium">
|
||||
➕ Add
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.querySelectorAll('[id^="chapter-list-"]').forEach(list => {
|
||||
new Sortable(list, { animation: 150 });
|
||||
|
||||
const sectionId = list.id.replace('chapter-list-', '');
|
||||
const form = document.getElementById('reorderForm-' + sectionId);
|
||||
const hiddenInput = document.getElementById('orderInput-' + sectionId);
|
||||
|
||||
form.addEventListener('submit', function () {
|
||||
const ids = Array.from(list.querySelectorAll('li')).map(li => li.dataset.id);
|
||||
hiddenInput.value = JSON.stringify(ids);
|
||||
});
|
||||
});
|
||||
|
||||
function confirmDelete(title) {
|
||||
return confirm(
|
||||
`Are you sure you want to delete the chapter "${title}"?\n\n` +
|
||||
`⚠️ This will also remove all topics and videos inside it.`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,258 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
|
||||
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg mb-8">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">Add New Student</h1>
|
||||
|
||||
{{-- Validation errors --}}
|
||||
@if ($errors->any())
|
||||
<div class="mb-6 p-4 bg-red-100 text-red-700 rounded">
|
||||
<ul class="list-disc ml-5">
|
||||
@foreach ($errors->all() as $err)
|
||||
<li>{{ $err }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.students.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- STUDENT INFORMATION --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Student Information</h2>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-8">
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">First Name</label>
|
||||
<input type="text" name="student_first_name" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Last Name</label>
|
||||
<input type="text" name="student_last_name" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Email</label>
|
||||
<input type="email" name="student_email" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Phone (optional)</label>
|
||||
<input type="text" name="student_phone" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Date of Birth (optional)</label>
|
||||
<input type="date" name="dob" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- STUDENT TYPE --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Student Type</h2>
|
||||
|
||||
<div class="mb-8">
|
||||
<label class="block mb-1 font-medium">Select Student Type</label>
|
||||
<select name="student_type_id" class="w-full border-gray-300 rounded" required>
|
||||
<option value="">Choose a type...</option>
|
||||
@foreach ($studentTypes as $type)
|
||||
<option value="{{ $type->id }}">{{ $type->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- PARENT INFORMATION --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Parent / Guardian</h2>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block mb-1 font-medium">Parent Option</label>
|
||||
<select name="parent_option" id="parent_option"
|
||||
class="w-full border-gray-300 rounded"
|
||||
onchange="toggleParentSections()" required>
|
||||
<option value="none">No Parent</option>
|
||||
<option value="existing">Select Existing Parent</option>
|
||||
<option value="new">Create New Parent</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- EXISTING PARENT --}}
|
||||
<div id="existing_parent_section" class="hidden mb-8">
|
||||
<label class="block mb-1 font-medium">Select Parent</label>
|
||||
<select name="existing_parent_profile_id" class="w-full border-gray-300 rounded">
|
||||
<option value="">Choose parent...</option>
|
||||
@foreach ($parents as $parent)
|
||||
<option value="{{ $parent->id }}">
|
||||
{{ $parent->first_name }} {{ $parent->last_name }} ({{ $parent->email }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- NEW PARENT --}}
|
||||
<div id="new_parent_section" class="hidden mb-8">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">First Name</label>
|
||||
<input type="text" name="parent_first_name" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Last Name</label>
|
||||
<input type="text" name="parent_last_name" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Email</label>
|
||||
<input type="email" name="parent_email" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Phone (optional)</label>
|
||||
<input type="text" name="parent_phone" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- BILLING ACCOUNT --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Billing Account</h2>
|
||||
|
||||
<div id="billing-alert" class="hidden mt-4 p-4 rounded-lg border border-yellow-300 bg-yellow-100 text-yellow-900">
|
||||
<strong>Existing Billing Account Found</strong><br>
|
||||
This email already has a billing account. The student will be linked to it.<br>
|
||||
<span id="billing-name-display" class="font-semibold"></span>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="billing_account_id" id="billing_account_id">
|
||||
<div id="billing-fields">
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-8">
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Billing Account Name</label>
|
||||
<input type="text" name="billing_name" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Invoice Email</label>
|
||||
<input type="email" name="invoice_email" id="invoice_email" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Billing Phone (optional)</label>
|
||||
<input type="text" name="billing_phone" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Contact Name (optional)</label>
|
||||
<input type="text" name="contact_name" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Address Line 1</label>
|
||||
<input type="text" name="address_line1" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Address Line 2 (optional)</label>
|
||||
<input type="text" name="address_line2" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Town / City</label>
|
||||
<input type="text" name="town" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Postcode</label>
|
||||
<input type="text" name="postcode" class="w-full border-gray-300 rounded" required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Country</label>
|
||||
<input type="text" name="country" class="w-full border-gray-300 rounded" required value="United Kingdom">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- SUBMIT --}}
|
||||
<button type="submit"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded">
|
||||
Create Student
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Simple JS for toggling parent sections --}}
|
||||
<script>
|
||||
function toggleParentSections() {
|
||||
const opt = document.getElementById('parent_option').value;
|
||||
|
||||
document.getElementById('existing_parent_section').classList.toggle('hidden', opt !== 'existing');
|
||||
document.getElementById('new_parent_section').classList.toggle('hidden', opt !== 'new');
|
||||
}
|
||||
|
||||
function disableBillingFields(disabled) {
|
||||
const fields = document.querySelectorAll('#billing-fields input, #billing-fields select');
|
||||
|
||||
fields.forEach(field => {
|
||||
field.disabled = disabled;
|
||||
|
||||
if (disabled) {
|
||||
field.classList.add('bg-gray-100', 'cursor-not-allowed', 'text-gray-500');
|
||||
} else {
|
||||
field.classList.remove('bg-gray-100', 'cursor-not-allowed', 'text-gray-500');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('invoice_email').addEventListener('change', function () {
|
||||
fetch('/admin/billing/check-email?email=' + this.value)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.exists) {
|
||||
// Show alert
|
||||
document.getElementById('billing-alert').classList.remove('hidden');
|
||||
|
||||
// Show account name
|
||||
document.getElementById('billing-name-display').textContent =
|
||||
'Billing account: ' + data.billing_name;
|
||||
|
||||
// Set hidden input
|
||||
document.getElementById('billing_account_id').value = data.billing_account_id;
|
||||
|
||||
// Disable billing fields
|
||||
disableBillingFields(true);
|
||||
} else {
|
||||
// Hide alert
|
||||
document.getElementById('billing-alert').classList.add('hidden');
|
||||
|
||||
// Clear hidden input
|
||||
document.getElementById('billing_account_id').value = '';
|
||||
|
||||
// Enable billing fields
|
||||
disableBillingFields(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,269 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
|
||||
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg mb-8">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
<h1 class="text-2xl font-bold mb-6">
|
||||
Edit Student: {{ $student->user->first_name }} {{ $student->user->last_name }}
|
||||
</h1>
|
||||
|
||||
{{-- Validation errors --}}
|
||||
@if ($errors->any())
|
||||
<div class="mb-6 p-4 bg-red-100 text-red-700 rounded">
|
||||
<ul class="list-disc ml-5">
|
||||
@foreach ($errors->all() as $err)
|
||||
<li>{{ $err }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.students.update', $student->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- STUDENT INFORMATION --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Student Information</h2>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-8">
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">First Name</label>
|
||||
<input type="text"
|
||||
name="student_first_name"
|
||||
value="{{ old('student_first_name', $student->user->first_name) }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Last Name</label>
|
||||
<input type="text"
|
||||
name="student_last_name"
|
||||
value="{{ old('student_last_name', $student->user->last_name) }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Email</label>
|
||||
<input type="email"
|
||||
name="student_email"
|
||||
value="{{ old('student_email', $student->user->email) }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Phone</label>
|
||||
<input type="text"
|
||||
name="student_phone"
|
||||
value="{{ old('student_phone', $student->user->phone) }}"
|
||||
class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Date of Birth</label>
|
||||
<input type="date"
|
||||
name="date_of_birth"
|
||||
value="{{ old('date_of_birth', $student->date_of_birth?->format('Y-m-d')) }}"
|
||||
class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- STUDENT TYPE --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Student Type</h2>
|
||||
|
||||
<div class="mb-8">
|
||||
<label class="block mb-1 font-medium">Select Student Type</label>
|
||||
<select name="student_type_id"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
@foreach ($studentTypes as $type)
|
||||
<option value="{{ $type->id }}"
|
||||
{{ $student->student_type_id == $type->id ? 'selected' : '' }}>
|
||||
{{ $type->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- PARENT SELECTION --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Parent / Guardian</h2>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block mb-1 font-medium">Parent Option</label>
|
||||
<select name="parent_option"
|
||||
id="parent_option"
|
||||
onchange="toggleParentSections()"
|
||||
class="w-full border-gray-300 rounded">
|
||||
|
||||
<option value="none"
|
||||
{{ $student->billingAccount && $student->billingAccount->primary_parent_user_id ? '' : 'selected' }}>
|
||||
No Parent
|
||||
</option>
|
||||
|
||||
<option value="existing">Select Existing Parent</option>
|
||||
|
||||
<option value="new">Create New Parent</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Existing Parent --}}
|
||||
<div id="existing_parent_section" class="hidden mb-8">
|
||||
<label class="block mb-1 font-medium">Select Parent</label>
|
||||
<select name="existing_parent_id" class="w-full border-gray-300 rounded">
|
||||
<option value="">Choose parent...</option>
|
||||
|
||||
@foreach ($parents as $parent)
|
||||
|
||||
<option value="{{ $parent->user_id }}">
|
||||
@if($student->billingAccount && $student->billingAccount->primary_parent_user_id == $parent->user_id)
|
||||
selected
|
||||
@endif
|
||||
{{ $parent->user->first_name }} {{ $parent->user->last_name }} ({{ $parent->user->email }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- New Parent --}}
|
||||
<div id="new_parent_section" class="hidden mb-8">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Parent First Name</label>
|
||||
<input type="text" name="parent_first_name" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Parent Last Name</label>
|
||||
<input type="text" name="parent_last_name" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Parent Email</label>
|
||||
<input type="email" name="parent_email" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Parent Phone</label>
|
||||
<input type="text" name="parent_phone" class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ========================================= --}}
|
||||
{{-- BILLING INFORMATION --}}
|
||||
{{-- ========================================= --}}
|
||||
<h2 class="text-xl font-semibold mb-4">Billing Account</h2>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-8">
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Billing Account Name</label>
|
||||
<input type="text"
|
||||
name="billing_name"
|
||||
value="{{ old('billing_name', $student->billingAccount->name ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Invoice Email</label>
|
||||
<input type="email"
|
||||
name="invoice_email"
|
||||
value="{{ old('invoice_email', $student->billingAccount->invoice_email ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Billing Phone</label>
|
||||
<input type="text"
|
||||
name="billing_phone"
|
||||
value="{{ old('billing_phone', $student->billingAccount->phone ?? '') }}"
|
||||
class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Contact Name</label>
|
||||
<input type="text"
|
||||
name="contact_name"
|
||||
value="{{ old('contact_name', $student->billingAccount->contact_name ?? '') }}"
|
||||
class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Address Line 1</label>
|
||||
<input type="text"
|
||||
name="address_line1"
|
||||
value="{{ old('address_line1', $student->billingAccount->address_line1 ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Address Line 2</label>
|
||||
<input type="text"
|
||||
name="address_line2"
|
||||
value="{{ old('address_line2', $student->billingAccount->address_line2 ?? '') }}"
|
||||
class="w-full border-gray-300 rounded">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Town</label>
|
||||
<input type="text"
|
||||
name="town"
|
||||
value="{{ old('town', $student->billingAccount->town ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-1 font-medium">Postcode</label>
|
||||
<input type="text"
|
||||
name="postcode"
|
||||
value="{{ old('postcode', $student->billingAccount->postcode ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block mb-1 font-medium">Country</label>
|
||||
<input type="text"
|
||||
name="country"
|
||||
value="{{ old('country', $student->billingAccount->country ?? '') }}"
|
||||
class="w-full border-gray-300 rounded"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- SUBMIT --}}
|
||||
<button type="submit"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded">
|
||||
Save Changes
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleParentSections() {
|
||||
const opt = document.getElementById('parent_option').value;
|
||||
|
||||
document.getElementById('existing_parent_section').classList.toggle('hidden', opt !== 'existing');
|
||||
document.getElementById('new_parent_section').classList.toggle('hidden', opt !== 'new');
|
||||
}
|
||||
</script>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,69 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
|
||||
{{-- Header --}}
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold">Students</h1>
|
||||
|
||||
<a href="{{ route('admin.students.create') }}"
|
||||
class="inline-block bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md">
|
||||
+ Add Student
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Table --}}
|
||||
@if($students->isEmpty())
|
||||
<p>No students found.</p>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-auto w-full border-collapse border border-gray-300">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="border border-gray-300 px-4 py-2 text-left">Name</th>
|
||||
<th class="border border-gray-300 px-4 py-2 text-left">Email</th>
|
||||
<th class="border border-gray-300 px-4 py-2 text-left">Billing Account</th>
|
||||
<th class="border border-gray-300 px-4 py-2 text-left">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($students as $student)
|
||||
<tr>
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
{{ $student->user->first_name }} {{ $student->user->last_name }}
|
||||
</td>
|
||||
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
{{ $student->user->email }}
|
||||
</td>
|
||||
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
@if ($student->billingAccount)
|
||||
{{ $student->billingAccount->name }}
|
||||
@else
|
||||
<span class="text-gray-400">None</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
<a href="{{ route('admin.students.edit', $student->id) }}"
|
||||
class="text-blue-600 hover:text-blue-800">
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,40 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-4">Add Subject</h1>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="bg-red-100 text-red-800 p-3 rounded mb-4">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.subjects.store') }}" method="POST" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<label for="name" class="block font-medium text-sm text-gray-700">Subject Name</label>
|
||||
<input id="name" type="text" name="name" class="form-input mt-1 block w-full"
|
||||
value="{{ old('name') }}" required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-gray-200 border border-gray-300 rounded-md
|
||||
font-semibold text-sm text-black hover:bg-gray-300
|
||||
focus:outline-none focus:ring focus:ring-gray-400 transition">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,36 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-4">Subjects</h1>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($subjects->isEmpty())
|
||||
<p>No subjects found.</p>
|
||||
@else
|
||||
<ul class="list-disc pl-5">
|
||||
@foreach ($subjects as $subject)
|
||||
<li>{{ $subject->name }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
<div class="mt-6">
|
||||
<a href="{{ route('admin.subjects.create') }}"
|
||||
class="px-4 py-2 bg-gray-200 border border-gray-300 rounded-md
|
||||
font-semibold text-sm text-black hover:bg-gray-300
|
||||
focus:outline-none focus:ring focus:ring-gray-400 transition">
|
||||
Add New Subject
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,106 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-6">Add Teacher</h1>
|
||||
|
||||
<form method="POST" action="{{ route('admin.teachers.store') }}" enctype="multipart/form-data" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<!-- Name -->
|
||||
<div>
|
||||
<label for="name" class="block font-medium text-sm text-gray-700">
|
||||
Name
|
||||
</label>
|
||||
<input id="name" type="text" name="name" class="form-input mt-1 block w-full" required />
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label for="email" class="block font-medium text-sm text-gray-700">
|
||||
Email
|
||||
</label>
|
||||
<input id="email" type="email" name="email" class="form-input mt-1 block w-full" required />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label for="password" class="block font-medium text-sm text-gray-700">
|
||||
Password
|
||||
</label>
|
||||
<input id="password" type="password" name="password" class="form-input mt-1 block w-full" required />
|
||||
</div>
|
||||
|
||||
<!-- Bio -->
|
||||
<div>
|
||||
<label for="bio" class="block font-medium text-sm text-gray-700">
|
||||
Bio
|
||||
</label>
|
||||
<textarea id="bio" name="bio" rows="4" class="form-textarea mt-1 block w-full"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Picture -->
|
||||
<div>
|
||||
<label for="picture" class="block font-medium text-sm text-gray-700">
|
||||
Profile Picture
|
||||
</label>
|
||||
<input id="picture" type="file" name="picture" class="mt-1 block w-full" />
|
||||
<p class="text-sm text-gray-500 mt-1">Leave empty to use the default placeholder.</p>
|
||||
</div>
|
||||
|
||||
<!-- Location -->
|
||||
<div>
|
||||
<label for="location" class="block font-medium text-sm text-gray-700">
|
||||
Location
|
||||
</label>
|
||||
<input id="location" type="text" name="location" class="form-input mt-1 block w-full" />
|
||||
</div>
|
||||
|
||||
<!-- Hourly rate -->
|
||||
<div>
|
||||
<label for="hourly_rate" class="block font-medium text-sm text-gray-700">
|
||||
Hourly Rate (£)
|
||||
</label>
|
||||
<input id="hourly_rate" type="number" step="0.01" name="hourly_rate"
|
||||
class="form-input mt-1 block w-full" />
|
||||
</div>
|
||||
|
||||
<!-- Subjects -->
|
||||
<div>
|
||||
<label for="subjects" class="block font-medium text-sm text-gray-700">
|
||||
Subjects
|
||||
</label>
|
||||
<select id="subjects" name="subjects[]" multiple class="form-multiselect mt-1 block w-full">
|
||||
@foreach($subjects as $subject)
|
||||
<option value="{{ $subject->id }}">{{ $subject->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="text-sm text-gray-500 mt-1">Hold Ctrl (Windows) or Command (Mac) to select multiple.</p>
|
||||
</div>
|
||||
|
||||
<!-- Levels -->
|
||||
<div>
|
||||
<label for="levels" class="block font-medium text-sm text-gray-700">
|
||||
Levels
|
||||
</label>
|
||||
<select id="levels" name="levels[]" multiple class="form-multiselect mt-1 block w-full">
|
||||
@foreach($levels as $level)
|
||||
<option value="{{ $level->id }}">{{ $level->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="text-sm text-gray-500 mt-1">Hold Ctrl (Windows) or Command (Mac) to select multiple.</p>
|
||||
</div>
|
||||
|
||||
<!-- Submit -->
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Save Teacher
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,48 @@
|
||||
<x-app-layout>
|
||||
<div class="py-12">
|
||||
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
<h1 class="text-2xl font-bold mb-4">All Teachers</h1>
|
||||
|
||||
@if($teachers->isEmpty())
|
||||
<p>No teachers found.</p>
|
||||
@else
|
||||
<table class="table-auto w-full border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="px-4 py-2 text-left">Name</th>
|
||||
<th class="px-4 py-2 text-left">Email</th>
|
||||
<th class="px-4 py-2 text-left">Bio</th>
|
||||
<th class="px-4 py-2 text-left">Location</th>
|
||||
<th class="px-4 py-2 text-left">Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($teachers as $teacher)
|
||||
<tr class="border-t">
|
||||
<td class="px-4 py-2">
|
||||
{{ $teacher->user->name ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $teacher->user->email ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $teacher->bio ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $teacher->location ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $teacher->hourly_rate ? '£'.$teacher->hourly_rate : '-' }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,37 @@
|
||||
<x-app-layout>
|
||||
<h1 class="text-xl font-bold mb-4">Manage Users</h1>
|
||||
|
||||
<table class="table-auto w-full border">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="px-4 py-2 text-left">Name</th>
|
||||
<th class="px-4 py-2 text-left">Email</th>
|
||||
<th class="px-4 py-2 text-left">Current Role</th>
|
||||
<th class="px-4 py-2 text-left">Change Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($users as $user)
|
||||
<tr class="border-t">
|
||||
<td class="px-4 py-2">{{ $user->name }}</td>
|
||||
<td class="px-4 py-2">{{ $user->email }}</td>
|
||||
<td class="px-4 py-2">{{ $user->roles->pluck('name')->implode(', ') }}</td>
|
||||
<td class="px-4 py-2">
|
||||
<form method="POST" action="{{ route('admin.users.updateRole', $user) }}">
|
||||
@csrf
|
||||
<select name="role" class="border rounded px-2 py-1">
|
||||
@foreach($roles as $role)
|
||||
<option value="{{ $role->name }}"
|
||||
@selected($user->roles->contains('name', $role->name))>
|
||||
{{ ucfirst($role->name) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="ml-2 bg-blue-500 text-white px-2 py-1 rounded">Update</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</x-app-layout>
|
||||
Reference in new issue
Block a user