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,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>