Open ryanmortier opened 3 months ago
I think I've managed to get it to work. I was using a protected property rather than a public property for my campaign and so it wasn't being available on subsequent requests.
I have some source code implementing a kanban per resource, if you are interested we can work in a PR request to install either the board globally or in a resource
Hi @ryanmortier i used your code to replicate same scenario. When i type an id for campaign, it works great but with query string there is a problem on subsequent requests. Query string return null. Did you find a way to solve query string problem ?
@ahmetkocabiyik yes you'll need to change your query string variable from protected
to public
for Livewire to persist it through subsequent requests. Here is an updated version of my page, take from it what you need:
<?php
namespace App\Filament\Crm\Pages;
use App\Filament\Crm\Resources\CampaignResource;
use App\Models\Crm\Account;
use App\Models\Crm\AccountCampaign;
use App\Models\Crm\Campaign;
use App\Models\Crm\CampaignStage;
use Filament\Actions;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Mokhosh\FilamentKanban\Pages\KanbanBoard;
class CampaignKanbanBoard extends KanbanBoard
{
protected static ?string $slug = 'campaigns/board';
protected static string $model = Account::class;
protected static string $recordTitleAttribute = 'name';
protected static string $recordStatusAttribute = 'sort_order';
protected static bool $shouldRegisterNavigation = false;
public bool $disableEditModal = true;
public Campaign $campaign;
public function mount(): void
{
parent::mount();
$this->campaign = Campaign::findOrFail(request()->query('id'));
}
public function getBreadcrumbs(): array
{
return [
route('filament.crm.resources.campaigns.index') => 'Campaigns',
route('filament.crm.pages.campaigns.board', ['id' => $this->campaign->id]) => $this->campaign->name,
'Board',
];
}
public function getHeading(): string|Htmlable
{
return $this->campaign->name;
}
public function getSubheading(): string|Htmlable|null
{
$subheading = '<strong>'.e($this->campaign->start->format('M j, Y')).'</strong>';
$subheading .= ' to ';
$subheading .= '<strong>'.e($this->campaign->end->format('M j, Y')).'</strong>';
if ($this->campaign->description) {
$subheading .= '<br><p class="text-gray-600 dark:text-gray-400">'.nl2br(e($this->campaign->description)).'</p>';
}
return new HtmlString($subheading);
}
public function onStatusChanged(int $recordId, string $status, array $fromOrderedIds, array $toOrderedIds): void
{
$stage = CampaignStage::find($status);
$pivot = AccountCampaign::query()
->where('campaign_id', $this->campaign->id)
->where('account_id', $recordId)
->first();
if (! $pivot) {
return;
}
if ($stage) {
$pivot->stage()->associate($stage);
} else {
$pivot->stage()->dissociate();
}
$pivot->save();
$this->changeSortOrder($toOrderedIds);
}
protected function changeSortOrder(array $ids): void
{
AccountCampaign::setNewOrder(
$ids,
1,
'account_id',
function (Builder $query): Builder {
return $query->where('campaign_id', '=', $this->campaign->id);
}
);
}
public function onSortChanged(int $recordId, string $status, array $orderedIds): void
{
$this->changeSortOrder($orderedIds);
}
protected function records(): Collection
{
return $this->campaign
->accounts()
->orderByPivot('sort_order')
->orderBy('name')
->get();
}
protected function getHeaderActions(): array
{
return [
Actions\EditAction::make()
->url(fn (): string => CampaignResource::getUrl(
'edit',
['record' => $this->campaign]
)),
];
}
protected function statuses(): Collection
{
$new = ['id' => 0, 'title' => 'New'];
return $this->campaign->stages()->ordered()->get()->map(function ($stage) {
return ['id' => $stage->id, 'title' => $stage->name];
})->prepend($new);
}
protected function filterRecordsByStatus(Collection $records, array $status): array
{
return $records->where('pivot.campaign_stage_id', '=', $status['id'])->all();
}
}
What happened?
I'm looking to be able to dynamically show a Kanban board per resource record. Take for instance the following example:
A
Campaign
model has abelongsToMany
relationship to anAccount
model. ACampaign
also has ahasMany
relationship to aCampaignStage
model.The flow from a user would be that they create a campaign and attach accounts to the campaign. An account could potentially be added to many other campaigns. The user would also add a list of stages to each campaign and therefore each campaign may have a different set of stages (statuses).
On the API side, I would like to add a page to the
CampaignResource::class
and add the page to thegetPages()
method which would allow the page to receive the record via theInteractsWithRecords
trait. When a user clicks the table record on the list page, they would be linked to the kanban board with the record passed in the route.Also, since the campaigns and accounts are a many-to-many relationship, the status column cannot exist on the records (in this case the accounts table) and must instead be on the pivot table.
I tried my best to work around the package as it currently stands.
One suggestion you had made in #10 is to use a query string. The problem here is that Livewire can only get the request on mount, and not subsequent requests. Therefore the query string is null on subsequent requests.
This is my code:
Page
Models
Migrations