Skip to content

Commit 8accdb0

Browse files
committed
UP
1 parent ab9221c commit 8accdb0

File tree

6 files changed

+162
-35
lines changed

6 files changed

+162
-35
lines changed

resources/views/livewire/column.blade.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ class="text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:
2222
x-sortable
2323
x-sortable-group="cards"
2424
data-column-id="{{ $columnId }}"
25-
@end.stop="
26-
27-
$wire.updateStatus($event.item.getAttribute('x-sortable-item'), $event.to.getAttribute('data-column-id'))"
28-
25+
@end.stop="$wire.updateColumnCards($event.to.getAttribute('data-column-id'), $event.to.sortable.toArray())"
2926
>
3027
@foreach($column['items'] as $card)
3128
<x-flowforge::kanban.card

src/Adapters/DefaultKanbanAdapter.php

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Relaticle\Flowforge\Adapters;
66

7+
use DB;
78
use Illuminate\Database\Eloquent\Model;
89
use Illuminate\Support\Collection;
910
use Livewire\Wireable;
@@ -46,6 +47,13 @@ class DefaultKanbanAdapter implements IKanbanAdapter, Wireable
4647
*/
4748
protected array $cardAttributes = [];
4849

50+
/**
51+
* The order field for the model.
52+
*
53+
* @var string|null
54+
*/
55+
protected ?string $orderField = null;
56+
4957
/**
5058
* The model class for the adapter.
5159
*
@@ -62,21 +70,24 @@ class DefaultKanbanAdapter implements IKanbanAdapter, Wireable
6270
* @param string $titleAttribute The title attribute
6371
* @param string|null $descriptionAttribute The description attribute
6472
* @param array<string> $cardAttributes The card attributes
73+
* @param string|null $orderField The order field
6574
*/
6675
public function __construct(
6776
string $modelClass,
6877
string $statusField,
6978
array $statusValues,
7079
string $titleAttribute,
7180
?string $descriptionAttribute = null,
72-
array $cardAttributes = []
81+
array $cardAttributes = [],
82+
?string $orderField = null
7383
) {
7484
$this->modelClass = $modelClass;
7585
$this->statusField = $statusField;
7686
$this->statusValues = $statusValues;
7787
$this->titleAttribute = $titleAttribute;
7888
$this->descriptionAttribute = $descriptionAttribute;
7989
$this->cardAttributes = $cardAttributes;
90+
$this->orderField = $orderField;
8091
}
8192

8293
/**
@@ -151,6 +162,16 @@ public function getCardAttributes(): array
151162
return $this->cardAttributes;
152163
}
153164

165+
/**
166+
* Get the order field for the model.
167+
*
168+
* @return string|null
169+
*/
170+
public function getOrderField(): ?string
171+
{
172+
return $this->orderField;
173+
}
174+
154175
/**
155176
* Get the items for all statuses.
156177
*
@@ -171,7 +192,14 @@ public function getItems(): Collection
171192
public function getItemsForStatus(string $status): Collection
172193
{
173194
$modelClass = $this->getModel();
174-
return $modelClass::where($this->getStatusField(), $status)->get();
195+
$query = $modelClass::where($this->getStatusField(), $status);
196+
197+
// Add ordering if order field is set
198+
if ($this->getOrderField()) {
199+
$query->orderBy($this->getOrderField());
200+
}
201+
202+
return $query->get();
175203
}
176204

177205
/**
@@ -187,6 +215,41 @@ public function updateStatus(Model $model, string $status): bool
187215
return $model->save();
188216
}
189217

218+
/**
219+
* Update the order of a model.
220+
*
221+
* @param string|int $columnId
222+
* @param array $cards
223+
* @return bool
224+
* @throws \Throwable
225+
*/
226+
public function updateColumnCards(string|int $columnId, array $cards): bool
227+
{
228+
if (!$this->getOrderField()) {
229+
return false;
230+
}
231+
232+
$model = app($this->getModel());
233+
234+
// Validate column ID exists in status values
235+
if (!array_key_exists((string)$columnId, $this->getStatusValues())) {
236+
return false;
237+
}
238+
239+
return DB::transaction(function() use ($model, $columnId, $cards) {
240+
foreach ($cards as $index => $id) {
241+
$model->newQuery()
242+
->where($model->getQualifiedKeyName(), $id)
243+
->update([
244+
$this->getStatusField() => $columnId,
245+
$this->getOrderField() => $index,
246+
]);
247+
}
248+
249+
return true;
250+
});
251+
}
252+
190253
/**
191254
* Create a new card with the given attributes.
192255
*
@@ -202,6 +265,15 @@ public function createCard(array $attributes): ?Model
202265
$status = $attributes[$this->getStatusField()] ?? array_key_first($this->getStatusValues());
203266
$card->{$this->getStatusField()} = $status;
204267

268+
// Set order if the field exists
269+
if ($this->getOrderField()) {
270+
// Set the highest order by default (add to the end of the column)
271+
$maxOrder = $modelClass::where($this->getStatusField(), $status)
272+
->max($this->getOrderField()) ?? 0;
273+
274+
$card->{$this->getOrderField()} = $maxOrder + 1;
275+
}
276+
205277
// Set title
206278
if (isset($attributes[$this->getTitleAttribute()])) {
207279
$card->{$this->getTitleAttribute()} = $attributes[$this->getTitleAttribute()];
@@ -281,6 +353,7 @@ public function toLivewire(): array
281353
'titleAttribute' => $this->getTitleAttribute(),
282354
'descriptionAttribute' => $this->getDescriptionAttribute(),
283355
'cardAttributes' => $this->getCardAttributes(),
356+
'orderField' => $this->getOrderField(),
284357
];
285358
}
286359

@@ -298,7 +371,8 @@ public static function fromLivewire($value)
298371
$value['statusValues'],
299372
$value['titleAttribute'],
300373
$value['descriptionAttribute'] ?? null,
301-
$value['cardAttributes'] ?? []
374+
$value['cardAttributes'] ?? [],
375+
$value['orderField'] ?? null
302376
);
303377
}
304378
}

src/Contracts/IKanbanAdapter.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,20 @@ public function updateCard(Model $card, array $attributes): bool;
102102
* @return bool
103103
*/
104104
public function deleteCard(Model $card): bool;
105+
106+
/**
107+
* Get the order field name for the model.
108+
*
109+
* @return string|null
110+
*/
111+
public function getOrderField(): ?string;
112+
113+
/**
114+
* Update the status and order of an item.
115+
*
116+
* @param string|int $columnId
117+
* @param array $cards
118+
* @return bool
119+
*/
120+
public function updateColumnCards(string|int $columnId, array $cards): bool;
105121
}

src/Filament/Resources/Pages/KanbanBoard.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class KanbanBoard extends Page
3535
*/
3636
protected array $cardAttributes = [];
3737

38+
/**
39+
* @var string|null
40+
*/
41+
protected ?string $orderField = null;
42+
3843
/**
3944
* @var IKanbanAdapter|null
4045
*/
@@ -115,6 +120,19 @@ public function cardAttributes(array $attributes): static
115120
return $this;
116121
}
117122

123+
/**
124+
* Set the order field for the Kanban board.
125+
*
126+
* @param string $field
127+
* @return static
128+
*/
129+
public function orderField(string $field): static
130+
{
131+
$this->orderField = $field;
132+
133+
return $this;
134+
}
135+
118136
/**
119137
* Set a custom adapter for the Kanban board.
120138
*
@@ -168,6 +186,10 @@ public function getAdapter(): IKanbanAdapter
168186
$instance->setKanbanCardAttributes($this->cardAttributes);
169187
}
170188

189+
if (method_exists($instance, 'setKanbanOrderField') && $this->orderField) {
190+
$instance->setKanbanOrderField($this->orderField);
191+
}
192+
171193
return $instance->getKanbanAdapter();
172194
}
173195

src/Livewire/KanbanBoard.php

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public function mount(IKanbanAdapter $adapter): void
4949
'titleAttribute' => $adapter->getTitleAttribute(),
5050
'descriptionAttribute' => $adapter->getDescriptionAttribute(),
5151
'cardAttributes' => $adapter->getCardAttributes(),
52+
'orderField' => $adapter->getOrderField(),
5253
];
5354
$this->loadColumnsData();
5455
}
@@ -120,39 +121,19 @@ protected function formatItems(Collection $items): array
120121
}
121122

122123
/**
123-
* Update the status of an item.
124+
* Update the order of an item within the same column.
124125
*
125-
* @param mixed $id The ID of the item
126-
* @param string $status The new status value
126+
* @param $columnId
127+
* @param $cards
127128
* @return bool
128129
*/
129-
public function updateStatus($id, $status): bool
130+
public function updateColumnCards($columnId, $cards): bool
130131
{
131-
$item = $this->adapter->getModelById($id);
132+
$this->adapter->updateColumnCards($columnId, $cards);
132133

133-
if (!$item) {
134-
return false;
135-
}
136-
137-
138-
$result = $this->adapter->updateStatus($item, $status);
139-
140-
if ($result) {
141-
$this->loadColumnsData();
142-
$this->dispatch('kanban-item-moved', ['id' => $id, 'status' => $status]);
143-
}
144-
145-
return $result;
146-
}
147-
148-
/**
149-
* Refresh the Kanban board.
150-
*
151-
* @return void
152-
*/
153-
public function refreshBoard(): void
154-
{
155134
$this->loadColumnsData();
135+
136+
return true;
156137
}
157138

158139
/**

src/Traits/HasKanbanBoard.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ trait HasKanbanBoard
4444
*/
4545
protected ?array $kanbanCardAttributes = null;
4646

47+
/**
48+
* Order field for Kanban board.
49+
*
50+
* @var string|null
51+
*/
52+
protected ?string $kanbanOrderField = null;
53+
4754
/**
4855
* Get the Kanban adapter for the model.
4956
*
@@ -67,14 +74,16 @@ protected function createDefaultKanbanAdapter(): IKanbanAdapter
6774
$titleAttribute = $this->getKanbanTitleAttribute();
6875
$descriptionAttribute = $this->getKanbanDescriptionAttribute();
6976
$cardAttributes = $this->getKanbanCardAttributes();
77+
$orderField = $this->getKanbanOrderField();
7078

7179
return new DefaultKanbanAdapter(
7280
static::class,
7381
$statusField,
7482
$statusValues,
7583
$titleAttribute,
7684
$descriptionAttribute,
77-
$cardAttributes
85+
$cardAttributes,
86+
$orderField
7887
);
7988
}
8089

@@ -217,6 +226,34 @@ public function setKanbanCardAttributes(array $attributes): self
217226
$this->kanbanCardAttributes = $attributes;
218227
return $this;
219228
}
229+
230+
/**
231+
* Get the order field for Kanban board.
232+
*
233+
* @return string|null
234+
*/
235+
public function getKanbanOrderField(): ?string
236+
{
237+
if ($this->kanbanOrderField !== null) {
238+
return $this->kanbanOrderField;
239+
}
240+
241+
return method_exists($this, 'kanbanOrderField')
242+
? $this->kanbanOrderField()
243+
: null;
244+
}
245+
246+
/**
247+
* Set the order field for Kanban board.
248+
*
249+
* @param string|null $field
250+
* @return self
251+
*/
252+
public function setKanbanOrderField(?string $field): self
253+
{
254+
$this->kanbanOrderField = $field;
255+
return $this;
256+
}
220257

221258
/**
222259
* Get the default status values for the model.

0 commit comments

Comments
 (0)