Skip to content

Commit d74d05a

Browse files
committed
refactor(search-filters): rebuild filter model for all-project scope, conflict-safe UX, richer task/project results, and kanban quick actions
1 parent d143270 commit d74d05a

3 files changed

Lines changed: 677 additions & 123 deletions

File tree

src/app/features/search/components/search-filters/search-filters.component.html

Lines changed: 207 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<div class="search-header">
66
<div class="search-header__title">
77
<h2>Search & Filters</h2>
8-
<p>Find users in projects, projects for a user, and user/unassigned tasks.</p>
8+
<p>Find project members, projects for a selected user, and tasks across one or all projects.</p>
99
<div class="search-header__meta" *ngIf="selectedProjectId">
1010
<span class="meta-pill">
1111
<i class="pi pi-folder"></i>
@@ -22,7 +22,7 @@ <h2>Search & Filters</h2>
2222

2323
<div class="search-kpis">
2424
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--info"><div class="kpi-card__head"><span class="kpi-card__label">Users In Project</span><span class="kpi-card__tag">People</span></div><span class="kpi-card__value">{{ usersInProjectCount }}</span></div></article>
25-
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--neutral"><div class="kpi-card__head"><span class="kpi-card__label">Projects For User</span><span class="kpi-card__tag">Scope</span></div><span class="kpi-card__value">{{ projectsForUserCount }}</span></div></article>
25+
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--neutral"><div class="kpi-card__head"><span class="kpi-card__label">Projects Matched</span><span class="kpi-card__tag">Scope</span></div><span class="kpi-card__value">{{ projectsForUserCount }}</span></div></article>
2626
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--accent"><div class="kpi-card__head"><span class="kpi-card__label">Filtered Tasks</span><span class="kpi-card__tag">Results</span></div><span class="kpi-card__value">{{ filteredTasks.length }}</span></div></article>
2727
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--done"><div class="kpi-card__head"><span class="kpi-card__label">Completed</span><span class="kpi-card__tag">Done</span></div><span class="kpi-card__value">{{ completedTasksCount }}</span></div></article>
2828
<article class="kpi-card"><div class="kpi-card__shell kpi-card__shell--warning"><div class="kpi-card__head"><span class="kpi-card__label">Unassigned</span><span class="kpi-card__tag">Risk</span></div><span class="kpi-card__value">{{ unassignedTasksCount }}</span></div></article>
@@ -35,22 +35,38 @@ <h2>Search & Filters</h2>
3535
<i class="pi pi-sliders-h"></i>
3636
<span>Filter Workspace</span>
3737
</div>
38+
<button
39+
pButton
40+
type="button"
41+
icon="pi pi-filter-slash"
42+
label="Reset All Filters"
43+
class="p-button-text p-button-sm"
44+
(click)="resetAllFilters()">
45+
</button>
3846
</div>
3947

4048
<div class="search-controls">
4149
<section class="control-section">
4250
<h4>People Context</h4>
4351
<div class="control-section__grid">
4452
<div class="control-group">
45-
<label for="projectId">Project</label>
53+
<label for="projectId" class="control-group__label-with-help">
54+
<span>Project</span>
55+
<i
56+
*ngIf="projectFilterDisableReason as reason"
57+
class="pi pi-question-circle control-group__help-icon"
58+
[pTooltip]="reason"
59+
tooltipPosition="top">
60+
</i>
61+
</label>
4662
<p-dropdown
4763
inputId="projectId"
48-
[options]="projects"
64+
[options]="projectOptions"
4965
optionLabel="name"
5066
optionValue="id"
5167
[ngModel]="selectedProjectId"
5268
[disabled]="isLoading || projects.length === 0"
53-
placeholder="Select project"
69+
placeholder="All Projects"
5470
[filter]="true"
5571
filterBy="name"
5672
[appendTo]="'body'"
@@ -72,15 +88,28 @@ <h4>People Context</h4>
7288
</div>
7389

7490
<div class="control-group">
75-
<label for="userId">User In Project</label>
91+
<label for="userId" class="control-group__label-with-help">
92+
<span>Find Projects For User</span>
93+
<i
94+
class="pi pi-info-circle control-group__help-icon"
95+
pTooltip="Shows projects where the selected user is a member."
96+
tooltipPosition="top">
97+
</i>
98+
<i
99+
*ngIf="memberContextDisableReason as reason"
100+
class="pi pi-question-circle control-group__help-icon"
101+
[pTooltip]="reason"
102+
tooltipPosition="top">
103+
</i>
104+
</label>
76105
<p-dropdown
77106
inputId="userId"
78-
[options]="filteredProjectMembers"
107+
[options]="memberContextOptions"
79108
optionLabel="displayName"
80109
optionValue="userId"
81110
[ngModel]="selectedUserId"
82-
[disabled]="isLoading || filteredProjectMembers.length === 0"
83-
placeholder="Select user"
111+
[disabled]="isLoading || !isUserSelectionEnabled"
112+
[placeholder]="isUserSelectionEnabled ? 'Select user' : 'No users available'"
84113
[filter]="true"
85114
filterBy="displayName,email"
86115
[appendTo]="'body'"
@@ -102,7 +131,32 @@ <h4>Task Filters</h4>
102131
optionValue="value"
103132
[ngModel]="taskOwnershipFilter"
104133
[appendTo]="'body'"
105-
(onChange)="taskOwnershipFilter = $event.value; onTaskFiltersChanged()">
134+
(onChange)="onTaskOwnershipFilterChange($event.value)">
135+
</p-dropdown>
136+
</div>
137+
138+
<div class="control-group">
139+
<label for="taskAssignee" class="control-group__label-with-help">
140+
<span>Assigned User</span>
141+
<i
142+
*ngIf="taskAssigneeDisableReason as reason"
143+
class="pi pi-question-circle control-group__help-icon"
144+
[pTooltip]="reason"
145+
tooltipPosition="top">
146+
</i>
147+
</label>
148+
<p-dropdown
149+
inputId="taskAssignee"
150+
[options]="assigneeOptions"
151+
optionLabel="label"
152+
optionValue="value"
153+
[ngModel]="taskAssigneeUserId"
154+
[disabled]="!isTaskAssigneeSelectionEnabled || assigneeOptions.length === 0"
155+
[placeholder]="isTaskAssigneeSelectionEnabled ? 'Any assigned user' : 'Enable Assigned ownership'"
156+
[filter]="true"
157+
filterBy="label"
158+
[appendTo]="'body'"
159+
(onChange)="taskAssigneeUserId = $event.value; onTaskFiltersChanged()">
106160
</p-dropdown>
107161
</div>
108162

@@ -120,13 +174,96 @@ <h4>Task Filters</h4>
120174
</div>
121175

122176
<div class="control-group">
123-
<label for="search">Task Search</label>
177+
<label for="taskCreatedBy">Created By</label>
178+
<p-dropdown
179+
inputId="taskCreatedBy"
180+
[options]="actorOptions"
181+
optionLabel="label"
182+
optionValue="value"
183+
[ngModel]="taskCreatedByUserId"
184+
[showClear]="true"
185+
placeholder="All creators"
186+
[filter]="true"
187+
filterBy="label"
188+
[appendTo]="'body'"
189+
(onChange)="taskCreatedByUserId = $event.value; onTaskFiltersChanged()">
190+
</p-dropdown>
191+
</div>
192+
193+
<div class="control-group">
194+
<label for="taskUpdatedBy">Last Updated By</label>
195+
<p-dropdown
196+
inputId="taskUpdatedBy"
197+
[options]="actorOptions"
198+
optionLabel="label"
199+
optionValue="value"
200+
[ngModel]="taskUpdatedByUserId"
201+
[showClear]="true"
202+
placeholder="All updaters"
203+
[filter]="true"
204+
filterBy="label"
205+
[appendTo]="'body'"
206+
(onChange)="taskUpdatedByUserId = $event.value; onTaskFiltersChanged()">
207+
</p-dropdown>
208+
</div>
209+
210+
<div class="control-group">
211+
<label for="search">Task Title</label>
124212
<input
125213
id="search"
126214
pInputText
127-
[ngModel]="taskSearch"
128-
(ngModelChange)="taskSearch = $event; onTaskFiltersChanged()"
129-
placeholder="Title or description" />
215+
[ngModel]="taskSearchTitle"
216+
(ngModelChange)="taskSearchTitle = $event; onTaskFiltersChanged()"
217+
placeholder="Search task title" />
218+
</div>
219+
</div>
220+
</section>
221+
222+
<section class="control-section">
223+
<h4>Project Filters</h4>
224+
<div class="control-section__grid">
225+
<div class="control-group">
226+
<label for="projectCreatedBy">Created By</label>
227+
<p-dropdown
228+
inputId="projectCreatedBy"
229+
[options]="actorOptions"
230+
optionLabel="label"
231+
optionValue="value"
232+
[ngModel]="projectCreatedByUserId"
233+
[showClear]="true"
234+
placeholder="All creators"
235+
[filter]="true"
236+
filterBy="label"
237+
[appendTo]="'body'"
238+
(onChange)="projectCreatedByUserId = $event.value; onProjectFiltersChanged()">
239+
</p-dropdown>
240+
</div>
241+
242+
<div class="control-group">
243+
<label for="projectUpdatedBy">Last Updated By</label>
244+
<p-dropdown
245+
inputId="projectUpdatedBy"
246+
[options]="actorOptions"
247+
optionLabel="label"
248+
optionValue="value"
249+
[ngModel]="projectUpdatedByUserId"
250+
[showClear]="true"
251+
placeholder="All updaters"
252+
[filter]="true"
253+
filterBy="label"
254+
[appendTo]="'body'"
255+
(onChange)="projectUpdatedByUserId = $event.value; onProjectFiltersChanged()">
256+
</p-dropdown>
257+
</div>
258+
259+
<div class="control-group">
260+
<label for="projectSearchTitle">Project Title</label>
261+
<input
262+
id="projectSearchTitle"
263+
pInputText
264+
[ngModel]="projectSearchTitle"
265+
(ngModelChange)="projectSearchTitle = $event; onProjectFiltersChanged()"
266+
placeholder="Search project title" />
130267
</div>
131268
</div>
132269
</section>
@@ -137,8 +274,8 @@ <h4>Task Filters</h4>
137274
<div class="results-grid">
138275
<p-card styleClass="results-card">
139276
<div class="results-card__title">
140-
<h3>Users In Project</h3>
141-
<p>Members available in the selected project.</p>
277+
<h3>{{ usersResultTitle }}</h3>
278+
<p>{{ usersResultSubtitle }}</p>
142279
</div>
143280
<div class="results-card__body">
144281
<ul class="simple-list">
@@ -159,20 +296,30 @@ <h3>Users In Project</h3>
159296

160297
<p-card styleClass="results-card">
161298
<div class="results-card__title">
162-
<h3>Projects For User</h3>
163-
<p *ngIf="selectedUserId; else projectsHint">Showing projects for {{ selectedUserName }}.</p>
299+
<h3>Projects For Selected User</h3>
300+
<p *ngIf="selectedUserId; else projectsHint">Showing projects where <strong>{{ selectedUserName }}</strong> is a member.</p>
164301
<ng-template #projectsHint>
165-
<p>Select a user to list matching projects.</p>
302+
<p>Select “Find Projects For User” to narrow this list by a specific member.</p>
166303
</ng-template>
167304
</div>
168305
<div class="results-card__body">
169306
<ul class="simple-list">
170-
<li *ngFor="let project of userProjects" class="project-list-item">
171-
<div class="simple-list__meta simple-list__meta--top">
172-
<i class="pi pi-folder text-color-secondary"></i>
173-
<div class="simple-list__text">
174-
<span>{{ project.name }}</span>
307+
<li *ngFor="let project of filteredProjectResults" class="project-list-item">
308+
<div class="project-list-item__header">
309+
<div class="simple-list__meta simple-list__meta--top">
310+
<i class="pi pi-folder text-color-secondary"></i>
311+
<div class="simple-list__text">
312+
<span>{{ project.name }}</span>
313+
</div>
175314
</div>
315+
<button
316+
pButton
317+
type="button"
318+
icon="pi pi-th-large"
319+
label="Open Kanban"
320+
class="p-button-sm p-button-outlined"
321+
(click)="openProjectKanban(project.id)">
322+
</button>
176323
</div>
177324
<div class="project-meta-chips project-meta-chips--below-icon">
178325
<span class="project-meta-chip">
@@ -183,18 +330,32 @@ <h3>Projects For User</h3>
183330
</span>
184331
</div>
185332
</li>
186-
<li *ngIf="userProjects.length === 0" class="muted">No matching projects.</li>
333+
<li *ngIf="filteredProjectResults.length === 0" class="muted">No matching projects.</li>
187334
</ul>
188335
</div>
189336
</p-card>
190337
</div>
191338

192-
<p-card header="Tasks">
339+
<p-card styleClass="tasks-results-card">
340+
<div class="results-card__title">
341+
<h3>Task Results</h3>
342+
<p>Review filtered tasks and jump directly to the project Kanban board.</p>
343+
</div>
193344
<div class="tasks-meta">
194-
<span>{{ filteredTasks.length }} tasks</span>
345+
<div class="tasks-meta__summary">
346+
<span class="tasks-meta__summary-icon">
347+
<i class="pi pi-list-check"></i>
348+
</span>
349+
<div class="tasks-meta__summary-text">
350+
<strong>{{ filteredTasks.length }} tasks</strong>
351+
<small>matching current filters</small>
352+
</div>
353+
</div>
195354
<div class="pagination-controls">
196355
<p-dropdown
197-
[options]="pageSizeOptions"
356+
[options]="pageSizeSelectOptions"
357+
optionLabel="label"
358+
optionValue="value"
198359
[ngModel]="pageSize"
199360
(onChange)="pageSize = $event.value; page = 1"
200361
appendTo="body">
@@ -209,12 +370,28 @@ <h3>Projects For User</h3>
209370
<article class="task-item" *ngFor="let task of pagedTasks">
210371
<div class="task-item__head">
211372
<strong>{{ task.title }}</strong>
212-
<p-tag [value]="getTaskStatusLabel(task.status)" [severity]="getTaskStatusSeverity(task.status)"></p-tag>
373+
<div class="task-item__head-actions">
374+
<p-tag [value]="getTaskStatusLabel(task.status)" [severity]="getTaskStatusSeverity(task.status)"></p-tag>
375+
<button
376+
pButton
377+
type="button"
378+
icon="pi pi-th-large"
379+
label="Open Kanban"
380+
class="p-button-sm p-button-outlined"
381+
(click)="openTaskKanban(task)">
382+
</button>
383+
</div>
213384
</div>
214-
<p class="task-item__desc">{{ task.description || 'No description provided.' }}</p>
385+
<app-expandable-text
386+
class="task-item__desc"
387+
[text]="task.description"
388+
[maxLength]="140">
389+
</app-expandable-text>
215390
<div class="task-item__meta">
216391
<span><i class="pi pi-user"></i> {{ task.assignedUserName || 'Unassigned' }}</span>
217392
<span><i class="pi pi-folder"></i> {{ task.projectName }}</span>
393+
<span><i class="pi pi-pencil"></i> Created {{ task.createdAt | date:'mediumDate' }} by {{ task.createdByUserName || 'Unknown' }}</span>
394+
<span><i class="pi pi-history"></i> Updated {{ task.lastModifiedAt | date:'mediumDate' }} by {{ task.lastModifiedByUserName || 'Unknown' }}</span>
218395
</div>
219396
</article>
220397

0 commit comments

Comments
 (0)