added the listing of the clubs but didnt succseed
This commit is contained in:
parent
0efe1f69e5
commit
59ad30575c
@ -54,9 +54,9 @@ class ClubController extends Controller
|
|||||||
'gps_lat' => (float) $club->gps_lat,
|
'gps_lat' => (float) $club->gps_lat,
|
||||||
'gps_long' => (float) $club->gps_long,
|
'gps_long' => (float) $club->gps_long,
|
||||||
'distance' => round($distance, 2), // distance in kilometers
|
'distance' => round($distance, 2), // distance in kilometers
|
||||||
'owner_name' => $club->owner->full_name,
|
'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
|
||||||
'owner_email' => $club->owner->email,
|
'owner_email' => $club->owner ? $club->owner->email : null,
|
||||||
'owner_mobile' => $club->owner->mobile,
|
'owner_mobile' => $club->owner ? $club->owner->mobile : null,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,9 +107,7 @@ class ClubController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function all()
|
public function all()
|
||||||
{
|
{
|
||||||
$clubs = Tenant::whereNotNull('gps_lat')
|
$clubs = Tenant::with('owner')
|
||||||
->whereNotNull('gps_long')
|
|
||||||
->with('owner')
|
|
||||||
->get()
|
->get()
|
||||||
->map(function ($club) {
|
->map(function ($club) {
|
||||||
return [
|
return [
|
||||||
@ -117,9 +115,9 @@ class ClubController extends Controller
|
|||||||
'club_name' => $club->club_name,
|
'club_name' => $club->club_name,
|
||||||
'slug' => $club->slug,
|
'slug' => $club->slug,
|
||||||
'logo' => $club->logo,
|
'logo' => $club->logo,
|
||||||
'gps_lat' => (float) $club->gps_lat,
|
'gps_lat' => $club->gps_lat ? (float) $club->gps_lat : null,
|
||||||
'gps_long' => (float) $club->gps_long,
|
'gps_long' => $club->gps_long ? (float) $club->gps_long : null,
|
||||||
'owner_name' => $club->owner->full_name,
|
'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -31,9 +31,8 @@ class FamilyController extends Controller
|
|||||||
->sortBy(function($relationship) {
|
->sortBy(function($relationship) {
|
||||||
return $relationship->dependent->full_name;
|
return $relationship->dependent->full_name;
|
||||||
});
|
});
|
||||||
$familyInvoices = $this->familyService->getFamilyInvoices($user->id);
|
|
||||||
|
|
||||||
return view('family.dashboard', compact('user', 'dependents', 'familyInvoices'));
|
return view('family.dashboard', compact('user', 'dependents'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class InvoiceController extends Controller
|
|||||||
* @param int $id
|
* @param int $id
|
||||||
* @return \Illuminate\View\View
|
* @return \Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function show($id)
|
public function show(Request $request, $id)
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$invoice = Invoice::where('id', $id)
|
$invoice = Invoice::where('id', $id)
|
||||||
@ -42,9 +42,30 @@ class InvoiceController extends Controller
|
|||||||
->with(['student', 'tenant'])
|
->with(['student', 'tenant'])
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
|
if ($request->ajax()) {
|
||||||
|
return response()->json(['html' => view('invoices._show_modal', compact('invoice'))->render()]);
|
||||||
|
}
|
||||||
|
|
||||||
return view('invoices.show', compact('invoice'));
|
return view('invoices.show', compact('invoice'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the receipt for the specified invoice.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function receipt($id)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
$invoice = Invoice::where('id', $id)
|
||||||
|
->where('payer_user_id', $user->id)
|
||||||
|
->with(['student', 'tenant'])
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
return view('invoices.receipt', compact('invoice'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process payment for the specified invoice.
|
* Process payment for the specified invoice.
|
||||||
*
|
*
|
||||||
@ -63,7 +84,7 @@ class InvoiceController extends Controller
|
|||||||
'status' => 'paid'
|
'status' => 'paid'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('invoices.show', $invoice->id)
|
return redirect()->route('bills.show', $invoice->id)
|
||||||
->with('success', 'Payment processed successfully.');
|
->with('success', 'Payment processed successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +107,7 @@ class InvoiceController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('invoices.index')
|
return redirect()->route('bills.index')
|
||||||
->with('success', 'All payments processed successfully.');
|
->with('success', 'All payments processed successfully.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use App\Models\Membership;
|
||||||
|
use App\Models\Invoice;
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
@ -17,11 +20,47 @@ class DatabaseSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
// User::factory(10)->create();
|
// User::factory(10)->create();
|
||||||
|
|
||||||
User::factory()->create([
|
$user = User::firstOrCreate([
|
||||||
'name' => 'Test User',
|
|
||||||
'email' => 'test@example.com',
|
'email' => 'test@example.com',
|
||||||
|
], [
|
||||||
|
'name' => 'Test User',
|
||||||
'full_name' => 'Test User',
|
'full_name' => 'Test User',
|
||||||
'mobile' => ['code' => '+1', 'number' => '1234567890'],
|
'mobile' => ['code' => '+1', 'number' => '1234567890'],
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a tenant (club)
|
||||||
|
$tenant = Tenant::create([
|
||||||
|
'owner_user_id' => $user->id,
|
||||||
|
'club_name' => 'Test Club',
|
||||||
|
'slug' => 'test-club',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create membership
|
||||||
|
Membership::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a paid invoice
|
||||||
|
Invoice::create([
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'student_user_id' => $user->id,
|
||||||
|
'payer_user_id' => $user->id,
|
||||||
|
'amount' => 50.00,
|
||||||
|
'status' => 'paid',
|
||||||
|
'due_date' => now()->addDays(30),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a pending invoice
|
||||||
|
Invoice::create([
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'student_user_id' => $user->id,
|
||||||
|
'payer_user_id' => $user->id,
|
||||||
|
'amount' => 25.00,
|
||||||
|
'status' => 'pending',
|
||||||
|
'due_date' => now()->addDays(15),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,8 +89,8 @@
|
|||||||
<!-- Club cards will be inserted here -->
|
<!-- Club cards will be inserted here -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 d-flex justify-content-center">
|
<div class="col-12 d-flex justify-content-center" id="noResultsContainer" style="display: none;">
|
||||||
<div id="noResults" class="d-flex flex-column align-items-center justify-content-center text-center" style="display: none; min-height: 400px;">
|
<div id="noResults" class="d-flex flex-column align-items-center justify-content-center text-center" style="min-height: 400px;">
|
||||||
<i class="bi bi-inbox" style="font-size: 4rem; color: #dee2e6;"></i>
|
<i class="bi bi-inbox" style="font-size: 4rem; color: #dee2e6;"></i>
|
||||||
<h4 class="mt-3 text-muted">No Results Found</h4>
|
<h4 class="mt-3 text-muted">No Results Found</h4>
|
||||||
<p class="text-muted">Try adjusting your search or location</p>
|
<p class="text-muted">Try adjusting your search or location</p>
|
||||||
@ -284,6 +284,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
startWatchingLocation();
|
startWatchingLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initial load: fetch all clubs since 'all' is default
|
||||||
|
fetchAllClubs();
|
||||||
|
|
||||||
// Category buttons
|
// Category buttons
|
||||||
document.querySelectorAll('.category-btn').forEach(btn => {
|
document.querySelectorAll('.category-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function() {
|
||||||
@ -295,7 +298,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
this.classList.add('active', 'btn-primary');
|
this.classList.add('active', 'btn-primary');
|
||||||
|
|
||||||
currentCategory = this.dataset.category;
|
currentCategory = this.dataset.category;
|
||||||
filterClubs();
|
if (currentCategory === 'all') {
|
||||||
|
fetchAllClubs();
|
||||||
|
} else if (userLocation) {
|
||||||
|
fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
|
||||||
|
} else {
|
||||||
|
// If no location, show message or fetch all as fallback
|
||||||
|
fetchAllClubs();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -324,7 +334,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
mapModal.hide();
|
mapModal.hide();
|
||||||
|
|
||||||
if (userLocation) {
|
if (userLocation) {
|
||||||
fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
|
if (currentCategory === 'all') {
|
||||||
|
fetchAllClubs();
|
||||||
|
} else {
|
||||||
|
fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -339,7 +353,11 @@ function startWatchingLocation() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateLocationDisplay(userLocation.latitude, userLocation.longitude);
|
updateLocationDisplay(userLocation.latitude, userLocation.longitude);
|
||||||
fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
|
|
||||||
|
// If current category is not 'all', fetch nearby clubs
|
||||||
|
if (currentCategory !== 'all') {
|
||||||
|
fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
|
||||||
|
}
|
||||||
|
|
||||||
// Stop watching after first successful location
|
// Stop watching after first successful location
|
||||||
if (watchId) {
|
if (watchId) {
|
||||||
@ -361,7 +379,10 @@ function startWatchingLocation() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
showAlert(errorMessage, 'danger');
|
showAlert(errorMessage, 'danger');
|
||||||
document.getElementById('loadingSpinner').style.display = 'none';
|
// If location fails and category is not 'all', perhaps fetch all as fallback
|
||||||
|
if (currentCategory !== 'all') {
|
||||||
|
fetchAllClubs();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableHighAccuracy: true,
|
enableHighAccuracy: true,
|
||||||
@ -454,19 +475,50 @@ function fetchNearbyClubs(lat, lng) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch all clubs
|
||||||
|
function fetchAllClubs() {
|
||||||
|
document.getElementById('loadingSpinner').style.display = 'block';
|
||||||
|
document.getElementById('clubsGrid').style.display = 'none';
|
||||||
|
|
||||||
|
fetch(`{{ route('clubs.all') }}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('loadingSpinner').style.display = 'none';
|
||||||
|
document.getElementById('clubsGrid').style.display = 'block';
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
allClubs = data.clubs;
|
||||||
|
displayClubs(allClubs);
|
||||||
|
} else {
|
||||||
|
showAlert('Failed to fetch clubs', 'danger');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
document.getElementById('loadingSpinner').style.display = 'none';
|
||||||
|
showAlert('Error fetching clubs', 'danger');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Display clubs as cards
|
// Display clubs as cards
|
||||||
function displayClubs(clubs) {
|
function displayClubs(clubs) {
|
||||||
const container = document.getElementById('clubsContainer');
|
const container = document.getElementById('clubsContainer');
|
||||||
const noResults = document.getElementById('noResults');
|
const noResultsContainer = document.getElementById('noResultsContainer');
|
||||||
|
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
if (clubs.length === 0) {
|
if (clubs.length === 0) {
|
||||||
noResults.style.display = 'flex';
|
noResultsContainer.style.display = 'flex';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
noResults.style.display = 'none';
|
noResultsContainer.style.display = 'none';
|
||||||
|
|
||||||
clubs.forEach(club => {
|
clubs.forEach(club => {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
@ -491,7 +543,7 @@ function displayClubs(clubs) {
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-navigation w-4 h-4">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-navigation w-4 h-4">
|
||||||
<polygon points="3 11 22 2 13 21 11 13 3 11"></polygon>
|
<polygon points="3 11 22 2 13 21 11 13 3 11"></polygon>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="font-medium">${club.distance} km away</span>
|
<span class="font-medium">${club.distance ? club.distance + ' km away' : 'Location available'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 text-sm text-muted-foreground">
|
<div class="flex items-center gap-1 text-sm text-muted-foreground">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-building w-4 h-4">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-building w-4 h-4">
|
||||||
|
|||||||
@ -4,11 +4,6 @@
|
|||||||
<div class="container py-4">
|
<div class="container py-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h1 class="mb-0">Family</h1>
|
<h1 class="mb-0">Family</h1>
|
||||||
<div>
|
|
||||||
<a href="{{ route('invoices.index') }}" class="btn btn-outline-primary">
|
|
||||||
<i class="bi bi-receipt"></i> All Invoices
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Family Members Card Grid -->
|
<!-- Family Members Card Grid -->
|
||||||
@ -98,14 +93,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-2 border-top">
|
<div class="pt-2 border-top">
|
||||||
<div class="d-flex justify-content-between align-items-center small mb-2">
|
<div class="d-flex justify-content-between align-items-center small mb-2">
|
||||||
<span class="text-muted fw-medium">Next Birthday</span>
|
<span class="text-muted fw-medium">Next Birthday</span>
|
||||||
<span class="fw-semibold text-muted">
|
<span class="fw-semibold text-muted">
|
||||||
{{ $user->birthdate->copy()->year(now()->year)->isFuture()
|
@if($user->birthdate)
|
||||||
? $user->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
|
{{ $user->birthdate->copy()->year(now()->year)->isFuture()
|
||||||
: $user->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
|
? $user->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
|
||||||
</span>
|
: $user->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
|
||||||
</div>
|
@else
|
||||||
|
N/A
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center small">
|
<div class="d-flex justify-content-between align-items-center small">
|
||||||
<span class="text-muted fw-medium">Member Since</span>
|
<span class="text-muted fw-medium">Member Since</span>
|
||||||
<span class="fw-semibold text-muted">{{ $user->created_at->format('d/m/Y') }}</span>
|
<span class="fw-semibold text-muted">{{ $user->created_at->format('d/m/Y') }}</span>
|
||||||
@ -245,9 +244,13 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center small mb-2">
|
<div class="d-flex justify-content-between align-items-center small mb-2">
|
||||||
<span class="text-muted fw-medium">Next Birthday</span>
|
<span class="text-muted fw-medium">Next Birthday</span>
|
||||||
<span class="fw-semibold text-muted">
|
<span class="fw-semibold text-muted">
|
||||||
{{ $relationship->dependent->birthdate->copy()->year(now()->year)->isFuture()
|
@if($relationship->dependent->birthdate)
|
||||||
? $relationship->dependent->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
|
{{ $relationship->dependent->birthdate->copy()->year(now()->year)->isFuture()
|
||||||
: $relationship->dependent->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
|
? $relationship->dependent->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
|
||||||
|
: $relationship->dependent->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
|
||||||
|
@else
|
||||||
|
N/A
|
||||||
|
@endif
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center small">
|
<div class="d-flex justify-content-between align-items-center small">
|
||||||
@ -283,68 +286,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Family Payments Table -->
|
|
||||||
<div class="card shadow-sm mb-4">
|
|
||||||
<div class="card-header bg-white">
|
|
||||||
<h4 class="mb-0">Family Payments</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Student Name</th>
|
|
||||||
<th>Class/Package</th>
|
|
||||||
<th>Amount</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@forelse($familyInvoices as $invoice)
|
|
||||||
<tr>
|
|
||||||
<td>{{ $invoice->student->full_name }}</td>
|
|
||||||
<td>{{ $invoice->tenant->club_name }}</td>
|
|
||||||
<td>${{ number_format($invoice->amount, 2) }}</td>
|
|
||||||
<td>
|
|
||||||
@if($invoice->status === 'paid')
|
|
||||||
<span class="badge bg-success">Paid</span>
|
|
||||||
@elseif($invoice->status === 'pending')
|
|
||||||
<span class="badge bg-warning text-dark">Pending</span>
|
|
||||||
@else
|
|
||||||
<span class="badge bg-danger">Overdue</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{{ route('invoices.show', $invoice->id) }}" class="btn btn-sm btn-outline-primary">
|
|
||||||
<i class="bi bi-eye"></i> View
|
|
||||||
</a>
|
|
||||||
@if($invoice->status !== 'paid')
|
|
||||||
<a href="{{ route('invoices.pay', $invoice->id) }}" class="btn btn-sm btn-success">
|
|
||||||
<i class="bi bi-credit-card"></i> Pay
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@empty
|
|
||||||
<tr>
|
|
||||||
<td colspan="5" class="text-center py-4">
|
|
||||||
<p class="text-muted mb-0">No payments due at this time.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforelse
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer bg-white d-flex justify-content-end">
|
|
||||||
@if(count($familyInvoices->where('status', '!=', 'paid')) > 0)
|
|
||||||
<a href="{{ route('invoices.pay-all') }}" class="btn btn-success">
|
|
||||||
<i class="bi bi-credit-card"></i> Pay All
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add Family Member Modal -->
|
<!-- Add Family Member Modal -->
|
||||||
|
|||||||
83
resources/views/invoices/_show_modal.blade.php
Normal file
83
resources/views/invoices/_show_modal.blade.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
@if(session('success'))
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('success') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5>Billed To</h5>
|
||||||
|
<p class="mb-1">{{ Auth::user()->full_name }}</p>
|
||||||
|
<p class="mb-1">{{ Auth::user()->email }}</p>
|
||||||
|
@if(Auth::user()->mobile)
|
||||||
|
<p class="mb-0">{{ Auth::user()->mobile }}</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<h5>Invoice Details</h5>
|
||||||
|
<p class="mb-1">Invoice #: {{ $invoice->id }}</p>
|
||||||
|
<p class="mb-1">Due Date: {{ $invoice->due_date->format('F j, Y') }}</p>
|
||||||
|
<p class="mb-0">
|
||||||
|
Status:
|
||||||
|
@if($invoice->status === 'paid')
|
||||||
|
<span class="badge bg-success">Paid</span>
|
||||||
|
@elseif($invoice->status === 'pending')
|
||||||
|
<span class="badge bg-warning text-dark">Pending</span>
|
||||||
|
@else
|
||||||
|
<span class="badge bg-danger">Overdue</span>
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5>Club Information</h5>
|
||||||
|
<p class="mb-1">{{ $invoice->tenant->club_name }}</p>
|
||||||
|
<p class="mb-0">{{ $invoice->tenant->owner->full_name }} (Owner)</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<h5>Student Information</h5>
|
||||||
|
<p class="mb-1">{{ $invoice->student->full_name }}</p>
|
||||||
|
<p class="mb-0">Age: {{ $invoice->student->age }} ({{ $invoice->student->life_stage }})</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive mb-4">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="text-end">Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Club Membership Fee - {{ $invoice->tenant->club_name }}</td>
|
||||||
|
<td class="text-end">${{ number_format($invoice->amount, 2) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th>Total</th>
|
||||||
|
<th class="text-end">${{ number_format($invoice->amount, 2) }}</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
@if($invoice->status !== 'paid')
|
||||||
|
<a href="{{ route('bills.pay', $invoice->id) }}" class="btn btn-success">
|
||||||
|
<i class="bi bi-credit-card"></i> Pay Now
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<button class="btn btn-outline-success me-2" disabled>
|
||||||
|
<i class="bi bi-check-circle"></i> Paid
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('bills.receipt', $invoice->id) }}" class="btn btn-outline-primary" target="_blank">
|
||||||
|
<i class="bi bi-receipt"></i> View Receipt
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@ -3,21 +3,21 @@
|
|||||||
@section('content')
|
@section('content')
|
||||||
<div class="container py-4">
|
<div class="container py-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h1 class="mb-0">My Invoices</h1>
|
<h1 class="mb-0">My Bills</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||||||
<h4 class="mb-0">All Invoices</h4>
|
<h4 class="mb-0">All Bills</h4>
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-funnel"></i> Filter
|
<i class="bi bi-funnel"></i> Filter
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li><a class="dropdown-item" href="{{ route('invoices.index') }}">All</a></li>
|
<li><a class="dropdown-item" href="{{ route('bills.index') }}">All</a></li>
|
||||||
<li><a class="dropdown-item" href="{{ route('invoices.index', ['status' => 'pending']) }}">Pending</a></li>
|
<li><a class="dropdown-item" href="{{ route('bills.index', ['status' => 'pending']) }}">Pending</a></li>
|
||||||
<li><a class="dropdown-item" href="{{ route('invoices.index', ['status' => 'paid']) }}">Paid</a></li>
|
<li><a class="dropdown-item" href="{{ route('bills.index', ['status' => 'paid']) }}">Paid</a></li>
|
||||||
<li><a class="dropdown-item" href="{{ route('invoices.index', ['status' => 'overdue']) }}">Overdue</a></li>
|
<li><a class="dropdown-item" href="{{ route('bills.index', ['status' => 'overdue']) }}">Overdue</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,13 +62,17 @@
|
|||||||
<td>{{ $invoice->due_date->format('M j, Y') }}</td>
|
<td>{{ $invoice->due_date->format('M j, Y') }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{{ route('invoices.show', $invoice->id) }}" class="btn btn-sm btn-outline-primary">
|
<a href="#" class="btn btn-sm btn-outline-primary view-invoice" data-id="{{ $invoice->id }}">
|
||||||
<i class="bi bi-eye"></i> View
|
<i class="bi bi-eye"></i> View
|
||||||
</a>
|
</a>
|
||||||
@if($invoice->status !== 'paid')
|
@if($invoice->status !== 'paid')
|
||||||
<a href="{{ route('invoices.pay', $invoice->id) }}" class="btn btn-sm btn-success">
|
<a href="{{ route('bills.pay', $invoice->id) }}" class="btn btn-sm btn-success">
|
||||||
<i class="bi bi-credit-card"></i> Pay
|
<i class="bi bi-credit-card"></i> Pay
|
||||||
</a>
|
</a>
|
||||||
|
@else
|
||||||
|
<a href="{{ route('bills.receipt', $invoice->id) }}" class="btn btn-sm btn-outline-info" target="_blank">
|
||||||
|
<i class="bi bi-receipt"></i> Receipt
|
||||||
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -80,18 +84,57 @@
|
|||||||
@else
|
@else
|
||||||
<div class="text-center py-5">
|
<div class="text-center py-5">
|
||||||
<i class="bi bi-receipt" style="font-size: 3rem;"></i>
|
<i class="bi bi-receipt" style="font-size: 3rem;"></i>
|
||||||
<h4 class="mt-3">No Invoices Found</h4>
|
<h4 class="mt-3">No Bills Found</h4>
|
||||||
<p class="text-muted">There are no invoices matching your criteria.</p>
|
<p class="text-muted">There are no bills matching your criteria.</p>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if($invoices->where('status', '!=', 'paid')->count() > 0)
|
@if($invoices->where('status', '!=', 'paid')->count() > 0)
|
||||||
<div class="card-footer bg-white d-flex justify-content-end">
|
<div class="card-footer bg-white d-flex justify-content-end">
|
||||||
<a href="{{ route('invoices.pay-all') }}" class="btn btn-success">
|
<a href="{{ route('bills.pay-all') }}" class="btn btn-success">
|
||||||
<i class="bi bi-credit-card"></i> Pay All
|
<i class="bi bi-credit-card"></i> Pay All
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Invoice Modal -->
|
||||||
|
<div class="modal fade" id="invoiceModal" tabindex="-1" aria-labelledby="invoiceModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="invoiceModalLabel">Invoice Details</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="invoiceModalBody">
|
||||||
|
<!-- Content will be loaded here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('.view-invoice').forEach(function(button) {
|
||||||
|
button.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var invoiceId = this.getAttribute('data-id');
|
||||||
|
fetch('/bills/' + invoiceId, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML = data.html;
|
||||||
|
var modal = new bootstrap.Modal(document.getElementById('invoiceModal'));
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
429
resources/views/invoices/receipt.blade.php
Normal file
429
resources/views/invoices/receipt.blade.php
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Receipt</title>
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bahrain-red: #C41E3A;
|
||||||
|
--bahrain-white: #FFFFFF;
|
||||||
|
--text-color: #333;
|
||||||
|
--light-gray: #F2F2F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-container {
|
||||||
|
max-width: 148mm;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--bahrain-white);
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
position: relative;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: var(--bahrain-red);
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--bahrain-white);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-title-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid rgba(255, 255, 255, 0.5);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-title-wrapper h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-title-wrapper .slogan {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registration-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
margin-top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reg-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.white-box {
|
||||||
|
background-color: var(--bahrain-white);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 5px 10px;
|
||||||
|
/* Changed border-radius to a high value for a pill shape */
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed widths for the white boxes */
|
||||||
|
.reg-row .white-box:first-child {
|
||||||
|
flex: 7 1 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reg-row .white-box:last-child {
|
||||||
|
flex: 3 1 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header img {
|
||||||
|
max-width: 120px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 10px 20px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-details {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 500px) {
|
||||||
|
.invoice-info {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.info-details {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles for the info rows using Flexbox */
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--bahrain-red);
|
||||||
|
border-radius: 999px;
|
||||||
|
color: var(--bahrain-white);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px 15px;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 220px; /* Adjust width to fit content */
|
||||||
|
margin-bottom: 5px; /* Creates separation between rows */
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row span.white-pill {
|
||||||
|
background-color: var(--bahrain-white);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
color: var(--bahrain-red);
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead {
|
||||||
|
background-color: var(--bahrain-red);
|
||||||
|
color: var(--bahrain-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
table th, table td {
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr:nth-child(even) {
|
||||||
|
background-color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Aligning text in the new 'Total' column */
|
||||||
|
table td:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
/* Aligning text in 'Price' and 'Qty' columns */
|
||||||
|
table td:nth-child(3),
|
||||||
|
table td:nth-child(4) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-total-table {
|
||||||
|
width: 250px;
|
||||||
|
float: right;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-total-table td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-total-table tr:not(:last-child) {
|
||||||
|
background-color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-total-table .grand-total-row {
|
||||||
|
background-color: var(--bahrain-red) !important;
|
||||||
|
color: var(--bahrain-white);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New styles for the conditional note */
|
||||||
|
.receipt-note {
|
||||||
|
background-color: #fff3cd; /* A light, noticeable color */
|
||||||
|
border-left: 5px solid #ffc107; /* A bright border */
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #664d03;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: none; /* Hide by default */
|
||||||
|
}
|
||||||
|
.receipt-note strong {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
line-height: 1.4;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info-item .icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--bahrain-red);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: var(--bahrain-red);
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--bahrain-white);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom-left-radius: 6px;
|
||||||
|
border-bottom-right-radius: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="invoice-container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="header-info">
|
||||||
|
<div class="receipt-title-wrapper">
|
||||||
|
<h1>TAKEONE</h1>
|
||||||
|
<p class="slogan">Connect, Share, Thrive.</p>
|
||||||
|
</div>
|
||||||
|
<div class="registration-info">
|
||||||
|
<div class="reg-row">
|
||||||
|
<div class="white-box">REGISTRATION #</div>
|
||||||
|
<div class="white-box">001</div>
|
||||||
|
</div>
|
||||||
|
<div class="reg-row">
|
||||||
|
<div class="white-box">VAT REGISTRATION #</div>
|
||||||
|
<div class="white-box">N/A</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Company Logo">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="invoice-info">
|
||||||
|
<div>
|
||||||
|
<p class="to"><B>RECEIPT TO :</B></p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $invoice->student->full_name }}</strong><br>
|
||||||
|
{{ $invoice->student->mobile_formatted ?: 'N/A' }}<br>
|
||||||
|
{{ $invoice->student->email ?: 'N/A' }}<br>
|
||||||
|
{{ $invoice->student->addresses ? implode(', ', array_filter($invoice->student->addresses)) : 'N/A' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-details">
|
||||||
|
<div class="info-row">
|
||||||
|
<span>RECEIPT NO</span>
|
||||||
|
<span class="white-pill">{{ $invoice->id }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span>ISSUE DATE</span>
|
||||||
|
<span class="white-pill">{{ $invoice->created_at->format('d-m-Y') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="receipt-items">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Item No</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Qty</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>Club Membership Fee - {{ $invoice->tenant->club_name }}</td>
|
||||||
|
<td>1</td>
|
||||||
|
<td>{{ number_format($invoice->amount, 2) }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="payment-note" class="receipt-note">
|
||||||
|
<p><strong>Note:</strong> This payment covers the membership fee for <strong>{{ $invoice->student->full_name }}</strong> at <strong>{{ $invoice->tenant->club_name }}</strong>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="sub-total-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Sub Total:</td>
|
||||||
|
<td id="sub-total"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>VAT (0%):</td>
|
||||||
|
<td id="vat"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="grand-total-row">
|
||||||
|
<td>Grand Total:</td>
|
||||||
|
<td id="grand-total"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="contact-info">
|
||||||
|
<div class="contact-info-item">
|
||||||
|
<span class="icon">☎</span>
|
||||||
|
<div>
|
||||||
|
+1 123 456 7890
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact-info-item">
|
||||||
|
<span class="icon">✉</span>
|
||||||
|
<div>
|
||||||
|
support@takeone.com
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact-info-item">
|
||||||
|
<span class="icon">⚲</span>
|
||||||
|
<div>
|
||||||
|
123 Main St, City, Country
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
© 2025 TAKEONE. All Rights Reserved.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const tableBody = document.querySelector('#receipt-items tbody');
|
||||||
|
const subTotalElement = document.getElementById('sub-total');
|
||||||
|
const vatElement = document.getElementById('vat');
|
||||||
|
const grandTotalElement = document.getElementById('grand-total');
|
||||||
|
const paymentNote = document.getElementById('payment-note');
|
||||||
|
const vatRate = 0.0;
|
||||||
|
let subTotal = 0;
|
||||||
|
|
||||||
|
// --- CALCULATIONS ---
|
||||||
|
// Loop through each table row to calculate the total for each item
|
||||||
|
tableBody.querySelectorAll('tr').forEach(row => {
|
||||||
|
const qty = parseFloat(row.cells[2].textContent);
|
||||||
|
const price = parseFloat(row.cells[3].textContent);
|
||||||
|
const total = qty * price;
|
||||||
|
|
||||||
|
// Populate the 'Total' column
|
||||||
|
row.cells[4].textContent = total.toFixed(2) + ' USD';
|
||||||
|
|
||||||
|
// Add to the sub total
|
||||||
|
subTotal += total;
|
||||||
|
});
|
||||||
|
|
||||||
|
const vatAmount = subTotal * vatRate;
|
||||||
|
const grandTotal = subTotal + vatAmount;
|
||||||
|
|
||||||
|
// Update the sub total table
|
||||||
|
subTotalElement.textContent = subTotal.toFixed(2) + ' USD';
|
||||||
|
vatElement.textContent = vatAmount.toFixed(2) + ' USD';
|
||||||
|
grandTotalElement.textContent = grandTotal.toFixed(2) + ' USD';
|
||||||
|
|
||||||
|
// --- CONDITIONAL NOTE LOGIC ---
|
||||||
|
// Show the note
|
||||||
|
paymentNote.style.display = 'block';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -7,8 +7,8 @@
|
|||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||||||
<h4 class="mb-0">Invoice #{{ $invoice->id }}</h4>
|
<h4 class="mb-0">Invoice #{{ $invoice->id }}</h4>
|
||||||
<a href="{{ route('invoices.index') }}" class="btn btn-outline-secondary btn-sm">
|
<a href="{{ route('bills.index') }}" class="btn btn-outline-secondary btn-sm">
|
||||||
<i class="bi bi-arrow-left"></i> Back to Invoices
|
<i class="bi bi-arrow-left"></i> Back to Bills
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@ -83,13 +83,16 @@
|
|||||||
|
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
@if($invoice->status !== 'paid')
|
@if($invoice->status !== 'paid')
|
||||||
<a href="{{ route('invoices.pay', $invoice->id) }}" class="btn btn-success">
|
<a href="{{ route('bills.pay', $invoice->id) }}" class="btn btn-success">
|
||||||
<i class="bi bi-credit-card"></i> Pay Now
|
<i class="bi bi-credit-card"></i> Pay Now
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<button class="btn btn-outline-success" disabled>
|
<button class="btn btn-outline-success me-2" disabled>
|
||||||
<i class="bi bi-check-circle"></i> Paid
|
<i class="bi bi-check-circle"></i> Paid
|
||||||
</button>
|
</button>
|
||||||
|
<a href="{{ route('bills.receipt', $invoice->id) }}" class="btn btn-outline-primary" target="_blank">
|
||||||
|
<i class="bi bi-receipt"></i> View Receipt
|
||||||
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -391,6 +391,9 @@
|
|||||||
<a class="dropdown-item small" href="{{ route('family.dashboard') }}">
|
<a class="dropdown-item small" href="{{ route('family.dashboard') }}">
|
||||||
<i class="bi bi-people me-2"></i>My Family
|
<i class="bi bi-people me-2"></i>My Family
|
||||||
</a>
|
</a>
|
||||||
|
<a class="dropdown-item small" href="{{ route('bills.index') }}">
|
||||||
|
<i class="bi bi-receipt me-2"></i>My Bills
|
||||||
|
</a>
|
||||||
<a class="dropdown-item small" href="#">
|
<a class="dropdown-item small" href="#">
|
||||||
<i class="bi bi-gear me-2"></i>Settings
|
<i class="bi bi-gear me-2"></i>Settings
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -95,9 +95,10 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
Route::post('/family/{id}/upload-picture', [FamilyController::class, 'uploadFamilyMemberPicture'])->name('family.upload-picture');
|
Route::post('/family/{id}/upload-picture', [FamilyController::class, 'uploadFamilyMemberPicture'])->name('family.upload-picture');
|
||||||
Route::delete('/family/{id}', [FamilyController::class, 'destroy'])->name('family.destroy');
|
Route::delete('/family/{id}', [FamilyController::class, 'destroy'])->name('family.destroy');
|
||||||
|
|
||||||
// Invoice routes
|
// Bills routes
|
||||||
Route::get('/invoices', [InvoiceController::class, 'index'])->name('invoices.index');
|
Route::get('/bills', [InvoiceController::class, 'index'])->name('bills.index');
|
||||||
Route::get('/invoices/{id}', [InvoiceController::class, 'show'])->name('invoices.show');
|
Route::get('/bills/{id}', [InvoiceController::class, 'show'])->name('bills.show');
|
||||||
Route::get('/invoices/{id}/pay', [InvoiceController::class, 'pay'])->name('invoices.pay');
|
Route::get('/bills/{id}/receipt', [InvoiceController::class, 'receipt'])->name('bills.receipt');
|
||||||
Route::get('/invoices/pay-all', [InvoiceController::class, 'payAll'])->name('invoices.pay-all');
|
Route::get('/bills/{id}/pay', [InvoiceController::class, 'pay'])->name('bills.pay');
|
||||||
|
Route::get('/bills/pay-all', [InvoiceController::class, 'payAll'])->name('bills.pay-all');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user