Skip to content

Commit 88ec9bb

Browse files
authored
feat(ui): add subcommittee support and enhanced filtering (#43)
* feat(ui): add subcommittee support and enhanced filtering Add comprehensive committee management enhancements including: - Subcommittee support with parent committee selection dropdown - Enhanced filtering with dynamic counts for committee types and voting status - Committee table menu actions (View/Delete) following established patterns - Color-coded committee types with custom pipe for consistent styling - Performance improvements by replacing formatDate methods with Angular date pipe - Documentation updates for data transformation best practices The parent committee selection prevents circular references and only shows top-level committees as options. All filters now display counts and are sorted alphabetically for better UX. Generated with [Claude Code](https://claude.ai/code) Closes LFXV2-256 Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(ui): add null check for committee updated_at date pipe - Add conditional check to prevent date pipe failure when updated_at is undefined - Display '-' as fallback when no updated_at value exists - Ensures table renders correctly for committees without update timestamps 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com> * feat(ui): add comprehensive test ids and complete committee color scheme - Add data-testid attributes for all UI elements: New Committee CTA, statistics cards, search input, filters, and Add First Committee button - Map all 19 committee categories to specific colors based on meeting type color schemes - Group committees logically: Board/governance (red), Technical (purple/blue), Legal/compliance (amber), Marketing (green), Finance (emerald), Working groups (orange/yellow), Other (gray) - Remove unused treetable component to reduce bundle size - Ensure comprehensive test coverage with proper element targeting 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(ui): remove incomplete subcommittee references from committee view - Remove subcommittees table section that referenced non-existent data structure - Remove subcommittees count display from committee stats - Clean up unused TableComponent import and Injector dependency - Add TODO comments for future subcommittee implementation - Fixes TypeScript build errors and reduces bundle size 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(ui): update yellow to amber for color Signed-off-by: Asitha de Silva <asithade@gmail.com> --------- Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent caae8fe commit 88ec9bb

File tree

14 files changed

+655
-255
lines changed

14 files changed

+655
-255
lines changed

apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.html

Lines changed: 130 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -12,164 +12,148 @@
1212
}
1313

1414
@if (committees() && !committeesLoading()) {
15-
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6 items-stretch">
16-
<div class="lg:col-span-3 items-stretch h-full">
17-
<lfx-card header="Committees" styleClass="h-full" class="h-full">
18-
<!-- Search and Filter Controls -->
19-
<div class="flex flex-col sm:flex-row gap-4 mb-6">
20-
<!-- Search Input -->
21-
<div class="flex-1">
22-
<lfx-input-text
23-
[form]="searchForm"
24-
control="search"
25-
placeholder="Search committees..."
26-
icon="fa-light fa-search"
27-
styleClass="w-full"
28-
size="small"></lfx-input-text>
29-
</div>
15+
<div class="flex flex-col gap-6">
16+
<div class="flex items-center justify-between p-4 border-b border-gray-100">
17+
<div>
18+
<h2 class="text-[16px] font-bold text-gray-900">Committee Management</h2>
19+
<p class="text-[12px] font-normal text-gray-500 mt-1">
20+
Manage committees, their settings, and organizational structure. Sub-committees are indicated with indentation.
21+
</p>
22+
</div>
23+
<lfx-button
24+
label="New Committee"
25+
icon="fa-light fa-plus"
26+
severity="primary"
27+
(onClick)="openCreateDialog()"
28+
size="small"
29+
data-testid="committee-new-cta">
30+
</lfx-button>
31+
</div>
3032

31-
<!-- Category Filter Dropdown -->
32-
<div class="w-full sm:w-64">
33-
<lfx-select
34-
[form]="searchForm"
35-
control="category"
36-
size="small"
37-
[options]="categories()"
38-
placeholder="Select Category"
39-
[showClear]="true"
40-
styleClass="w-full"
41-
(onChange)="onCategoryChange($event.value)"></lfx-select>
33+
<!-- Statistics Cards -->
34+
<div class="grid grid-cols-1 md:grid-cols-4 gap-3 w-full">
35+
<lfx-card styleClass="bg-white border border-gray-100 shadow-sm h-full" data-testid="stats-total-committees">
36+
<div class="p-3">
37+
<div class="text-[10px] text-gray-500 font-normal mb-1">Total Committees</div>
38+
<div class="flex items-center gap-2">
39+
<span class="text-[24px] text-gray-900 font-normal">{{ totalCommittees() }}</span>
40+
<div class="flex items-center gap-1 text-emerald-600">
41+
<i class="fa-light fa-trending-up text-[8px]"></i>
42+
<span class="text-[10px] font-medium">+2 this week</span>
43+
</div>
4244
</div>
4345
</div>
46+
</lfx-card>
4447

45-
@if (committees().length > 0) {
46-
<!-- Committees Table -->
47-
<lfx-table
48-
[value]="filteredCommittees()"
49-
[paginator]="true"
50-
[totalRecords]="totalRecords()"
51-
[rowsPerPageOptions]="[10, 25, 50]"
52-
[showCurrentPageReport]="true"
53-
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} committees"
54-
(onPage)="onPageChange($event)"
55-
styleClass="p-datatable-sm animate-fade-in-up">
56-
<!-- Header Template -->
57-
<ng-template #header>
58-
<tr>
59-
<th>
60-
<span class="text-sm font-sans text-gray-500">Name</span>
61-
</th>
62-
<th>
63-
<span class="text-sm font-sans text-gray-500">Type</span>
64-
</th>
65-
<th>
66-
<div class="flex justify-center">
67-
<span class="text-sm font-sans text-gray-500">Members</span>
68-
</div>
69-
</th>
70-
<th>
71-
<div class="flex justify-center">
72-
<span class="text-sm font-sans text-gray-500">Voting</span>
73-
</div>
74-
</th>
75-
<th>
76-
<div class="flex justify-center">
77-
<span class="text-sm font-sans text-gray-500">Public</span>
78-
</div>
79-
</th>
80-
<th>
81-
<div class="flex justify-end">
82-
<span class="text-sm font-sans text-gray-500">Last Updated</span>
83-
</div>
84-
</th>
85-
<th style="width: 80px">
86-
<div class="flex justify-center">
87-
<span class="text-sm font-sans text-gray-500">Actions</span>
88-
</div>
89-
</th>
90-
</tr>
91-
</ng-template>
92-
93-
<!-- Body Template -->
94-
<ng-template #body let-committee>
95-
<tr>
96-
<td>
97-
<a [routerLink]="['/project', project()?.slug, 'committees', committee.id]" class="text-primary hover:text-primary-600 font-medium text-sm">
98-
{{ committee.name }}
99-
</a>
100-
</td>
101-
<td>
102-
<span class="text-sm font-sans text-gray-500">{{ committee.category || 'Uncategorized' }}</span>
103-
</td>
104-
<td class="text-center">
105-
<span class="text-sm font-sans text-gray-500"> {{ committee.total_members || 0 }} </span>
106-
</td>
107-
<td class="text-center">
108-
<span class="text-sm font-sans text-gray-500"> {{ committee.enable_voting ? 'Yes' : 'No' }} </span>
109-
</td>
110-
<td class="text-center">
111-
<i class="fa-light fa-circle-check text-green-500" *ngIf="committee.public_enabled"></i>
112-
<i class="fa-light fa-circle-xmark text-red-500" *ngIf="!committee.public_enabled"></i>
113-
</td>
114-
<td class="text-right">
115-
<span class="text-sm font-sans text-gray-500"> {{ formatDate(committee.updated_at) }} </span>
116-
</td>
117-
<td class="text-center relative">
118-
<lfx-button
119-
icon="fa-light fa-ellipsis-vertical"
120-
[text]="true"
121-
[rounded]="true"
122-
size="small"
123-
severity="secondary"
124-
(click)="toggleActionMenu($event, committee, actionMenu)"></lfx-button>
125-
</td>
126-
</tr>
127-
</ng-template>
128-
129-
<!-- Empty Message Template -->
130-
<ng-template #emptymessage>
131-
<tr>
132-
<td colspan="7" class="text-center py-8">
133-
<div class="flex flex-col items-center gap-2">
134-
<i class="fa-light fa-users text-4xl text-gray-400"></i>
135-
<p class="text-gray-600">No committees found</p>
136-
<p class="text-sm text-gray-500">Try adjusting your search or filters</p>
137-
</div>
138-
</td>
139-
</tr>
140-
</ng-template>
141-
</lfx-table>
142-
143-
<!-- Single action menu for all committees -->
144-
<lfx-menu #actionMenu [model]="actionMenuItems" [popup]="true"></lfx-menu>
48+
<lfx-card styleClass="bg-white border border-gray-100 shadow-sm h-full" data-testid="stats-public-committees">
49+
<div class="p-3">
50+
<div class="text-[10px] text-gray-500 font-normal mb-1">Public Committees</div>
51+
<div class="flex items-center gap-2">
52+
<span class="text-[24px] text-gray-900 font-normal">{{ publicCommittees() }}</span>
53+
<div class="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-blue-100 text-blue-600 border border-blue-200">
54+
<span class="text-[9px] font-medium"
55+
>{{ totalCommittees() > 0 ? ((publicCommittees() / totalCommittees()) * 100 | number: '1.0-0') : 0 }}% of total</span
56+
>
57+
</div>
58+
</div>
59+
</div>
60+
</lfx-card>
14561

146-
<!-- Confirmation dialog -->
147-
<p-confirmDialog></p-confirmDialog>
148-
} @else {
149-
<div class="flex justify-center items-center h-full">
150-
<div class="text-center py-8">
151-
<i class="fa-light fa-people-group text-4xl text-gray-400 mb-4"></i>
152-
<h3 class="text-lg font-medium text-gray-900 mb-2">No Committees Yet</h3>
153-
<p class="text-gray-600 mb-4">This project doesn't have any committees yet.</p>
154-
<lfx-button label="Add First Committee" icon="fa-light fa-people-group" severity="secondary" (onClick)="openCreateDialog()"></lfx-button>
62+
<lfx-card styleClass="bg-white border border-gray-100 shadow-sm h-full" data-testid="stats-active-voting">
63+
<div class="p-3">
64+
<div class="text-[10px] text-gray-500 font-normal mb-1">Active Voting</div>
65+
<div class="flex items-center gap-2">
66+
<span class="text-[24px] text-gray-900 font-normal">{{ activeVoting() }}</span>
67+
<div class="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-green-100 text-green-600 border border-green-200">
68+
<span class="text-[9px] font-medium">Voting enabled</span>
15569
</div>
15670
</div>
157-
}
71+
</div>
72+
</lfx-card>
73+
74+
<lfx-card styleClass="bg-white border border-gray-100 shadow-sm h-full items-center" data-testid="stats-upcoming-meeting">
75+
<div class="p-3">
76+
<div class="text-[10px] text-gray-500 font-normal mb-1">Upcoming Meeting</div>
77+
<lfx-upcoming-committee-meeting></lfx-upcoming-committee-meeting>
78+
</div>
15879
</lfx-card>
15980
</div>
160-
<div class="lg:col-span-1 items-stretch flex flex-col">
161-
<div class="sticky top-24">
162-
<lfx-card header="Quick Actions">
163-
<lfx-menu [model]="menuItems" styleClass="border-none"></lfx-menu>
164-
</lfx-card>
16581

166-
<div class="flex flex-col gap-4 mt-6">
167-
<lfx-card header="Upcoming Meeting" styleClass="h-full" class="h-full">
168-
<lfx-upcoming-committee-meeting></lfx-upcoming-committee-meeting>
169-
</lfx-card>
82+
<!-- Committee Management Section -->
83+
<lfx-card styleClass="h-full" class="h-full">
84+
<!-- Search and Filter Controls -->
85+
<div class="flex flex-col sm:flex-row gap-4 mb-6 p-4">
86+
<!-- Search Input -->
87+
<div class="flex-1">
88+
<lfx-input-text
89+
[form]="searchForm"
90+
control="search"
91+
placeholder="Search committees..."
92+
icon="fa-light fa-search"
93+
styleClass="w-full"
94+
size="small"
95+
data-testid="committee-search-input"></lfx-input-text>
96+
</div>
97+
98+
<!-- Committee Type Filter Dropdown -->
99+
<div class="w-full sm:w-64">
100+
<lfx-select
101+
[form]="searchForm"
102+
control="category"
103+
size="small"
104+
[options]="categories()"
105+
placeholder="Select Committee Type"
106+
[showClear]="true"
107+
styleClass="w-full"
108+
(onChange)="onCategoryChange($event.value)"
109+
data-testid="committee-type-filter"></lfx-select>
110+
</div>
111+
112+
<!-- Voting Status Filter Dropdown -->
113+
<div class="w-full sm:w-64">
114+
<lfx-select
115+
[form]="searchForm"
116+
control="votingStatus"
117+
size="small"
118+
[options]="votingStatusOptions()"
119+
placeholder="Select Voting Status"
120+
[showClear]="true"
121+
styleClass="w-full"
122+
(onChange)="onVotingStatusChange($event.value)"
123+
data-testid="committee-voting-status-filter"></lfx-select>
170124
</div>
171125
</div>
172-
</div>
126+
127+
@if (committees().length > 0) {
128+
<!-- Committee Table -->
129+
<div class="px-4 pb-4">
130+
<lfx-committee-table
131+
[committees]="filteredCommittees()"
132+
(editCommittee)="onEditCommittee($event)"
133+
(deleteCommittee)="onDeleteCommittee($event)"
134+
(viewCommittee)="onViewCommittee($event)"
135+
data-testid="committee-dashboard-table">
136+
</lfx-committee-table>
137+
</div>
138+
139+
<!-- Confirmation dialog -->
140+
<p-confirmDialog></p-confirmDialog>
141+
} @else {
142+
<div class="flex justify-center items-center h-full p-4">
143+
<div class="text-center py-8">
144+
<i class="fa-light fa-people-group text-4xl text-gray-400 mb-4"></i>
145+
<h3 class="text-lg font-medium text-gray-900 mb-2">No Committees Yet</h3>
146+
<p class="text-gray-600 mb-4">This project doesn't have any committees yet.</p>
147+
<lfx-button
148+
label="Add First Committee"
149+
icon="fa-light fa-people-group"
150+
severity="secondary"
151+
(onClick)="openCreateDialog()"
152+
data-testid="committee-add-first"></lfx-button>
153+
</div>
154+
</div>
155+
}
156+
</lfx-card>
173157
</div>
174158
}
175159
</div>

0 commit comments

Comments
 (0)