Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a7287e1
Add `ColumnType` model and `column_type_id` column to `columns` table
igorgaming May 18, 2022
528c999
Create `Module` model
igorgaming May 18, 2022
c4f1894
Add default value for `column_type_id` column
igorgaming May 18, 2022
27504ed
Add module service
igorgaming May 19, 2022
0225fdb
Add controller and endpoint
igorgaming May 19, 2022
8833b73
wip
igorgaming May 19, 2022
ca2febe
Make base rule for MaxItems rule
igorgaming May 19, 2022
02a9021
Add validation for columns count
igorgaming May 19, 2022
92df19c
Add tests for `ModuleService`
igorgaming May 19, 2022
3521460
Add test for optional columns
igorgaming May 20, 2022
7b5a7ae
Add tests
igorgaming May 20, 2022
0a96e92
Change `CardInSameColumn` and `ColumnInSameBoard` rules behavior
igorgaming May 20, 2022
3e933ff
Changes to enable kanban module request
igorgaming May 20, 2022
901a11f
Rename service, add method `canMoveCardToColumn`
igorgaming May 20, 2022
290390b
Add checks related to `Kanban` module on card moving
igorgaming May 20, 2022
b18e6f5
Rename module in all places, add check for disabled module
igorgaming May 20, 2022
692b0a1
Prohibit deleting of kanban-related columns
igorgaming May 20, 2022
af1fdc2
Ability to view all modules for board
igorgaming May 21, 2022
83d4015
Add `enabled` property in view all modules response
igorgaming May 21, 2022
74c6c0b
Allow load boards with modules
igorgaming May 21, 2022
0d4d6a2
Allow view kanban module settings
igorgaming May 21, 2022
69419fe
Fix bug(we need to pass `$request` because `request()` returns base c…
igorgaming May 21, 2022
5341ba9
Fix bug
igorgaming May 22, 2022
2c8e0d3
Return column type id by default
igorgaming May 22, 2022
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
57 changes: 57 additions & 0 deletions app/Http/Controllers/Api/V1/Board/ModuleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace App\Http\Controllers\Api\V1\Board;

use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V1\Module\EnableKanbanModuleRequest;
use App\Http\Resources\V1\Board\ModuleResource;
use App\Models\Board;
use App\Models\Module;
use App\Services\Contracts\Modules\KanbanService;

class ModuleController extends Controller
{
public function __construct(
protected KanbanService $kanban
) {
}

public function index(Board $board)
{
$modules = Module::all()->each(function ($module) use ($board) {
$module->enabled = $board->modules?->find($module->id) != null;
});

return ModuleResource::collection($modules);
}

/*
|--------------------------------------------------------------------------
| Kanban module
|--------------------------------------------------------------------------
|
| Endpoints related to Kanban module.
|
*/

public function kanbanSettings(Board $board)
{
$settings = $this->kanban->getSettings($board);

return response(['data' => $settings]);
}

public function enableKanban(EnableKanbanModuleRequest $request, Board $board)
{
$this->kanban->enable($board, $request->validated());

return response('', 204);
}

public function disableKanban(Board $board)
{
$this->kanban->disable($board);

return response('', 204);
}
}
1 change: 1 addition & 0 deletions app/Http/Controllers/Api/V1/Project/BoardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function index(Project $project)
{
$boards = QueryBuilder::for($project->boards())
->allowedFields([BoardResource::class], [BoardResource::class])
->allowedIncludes(['modules'])
->get();

return BoardResource::collection($boards);
Expand Down
76 changes: 76 additions & 0 deletions app/Http/Requests/Api/V1/Module/EnableKanbanModuleRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace App\Http\Requests\Api\V1\Module;

use App\Rules\Api\ColumnInSameBoard;
use App\Rules\Api\MaxColumnsPerBoard;
use App\Services\Modules\KanbanService;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class EnableKanbanModuleRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$board = $this->route('board');

return [
'todo_column_id' => [
'required', 'integer', 'min:0',
Rule::when($this->todo_column_id != 0, $this->getDifferentRule('todo_column_id')),
new ColumnInSameBoard($this, $board),
],
'inprogress_column_id' => [
'required', 'integer', 'min:0',
Rule::when($this->inprogress_column_id != 0, $this->getDifferentRule('inprogress_column_id')),
new ColumnInSameBoard($this, $board),
],
'done_column_id' => [
'required', 'integer', 'min:0',
Rule::when($this->done_column_id != 0, $this->getDifferentRule('done_column_id')),
new ColumnInSameBoard($this, $board),
],

// Optional columns.
'onreview_column_id' => [
'sometimes', 'required', 'integer', 'min:0',
Rule::when($this->onreview_column_id != 0, $this->getDifferentRule('onreview_column_id')),
new ColumnInSameBoard($this, $board),
],
];
}

/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
$data = $validator->getData();
$newColumns = 0;

foreach ($data as $key => $value) {
if (array_key_exists($key, KanbanService::$availableColumns) && $value == 0) {
$newColumns++;
}
}

if ($newColumns != 0) {
$validator->addRules(['board' => [new MaxColumnsPerBoard($this->route('board'), $newColumns)]]);
}
}

protected function getDifferentRule($column)
{
return array_map(function ($value) {
return 'different:' . $value;
}, array_keys(array_diff_key(KanbanService::$availableColumns, [$column => 0])));
}
}
1 change: 1 addition & 0 deletions app/Http/Resources/V1/Board/BoardResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function toArray($request)
'deleted_at' => $this->when($this->trashed(), $this->deleted_at),
], [
'project' => new ProjectResource($this->whenLoaded('project')),
'modules' => ModuleResource::collection($this->whenLoaded('modules')),
]);
}

Expand Down
23 changes: 23 additions & 0 deletions app/Http/Resources/V1/Board/ModuleResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Http\Resources\V1\Board;

use Illuminate\Http\Resources\Json\JsonResource;

class ModuleResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'enabled' => $this->whenNotNull($this->enabled),
];
}
}
4 changes: 2 additions & 2 deletions app/Http/Resources/V1/Column/ColumnResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ColumnResource extends JsonResource implements ResourceWithFields
public function toArray($request)
{
return $this->visible([
'id', 'name', 'created_at', 'updated_at',
'id', 'name', 'column_type_id', 'created_at', 'updated_at',
]);
}

Expand All @@ -27,7 +27,7 @@ public static function defaultName(): string

public static function defaultFields(): array
{
return ['id', 'name'];
return ['id', 'name', 'column_type_id'];
}

public static function allowedFields(): array
Expand Down
7 changes: 7 additions & 0 deletions app/Models/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
* @property-read \App\Models\Team $team
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Column[] $columns
* @property-read int|null $columns_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Module[] $modules
* @property-read int|null $modules_count
* @method static \Database\Factories\BoardFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|Board newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Board newQuery()
Expand Down Expand Up @@ -66,4 +68,9 @@ public function team()
return $this->belongsToThrough(Team::class, Project::class)
->withTrashedParents();
}

public function modules()
{
return $this->belongsToMany(Module::class);
}
}
20 changes: 20 additions & 0 deletions app/Models/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Models;

use App\Traits\HasOrder;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Znck\Eloquent\Traits\BelongsToThrough;
Expand All @@ -15,6 +16,8 @@
* @property double|null $order
* @property-read \App\Models\Board $board
* @property int $board_id
* @property-read \App\Models\ColumnType $columnType
* @property int $column_type_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Team $team
Expand All @@ -24,6 +27,7 @@
* @method static \Illuminate\Database\Eloquent\Builder|Column newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Column newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Column query()
* @method static \Illuminate\Database\Eloquent\Builder|Column kanbanRelated()
* @mixin \Eloquent
*/
class Column extends Model
Expand All @@ -39,6 +43,17 @@ public function getOrderQuery($query)
return $query->where('board_id', $this->board_id);
}

/*
|-------------------------------------------------------------
| Scopes
|-------------------------------------------------------------
*/

public function scopeKanbanRelated(Builder $query)
{
return $query->where('column_type_id', '!=', ColumnType::NONE);
}

/*
|-------------------------------------------------------------
| Relationships
Expand All @@ -60,4 +75,9 @@ public function team()
return $this->belongsToThrough(Team::class, [Project::class, Board::class])
->withTrashedParents();
}

public function columnType()
{
return $this->belongsTo(ColumnType::class);
}
}
30 changes: 30 additions & 0 deletions app/Models/ColumnType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
* App\Models\ColumnType
*
* @property int $id
* @property string $name
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ColumnType newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ColumnType newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ColumnType query()
* @mixin \Eloquent
*/
class ColumnType extends Model
{
public const NONE = 0;

public const TODO = 1;

public const IN_PROGRESS = 2;

public const DONE = 3;

public const ON_REVIEW = 4;
}
35 changes: 35 additions & 0 deletions app/Models/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
* App\Models\Module
*
* @property int $id
* @property string $name
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Board[] $boards
* @property-read int|null $boards_count
* @method static \Illuminate\Database\Eloquent\Builder|Module newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Module newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Module query()
* @mixin \Eloquent
*/
class Module extends Model
{
public const KANBAN = 1;

/*
|-------------------------------------------------------------
| Relationships
|-------------------------------------------------------------
*/

public function boards()
{
return $this->belongsToMany(Board::class);
}
}
14 changes: 13 additions & 1 deletion app/Policies/CardPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Models\Card;
use App\Models\Column;
use App\Models\User;
use App\Services\Contracts\Modules\KanbanService;
use Illuminate\Auth\Access\HandlesAuthorization;

class CardPolicy
Expand Down Expand Up @@ -57,10 +58,21 @@ public function update(User $user, Card $card)
return $card->team->isMember($user);
}

/**
* Determine whether the user can update the card.
*
* @param \App\Models\User $user
* @param \App\Models\Card $card
* @param \App\Models\Column $column
* @return \Illuminate\Auth\Access\Response|bool
*/
public function move(User $user, Card $card, Column $column)
{
return $card->team->isMember($user)
$baseCondition = $card->team->isMember($user)
&& $column->team->is($card->team);

return $baseCondition
&& app(KanbanService::class)->canMoveCardToColumn($card, $column);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion app/Policies/ColumnPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Models\Board;
use App\Models\Column;
use App\Models\ColumnType;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

Expand Down Expand Up @@ -68,6 +69,7 @@ public function update(User $user, Column $column)
*/
public function delete(User $user, Column $column)
{
return $column->team->isMember($user);
return $column->team->isMember($user)
&& $column->column_type_id == ColumnType::NONE;
}
}
Loading