Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/content/2.essentials/4.api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ navigation:
| `positionIdentifier(string)` | Field name for drag-drop ordering | Yes |
| `columns(array)` | Define board columns | Yes |
| `recordTitleAttribute(string)` | Field name for card titles | |
| `cardLabel(string)` | Name of a single card, e.g. "Task" | |
| `pluralCardLabel(string)` | Name of a set of cards, e.g. "Tasks" | |
| `cardSchema(Closure)` | Rich card content using Schema | |
| `cardActions(array)` | Actions available on each card | |
| `columnActions(array)` | Actions available on column headers | |
Expand Down Expand Up @@ -48,6 +50,8 @@ public function board(Board $board): Board

```php
->recordTitleAttribute('title') // Card title field
->cardLabel('Task') // Defaults to the model label
->pluralCardLabel('Tasks') // Defaults to the plural model label
->cardSchema(fn(Schema $schema) => $schema // Rich card content
->components([
TextEntry::make('description'),
Expand Down
2 changes: 1 addition & 1 deletion resources/lang/de/flowforge.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

return [
'loading_more_cards' => 'Lade mehr Karten...',
'no_cards_in_column' => 'Keine :cardLabel in dieser Spalte',
'no_cards_in_column' => 'Keine :CardLabel in dieser Spalte',
'cards_count' => '{0} :cards|{1} :card|[2,*] :cards',
'card_label' => 'Datensatz',
'plural_card_label' => 'Datensätze',
Expand Down
6 changes: 4 additions & 2 deletions src/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Relaticle\Flowforge\Concerns\HasBoardColumns;
use Relaticle\Flowforge\Concerns\HasBoardFilters;
use Relaticle\Flowforge\Concerns\HasBoardRecords;
use Relaticle\Flowforge\Concerns\HasCardLabels;
use Relaticle\Flowforge\Concerns\HasCardSchema;
use Relaticle\Flowforge\Concerns\InteractsWithKanbanQuery;
use Relaticle\Flowforge\Contracts\HasBoard;
Expand All @@ -23,6 +24,7 @@ class Board extends ViewComponent
use HasBoardColumns;
use HasBoardFilters;
use HasBoardRecords;
use HasCardLabels;
use HasCardSchema;
use InteractsWithKanbanQuery;

Expand Down Expand Up @@ -101,8 +103,8 @@ public function getViewData(): array
'config' => [
'recordTitleAttribute' => $this->getRecordTitleAttribute(),
'columnIdentifierAttribute' => $this->getColumnIdentifierAttribute(),
'cardLabel' => __('flowforge::flowforge.card_label'),
'pluralCardLabel' => __('flowforge::flowforge.plural_card_label'),
'cardLabel' => $this->getCardLabel(),
'pluralCardLabel' => $this->getPluralCardLabel(),
'headerToolbar' => $this->hasHeaderToolbar(),
],
];
Expand Down
114 changes: 114 additions & 0 deletions src/Concerns/HasCardLabels.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Relaticle\Flowforge\Concerns;

use Closure;
use Filament\Facades\Filament;
use Filament\Resources\Pages\Page as ResourcePage;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

trait HasCardLabels
{
protected string | Closure | null $cardLabel = null;

protected string | Closure | null $pluralCardLabel = null;

/**
* Name a single card, e.g. "Task". Defaults to the model label.
*/
public function cardLabel(string | Closure | null $label): static
{
$this->cardLabel = $label;

return $this;
}

/**
* Name a set of cards, e.g. "Tasks". Defaults to the plural model label.
*/
public function pluralCardLabel(string | Closure | null $label): static
{
$this->pluralCardLabel = $label;

return $this;
}

public function getCardLabel(): string
{
$label = $this->evaluate($this->cardLabel);

if (filled($label)) {
return $label;
}

$resource = $this->getCardResource();

if ($resource !== null) {
return $resource::getModelLabel();
}

$model = $this->getCardModel();

if ($model !== null) {
return Str::title(Str::snake(class_basename($model), ' '));
}

return __('flowforge::flowforge.card_label');
}

public function getPluralCardLabel(): string
{
$label = $this->evaluate($this->pluralCardLabel);

if (filled($label)) {
return $label;
}

$resource = $this->getCardResource();

if ($resource !== null) {
return $resource::getPluralModelLabel();
}

if ($this->getCardModel() !== null) {
return Str::plural($this->getCardLabel());
}

return __('flowforge::flowforge.plural_card_label');
}

protected function getCardModel(): ?Model
{
return $this->getQuery()?->getModel();
}

/**
* The Filament resource describing the cards, either the one the board page
* belongs to or the one registered for the model in the current panel.
*
* @return class-string|null
*/
protected function getCardResource(): ?string
{
$livewire = $this->getLivewire();

if ($livewire instanceof ResourcePage) {
return $livewire::getResource();
}

$model = $this->getCardModel();

if ($model === null || ! class_exists(Filament::class)) {
return null;
}

if (Filament::getCurrentPanel() === null) {
return null;
}

return Filament::getModelResource($model);
}
}
46 changes: 46 additions & 0 deletions tests/Feature/CardLabelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

use Livewire\Livewire;
use Relaticle\Flowforge\Tests\Fixtures\TestBoard;
use Relaticle\Flowforge\Tests\Fixtures\TestBoardResourcePage;
use Relaticle\Flowforge\Tests\Fixtures\TestResource;

describe('card labels', function () {
test('names the cards after the resource of the board page', function () {
$board = Livewire::test(TestBoardResourcePage::class)->instance()->getBoard();

expect($board->getCardLabel())->toBe(TestResource::getModelLabel())
->and($board->getPluralCardLabel())->toBe(TestResource::getPluralModelLabel());
});

test('names the cards after the resource registered for the model', function () {
$board = Livewire::test(TestBoard::class)->instance()->getBoard();

expect($board->getCardLabel())->toBe('Task')
->and($board->getPluralCardLabel())->toBe('Tasks');
});

test('explicit labels win over the model', function () {
$board = Livewire::test(TestBoard::class)->instance()->getBoard()
->cardLabel('Ticket')
->pluralCardLabel('Tickets');

expect($board->getCardLabel())->toBe('Ticket')
->and($board->getPluralCardLabel())->toBe('Tickets');
});

test('falls back to the translated label without a query', function () {
$board = Livewire::test(TestBoard::class)->instance()->getBoard()->query(fn () => null);

expect($board->getCardLabel())->toBe(__('flowforge::flowforge.card_label'))
->and($board->getPluralCardLabel())->toBe(__('flowforge::flowforge.plural_card_label'));
});

test('the empty column names the cards', function () {
Livewire::test(TestBoard::class)
->assertStatus(200)
->assertSee('No tasks in this column');
});
});
15 changes: 15 additions & 0 deletions tests/Unit/TranslationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

use Illuminate\Support\Facades\Blade;

test('the German empty column hint keeps the card label capitalised', function () {
app()->setLocale('de');

$html = Blade::render('<x-flowforge::empty-column :pluralCardLabel="$label" />', [
'label' => __('flowforge::flowforge.plural_card_label'),
]);

expect($html)->toContain('Keine Datensätze in dieser Spalte');
});