cms-frontend/resources/views/components/table-component.blade.php

525 lines
17 KiB
PHP

@props([
'pageTitle' => '',
'data' => [],
'columns' => [],
'allFields' => [],
'actions' => [],
'showAddButton' => false,
'addButtonUrl' => '#',
'showCheckboxes' => false,
'showBatchDelete' => false,
'showEditModal' => false,
'showViewModal' => false,
'baseRoute' => ''
])
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title">{{ $pageTitle }}</h5>
@if($showAddButton)
<a href="{{ $addButtonUrl }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-1"></i>Add New
</a>
@endif
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="dataTable">
<thead>
<tr>
@if($showCheckboxes)
<th width="50">
<input type="checkbox" class="form-check-input" id="selectAll">
</th>
@endif
@foreach($columns as $column)
<th>{{ $column['name'] }}</th>
@endforeach
@if(count($actions) > 0)
<th width="150">Actions</th>
@endif
</tr>
</thead>
</table>
</div>
</div>
</div>
@if($showEditModal)
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit {{ $pageTitle }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="editForm">
<div class="modal-body">
@foreach($allFields as $field)
<div class="mb-3">
<label class="form-label">{{ $field['name'] }}</label>
@if($field['type'] === 'select')
<select class="form-select" name="{{ $field['key'] }}" required="{{ $field['required'] ?? false }}">
@foreach($field['options'] as $option)
<option value="{{ $option }}">{{ $option }}</option>
@endforeach
</select>
@elseif($field['type'] === 'textarea')
<textarea class="form-control" name="{{ $field['key'] }}" rows="3" required="{{ $field['required'] ?? false }}"></textarea>
@else
<input type="{{ $field['type'] }}"
class="form-control"
name="{{ $field['key'] }}"
required="{{ $field['required'] ?? false }}"
@if(isset($field['min'])) min="{{ $field['min'] }}" @endif
@if(isset($field['step'])) step="{{ $field['step'] }}" @endif>
@endif
</div>
@endforeach
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
@endif
@if($showViewModal)
<!-- View Modal -->
<div class="modal fade" id="viewModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">View {{ $pageTitle }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
@foreach($allFields as $field)
<div class="mb-3">
<label class="fw-bold">{{ $field['name'] }}:</label>
<div id="view_{{ $field['key'] }}"></div>
</div>
@endforeach
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
@endif
@push('scripts')
<script>
$(document).ready(function() {
// Initialize DataTable
const table = $('#dataTable').DataTable({
processing: true,
serverSide: true,
ajax: {
url: '{{ route($baseRoute . ".data") }}',
type: 'GET'
},
columns: [
@if($showCheckboxes)
{
data: null,
orderable: false,
searchable: false,
render: function() {
return '<input type="checkbox" class="form-check-input row-checkbox">';
}
},
@endif
@foreach($columns as $column)
{
data: '{{ $column["key"] }}',
name: '{{ $column["key"] }}',
render: function(data, type, row) {
if ('{{ $column["key"] }}' === 'status') {
const colors = {
'Active': 'success',
'Inactive': 'danger',
'Pending': 'warning',
'Completed': 'success',
'Cancelled': 'danger'
};
return `<span class="badge bg-${colors[data] || 'secondary'}">${data}</span>`;
}
@if(isset($column['format']) && $column['format'] === 'currency')
return new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP'
}).format(data);
@elseif(isset($column['format']) && $column['format'] === 'date')
return moment(data).format('YYYY-MM-DD HH:mm:ss');
@else
return data;
@endif
}
},
@endforeach
@if(count($actions) > 0)
{
data: null,
orderable: false,
searchable: false,
render: function(data, type, row) {
let actions = '';
@foreach($actions as $action)
@if($action === 'edit')
actions += `<button class="btn btn-sm btn-primary me-1 edit-btn" data-id="${row.id}">
<i class="fa-solid fa-pen-to-square"></i>
</button>`;
@elseif($action === 'view')
actions += `<button class="btn btn-sm btn-info me-1 view-btn" data-id="${row.id}">
<i class="fa-solid fa-eye"></i>
</button>`;
@elseif($action === 'delete')
actions += `<button class="btn btn-sm btn-danger delete-btn" data-id="${row.id}">
<i class="fa-solid fa-trash"></i>
</button>`;
@endif
@endforeach
return actions;
}
}
@endif
],
order: [[1, 'desc']]
});
// Select All Checkbox
$('#selectAll').change(function() {
$('.row-checkbox').prop('checked', $(this).prop('checked'));
updateBatchDeleteButton();
});
$(document).on('change', '.row-checkbox', function() {
updateBatchDeleteButton();
});
function updateBatchDeleteButton() {
const checkedCount = $('.row-checkbox:checked').length;
$('#batchDeleteBtn').prop('disabled', checkedCount === 0);
}
// Edit Record
$(document).on('click', '.edit-btn', function() {
const id = $(this).data('id');
$.get(`{{ route($baseRoute . ".show", ":id") }}`.replace(':id', id), function(data) {
$('#editForm').data('id', id);
@foreach($allFields as $field)
$(`#editForm [name="{{ $field['key'] }}"]`).val(data.{{ $field['key'] }});
@endforeach
$('#editModal').modal('show');
});
});
// View Record
$(document).on('click', '.view-btn', function() {
const id = $(this).data('id');
$.get(`{{ route($baseRoute . ".show", ":id") }}`.replace(':id', id), function(data) {
@foreach($allFields as $field)
@if($field['type'] === 'currency')
$('#view_{{ $field["key"] }}').text(new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP'
}).format(data.{{ $field['key'] }}));
@elseif($field['type'] === 'date')
$('#view_{{ $field["key"] }}').text(moment(data.{{ $field['key'] }}).format('YYYY-MM-DD HH:mm:ss'));
@else
$('#view_{{ $field["key"] }}').text(data.{{ $field['key'] }});
@endif
@endforeach
$('#viewModal').modal('show');
});
});
// Delete Record
$(document).on('click', '.delete-btn', function() {
const id = $(this).data('id');
if (confirm('Are you sure you want to delete this record?')) {
$.ajax({
url: `{{ route($baseRoute . ".destroy", ":id") }}`.replace(':id', id),
type: 'DELETE',
success: function() {
table.ajax.reload();
toastr.success('Record deleted successfully');
},
error: function() {
toastr.error('Failed to delete record');
}
});
}
});
// Submit Edit Form
$('#editForm').submit(function(e) {
e.preventDefault();
const id = $(this).data('id');
$.ajax({
url: `{{ route($baseRoute . ".update", ":id") }}`.replace(':id', id),
type: 'PUT',
data: $(this).serialize(),
success: function() {
$('#editModal').modal('hide');
table.ajax.reload();
toastr.success('Record updated successfully');
},
error: function(xhr) {
const errors = xhr.responseJSON.errors;
Object.keys(errors).forEach(key => {
toastr.error(errors[key][0]);
});
}
});
});
// Batch Delete
$('#batchDeleteBtn').click(function() {
const ids = $('.row-checkbox:checked').map(function() {
return $(this).closest('tr').find('.delete-btn').data('id');
}).get();
if (ids.length && confirm('Are you sure you want to delete the selected records?')) {
$.ajax({
url: '{{ route($baseRoute . ".batch-destroy") }}',
type: 'DELETE',
data: { ids: ids },
success: function() {
table.ajax.reload();
$('#selectAll').prop('checked', false);
updateBatchDeleteButton();
toastr.success('Records deleted successfully');
},
error: function() {
toastr.error('Failed to delete records');
}
});
}
});
});
</script>
@endpush
<style>
.card,
.table,
.btn,
.form-control,
.input-group-text,
.modal-content {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
font-weight: 400;
line-height: 1.5;
}
.card-header h5,
.modal-title {
font-weight: 500;
}
.table thead th {
font-weight: 500;
font-size: 0.8rem;
}
.form-label {
font-weight: 500;
font-size: 0.9rem;
}
.card {
border-radius: 10px;
}
.card-header {
background-color: transparent;
}
.btn-primary {
background-color: #E74610;
border-color: #E74610;
}
.btn-primary:hover {
background-color: #E74610;
border-color: #E74610;
}
.sortable {
cursor: pointer;
}
.sortable:hover {
background-color: #f1f3f5;
}
.table {
font-size: 0.85rem;
width: 100%;
table-layout: auto;
}
.table th,
.table td {
padding: 0.5rem;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.table th:first-child,
.table td:first-child {
width: 40px;
text-align: center;
}
.table th:last-child,
.table td:last-child {
width: 120px;
text-align: center;
}
.table td:nth-child(5),
.table th:nth-child(5) {
width: 100px;
}
.table td:nth-child(6),
.table th:nth-child(6) {
max-width: 200px;
}
.table td:nth-child(7),
.table th:nth-child(7) {
width: 120px;
}
.status-btn {
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
}
.dropdown-menu-sm {
min-width: 120px;
}
.dropdown-item {
font-size: 0.85rem;
}
.table thead .sortable i {
font-size: 0.7rem;
vertical-align: middle;
}
.table-container {
overflow-x: hidden;
}
.modal-content {
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.modal-header {
border-bottom: 1px solid #e9ecef;
position: relative;
}
.modal-footer {
border-top: 1px solid #e9ecef;
}
.form-control,
.form-select {
font-size: 0.9rem;
border-radius: 5px;
}
.modal-close-btn {
background: none;
border: none;
font-size: 1rem;
color: #6c757d;
position: absolute;
right: 15px;
top: 15px;
cursor: pointer;
}
.modal-close-btn:hover {
color: #343a40;
}
.view-details p {
margin-bottom: 0.75rem;
font-size: 0.9rem;
}
.view-details strong {
display: inline-block;
width: 120px;
font-weight: 500;
color: #343a40;
}
.view-details span {
color: #495057;
}
.pagination-sm .page-link {
padding: 0.25rem 0.5rem;
font-size: 0.85rem;
border-color: #E74610;
color: #E74610;
}
.pagination-sm .page-item.active .page-link {
background-color: #E74610;
border-color: #E74610;
color: #fff;
}
.pagination-sm .page-link:hover {
background-color: #E74610;
border-color: #E74610;
color: #fff;
}
.pagination-sm .page-item.disabled .page-link {
border-color: #dee2e6;
color: #6c757d;
}
.edit-btn {
border-color: #E74610;
color: #E74610;
}
.edit-btn:hover {
background-color: #E74610;
border-color: #E74610;
color: #fff;
}
@media (max-width: 576px) {
.table {
font-size: 0.75rem;
}
.table thead th {
font-size: 0.75rem;
}
.table th,
.table td {
padding: 0.3rem;
}
.btn-sm {
padding: 0.2rem 0.4rem;
font-size: 0.75rem;
}
.input-group-sm>.form-control {
font-size: 0.75rem;
}
.status-btn {
font-size: 0.65rem;
}
.dropdown-item {
font-size: 0.75rem;
}
.table thead .sortable i {
font-size: 0.65rem;
}
.pagination-sm .page-link {
padding: 0.2rem 0.4rem;
font-size: 0.75rem;
}
.table td:nth-child(6) {
max-width: 150px;
}
.form-control,
.form-select {
font-size: 0.85rem;
}
.modal-close-btn {
font-size: 0.9rem;
}
.view-details p {
font-size: 0.85rem;
}
.view-details strong {
width: 100px;
}
}
</style>