Skip to content

Commit 10a3e33

Browse files
authored
feat: incomes and expenses acl between panels (#31)
* incomes acl added * expense acl added
1 parent a867de3 commit 10a3e33

13 files changed

+448
-20
lines changed

app/Filament/Concerns/IncomeExpenseResourceTrait.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use Filament\Forms\Form;
1313
use Filament\Tables\Actions\BulkActionGroup;
1414
use Filament\Tables\Actions\DeleteAction;
15-
use Filament\Tables\Actions\DeleteBulkAction;
1615
use Filament\Tables\Actions\EditAction;
1716
use Filament\Tables\Columns\TextColumn;
1817
use Filament\Tables\Enums\FiltersLayout;
@@ -27,6 +26,8 @@
2726
*/
2827
trait IncomeExpenseResourceTrait
2928
{
29+
use BulkDeleter, UserFilterable;
30+
3031
public static function form(Form $form): Form
3132
{
3233
return $form
@@ -81,6 +82,7 @@ public static function table(Table $table): Table
8182
{
8283
return $table
8384
->columns([
85+
self::getUserColumn(),
8486

8587
TextColumn::make('person.name'),
8688

@@ -103,6 +105,8 @@ public static function table(Table $table): Table
103105
->sortable(),
104106
])
105107
->filters([
108+
self::getUserFilter(),
109+
106110
Filter::make('transacted_at')
107111
->form([
108112
DatePicker::make('transacted_from')->default(now()->startOfMonth()),
@@ -141,7 +145,7 @@ public static function table(Table $table): Table
141145
])
142146
->bulkActions([
143147
BulkActionGroup::make([
144-
DeleteBulkAction::make(),
148+
self::deleteBulkAction(),
145149
]),
146150
]);
147151
}

app/Filament/Resources/ExpenseResource/Pages/ListExpenses.php

+6-8
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,27 @@
22

33
namespace App\Filament\Resources\ExpenseResource\Pages;
44

5+
use App\Filament\Concerns\UserFilterable;
56
use App\Filament\Imports\ExpenseImporter;
67
use App\Filament\Resources\ExpenseResource;
8+
use App\Models\Expense;
79
use Filament\Actions\CreateAction;
810
use Filament\Actions\ImportAction;
911
use Filament\Resources\Pages\ListRecords;
10-
use Illuminate\Database\Eloquent\Builder;
1112

1213
class ListExpenses extends ListRecords
1314
{
15+
use UserFilterable;
16+
1417
protected static string $resource = ExpenseResource::class;
1518

1619
protected function getHeaderActions(): array
1720
{
1821
return [
1922
CreateAction::make(),
2023
ImportAction::make()
21-
->importer(ExpenseImporter::class),
24+
->importer(ExpenseImporter::class)
25+
->visible(auth()->user()->can('import', Expense::class)),
2226
];
2327
}
24-
25-
public function filterTableQuery(Builder $query): Builder
26-
{
27-
return parent::filterTableQuery($query)
28-
->where('user_id', auth()->id());
29-
}
3028
}

app/Filament/Resources/IncomeResource/Pages/ListIncomes.php

+6-8
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,27 @@
22

33
namespace App\Filament\Resources\IncomeResource\Pages;
44

5+
use App\Filament\Concerns\UserFilterable;
56
use App\Filament\Imports\IncomeImporter;
67
use App\Filament\Resources\IncomeResource;
8+
use App\Models\Income;
79
use Filament\Actions\CreateAction;
810
use Filament\Actions\ImportAction;
911
use Filament\Resources\Pages\ListRecords;
10-
use Illuminate\Database\Eloquent\Builder;
1112

1213
class ListIncomes extends ListRecords
1314
{
15+
use UserFilterable;
16+
1417
protected static string $resource = IncomeResource::class;
1518

1619
protected function getHeaderActions(): array
1720
{
1821
return [
1922
CreateAction::make(),
2023
ImportAction::make()
21-
->importer(IncomeImporter::class),
24+
->importer(IncomeImporter::class)
25+
->visible(auth()->user()->can('import', Income::class)),
2226
];
2327
}
24-
25-
public function filterTableQuery(Builder $query): Builder
26-
{
27-
return parent::filterTableQuery($query)
28-
->where('user_id', auth()->id());
29-
}
3028
}

app/Policies/ExpensePolicy.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Policies;
44

5+
use App\Enums\PanelId;
56
use App\Models\Expense;
67
use App\Models\User;
78
use Illuminate\Auth\Access\HandlesAuthorization;
@@ -17,16 +18,29 @@ public function viewAny(User $user): bool
1718

1819
public function create(User $user): bool
1920
{
20-
return true;
21+
return PanelId::APP->isCurrentPanel();
2122
}
2223

2324
public function update(User $user, Expense $expense): bool
2425
{
26+
if (PanelId::FAMILY->isCurrentPanel()) {
27+
return false;
28+
}
29+
2530
return $user->id === $expense->user_id;
2631
}
2732

2833
public function delete(User $user, Expense $expense): bool
2934
{
35+
if (PanelId::FAMILY->isCurrentPanel()) {
36+
return false;
37+
}
38+
3039
return $user->id === $expense->user_id;
3140
}
41+
42+
public function import(User $user): bool
43+
{
44+
return PanelId::APP->isCurrentPanel();
45+
}
3246
}

app/Policies/IncomePolicy.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Policies;
44

5+
use App\Enums\PanelId;
56
use App\Models\Income;
67
use App\Models\User;
78
use Illuminate\Auth\Access\HandlesAuthorization;
@@ -17,16 +18,29 @@ public function viewAny(User $user): bool
1718

1819
public function create(User $user): bool
1920
{
20-
return true;
21+
return PanelId::APP->isCurrentPanel();
2122
}
2223

2324
public function update(User $user, Income $income): bool
2425
{
26+
if (PanelId::FAMILY->isCurrentPanel()) {
27+
return false;
28+
}
29+
2530
return $user->id === $income->user_id;
2631
}
2732

2833
public function delete(User $user, Income $income): bool
2934
{
35+
if (PanelId::FAMILY->isCurrentPanel()) {
36+
return false;
37+
}
38+
3039
return $user->id === $income->user_id;
3140
}
41+
42+
public function import(User $user): bool
43+
{
44+
return PanelId::APP->isCurrentPanel();
45+
}
3246
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
use App\Enums\PanelId;
4+
use App\Filament\Resources\ExpenseResource;
5+
use App\Filament\Resources\ExpenseResource\Pages\ListExpenses;
6+
use App\Models\Expense;
7+
use App\Models\User;
8+
use Illuminate\Foundation\Testing\RefreshDatabase;
9+
10+
use function Pest\Livewire\livewire;
11+
12+
uses(RefreshDatabase::class);
13+
14+
beforeEach(function () {
15+
$this->user = User::factory()->create();
16+
$this->actingAs($this->user);
17+
18+
$this->expense = Expense::factory()->for($this->user)->today()->create([
19+
'description' => 'User 1 Expense',
20+
]);
21+
22+
PanelId::FAMILY->setCurrentPanel();
23+
});
24+
25+
it('cannot display create action', function () {
26+
livewire(ListExpenses::class)
27+
->assertActionHidden('create');
28+
});
29+
30+
it('cannot display edit action', function () {
31+
livewire(ListExpenses::class)
32+
->assertTableActionHidden('edit', $this->expense->id);
33+
});
34+
35+
it('cannot display delete action', function () {
36+
livewire(ListExpenses::class)
37+
->assertTableActionHidden('delete', $this->expense->id);
38+
});
39+
40+
it('cannot display import action', function () {
41+
livewire(ListExpenses::class)
42+
->assertTableActionHidden('delete', $this->expense->id);
43+
});
44+
45+
it('cannot display bulk delete action', function () {
46+
livewire(ListExpenses::class)
47+
->set('selectedTableRecords', [$this->expense])
48+
->assertTableBulkActionHidden('delete');
49+
});
50+
51+
it('cannot render create expense page', function () {
52+
$this->get(ExpenseResource::getUrl('create'))
53+
->assertForbidden();
54+
});
55+
56+
it('cannot perform expense update action', function () {
57+
livewire(ExpenseResource\Pages\EditExpense::class, [
58+
'record' => $this->expense->getRouteKey(),
59+
])
60+
->assertForbidden();
61+
});
62+
63+
it('cannot perform delete expense action', function () {
64+
livewire(ExpenseResource\Pages\EditExpense::class, [
65+
'record' => $this->expense->getRouteKey(),
66+
])
67+
->assertForbidden();
68+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use App\Enums\PanelId;
4+
use App\Filament\Resources\ExpenseResource\Pages\ListExpenses;
5+
use App\Models\Expense;
6+
use App\Models\User;
7+
use Illuminate\Foundation\Testing\RefreshDatabase;
8+
9+
use function Pest\Livewire\livewire;
10+
11+
uses(RefreshDatabase::class);
12+
13+
beforeEach(function () {
14+
$this->user = User::factory()->create();
15+
$this->actingAs($this->user);
16+
17+
$this->expense1 = Expense::factory()->for($this->user)->today()->create([
18+
'description' => 'User 1 Expense',
19+
]);
20+
21+
$this->expense2 = Expense::factory()->for(User::factory())->today()->create([
22+
'description' => 'User 2 Expense',
23+
]);
24+
25+
PanelId::FAMILY->setCurrentPanel();
26+
});
27+
28+
it('can display user filter', function () {
29+
livewire(ListExpenses::class)
30+
->assertTableFilterVisible('user');
31+
});
32+
33+
it('can display user columns', function () {
34+
livewire(ListExpenses::class)
35+
->assertTableColumnVisible('user.name');
36+
});
37+
38+
it('display user columns', function () {
39+
livewire(ListExpenses::class)
40+
->assertSee([$this->expense1->description])
41+
->assertSee([$this->expense2->description]);
42+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
use App\Enums\PanelId;
4+
use App\Filament\Resources\ExpenseResource\Pages\ListExpenses;
5+
use App\Models\Expense;
6+
use App\Models\User;
7+
use Illuminate\Foundation\Testing\RefreshDatabase;
8+
9+
use function Pest\Livewire\livewire;
10+
11+
uses(RefreshDatabase::class);
12+
13+
beforeEach(function () {
14+
$this->user = User::factory()->create();
15+
$this->actingAs($this->user);
16+
17+
$this->expense = Expense::factory()->for($this->user)->today()->create([
18+
'description' => 'User 1 Expense',
19+
]);
20+
21+
PanelId::APP->setCurrentPanel();
22+
});
23+
24+
it('can display create action', function () {
25+
livewire(ListExpenses::class)
26+
->assertActionVisible('create');
27+
});
28+
29+
it('can display edit action', function () {
30+
livewire(ListExpenses::class)
31+
->assertTableActionVisible('edit', $this->expense->id);
32+
});
33+
34+
it('can display delete action', function () {
35+
livewire(ListExpenses::class)
36+
->assertTableActionVisible('delete', $this->expense->id);
37+
});
38+
39+
it('can display import action', function () {
40+
livewire(ListExpenses::class)
41+
->assertActionVisible('import');
42+
});
43+
44+
it('can display bulk delete action', function () {
45+
livewire(ListExpenses::class)
46+
->set('selectedTableRecords', [$this->expense])
47+
->assertTableBulkActionVisible('delete');
48+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use App\Enums\PanelId;
4+
use App\Filament\Resources\ExpenseResource\Pages\ListExpenses;
5+
use App\Models\Expense;
6+
use App\Models\User;
7+
use Illuminate\Foundation\Testing\RefreshDatabase;
8+
9+
use function Pest\Livewire\livewire;
10+
11+
uses(RefreshDatabase::class);
12+
13+
beforeEach(function () {
14+
$this->user = User::factory()->create();
15+
$this->actingAs($this->user);
16+
17+
$this->expense1 = Expense::factory()->for($this->user)->today()->create([
18+
'description' => 'User 1 Expense',
19+
]);
20+
21+
$this->expense2 = Expense::factory(User::factory())->today()->create([
22+
'description' => 'User 2 Expense',
23+
]);
24+
25+
PanelId::APP->setCurrentPanel();
26+
});
27+
28+
it('cannot display user filter', function () {
29+
livewire(ListExpenses::class)
30+
->assertTableFilterHidden('user');
31+
});
32+
33+
it('cannot display user columns', function () {
34+
livewire(ListExpenses::class)
35+
->assertTableColumnHidden('user.name');
36+
});
37+
38+
it('can only list auth user expenses', function () {
39+
livewire(ListExpenses::class)
40+
->assertSee([$this->expense1->description])
41+
->assertDontSee([$this->expense2->description]);
42+
});

0 commit comments

Comments
 (0)