evo-ui.issue-workspace is a provider-backed workspace surface for issue-like
manager workflows. dIssues is the current reference consumer, but the contract
is generic: evo-ui owns the UI shell and interaction contract; the consuming
module owns data, workflow rules and persistence.
Runtime markers that must stay documented include evo-ui.issue-workspace,
IssueWorkspaceProvider, IssueWorkspace and initIssueKanban.
return [
'key' => 'vendor.module.issues',
'provider' => Vendor\Module\Workspaces\IssuesWorkspaceData::class,
'default_display' => 'kanban',
'default_filters' => [
'category_ids' => [],
'status_ids' => [],
'assignee_ids' => [],
'archive' => 'active',
'display' => 'kanban',
'search' => '',
],
];Render:
<x-evo::issues.workspace
preset="vendor.module.issues"
:context="['moduleUrl' => $moduleUrl]"
/>The provider must implement EvoUI\Contracts\IssueWorkspaceProvider:
public function projects(): array;
public function categories(): array;
public function statuses(): array;
public function assignees(): array;
public function metrics(array $filters = []): array;
public function issueList(array $filters = []): array;
public function listRows(array $filters = []): array;
public function kanbanLanes(array $filters = []): array;
public function issuePreview(int $issueId): ?array;
public function issueDetail(int $issueId): ?array;
public function comments(int $issueId): array;
public function sortKanbanLanes(array $lanes): void;
public function assignIssueToMe(int $issueId): void;
public function assignIssue(int $issueId, int $userId): void;
public function unassignIssue(int $issueId): void;
public function replyIssue(int $issueId, string $body, ?int $parentCommentId = null): void;
public function replyAndCloseIssue(int $issueId, string $body, ?int $parentCommentId = null): void;
public function closeIssue(int $issueId): void;
public function reopenIssue(int $issueId): void;
public function diagnostics(array $filters = []): array;Providers may expose additional optional methods for newer workspace affordances
such as child issue creation or body editing. Optional methods must be checked
with method_exists() by the workspace before use.
Supported displays:
list: compact issue list with selected issue detailkanban: issues grouped by status/lane
The current display is part of workspace filter state and persists in the manager session. URL/query state should override restored session state when a screen is opened through an explicit link.
Generic filters are stored in the filters state array. Current shared filter
keys:
project_idscategory_idsstatus_idsassigneeassignee_idsarchivedisplaysearch
The provider decides how filters map to database queries. evo-ui owns the
filter controls, selected state and reset behavior.
Issue rows should be normalized arrays. Recommended keys:
[
'id' => 123,
'title' => 'Document issue workspace contract',
'body' => '<p>...</p>',
'project' => ['id' => 1, 'name' => 'evo-ui', 'color' => '#2563EB'],
'status' => ['id' => 2, 'name' => 'In progress', 'icon' => 'loader'],
'category' => ['id' => 1, 'name' => 'Feature', 'color' => '#22C55E'],
'assignee' => ['id' => 7, 'name' => 'MiddleDuck', 'avatar_url' => '...'],
'priority' => 'high',
'updated_at' => '2026-05-09 07:00:00',
]The provider may include parent/child metadata, comment counts, transition
metadata or external references. evo-ui renders known generic keys and should
ignore unknown keys safely.
The provider owns comment storage. evo-ui owns:
- reply editor shell
- reply-to-comment UI state
- reply and reply-and-close calls
- editor synchronization before submit
Comment payloads should include stable ids, author display data, body HTML/text, visibility and timestamps.
assignees() returns manager users or provider users in a normalized option
shape:
[
['id' => 1, 'label' => 'MiddleDuck', 'avatar_url' => null],
]The provider owns whether assignment is allowed and how user ids are validated.
evo-ui owns assignment controls and selected-user UI.
Parent/child UI is generic, but semantics are provider-owned. If a provider supports child creation, it should expose an optional method such as:
public function createChildIssue(int $parentIssueId): int;The workspace can select the new child id after creation. The provider must guard invalid parent ids and recursion.
Archive state is a generic filter. The provider decides what archived means: closed status, archived timestamp, hidden flag or another module-specific contract. The UI should not hard-code dIssues archive rules.
Kanban sorting sends normalized lanes to the provider:
[
[
'status_id' => 2,
'issue_ids' => [10, 11, 12],
],
]The provider must validate status ids and issue ids before persistence. evo-ui
owns drag/drop DOM behavior and the Livewire method contract; the provider owns
workflow validation and database writes.
diagnostics() should return read-only health or setup hints. Diagnostics are
useful for missing provider tokens, disabled sync, empty taxonomy setup or other
workspace-level setup states. They must not mutate data.
Keep in evo-ui:
- workspace layout
- list/kanban mode controls
- filter controls
- selected issue state
- generic issue card/detail rendering
- reply/editor shell
- drag/drop event contract
- manager-session persistence
Keep in the consumer:
- workflow states and allowed transitions
- database schema and migrations
- issue/comment persistence
- external provider sync
- user assignment rules
- business validation
dIssues proves the current contract with local projects, statuses, categories,
phases, comments, assignees, archive behavior and Kanban sorting. Use it as the
reference implementation, not as code that should be copied into evo-ui.