Skip to content

Commit c4365e3

Browse files
Fix actions view (#6)
1 parent 59fd687 commit c4365e3

File tree

5 files changed

+211
-305
lines changed

5 files changed

+211
-305
lines changed

README.md

Lines changed: 12 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Ginkelsoft DataTables is a flexible and easy-to-use package for managing tabular
2020
## Requirements
2121

2222
- **PHP 8.2+**
23-
- **Laravel 10.0+** 
23+
- **Laravel 10.0+**
2424
- **Livewire** *(Optional, only if you need AJAX-driven data tables.)*
2525

2626
---
@@ -30,7 +30,7 @@ Ginkelsoft DataTables is a flexible and easy-to-use package for managing tabular
3030
1. **Require the package**:
3131

3232
```bash
33-
composer require ginkelsoft/datatables:dev-main
33+
composer require ginkelsoft/datatables
3434
```
3535

3636
2. **Publish the package views** (optional) for customization:
@@ -41,60 +41,6 @@ Ginkelsoft DataTables is a flexible and easy-to-use package for managing tabular
4141

4242
---
4343

44-
## Usage (Without Livewire)
45-
46-
For a traditional server-rendered app:
47-
48-
```php
49-
use Ginkelsoft\DataTables\DataTable;
50-
use Ginkelsoft\DataTables\Column;
51-
use App\Models\User;
52-
53-
public function index()
54-
{
55-
$query = User::query();
56-
57-
$datatable = (new DataTable($query))
58-
->setColumns([
59-
Column::make('id', 'ID'),
60-
Column::make('name', 'Name'),
61-
Column::make('email', 'Email'),
62-
])
63-
->setPerPage(10);
64-
65-
$rows = $datatable->getRows();
66-
67-
return view('users.index', compact('rows'));
68-
}
69-
```
70-
71-
And in `resources/views/users/index.blade.php`:
72-
73-
```blade
74-
<table>
75-
<thead>
76-
<tr>
77-
<th>ID</th>
78-
<th>Name</th>
79-
<th>Email</th>
80-
</tr>
81-
</thead>
82-
<tbody>
83-
@foreach($rows as $row)
84-
<tr>
85-
<td>{{ $row->id }}</td>
86-
<td>{{ $row->name }}</td>
87-
<td>{{ $row->email }}</td>
88-
</tr>
89-
@endforeach
90-
</tbody>
91-
</table>
92-
93-
{{ $rows->links() }}
94-
```
95-
96-
---
97-
9844
## Usage (With Livewire)
9945

10046
If you prefer an **AJAX-driven** workflow with real-time sorting, searching, and pagination:
@@ -104,8 +50,8 @@ If you prefer an **AJAX-driven** workflow with real-time sorting, searching, and
10450
model="App\\Models\\User"
10551
:columns="['id', 'name', 'email']"
10652
:actions="[
107-
['label' => 'Edit', 'route' => 'users.edit'],
108-
['label' => 'Delete', 'route' => 'users.destroy']
53+
['label' => 'Edit', 'route' => 'users.edit', 'class' => 'bg-blue-500 hover:bg-blue-600 text-white px-2 py-1 rounded', 'style' => 'margin-right: 5px;'],
54+
['label' => 'Delete', 'route' => 'users.destroy', 'class' => 'bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded']
10955
]"
11056
:bulkActions="[
11157
'delete' => ['label' => 'Delete', 'route' => 'users.bulk.delete'],
@@ -127,18 +73,20 @@ You can now:
12773

12874
### Row Actions
12975

130-
Specify row-level actions in your Blade:
76+
Actions now support custom **classes** and **styles** for better UI customization:
13177

13278
```blade
13379
:actions="[
134-
['label' => 'Edit', 'route' => 'users.edit'],
135-
['label' => 'Delete', 'route' => 'users.destroy']
80+
['label' => 'Edit', 'route' => 'users.edit', 'class' => 'bg-blue-500 hover:bg-blue-600 text-white px-2 py-1 rounded', 'style' => 'margin-right: 5px;'],
81+
['label' => 'Delete', 'route' => 'users.destroy', 'class' => 'bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded']
13682
]"
13783
```
13884

85+
These actions will be rendered as buttons with the specified styles.
86+
13987
### Bulk Actions
14088

141-
For bulk actions (like deleting multiple rows) set `:bulkActions`:
89+
For bulk actions (like deleting multiple rows), define them as follows:
14290

14391
```blade
14492
:bulkActions="[
@@ -157,6 +105,8 @@ When multiple rows are selected, the component redirects to the specified route
157105
- **Filter Class** for custom filters (status, categories, etc.).
158106
- **Sorting Class** for ascending/descending ordering.
159107
- **Select All** (with confirmation modal) to choose between only visible rows or all rows.
108+
- **Custom Actions** now support custom classes and inline styles.
109+
- **Prevent row selection when clicking an action button**, ensuring that clicking an action does not check the row checkbox.
160110

161111
---
162112

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div class="flex items-center space-x-2">
2+
@foreach($actions as $action)
3+
<a href="{{ route($action['route'], $row->id) }}"
4+
class="{{ $action['class'] ?? 'px-2 py-1 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-600' }}"
5+
style="{{ $action['style'] ?? '' }}"
6+
onclick="event.stopPropagation();">
7+
{{ $action['label'] }}
8+
</a>
9+
@endforeach
10+
</div>
Lines changed: 186 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,191 @@
1-
<div>
2-
{{-- 🔍 Zoekbalk --}}
3-
<div class="flex items-center mb-4">
4-
<input type="text" wire:model.debounce.500ms="search" placeholder="Zoeken..." class="border px-3 py-1 rounded">
5-
<select wire:model="perPage" class="ml-2 border px-2 py-1 rounded">
6-
<option value="10">10</option>
7-
<option value="25">25</option>
8-
<option value="50">50</option>
9-
</select>
10-
</div>
1+
<div wire:loading.class="opacity-50">
2+
<div class="bg-white shadow-md rounded-lg p-6">
3+
<div class="flex justify-between items-center mb-4">
4+
{{-- 🔍 Zoekbalk --}}
5+
<div class="flex justify-between items-center mb-4">
6+
{{-- 🔍 Zoekbalk met knop --}}
7+
<div class="flex items-center gap-2">
8+
<input type="text" wire:model="search"
9+
placeholder="Zoekterm..."
10+
class="border border-gray-300 rounded px-3 py-1 text-sm w-64">
11+
12+
{{-- Zoekknop --}}
13+
<button wire:click="applySearch"
14+
class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm">
15+
Zoeken
16+
</button>
17+
18+
{{-- Resetknop --}}
19+
<button wire:click="resetSearch"
20+
class="bg-gray-300 hover:bg-gray-400 text-gray-700 px-3 py-1 rounded text-sm">
21+
Reset
22+
</button>
23+
</div>
24+
</div>
25+
26+
27+
{{-- Selectie aantal resultaten per pagina --}}
28+
<div class="flex items-center gap-2">
29+
<label for="perPage" class="text-sm text-gray-600">Resultaten per pagina:</label>
30+
<select wire:change="updatePerPage($event.target.value)" id="perPage"
31+
class="border border-gray-300 rounded px-3 py-1 text-sm w-24">
32+
<option value="10">10</option>
33+
<option value="25">25</option>
34+
<option value="50">50</option>
35+
<option value="100">100</option>
36+
</select>
37+
</div>
38+
</div>
39+
40+
<div class="flex justify-between items-center mb-4">
41+
{{-- ✅ Bulkactie dropdown - Alleen tonen als er rijen geselecteerd zijn --}}
42+
<div class="flex items-center gap-2" wire:key="bulk-actions">
43+
@if(count($selectedRows) > 0)
44+
<select wire:model="bulkAction"
45+
class="border border-gray-300 rounded px-3 py-1 text-sm w-48">
46+
<option value="">-- Kies een actie --</option>
47+
@foreach($bulkActions as $key => $action)
48+
<option value="{{ $key }}">{{ $action['label'] }}</option>
49+
@endforeach
50+
</select>
51+
52+
<button wire:click="executeBulkAction"
53+
class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm">
54+
Uitvoeren
55+
</button>
56+
@endif
57+
</div>
58+
</div>
1159

12-
{{-- 📊 Datatable --}}
13-
<table class="border-collapse border border-gray-300 w-full">
14-
<thead>
15-
<tr>
16-
@foreach($columns as $column)
17-
<th class="border border-gray-300 p-2">
18-
<a wire:click="sortBy('{{ $column }}')" class="cursor-pointer">
19-
{{ ucfirst($column) }}
20-
@if ($sortColumn === $column)
21-
<span>{{ $sortDirection === 'asc' ? '' : '' }}</span>
22-
@endif
23-
</a>
24-
</th>
25-
@endforeach
26-
@if (!empty($actions))
27-
<th class="border border-gray-300 p-2">Acties</th>
28-
@endif
29-
</tr>
30-
</thead>
31-
<tbody>
32-
@foreach($rows as $row)
33-
<tr>
34-
@foreach($columns as $column)
35-
<td class="border border-gray-300 p-2">{{ $row->$column }}</td>
36-
@endforeach
37-
@if (!empty($actions))
38-
<td class="border border-gray-300 p-2">
39-
@foreach($actions as $action)
40-
<a href="{{ route($action['route'], ['id' => $row->id]) }}" class="text-blue-500">{{ $action['label'] }}</a>
60+
<div wire:loading.class="opacity-50">
61+
<div class="overflow-x-auto">
62+
<table class="min-w-full divide-y divide-gray-200 border border-gray-300 rounded-lg">
63+
<thead class="bg-gray-100">
64+
<tr>
65+
{{-- "Selecteer alles" checkbox --}}
66+
<th class="px-4 py-3 text-left text-sm font-semibold text-gray-700 border">
67+
<input type="checkbox" wire:click="toggleSelectAll"
68+
class="h-4 w-4 text-blue-500 border-gray-300 rounded">
69+
</th>
70+
@foreach($columns as $column)
71+
@if(!in_array($column, $hiddenColumns))
72+
{{-- 🟢 Alleen tonen als niet hidden --}}
73+
<th class="px-4 py-3 text-left text-sm font-semibold text-gray-700 border cursor-pointer"
74+
wire:click="sortBy('{{ $column }}')">
75+
<div class="flex items-center">
76+
{{ $columnLabels[$column] ?? ucfirst(str_replace('_', ' ', $column)) }}
77+
@if ($sortColumn === $column)
78+
<span class="ml-2">
79+
@if ($sortDirection === 'asc')
80+
81+
@else
82+
83+
@endif
84+
</span>
85+
@endif
86+
</div>
87+
</th>
88+
@endif
4189
@endforeach
42-
</td>
90+
<th class="px-4 py-3 text-left text-sm font-semibold text-gray-700 border">Acties</th>
91+
</tr>
92+
</thead>
93+
<tbody class="bg-white divide-y divide-gray-200">
94+
@foreach($rows as $row)
95+
<tr class="hover:bg-gray-100 transition cursor-pointer"
96+
wire:click.prevent="toggleRowSelection({{ $row->id }})">
97+
<td class="px-4 py-3 border">
98+
<input type="checkbox" wire:model="selectedRows"
99+
value="{{ $row->id }}"
100+
class="h-4 w-4 text-blue-500 border-gray-300 rounded"
101+
onclick="event.stopPropagation();">
102+
</td>
103+
104+
@foreach($columns as $column)
105+
@if(!in_array($column, $hiddenColumns))
106+
{{-- 🟢 Alleen tonen als niet hidden --}}
107+
<td class="px-4 py-3 border text-sm text-gray-700">
108+
{{ $row->$column }}
109+
</td>
110+
@endif
111+
@endforeach
112+
113+
<td class="px-4 py-3 border">
114+
@includeIf($actionsView, ['row' => $row])
115+
</td>
116+
</tr>
117+
@endforeach
118+
</tbody>
119+
</table>
120+
</div>
121+
</div>
122+
123+
{{-- Paginering --}}
124+
<div class="mt-4 flex justify-between items-center">
125+
<span class="text-sm text-gray-600">
126+
Pagina {{ $rows->currentPage() }} van {{ $rows->lastPage() }} - {{ $rows->total() }} resultaten
127+
</span>
128+
129+
<div class="flex gap-2">
130+
{{-- Vorige knop --}}
131+
<button wire:click="previousPage"
132+
class="px-4 py-2 rounded {{ $rows->onFirstPage() ? 'bg-gray-200 text-gray-400 cursor-not-allowed' : 'bg-gray-100 hover:bg-gray-200 text-gray-700' }}"
133+
{{ $rows->onFirstPage() ? 'disabled' : '' }}>
134+
Vorige
135+
</button>
136+
137+
{{-- ✅ Als er 10 of minder pagina’s zijn: toon paginanummers als knoppen --}}
138+
@if ($rows->lastPage() <= 10)
139+
@for ($page = 1; $page <= $rows->lastPage(); $page++)
140+
<button wire:click="gotoPage({{ $page }})"
141+
class="px-4 py-2 rounded {{ $rows->currentPage() == $page ? 'bg-blue-500 text-white' : 'bg-gray-100 hover:bg-gray-200 text-gray-700' }}">
142+
{{ $page }}
143+
</button>
144+
@endfor
145+
@else
146+
{{-- ✅ Meer dan 10 pagina’s: gebruik een dropdown --}}
147+
<select wire:change="gotoPage($event.target.value)"
148+
class="border border-gray-300 rounded px-3 py-1 text-sm w-16">
149+
@for ($page = 1; $page <= $rows->lastPage(); $page++)
150+
<option value="{{ $page }}" {{ $rows->currentPage() == $page ? 'selected' : '' }}>
151+
{{ $page }}
152+
</option>
153+
@endfor
154+
</select>
43155
@endif
44-
</tr>
45-
@endforeach
46-
</tbody>
47-
</table>
48-
49-
{{-- Paginering --}}
50-
<div class="mt-4">
51-
{{ $rows->links() }}
156+
157+
{{-- Volgende knop --}}
158+
<button wire:click="nextPage"
159+
class="px-4 py-2 rounded {{ $rows->hasMorePages() ? 'bg-gray-100 hover:bg-gray-200 text-gray-700' : 'bg-gray-200 text-gray-400 cursor-not-allowed' }}"
160+
{{ $rows->hasMorePages() ? '' : 'disabled' }}>
161+
Volgende
162+
</button>
163+
</div>
164+
</div>
52165
</div>
166+
167+
@if($showSelectAllModal)
168+
<div class="fixed inset-0 bg-gray-900 bg-opacity-50 flex justify-center items-center z-50">
169+
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
170+
<h2 class="text-lg font-semibold mb-4">Selecteer alle rijen?</h2>
171+
<p class="text-gray-700 text-sm mb-4">
172+
Wil je alleen de zichtbare rijen op deze pagina selecteren, of alle rijen in de database?
173+
</p>
174+
<div class="flex justify-end gap-3">
175+
<button wire:click="confirmSelectAll('visible')"
176+
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded">
177+
Alleen zichtbare
178+
</button>
179+
<button wire:click="confirmSelectAll('all')"
180+
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded">
181+
Alle rijen
182+
</button>
183+
<button wire:click="cancelSelectAll"
184+
class="bg-gray-300 hover:bg-gray-400 text-gray-700 px-4 py-2 rounded">
185+
Annuleren
186+
</button>
187+
</div>
188+
</div>
189+
</div>
190+
@endif
53191
</div>

src/DataTableServiceProvider.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class DataTableServiceProvider extends ServiceProvider
1616
*/
1717
public function register(): void
1818
{
19-
// Load package views
20-
$this->loadViewsFrom(__DIR__ . '/Resources/views', 'datatable');
19+
// Load package views from the correct
20+
$this->loadViewsFrom(__DIR__ . '/../resources/views/vendor/datatables', 'datatable');
2121
}
2222

2323
/**
@@ -32,9 +32,8 @@ public function boot(): void
3232
{
3333
// Publish views to allow customization
3434
$this->publishes([
35-
__DIR__ . '/Resources/views' => resource_path('views/vendor/datatables'),
35+
__DIR__ . '/../resources/views' => resource_path('views/vendor/datatables'),
3636
], 'views');
37-
3837
// Register the Livewire component for the DataTable
3938
Livewire::component('datatable', \Ginkelsoft\DataTables\Livewire\DataTableComponent::class);
4039
}

0 commit comments

Comments
 (0)