Skip to content

Commit 94da745

Browse files
authored
feat(mailing-lists): implement create and edit functionality (#217)
* feat(mailing-lists): implement create and edit functionality Implement comprehensive mailing list management features: - Add mailing-list-manage component with stepper-based create/edit flow - Add mailing-list-settings component for configuration - Add settings-card shared component for consistent UI - Update mailing-list-basic-info, mailing-list-dashboard, and mailing-list-table components - Implement shared service creation for projects without existing services - Add form validation with cross-field validation rules - Update service layer for mailing list CRUD operations - Update shared package with interfaces, constants, and enums - Add route configuration for create/edit paths Migrate UI components to flex+gap pattern: - Update committees, dashboards, and meetings modules - Replace space-y with flex+flex-col+gap for better consistency Update project documentation in CLAUDE.md LFXV2-943 LFXV2-944 LFXV2-945 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * fix(mailing-lists): improve error handling, validation, and code quality - Fixed Observable error handling to use throwError() instead of synchronous throw - Added form-level cross-field validation for announcement visibility requirement - Moved announcementVisibilityValidator to shared package for reusability - Fixed needsSharedServiceCreation logic (changed > 0 to === 0) - Improved CSS selector efficiency for table icon styling using explicit class exclusions LFXV2-943 LFXV2-944 LFXV2-945 Signed-off-by: Asitha de Silva <[email protected]> * refactor(ui): reorganize component styles and improve CSS selectors - Move component-specific styles from app.component.scss to individual wrapper components - Create SCSS files for 12 wrapper components (card, avatar, toggle, select, multi-select, inplace, textarea, file-upload, autocomplete, menu, message, breadcrumb) - Reduce app.component.scss from 134 to 21 lines, keeping only global styles - Improve icon selector efficiency using explicit :not() selectors instead of attribute substring matching - Move datatable styles to table.component.scss with improved selectors - Update component TypeScript files to include styleUrl property LFXV2-943 Signed-off-by: Asitha de Silva <[email protected]> * refactor(ui): move button styles to component and remove unused pill - Move p-button styles from styles.scss to button.component.scss - Create button.component.scss with all p-button styling (secondary, text, sm variants) - Update button.component.ts to include styleUrl property - Remove unused .pill class from app.component.scss - Reduce global styles bundle size by 1.33 kB LFXV2-943 Signed-off-by: Asitha de Silva <[email protected]> * fix: loading indicators Signed-off-by: Asitha de Silva <[email protected]> --------- Signed-off-by: Asitha de Silva <[email protected]>
1 parent 09de2a9 commit 94da745

File tree

99 files changed

+1392
-361
lines changed

Some content is hidden

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

99 files changed

+1392
-361
lines changed

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,4 @@ Before starting any work or commits:
456456
- Always use sequential thinking mcp for planning before doing any changes
457457
- Always run yarn build to validate that your changes are building too for validation
458458
- Always remember that our JIRA sprint field is customfield_10020. When we create tickets, we need to assign them to the current user and set it to the current sprint.
459+
- Always use `flex + flex-col + gap-*` instead of `space-y-*`

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

Lines changed: 0 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -2,158 +2,15 @@
22
/* SPDX-License-Identifier: MIT */
33

44
::ng-deep {
5-
.p-card {
6-
.p-card-title {
7-
@apply font-display;
8-
}
9-
}
10-
11-
.p-avatar {
12-
@apply flex;
13-
}
14-
15-
.p-datatable {
16-
.p-datatable-thead {
17-
tr {
18-
th {
19-
@apply text-xs text-gray-500 font-medium;
20-
}
21-
}
22-
}
23-
24-
.p-datatable-tbody {
25-
tr {
26-
td {
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-
}
38-
}
39-
}
40-
}
41-
}
42-
435
.p-tabs {
446
.p-tablist-content {
457
@apply px-3;
468
}
479
}
4810

49-
.p-toggleswitch {
50-
@apply flex;
51-
52-
.p-toggleswitch-handle {
53-
@apply rounded-full;
54-
}
55-
}
56-
57-
.p-select-overlay,
58-
.p-multiselect-overlay {
59-
.p-select-list-container,
60-
.p-multiselect-list-container {
61-
.p-select-option,
62-
.p-multiselect-option {
63-
@apply text-sm;
64-
}
65-
}
66-
}
67-
6811
.p-tooltip {
6912
.p-tooltip-text {
7013
@apply text-sm;
7114
}
7215
}
73-
74-
.p-inplace-display {
75-
@apply w-full;
76-
77-
&:hover {
78-
@apply bg-transparent;
79-
}
80-
}
81-
82-
.p-textarea {
83-
field-sizing: content;
84-
}
85-
86-
.pill {
87-
@apply inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-full transition-colors text-gray-600 border border-gray-200 hover:bg-gray-50;
88-
}
89-
90-
.p-fileupload {
91-
.p-fileupload-header {
92-
@apply hidden;
93-
}
94-
95-
.p-fileupload-content {
96-
.p-message-error {
97-
.p-message-text {
98-
@apply text-xs;
99-
}
100-
}
101-
}
102-
103-
.p-fileupload-content {
104-
.p-fileupload-file-list {
105-
@apply hidden;
106-
}
107-
108-
p-progressbar {
109-
@apply hidden;
110-
}
111-
}
112-
}
113-
114-
.p-autocomplete-list-container {
115-
.p-autocomplete-list {
116-
.p-autocomplete-option {
117-
span {
118-
@apply text-ellipsis whitespace-nowrap overflow-hidden;
119-
}
120-
}
121-
}
122-
}
123-
124-
.p-menu {
125-
.p-menu-item-link {
126-
.p-menu-item-icon {
127-
@apply w-5;
128-
}
129-
}
130-
}
131-
132-
.p-message-content {
133-
@apply w-full;
134-
135-
.p-message-text {
136-
@apply w-full;
137-
}
138-
}
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-
}
15916
}

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,29 @@ <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-gray-300 rounded-lg">
83-
<div class="text-center max-w-md">
84-
<div class="text-gray-400 mb-4">
85-
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
86-
<h3 class="text-gray-600 mt-2">Your project has no {{ committeeLabelPlural.toLowerCase() }}, yet.</h3>
82+
<lfx-card>
83+
<div class="flex items-center justify-center p-16">
84+
<div class="text-center max-w-md">
85+
<div class="text-gray-400 mb-4">
86+
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
87+
<h3 class="text-gray-600 mt-2">Your project has no {{ committeeLabelPlural.toLowerCase() }}, yet.</h3>
88+
</div>
8789
</div>
8890
</div>
89-
</div>
91+
</lfx-card>
9092
} @else if (filteredCommittees().length === 0) {
9193
<!-- 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-gray-300 rounded-lg">
93-
<div class="text-center max-w-md">
94-
<div class="text-gray-400 mb-4">
95-
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
94+
<lfx-card>
95+
<div class="flex items-center justify-center p-16">
96+
<div class="text-center max-w-md">
97+
<div class="text-gray-400 mb-4">
98+
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
99+
</div>
100+
<h3 class="text-xl font-semibold text-gray-900 mt-4">No {{ committeeLabelPlural.toLowerCase() }} Found</h3>
101+
<p class="text-gray-600 mt-2">Try adjusting your search or filter criteria</p>
96102
</div>
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>
99103
</div>
100-
</div>
104+
</lfx-card>
101105
} @else {
102106
<lfx-committee-table
103107
[committees]="filteredCommittees()"

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { toObservable, toSignal } from '@angular/core/rxjs-interop';
66
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
77
import { Router } from '@angular/router';
88
import { ButtonComponent } from '@components/button/button.component';
9+
import { CardComponent } from '@components/card/card.component';
910
import { InputTextComponent } from '@components/input-text/input-text.component';
1011
import { SelectComponent } from '@components/select/select.component';
1112
import { COMMITTEE_LABEL } from '@lfx-one/shared/constants';
@@ -21,7 +22,7 @@ import { CommitteeTableComponent } from '../components/committee-table/committee
2122

2223
@Component({
2324
selector: 'lfx-committee-dashboard',
24-
imports: [ReactiveFormsModule, InputTextComponent, SelectComponent, ButtonComponent, CommitteeTableComponent],
25+
imports: [ReactiveFormsModule, InputTextComponent, SelectComponent, ButtonComponent, CardComponent, CommitteeTableComponent],
2526
templateUrl: './committee-dashboard.component.html',
2627
styleUrl: './committee-dashboard.component.scss',
2728
})

apps/lfx-one/src/app/modules/committees/components/committee-basic-info/committee-basic-info.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
22
<!-- SPDX-License-Identifier: MIT -->
33

4-
<div class="space-y-6">
4+
<div class="flex flex-col gap-6">
55
<!-- Header -->
66
<div class="text-center mb-8">
77
<h2 class="mb-2">Basic Information</h2>
88
<p class="text-gray-500 text-base">Enter the basic details for your {{ committeeLabel.toLowerCase() }}</p>
99
</div>
1010

1111
<!-- Form Fields -->
12-
<div class="space-y-6">
12+
<div class="flex flex-col gap-6">
1313
<!-- Committee Name -->
1414
<div class="flex flex-col gap-2">
1515
<label for="committeeName" class="text-gray-950 block text-sm font-medium"> {{ committeeLabel }} Name <span class="text-red-500">*</span> </label>

apps/lfx-one/src/app/modules/committees/components/committee-category-selection/committee-category-selection.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
22
<!-- SPDX-License-Identifier: MIT -->
33

4-
<div class="space-y-6">
4+
<div class="flex flex-col gap-6">
55
<!-- Header -->
66
<div class="text-center mb-8">
77
<h2 class="mb-2">Select {{ committeeLabel }} Type</h2>
@@ -20,7 +20,7 @@ <h4 class="text-sm font-semibold mb-1">{{ committeeLabel }} Types</h4>
2020
</lfx-message>
2121

2222
<!-- Category Card Selector -->
23-
<div class="space-y-4">
23+
<div class="flex flex-col gap-4">
2424
<lfx-card-selector
2525
[options]="categoryOptions()"
2626
[form]="form()"

apps/lfx-one/src/app/modules/committees/components/committee-members-manager/committee-members-manager.component.html

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h2 class="mb-2">Add Members</h2>
2525
</div>
2626

2727
<!-- Main members content -->
28-
<div class="space-y-4">
28+
<div class="flex flex-col gap-4">
2929
<!-- Stats and filters section -->
3030
<div class="flex items-center justify-between">
3131
<h3 class="text-lg font-semibold text-gray-900">Members ({{ memberCount() }})</h3>
@@ -59,17 +59,23 @@ <h3 class="text-lg font-semibold text-gray-900">Members ({{ memberCount() }})</h
5959

6060
<!-- Empty state -->
6161
@if (!loading() && filteredMembers().length === 0) {
62-
<div class="text-center py-8" data-testid="empty-state">
63-
<i class="fa-light fa-users text-4xl text-gray-300 mb-4"></i>
64-
<h3 class="text-lg font-medium text-gray-600 mb-2">No members found</h3>
65-
<p class="text-gray-500 mb-4">
66-
@if (memberCount() === 0) {
67-
Get started by adding your first member
68-
} @else {
69-
Try adjusting your search terms or filters
70-
}
71-
</p>
72-
</div>
62+
<lfx-card styleClass="my-4">
63+
<div class="flex items-center justify-center">
64+
<div class="text-center max-w-md">
65+
<div class="text-gray-400 mb-4">
66+
<i class="fa-light fa-eyes text-[2rem] mb-4"></i>
67+
<h3 class="text-gray-600 mt-2">No members found</h3>
68+
</div>
69+
<p class="text-gray-500">
70+
@if (memberCount() === 0) {
71+
Get started by adding your first member
72+
} @else {
73+
Try adjusting your search terms or filters
74+
}
75+
</p>
76+
</div>
77+
</div>
78+
</lfx-card>
7379
}
7480

7581
<!-- Members list -->

apps/lfx-one/src/app/modules/committees/components/committee-members-manager/committee-members-manager.component.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Component, computed, DestroyRef, inject, input, OnInit, output, signal,
66
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
77
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
88
import { ButtonComponent } from '@components/button/button.component';
9+
import { CardComponent } from '@components/card/card.component';
910
import { InputTextComponent } from '@components/input-text/input-text.component';
1011
import { SelectComponent } from '@components/select/select.component';
1112
import { COMMITTEE_LABEL } from '@lfx-one/shared/constants';
@@ -30,7 +31,17 @@ import { MemberFormComponent } from '../member-form/member-form.component';
3031

3132
@Component({
3233
selector: 'lfx-committee-members-manager',
33-
imports: [NgClass, ReactiveFormsModule, ButtonComponent, InputTextComponent, SelectComponent, ConfirmDialogModule, DynamicDialogModule, TooltipModule],
34+
imports: [
35+
NgClass,
36+
ReactiveFormsModule,
37+
ButtonComponent,
38+
CardComponent,
39+
InputTextComponent,
40+
SelectComponent,
41+
ConfirmDialogModule,
42+
DynamicDialogModule,
43+
TooltipModule,
44+
],
3445
providers: [ConfirmationService, DialogService],
3546
templateUrl: './committee-members-manager.component.html',
3647
})

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ <h3 class="text-lg font-medium text-gray-900">{{ committeeLabel.singular }} Memb
125125
<span class="text-sm font-sans text-gray-500">{{ member.voting?.status || 'None' }}</span>
126126
</td>
127127
}
128-
<td class="text-center relative">
128+
<td class="text-right relative">
129129
<lfx-menu #memberActionMenu [model]="memberActionMenuItems" [popup]="true" appendTo="body"> </lfx-menu>
130130
@if (committee()?.writer) {
131131
<lfx-button
@@ -164,9 +164,8 @@ <h3 class="text-lg font-medium text-gray-900">{{ committeeLabel.singular }} Memb
164164
<tr>
165165
<td [attr.colspan]="committee()?.enable_voting ? 6 : 4" class="text-center py-8">
166166
<div class="text-center">
167-
<i class="fa-light fa-users text-4xl text-gray-400 mb-4"></i>
168-
<h3 class="text-lg font-medium text-gray-900 mb-2">No Members Yet</h3>
169-
<p class="text-gray-600">This {{ committeeLabel.singular }} doesn't have any members yet.</p>
167+
<i class="fa-light fa-eyes text-3xl text-gray-400 mb-2"></i>
168+
<p class="text-sm text-gray-500">No members found</p>
170169
</div>
171170
</td>
172171
</tr>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
22
<!-- SPDX-License-Identifier: MIT -->
33

4-
<div class="space-y-6">
4+
<div class="flex flex-col gap-6">
55
<!-- Header -->
66
<div class="text-center mb-8">
77
<h2 class="mb-2">{{ committeeLabel }} Settings</h2>
@@ -20,7 +20,7 @@ <h4 class="text-sm font-semibold mb-1">{{ committeeLabel }} Features</h4>
2020
</lfx-message>
2121

2222
<!-- Member Visibility Setting -->
23-
<div class="space-y-2">
23+
<div class="flex flex-col gap-2">
2424
<h3 class="text-base font-medium text-gray-900">Member Visibility</h3>
2525
<div class="bg-white border border-gray-200 p-4 rounded-lg">
2626
<div class="flex flex-col gap-3">
@@ -48,7 +48,7 @@ <h3 class="text-base font-medium text-gray-900">Member Visibility</h3>
4848
</div>
4949

5050
<!-- Committee Settings Features -->
51-
<div class="space-y-4">
51+
<div class="flex flex-col gap-4">
5252
<h3 class="text-base font-medium text-gray-900">{{ committeeLabel }} Features</h3>
5353

5454
@for (feature of features; track feature.key) {

0 commit comments

Comments
 (0)