Skip to content

Commit 8981514

Browse files
authored
feat(committees): refactor view ui with breadcrumb navigation (#215)
* feat(committees): refactor view UI with breadcrumb navigation LFXV2-933 ## Committee View - Replace back button with breadcrumb navigation (Groups / {group name}) - Change from 2/3 + 1/3 column layout to single column layout - Add Configurations card with 2-column grid displaying Enabled/Disabled tags - Simplify header with inline description and direct Edit Group button ## Committee Members - Remove table/cards view toggle (table only) - Clean up unused components (MemberCardComponent, SelectButtonComponent, AnimateOnScrollModule) ## Committee Table - Simplify table header styling (remove custom font classes) - Reorganize columns: Name, Type (category tag), Description, Members, Voting, Last Updated - Improve cell layout and link styling - Add committeeCategorySeverity pipe for tag colors ## Shared Components - Table: Add rowHover input property - Breadcrumb: Simplify template structure ## Styling - Add breadcrumb CSS variables (transparent bg, no padding) - Clean up datatable padding CSS variables - Update meeting type colors (purple to violet, green to emerald) - Add PrimeNG theme constants file - Add tag severity helper for committee categories 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * fix(committees): add data-testid attributes per PR review - Add data-testid to dashboard hero description - Add data-testid to committee view header, breadcrumb, name, and description - Add data-testid to configurations card and individual config items LFXV2-933 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * fix(committees): address PR review comments - Fix dynamic colspan in committee-members empty message template - Fix pipe operator precedence in committee-table severity binding LFXV2-933 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> --------- Signed-off-by: Asitha de Silva <[email protected]>
1 parent a41caad commit 8981514

File tree

55 files changed

+534
-697
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+534
-697
lines changed

apps/lfx-one/src/app/app.component.scss

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,29 @@
1212
@apply flex;
1313
}
1414

15-
.p-table {
15+
.p-datatable {
16+
.p-datatable-thead {
17+
tr {
18+
th {
19+
@apply text-xs text-gray-500 font-medium;
20+
}
21+
}
22+
}
23+
1624
.p-datatable-tbody {
1725
tr {
1826
td {
19-
@apply text-sm;
27+
@apply text-xs leading-4 text-black;
28+
29+
a,
30+
button,
31+
i {
32+
@apply text-sm;
33+
}
34+
35+
a {
36+
@apply text-blue-500 hover:text-blue-600 hover:underline cursor-pointer text-sm font-medium;
37+
}
2038
}
2139
}
2240
}
@@ -118,4 +136,24 @@
118136
@apply w-full;
119137
}
120138
}
139+
140+
.p-breadcrumb {
141+
.p-breadcrumb-list {
142+
.p-breadcrumb-item:not(:last-child) {
143+
@apply text-sm;
144+
145+
a {
146+
@apply text-blue-500 hover:text-blue-600 hover:underline cursor-pointer text-sm font-medium;
147+
}
148+
}
149+
150+
.p-breadcrumb-item:last-child {
151+
@apply text-sm;
152+
153+
span {
154+
@apply text-black;
155+
}
156+
}
157+
}
158+
}
121159
}

apps/lfx-one/src/app/app.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { provideClientHydration, withEventReplay, withHttpTransferCacheOptions,
77
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
88
import { provideRouter, withInMemoryScrolling, withPreloading } from '@angular/router';
99
import { lfxPreset } from '@linuxfoundation/lfx-ui-core';
10+
import { lfxCardTheme, lfxDataTableTheme } from '@lfx-one/shared';
1011
import { definePreset } from '@primeuix/themes';
1112
import Aura from '@primeuix/themes/aura';
1213
import { authenticationInterceptor } from '@shared/interceptors/authentication.interceptor';
@@ -23,6 +24,8 @@ const customPreset = definePreset(Aura, {
2324
semantic: lfxPreset.semantic,
2425
components: {
2526
...lfxPreset.component,
27+
card: lfxCardTheme,
28+
datatable: lfxDataTableTheme,
2629
} as any,
2730
});
2831

apps/lfx-one/src/app/layouts/project-layout/project-layout.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ export class ProjectLayoutComponent {
6666
},
6767
{
6868
label: 'Committees',
69-
icon: 'fa-light fa-people-group text-green-500',
69+
icon: 'fa-light fa-people-group text-emerald-500',
7070
routerLink: `/project/${this.projectSlug()}/committees`,
7171
routerLinkActiveOptions: { exact: false },
7272
},
7373
]);
7474

7575
public readonly metrics = computed(() => [
7676
{ icon: 'fa-light fa-calendar-days text-blue-500', label: 'Meetings', value: this.project()?.meetings_count || 0 },
77-
{ icon: 'fa-light fa-people-group text-green-500', label: 'Committees', value: this.project()?.committees_count || 0 },
77+
{ icon: 'fa-light fa-people-group text-emerald-500', label: 'Committees', value: this.project()?.committees_count || 0 },
7878
{ icon: 'fa-light fa-envelope text-amber-500', label: 'Mailing Lists', value: this.project()?.mailing_list_count || 0 },
7979
]);
8080

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ <h1>{{ committeeLabelPlural }}</h1>
2626
</lfx-button>
2727
}
2828
</div>
29-
<p class="mt-2 text-gray-500">A group is a team of people like committees, boards, or working groups organized to collaborate across your project</p>
29+
<p class="mt-2 text-gray-500" data-testid="dashboard-hero-description">Organize people and governance for your project.</p>
3030
</div>
3131

3232
<!-- Sticky Top Bar with Search and Filters -->
33-
<div class="sticky top-0 md:top-6 z-10 bg-white rounded-lg border border-slate-200 p-4 -mx-0 mb-8">
33+
<div class="sticky top-0 md:top-6 z-10 bg-white rounded-lg border border-gray-200 p-4 -mx-0 mb-8">
3434
<div class="flex flex-col md:flex-row md:items-center gap-3 md:gap-4">
3535
<!-- Search Input -->
3636
<div class="flex-1">
@@ -79,23 +79,23 @@ <h1>{{ committeeLabelPlural }}</h1>
7979
<div class="min-h-[400px]">
8080
@if (committees().length === 0 && project()?.uid) {
8181
<!-- Empty state: No committees exist -->
82-
<div class="flex items-center justify-center p-16 bg-white border border-dashed border-slate-300 rounded-lg">
82+
<div class="flex items-center justify-center p-16 bg-white border border-dashed border-gray-300 rounded-lg">
8383
<div class="text-center max-w-md">
84-
<div class="text-slate-400 mb-4">
84+
<div class="text-gray-400 mb-4">
8585
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
86-
<h3 class="text-slate-600 mt-2">Your project has no {{ committeeLabelPlural.toLowerCase() }}, yet.</h3>
86+
<h3 class="text-gray-600 mt-2">Your project has no {{ committeeLabelPlural.toLowerCase() }}, yet.</h3>
8787
</div>
8888
</div>
8989
</div>
9090
} @else if (filteredCommittees().length === 0) {
9191
<!-- Empty state: Committees exist but filters returned no results -->
92-
<div class="flex items-center justify-center p-16 bg-white border border-dashed border-slate-300 rounded-lg">
92+
<div class="flex items-center justify-center p-16 bg-white border border-dashed border-gray-300 rounded-lg">
9393
<div class="text-center max-w-md">
94-
<div class="text-slate-400 mb-4">
94+
<div class="text-gray-400 mb-4">
9595
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
9696
</div>
97-
<h3 class="text-xl font-semibold text-slate-900 mt-4">No {{ committeeLabelPlural.toLowerCase() }} Found</h3>
98-
<p class="text-slate-600 mt-2">Try adjusting your search or filter criteria</p>
97+
<h3 class="text-xl font-semibold text-gray-900 mt-4">No {{ committeeLabelPlural.toLowerCase() }} Found</h3>
98+
<p class="text-gray-600 mt-2">Try adjusting your search or filter criteria</p>
9999
</div>
100100
</div>
101101
} @else {

apps/lfx-one/src/app/modules/committees/committee-manage/committee-manage.component.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
</a>
1111
</div>
1212

13-
<div class="bg-white rounded-lg border border-slate-200 shadow-sm">
13+
<div class="bg-white rounded-lg border border-gray-200 shadow-sm">
1414
<!-- Header -->
15-
<div class="border-b border-slate-200 p-6">
15+
<div class="border-b border-gray-200 p-6">
1616
<div class="flex items-center justify-between">
1717
<h1 data-testid="committee-manage-title">
1818
{{ isEditMode() ? 'Edit ' + committeeLabel : 'Create ' + committeeLabel }}
1919
</h1>
20-
<div class="text-sm text-slate-500" data-testid="committee-manage-step-indicator">Step {{ currentStep() }} of {{ totalSteps }}</div>
20+
<div class="text-sm text-gray-500" data-testid="committee-manage-step-indicator">Step {{ currentStep() }} of {{ totalSteps }}</div>
2121
</div>
2222
</div>
2323

@@ -80,7 +80,7 @@ <h1 data-testid="committee-manage-title">
8080
</div>
8181

8282
<!-- Footer Navigation -->
83-
<div class="border-t border-slate-200 p-6">
83+
<div class="border-t border-gray-200 p-6">
8484
<div class="flex justify-between">
8585
<!-- Previous Button -->
8686
@if (!isLastStep()) {

apps/lfx-one/src/app/modules/committees/committee-view/committee-view.component.html

Lines changed: 48 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -29,185 +29,70 @@ <h2 class="mb-2">Group Not Found</h2>
2929
<div class="flex flex-col gap-6">
3030
<!-- Header Section -->
3131
<div class="flex justify-between items-start">
32-
<div>
33-
<div class="flex items-center gap-3 mb-2">
34-
<button (click)="goBack()" class="inline-flex items-center text-gray-500 hover:text-gray-700 transition-colors">
35-
<i class="fa-light fa-arrow-left mr-2"></i>
36-
Back to Groups
37-
</button>
32+
<div class="flex flex-col gap-4" data-testid="committee-view-header">
33+
<lfx-breadcrumb [model]="breadcrumbItems()" data-testid="committee-view-breadcrumb"></lfx-breadcrumb>
34+
<div class="flex flex-col gap-1">
35+
<h1 data-testid="committee-view-name">{{ committee()?.name }}</h1>
36+
<p class="text-gray-500" data-testid="committee-view-description">{{ committee()?.description }}</p>
3837
</div>
39-
<h1 class="mb-2">{{ committee()?.name }}</h1>
4038
<div class="flex items-center gap-4 text-sm text-gray-500">
4139
@if (committee()?.category) {
4240
<lfx-tag [value]="committee()!.category" [severity]="categorySeverity()"></lfx-tag>
4341
}
44-
<span>Created {{ formattedCreatedDate() }}</span>
42+
<span class="text-gray-600">Created {{ formattedCreatedDate() }}</span>
4543
@if (committee()?.updated_at) {
46-
<span>Last updated {{ formattedUpdatedDate() }}</span>
44+
<span class="text-gray-600">Last updated {{ formattedUpdatedDate() }}</span>
4745
}
4846
</div>
4947
</div>
5048

5149
<!-- Action Menu -->
5250
<div class="flex items-center gap-3">
53-
<lfx-menu #actionMenu [model]="actionMenuItems" [popup]="true"> </lfx-menu>
54-
<lfx-button icon="fa-light fa-ellipsis-vertical" styleClass="p-button-outlined p-button-sm" (onClick)="toggleActionMenu($event, actionMenu)">
55-
</lfx-button>
51+
<lfx-button icon="fa-light fa-file-lines" label="Edit Group" size="small" [routerLink]="['/groups', committee()?.uid || '', 'edit']"> </lfx-button>
5652
</div>
5753
</div>
5854

59-
<!-- Main Content Grid -->
60-
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
61-
<!-- Left Column - Committee Details -->
62-
<div class="lg:col-span-3 flex flex-col gap-6">
63-
<!-- Members Section -->
64-
@if (committee()?.uid) {
65-
<lfx-committee-members
66-
[committee]="committee()"
67-
[members]="members()"
68-
[membersLoading]="membersLoading()"
69-
(refresh)="refreshMembers()"></lfx-committee-members>
70-
}
71-
</div>
72-
73-
<!-- Right Column - Settings & Stats -->
74-
<div class="flex flex-col gap-6">
75-
<!-- Upcoming Meeting -->
76-
<lfx-card title="Next Meeting">
77-
@if (committee()?.uid) {
78-
<lfx-upcoming-committee-meeting [committeeId]="committee()!.uid"></lfx-upcoming-committee-meeting>
79-
}
80-
</lfx-card>
81-
82-
<!-- Committee Information & Statistics -->
83-
<lfx-card title="Committee Details">
84-
<div class="">
85-
@if (committee()?.description) {
86-
<div class="pb-4 border-b border-gray-100">
87-
<h4 class="font-medium text-gray-700 mb-2">Description</h4>
88-
<p class="text-gray-500 text-sm">{{ committee()?.description }}</p>
89-
</div>
90-
}
91-
92-
<div class="flex justify-between items-center py-2 border-t border-gray-100">
93-
<span class="text-gray-600">Total Members</span>
94-
<span class="font-semibold text-gray-900">{{ committee()?.total_members || 0 }}</span>
95-
</div>
96-
97-
<div class="flex justify-between items-center py-2 border-t border-gray-100">
98-
<span class="text-gray-600">Voting Representatives</span>
99-
<span class="font-semibold text-gray-900">{{ committee()?.total_voting_reps || 0 }}</span>
100-
</div>
101-
102-
@if (committee()?.website) {
103-
<div class="flex justify-between items-center py-2 border-t border-gray-100">
104-
<span class="text-gray-600">Website</span>
105-
<a [href]="committee()?.website" target="_blank" class="text-blue-600 hover:text-blue-800 underline text-sm" rel="noopener noreferrer">
106-
Visit
107-
</a>
108-
</div>
109-
}
110-
111-
@if (committee()?.display_name) {
112-
<div class="flex justify-between items-center py-2 border-t border-gray-100">
113-
<span class="text-gray-600">Public Name</span>
114-
<span class="text-gray-900">{{ committee()?.display_name }}</span>
115-
</div>
116-
}
117-
118-
@if (committee()?.sso_group_name) {
119-
<div class="flex justify-between items-center py-2 border-t border-gray-100">
120-
<span class="text-gray-600">SSO Group</span>
121-
<span class="text-gray-900">{{ committee()?.sso_group_name }}</span>
122-
</div>
123-
}
124-
</div>
125-
</lfx-card>
126-
127-
<!-- Committee Settings -->
128-
<lfx-card title="Settings">
129-
<div class="space-y-3">
130-
<div class="flex justify-between items-center">
131-
<span class="text-gray-600 text-sm">Public Enabled</span>
132-
<span class="inline-flex items-center">
133-
<i
134-
class="fa-light text-sm mr-1"
135-
[class.fa-check-circle]="committee()?.public"
136-
[class.text-green-500]="committee()?.public"
137-
[class.fa-times-circle]="!committee()?.public"
138-
[class.text-red-500]="!committee()?.public"></i>
139-
<span class="text-sm" [class.text-green-600]="committee()?.public" [class.text-red-600]="!committee()?.public">
140-
{{ committee()?.public ? 'Enabled' : 'Disabled' }}
141-
</span>
142-
</span>
143-
</div>
144-
145-
<div class="flex justify-between items-center">
146-
<span class="text-gray-600 text-sm">Voting Enabled</span>
147-
<span class="inline-flex items-center">
148-
<i
149-
class="fa-light text-sm mr-1"
150-
[class.fa-check-circle]="committee()?.enable_voting"
151-
[class.text-green-500]="committee()?.enable_voting"
152-
[class.fa-times-circle]="!committee()?.enable_voting"
153-
[class.text-red-500]="!committee()?.enable_voting"></i>
154-
<span class="text-sm" [class.text-green-600]="committee()?.enable_voting" [class.text-red-600]="!committee()?.enable_voting">
155-
{{ committee()?.enable_voting ? 'Enabled' : 'Disabled' }}
156-
</span>
157-
</span>
158-
</div>
159-
160-
<div class="flex justify-between items-center">
161-
<span class="text-gray-600 text-sm">Business Email Required</span>
162-
<span class="inline-flex items-center">
163-
<i
164-
class="fa-light text-sm mr-1"
165-
[class.fa-check-circle]="committee()?.business_email_required"
166-
[class.text-green-500]="committee()?.business_email_required"
167-
[class.fa-times-circle]="!committee()?.business_email_required"
168-
[class.text-red-500]="!committee()?.business_email_required"></i>
169-
<span
170-
class="text-sm"
171-
[class.text-green-600]="committee()?.business_email_required"
172-
[class.text-red-600]="!committee()?.business_email_required">
173-
{{ committee()?.business_email_required ? 'Enabled' : 'Disabled' }}
174-
</span>
175-
</span>
176-
</div>
177-
178-
<div class="flex justify-between items-center">
179-
<span class="text-gray-600 text-sm">SSO Group Enabled</span>
180-
<span class="inline-flex items-center">
181-
<i
182-
class="fa-light text-sm mr-1"
183-
[class.fa-check-circle]="committee()?.sso_group_enabled"
184-
[class.text-green-500]="committee()?.sso_group_enabled"
185-
[class.fa-times-circle]="!committee()?.sso_group_enabled"
186-
[class.text-red-500]="!committee()?.sso_group_enabled"></i>
187-
<span class="text-sm" [class.text-green-600]="committee()?.sso_group_enabled" [class.text-red-600]="!committee()?.sso_group_enabled">
188-
{{ committee()?.sso_group_enabled ? 'Enabled' : 'Disabled' }}
189-
</span>
190-
</span>
191-
</div>
192-
193-
<div class="flex justify-between items-center">
194-
<span class="text-gray-600 text-sm">Audit Enabled</span>
195-
<span class="inline-flex items-center">
196-
<i
197-
class="fa-light text-sm mr-1"
198-
[class.fa-check-circle]="committee()?.is_audit_enabled"
199-
[class.text-green-500]="committee()?.is_audit_enabled"
200-
[class.fa-times-circle]="!committee()?.is_audit_enabled"
201-
[class.text-red-500]="!committee()?.is_audit_enabled"></i>
202-
<span class="text-sm" [class.text-green-600]="committee()?.is_audit_enabled" [class.text-red-600]="!committee()?.is_audit_enabled">
203-
{{ committee()?.is_audit_enabled ? 'Enabled' : 'Disabled' }}
204-
</span>
205-
</span>
206-
</div>
207-
</div>
208-
</lfx-card>
55+
<!-- Configurations Section -->
56+
<lfx-card data-testid="committee-view-configurations">
57+
<h3 class="text-sm font-medium mb-4" data-testid="committee-view-configurations-title">Configurations</h3>
58+
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-3">
59+
<div class="flex items-center justify-between" data-testid="committee-view-config-public">
60+
<span class="text-sm">Public</span>
61+
<lfx-tag [value]="committee()?.public ? 'Enabled' : 'Disabled'" [severity]="committee()?.public ? 'success' : 'danger'"></lfx-tag>
62+
</div>
63+
<div class="flex items-center justify-between" data-testid="committee-view-config-voting">
64+
<span class="text-sm">Voting</span>
65+
<lfx-tag [value]="committee()?.enable_voting ? 'Enabled' : 'Disabled'" [severity]="committee()?.enable_voting ? 'success' : 'danger'"></lfx-tag>
66+
</div>
67+
<div class="flex items-center justify-between" data-testid="committee-view-config-business-email">
68+
<span class="text-sm">Business Email Required</span>
69+
<lfx-tag
70+
[value]="committee()?.business_email_required ? 'Enabled' : 'Disabled'"
71+
[severity]="committee()?.business_email_required ? 'success' : 'danger'"></lfx-tag>
72+
</div>
73+
<div class="flex items-center justify-between" data-testid="committee-view-config-sso-group">
74+
<span class="text-sm">SSO Group</span>
75+
<lfx-tag
76+
[value]="committee()?.sso_group_enabled ? 'Enabled' : 'Disabled'"
77+
[severity]="committee()?.sso_group_enabled ? 'success' : 'danger'"></lfx-tag>
78+
</div>
79+
<div class="flex items-center justify-between" data-testid="committee-view-config-audit">
80+
<span class="text-sm">Audit</span>
81+
<lfx-tag
82+
[value]="committee()?.is_audit_enabled ? 'Enabled' : 'Disabled'"
83+
[severity]="committee()?.is_audit_enabled ? 'success' : 'danger'"></lfx-tag>
84+
</div>
20985
</div>
210-
</div>
86+
</lfx-card>
87+
88+
<!-- Members Section -->
89+
@if (committee()?.uid) {
90+
<lfx-committee-members
91+
[committee]="committee()"
92+
[members]="members()"
93+
[membersLoading]="membersLoading()"
94+
(refresh)="refreshMembers()"></lfx-committee-members>
95+
}
21196
</div>
21297
}
21398

0 commit comments

Comments
 (0)