Skip to content

Commit 67daed3

Browse files
committed
feat(kanban): add assignment filters
1 parent 9e1a094 commit 67daed3

3 files changed

Lines changed: 305 additions & 47 deletions

File tree

src/app/features/projects/components/project-kanban/project-kanban.component.html

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,29 @@ <h2>Project Kanban Board</h2>
2020
<i class="pi pi-list"></i>
2121
{{ selectedProjectTaskCount }} tasks
2222
</span>
23+
<span class="meta-pill" *ngIf="isAssigneeFilterActive">
24+
<i class="pi pi-filter"></i>
25+
{{ visibleTaskCount }} shown · {{ selectedAssigneeFilterLabel }}
26+
</span>
2327
<span class="meta-pill" *ngIf="isPreviewMode">
2428
<i class="pi pi-eye"></i>
2529
Preview mode
2630
</span>
2731
</div>
2832
</div>
33+
</div>
34+
</p-card>
2935

30-
<div class="project-console" *ngIf="selectedProject as project">
31-
<div class="project-console__top">
32-
<span class="project-console__heading">Project Context</span>
33-
<span class="project-console__counter">{{ selectedProjectIndex + 1 }} / {{ projects.length }}</span>
36+
<p-card class="kanban-controls-card">
37+
<div class="kanban-controls-grid">
38+
<section class="kanban-control-block">
39+
<div class="kanban-control-block__top">
40+
<span class="kanban-control-block__heading">Project Scope</span>
41+
<span class="kanban-control-block__counter">{{ selectedProjectIndex + 1 }} / {{ projects.length }}</span>
3442
</div>
3543

36-
<div class="project-console__picker">
37-
<button pButton type="button" icon="pi pi-angle-left" class="p-button-rounded p-button-text p-button-sm project-console__nav" [disabled]="!canSelectPreviousProject || isLoadingProjects" (click)="selectPreviousProject()"></button>
44+
<div class="kanban-control-block__picker">
45+
<button pButton type="button" icon="pi pi-angle-left" class="p-button-rounded p-button-text p-button-sm kanban-control-nav" [disabled]="!canSelectPreviousProject || isLoadingProjects" (click)="selectPreviousProject()"></button>
3846

3947
<p-dropdown
4048
inputId="projectPicker"
@@ -45,17 +53,70 @@ <h2>Project Kanban Board</h2>
4553
[filter]="true"
4654
filterBy="name"
4755
[showClear]="false"
56+
[appendTo]="'body'"
4857
[disabled]="isLoadingProjects || projects.length === 0"
4958
placeholder="Select project"
50-
styleClass="project-console__dropdown"
59+
styleClass="kanban-control-dropdown"
5160
(onChange)="onProjectSelected($event.value)">
5261
</p-dropdown>
5362

54-
<button pButton type="button" icon="pi pi-angle-right" class="p-button-rounded p-button-text p-button-sm project-console__nav" [disabled]="!canSelectNextProject || isLoadingProjects" (click)="selectNextProject()"></button>
63+
<button pButton type="button" icon="pi pi-angle-right" class="p-button-rounded p-button-text p-button-sm kanban-control-nav" [disabled]="!canSelectNextProject || isLoadingProjects" (click)="selectNextProject()"></button>
5564
</div>
65+
</section>
5666

57-
<span class="project-console__hint">Use arrows or search to switch project scope.</span>
58-
</div>
67+
<section class="kanban-control-block">
68+
<div class="kanban-control-block__top">
69+
<span class="kanban-control-block__heading">Task Lens</span>
70+
<span class="kanban-control-block__counter">{{ selectedAssigneeFilterLabel }}</span>
71+
</div>
72+
73+
<div class="kanban-control-block__filter">
74+
<p-dropdown
75+
*ngIf="canManageAllTasks; else collaboratorLens"
76+
inputId="kanbanAssigneeFilter"
77+
[options]="managementAssigneeFilterOptions"
78+
optionLabel="label"
79+
optionValue="value"
80+
[(ngModel)]="selectedAssigneeFilter"
81+
[filter]="true"
82+
filterBy="label"
83+
[showClear]="false"
84+
[appendTo]="'body'"
85+
styleClass="kanban-control-dropdown"
86+
(onChange)="onAssigneeFilterChange($any($event).value)">
87+
</p-dropdown>
88+
89+
<ng-template #collaboratorLens>
90+
<div class="kanban-control-filter-inline">
91+
<div class="kanban-control-filter-buttons">
92+
<button
93+
*ngFor="let option of collaboratorAssigneeFilterOptions"
94+
pButton
95+
type="button"
96+
class="p-button-sm p-button-outlined kanban-control-filter-button"
97+
[class.kanban-control-filter-button--active]="selectedAssigneeFilter === option.value"
98+
[label]="option.label"
99+
(click)="onAssigneeFilterChange(option.value)">
100+
</button>
101+
</div>
102+
103+
<p-dropdown
104+
inputId="kanbanAssigneeFilterUser"
105+
[options]="collaboratorAssigneeDirectoryOptions"
106+
optionLabel="label"
107+
optionValue="value"
108+
[ngModel]="selectedAssigneeFilter"
109+
[filter]="true"
110+
filterBy="label"
111+
[showClear]="false"
112+
[appendTo]="'body'"
113+
styleClass="kanban-control-dropdown kanban-control-dropdown--user"
114+
(onChange)="onAssigneeFilterChange($any($event).value)">
115+
</p-dropdown>
116+
</div>
117+
</ng-template>
118+
</div>
119+
</section>
59120
</div>
60121
</p-card>
61122

@@ -94,7 +155,7 @@ <h2>Project Kanban Board</h2>
94155
<div class="kanban-column-body">
95156
<div class="task-stack">
96157
<div
97-
*ngIf="getColumnTaskCount(column.status) === 0 && selectedProjectTaskCount > 0"
158+
*ngIf="getColumnTaskCount(column.status) === 0 && visibleTaskCount > 0"
98159
class="kanban-empty-drop-target"
99160
[class.kanban-empty-drop-target--active]="isDropSlotActive(column.status, 0)"
100161
(dragover)="onDropSlotDragOver(column.status, 0, $event)"
@@ -179,7 +240,7 @@ <h2>Project Kanban Board</h2>
179240
</ng-container>
180241

181242
<div
182-
*ngIf="selectedProjectTaskCount > 0"
243+
*ngIf="visibleTaskCount > 0"
183244
class="kanban-drop-slot kanban-drop-slot--end"
184245
[class.kanban-drop-slot--active]="isDropSlotActive(column.status, getColumnTaskCount(column.status))"
185246
(dragover)="onDropSlotDragOver(column.status, getColumnTaskCount(column.status), $event)"
@@ -214,7 +275,7 @@ <h2>Project Kanban Board</h2>
214275
</div>
215276

216277
<div *ngIf="selectedProjectId && getColumnTaskCount(column.status) === 0" class="kanban-column-empty">
217-
No tasks in this column yet.
278+
{{ isAssigneeFilterActive ? 'No tasks match this filter in this column.' : 'No tasks in this column yet.' }}
218279
</div>
219280
</div>
220281
</p-card>

src/app/features/projects/components/project-kanban/project-kanban.component.scss

Lines changed: 101 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
}
66

77
.kanban-toolbar {
8-
display: flex;
9-
justify-content: space-between;
10-
align-items: flex-end;
11-
gap: 1rem;
12-
flex-wrap: wrap;
8+
display: block;
139
}
1410

1511
.kanban-toolbar__title h2 {
@@ -40,27 +36,47 @@
4036
background: color-mix(in srgb, var(--surface-100) 70%, transparent);
4137
}
4238

43-
.project-console {
44-
min-width: 19.5rem;
45-
max-width: 27rem;
46-
width: min(100%, 27rem);
47-
display: flex;
48-
flex-direction: column;
49-
gap: 0.45rem;
39+
.kanban-controls-card {
40+
margin-top: -0.25rem;
41+
}
42+
43+
:host ::ng-deep .kanban-controls-card .p-card-body {
44+
padding: 0;
45+
}
46+
47+
:host ::ng-deep .kanban-controls-card .p-card-content {
48+
padding: 0;
49+
}
50+
51+
.kanban-controls-grid {
52+
display: grid;
53+
grid-template-columns: repeat(2, minmax(0, 1fr));
54+
gap: 0;
5055
border: 1px solid var(--surface-border);
5156
border-radius: 12px;
52-
padding: 0.55rem 0.6rem;
53-
background: color-mix(in srgb, var(--surface-100) 70%, transparent);
57+
background: color-mix(in srgb, var(--surface-100) 68%, transparent);
58+
overflow: hidden;
5459
}
5560

56-
.project-console__top {
61+
.kanban-control-block {
62+
display: flex;
63+
flex-direction: column;
64+
gap: 0.5rem;
65+
padding: 0.8rem 0.9rem;
66+
}
67+
68+
.kanban-control-block + .kanban-control-block {
69+
border-left: 1px solid var(--surface-border);
70+
}
71+
72+
.kanban-control-block__top {
5773
display: flex;
5874
align-items: center;
5975
justify-content: space-between;
6076
gap: 0.5rem;
6177
}
6278

63-
.project-console__heading {
79+
.kanban-control-block__heading {
6480
font-size: 0.69rem;
6581
letter-spacing: 0.07em;
6682
text-transform: uppercase;
@@ -69,45 +85,91 @@
6985
opacity: 0.9;
7086
}
7187

72-
.project-console__counter {
88+
.kanban-control-block__counter {
7389
font-size: 0.72rem;
7490
color: var(--text-color);
7591
border: 1px solid var(--surface-border);
7692
border-radius: 999px;
7793
padding: 0.14rem 0.48rem;
7894
background: color-mix(in srgb, var(--surface-200) 78%, transparent);
95+
white-space: nowrap;
7996
}
8097

81-
.project-console__picker {
98+
.kanban-control-block__picker {
8299
display: grid;
83100
grid-template-columns: auto minmax(0, 1fr) auto;
84101
gap: 0.35rem;
85102
align-items: center;
86103
width: 100%;
87104
}
88105

89-
:host ::ng-deep .project-console__picker .p-dropdown {
106+
:host ::ng-deep .kanban-control-block__picker .p-dropdown {
90107
min-width: 12rem;
91108
}
92109

93-
:host ::ng-deep .project-console__picker .p-dropdown .p-dropdown-label {
110+
:host ::ng-deep .kanban-control-block__picker .p-dropdown .p-dropdown-label {
94111
padding-top: 0.45rem;
95112
padding-bottom: 0.45rem;
96113
font-size: 0.82rem;
97114
}
98115

99-
:host ::ng-deep .project-console__picker .p-button.p-button-sm {
116+
:host ::ng-deep .kanban-control-block__picker .p-button.p-button-sm {
100117
width: 1.95rem;
101118
height: 1.95rem;
102119
}
103120

104-
.project-console__hint {
105-
font-size: 0.72rem;
106-
color: var(--text-color-secondary);
107-
line-height: 1.2;
121+
.kanban-control-block__filter {
122+
display: flex;
123+
flex-direction: column;
124+
gap: 0.35rem;
108125
}
109126

110-
.project-console__nav {
127+
:host ::ng-deep .kanban-control-dropdown--user .p-dropdown-label {
128+
font-size: 0.78rem;
129+
padding-top: 0.4rem;
130+
padding-bottom: 0.4rem;
131+
}
132+
133+
:host ::ng-deep .kanban-control-dropdown .p-dropdown-label {
134+
font-size: 0.82rem;
135+
padding-top: 0.45rem;
136+
padding-bottom: 0.45rem;
137+
}
138+
139+
.kanban-control-filter-buttons {
140+
display: grid;
141+
grid-template-columns: repeat(3, minmax(0, 1fr));
142+
gap: 0.35rem;
143+
}
144+
145+
.kanban-control-filter-inline {
146+
display: grid;
147+
grid-template-columns: minmax(0, 1fr) minmax(12rem, 14rem);
148+
gap: 0.5rem;
149+
align-items: center;
150+
}
151+
152+
:host ::ng-deep .kanban-control-dropdown--user {
153+
width: 100%;
154+
}
155+
156+
.kanban-control-filter-button {
157+
justify-content: center;
158+
font-size: 0.78rem !important;
159+
padding: 0.38rem 0.45rem !important;
160+
}
161+
162+
.kanban-control-filter-button--active {
163+
border-color: color-mix(in srgb, var(--primary-color) 62%, var(--surface-border)) !important;
164+
background: color-mix(in srgb, var(--primary-color) 12%, var(--surface-100)) !important;
165+
color: color-mix(in srgb, var(--primary-color) 72%, var(--text-color)) !important;
166+
}
167+
168+
.kanban-control-filter-button:not(.kanban-control-filter-button--active):hover {
169+
border-color: color-mix(in srgb, var(--primary-color) 35%, var(--surface-border)) !important;
170+
}
171+
172+
.kanban-control-nav {
111173
border: 1px solid var(--surface-border) !important;
112174
background: color-mix(in srgb, var(--surface-100) 80%, transparent) !important;
113175
}
@@ -627,20 +689,28 @@
627689
min-height: auto;
628690
}
629691

630-
.project-console {
631-
min-width: 100%;
632-
max-width: 100%;
692+
.kanban-controls-grid {
693+
grid-template-columns: 1fr;
694+
}
695+
696+
.kanban-control-block + .kanban-control-block {
697+
border-left: 0;
698+
border-top: 1px solid var(--surface-border);
633699
}
634700

635-
.project-console__picker {
701+
.kanban-control-block__picker {
636702
min-width: 100%;
637703
}
638704

639-
:host ::ng-deep .project-console__picker .p-dropdown {
705+
:host ::ng-deep .kanban-control-block__picker .p-dropdown {
640706
min-width: 0;
641707
width: 100%;
642708
}
643709

710+
.kanban-control-filter-inline {
711+
grid-template-columns: 1fr;
712+
}
713+
644714
.task-dialog-grid {
645715
grid-template-columns: 1fr;
646716
}

0 commit comments

Comments
 (0)