Skip to content

Commit 3c5bfd4

Browse files
authored
feat(backend): implement controller-service architecture and query service integration (#44)
1 parent 88ec9bb commit 3c5bfd4

File tree

34 files changed

+1659
-418
lines changed

34 files changed

+1659
-418
lines changed

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,5 @@ lfx-pcc-v3/
122122
- All commits and pull requests need to be associated to a JIRA ticket. If there isn't one, we need to create it and reference it moving forward.
123123
- Branch names should be following the commit types (feat,fix,docs, etc) followed by the JIRA ticket number. i.e; feat/LFXV2-123 or ci/LFXV2-456
124124
- PR titles must also follow a similar format as conventional commits - `type(scope): description`. The scope has to follow the angular config for conventional commit and not include the JIRA ticket in the title, and everything should be in lowercase.
125+
126+
- All interfaces, resuable constants, and enums should live in the shared package.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class CommitteeDashboardComponent {
7474

7575
// Statistics calculations
7676
public totalCommittees: Signal<number> = computed(() => this.committees().length);
77-
public publicCommittees: Signal<number> = computed(() => this.committees().filter((c) => c.public_enabled).length);
77+
public publicCommittees: Signal<number> = computed(() => this.committees().filter((c) => c.public).length);
7878
public activeVoting: Signal<number> = computed(() => this.committees().filter((c) => c.enable_voting).length);
7979

8080
public constructor() {
@@ -162,7 +162,7 @@ export class CommitteeDashboardComponent {
162162
const committee = this.selectedCommittee();
163163
const projectId = this.project()?.uid;
164164
if (committee && projectId) {
165-
this.router.navigate(['/project', this.project()?.slug, 'committees', committee.id]);
165+
this.router.navigate(['/project', this.project()?.slug, 'committees', committee.uid]);
166166
}
167167
}
168168

@@ -188,7 +188,7 @@ export class CommitteeDashboardComponent {
188188
private performDelete(committee: Committee): void {
189189
this.isDeleting.set(true);
190190

191-
this.committeeService.deleteCommittee(committee.id).subscribe({
191+
this.committeeService.deleteCommittee(committee.uid).subscribe({
192192
next: () => {
193193
this.isDeleting.set(false);
194194
// Refresh the committees list by reloading
@@ -219,7 +219,7 @@ export class CommitteeDashboardComponent {
219219
data: {
220220
isEditing: true,
221221
committee: committee,
222-
committeeId: committee.id,
222+
committeeId: committee.uid,
223223
onCancel: () => this.dialogRef?.close(),
224224
},
225225
});

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h2 class="text-xl font-semibold text-gray-900 mb-2">Committee Not Found</h2>
2525
}
2626

2727
<!-- Committee Content -->
28-
@if (committee()?.id && !loading() && !error()) {
28+
@if (committee()?.uid && !loading() && !error()) {
2929
<div class="flex flex-col gap-6">
3030
<!-- Header Section -->
3131
<div class="flex justify-between items-start">
@@ -61,7 +61,7 @@ <h1 class="text-3xl font-bold text-gray-900 mb-2">{{ committee()?.name }}</h1>
6161
<!-- Left Column - Committee Details -->
6262
<div class="lg:col-span-2 flex flex-col gap-6">
6363
<!-- Members Section -->
64-
@if (committee()?.id) {
64+
@if (committee()?.uid) {
6565
<lfx-committee-members
6666
[committee]="committee()"
6767
[members]="members()"
@@ -74,8 +74,8 @@ <h1 class="text-3xl font-bold text-gray-900 mb-2">{{ committee()?.name }}</h1>
7474
<div class="flex flex-col gap-6">
7575
<!-- Upcoming Meeting -->
7676
<lfx-card title="Next Meeting">
77-
@if (committee()?.id) {
78-
<lfx-upcoming-committee-meeting [committeeId]="committee()!.id"></lfx-upcoming-committee-meeting>
77+
@if (committee()?.uid) {
78+
<lfx-upcoming-committee-meeting [committeeId]="committee()!.uid"></lfx-upcoming-committee-meeting>
7979
}
8080
</lfx-card>
8181

@@ -99,19 +99,19 @@ <h4 class="font-medium text-gray-700 mb-2">Description</h4>
9999
<span class="font-semibold text-gray-900">{{ committee()?.total_voting_reps || 0 }}</span>
100100
</div>
101101

102-
@if (committee()?.committee_website) {
102+
@if (committee()?.website) {
103103
<div class="flex justify-between items-center py-2 border-t border-gray-100">
104104
<span class="text-gray-600">Website</span>
105-
<a [href]="committee()?.committee_website" target="_blank" class="text-blue-600 hover:text-blue-800 underline text-sm">
106-
{{ committee()?.committee_website }}
105+
<a [href]="committee()?.website" target="_blank" class="text-blue-600 hover:text-blue-800 underline text-sm" rel="noopener noreferrer">
106+
{{ committee()?.website }}
107107
</a>
108108
</div>
109109
}
110110

111-
@if (committee()?.public_name) {
111+
@if (committee()?.display_name) {
112112
<div class="flex justify-between items-center py-2 border-t border-gray-100">
113113
<span class="text-gray-600">Public Name</span>
114-
<span class="text-gray-900">{{ committee()?.public_name }}</span>
114+
<span class="text-gray-900">{{ committee()?.display_name }}</span>
115115
</div>
116116
}
117117

@@ -132,12 +132,12 @@ <h4 class="font-medium text-gray-700 mb-2">Description</h4>
132132
<span class="inline-flex items-center">
133133
<i
134134
class="fa-light text-sm mr-1"
135-
[class.fa-check-circle]="committee()?.public_enabled"
136-
[class.text-green-500]="committee()?.public_enabled"
137-
[class.fa-times-circle]="!committee()?.public_enabled"
138-
[class.text-red-500]="!committee()?.public_enabled"></i>
139-
<span class="text-sm" [class.text-green-600]="committee()?.public_enabled" [class.text-red-600]="!committee()?.public_enabled">
140-
{{ committee()?.public_enabled ? 'Enabled' : 'Disabled' }}
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' }}
141141
</span>
142142
</span>
143143
</div>

apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class CommitteeViewComponent {
113113
private performDelete(committee: Committee): void {
114114
this.isDeleting.set(true);
115115

116-
this.committeeService.deleteCommittee(committee.id).subscribe({
116+
this.committeeService.deleteCommittee(committee.uid).subscribe({
117117
next: () => {
118118
this.isDeleting.set(false);
119119
this.goBack();
@@ -138,7 +138,7 @@ export class CommitteeViewComponent {
138138
data: {
139139
isEditing: true,
140140
committee: committee,
141-
committeeId: committee.id,
141+
committeeId: committee.uid,
142142
onCancel: () => this.dialogRef?.close(),
143143
},
144144
});

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,19 @@ <h3 class="text-lg font-medium text-gray-900">Public Settings</h3>
107107
<lfx-toggle
108108
size="small"
109109
[form]="form()"
110-
control="public_enabled"
110+
control="public"
111111
label="Make Committee Public"
112112
id="public-toggle"
113113
tooltip="When enabled, the committee will be visible to the public on LFX tools like the public meeting calendar"
114114
tooltipPosition="right"></lfx-toggle>
115115

116116
<!-- Conditional Public Name Field -->
117-
<div *ngIf="form().get('public_enabled')?.value === true">
117+
<div *ngIf="form().get('public')?.value === true">
118118
<label for="public-name" class="block text-sm font-medium text-gray-700 mb-1"> Public Name </label>
119119
<lfx-input-text
120120
size="small"
121121
[form]="form()"
122-
control="public_name"
122+
control="display_name"
123123
id="public-name"
124124
placeholder="Enter public display name"
125125
styleClass="w-full"></lfx-input-text>
@@ -156,14 +156,12 @@ <h3 class="text-lg font-medium text-gray-900">Additional Information</h3>
156156
<lfx-input-text
157157
size="small"
158158
[form]="form()"
159-
control="committee_website"
159+
control="website"
160160
id="committee-website"
161161
type="url"
162162
placeholder="https://example.com"
163163
styleClass="w-full"></lfx-input-text>
164-
<p class="mt-1 text-sm text-red-600" *ngIf="form().get('committee_website')?.errors?.['pattern'] && form().get('committee_website')?.touched">
165-
Please enter a valid URL
166-
</p>
164+
<p class="mt-1 text-sm text-red-600" *ngIf="form().get('website')?.errors?.['pattern'] && form().get('website')?.touched">Please enter a valid URL</p>
167165
</div>
168166
</div>
169167

apps/lfx-pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,16 @@ export class CommitteeFormComponent {
6262

6363
if (this.form().valid) {
6464
const isEditingMode = this.isEditing();
65-
const formValue = this.form().value;
65+
const rawFormValue = {
66+
...this.form().value,
67+
calendar: {
68+
public: this.form().value.public || false,
69+
},
70+
display_name: this.form().value.display_name || this.form().value.name,
71+
website: this.form().value.website || null,
72+
public: true,
73+
};
74+
const formValue = this.cleanFormData(rawFormValue);
6675
const committeeId = this.committeeId();
6776

6877
this.submitting.set(true);
@@ -132,6 +141,23 @@ export class CommitteeFormComponent {
132141
});
133142
}
134143

144+
// Helper method to clean form data - convert empty strings to null
145+
private cleanFormData(formData: any): any {
146+
const cleaned: any = {};
147+
148+
Object.keys(formData).forEach((key) => {
149+
const value = formData[key];
150+
// Convert empty strings to null for optional string fields
151+
if (typeof value === 'string' && value.trim() === '') {
152+
cleaned[key] = null;
153+
} else {
154+
cleaned[key] = value;
155+
}
156+
});
157+
158+
return cleaned;
159+
}
160+
135161
// Success handler
136162
private onSuccess(): void {
137163
const isEditing = this.isEditing();
@@ -190,12 +216,12 @@ export class CommitteeFormComponent {
190216

191217
// If editing, exclude the current committee
192218
const currentCommitteeId = this.committee()?.id;
193-
const availableCommittees = currentCommitteeId ? topLevelCommittees.filter((committee) => committee.id !== currentCommitteeId) : topLevelCommittees;
219+
const availableCommittees = currentCommitteeId ? topLevelCommittees.filter((committee) => committee.uid !== currentCommitteeId) : topLevelCommittees;
194220

195221
// Transform to dropdown options
196222
const options = availableCommittees.map((committee) => ({
197223
label: committee.name,
198-
value: committee.id,
224+
value: committee.uid,
199225
}));
200226

201227
// Add "No Parent Committee" option at the beginning
@@ -215,11 +241,11 @@ export class CommitteeFormComponent {
215241
business_email_required: new FormControl(committee?.business_email_required || false),
216242
enable_voting: new FormControl(committee?.enable_voting || false),
217243
is_audit_enabled: new FormControl(committee?.is_audit_enabled || false),
218-
public_enabled: new FormControl(committee?.public_enabled || false),
219-
public_name: new FormControl(committee?.public_name || ''),
244+
public: new FormControl(committee?.public || false),
245+
display_name: new FormControl(committee?.display_name || ''),
220246
sso_group_enabled: new FormControl(committee?.sso_group_enabled || false),
221247
sso_group_name: new FormControl(committee?.sso_group_name || ''),
222-
committee_website: new FormControl(committee?.committee_website || '', [Validators.pattern(/^https?:\/\/.+\..+/)]),
248+
website: new FormControl(committee?.website || '', [Validators.pattern(/^https?:\/\/.+\..+/)]),
223249
project_uid: new FormControl(committee?.project_uid || ''),
224250
joinable: new FormControl(committee?.joinable || false),
225251
});

apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export class CommitteeMembersComponent implements OnInit {
196196

197197
private performDelete(member: CommitteeMember): void {
198198
const committee = this.committee();
199-
if (!committee || !committee.id) {
199+
if (!committee || !committee.uid) {
200200
this.messageService.add({
201201
severity: 'error',
202202
summary: 'Error',
@@ -207,7 +207,7 @@ export class CommitteeMembersComponent implements OnInit {
207207

208208
this.isDeleting.set(true);
209209

210-
this.committeeService.deleteCommitteeMember(committee.id, member.id).subscribe({
210+
this.committeeService.deleteCommitteeMember(committee.uid, member.id).subscribe({
211211
next: () => {
212212
this.isDeleting.set(false);
213213

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
<!-- Body template -->
3737
<ng-template #body let-committee>
38-
<tr class="border-b border-gray-50 hover:bg-gray-50/50 transition-colors" [attr.data-testid]="'committee-table-row-' + committee.id">
38+
<tr class="border-b border-gray-50 hover:bg-gray-50/50 transition-colors" [attr.data-testid]="'committee-table-row-' + committee.uid">
3939
<!-- Name column with hierarchy indicator -->
4040
<td class="px-4 py-3">
4141
<div class="flex items-center gap-2">
@@ -47,7 +47,7 @@
4747
}
4848
<div>
4949
<a
50-
[routerLink]="['/project', project()?.slug, 'committees', committee.id]"
50+
[routerLink]="['/project', project()?.slug, 'committees', committee.uid]"
5151
class="text-sm font-medium text-gray-900 hover:text-blue-600 transition-colors">
5252
{{ committee.name }}
5353
</a>
@@ -121,7 +121,7 @@
121121
severity="secondary"
122122
styleClass="h-8 w-8 p-0 text-gray-500 hover:text-gray-700"
123123
(onClick)="onEdit(committee)"
124-
[attr.data-testid]="'committee-edit-' + committee.id" />
124+
[attr.data-testid]="'committee-edit-' + committee.uid" />
125125
<lfx-button
126126
icon="fa-light fa-users"
127127
[text]="true"
@@ -130,7 +130,7 @@
130130
severity="secondary"
131131
styleClass="h-8 w-8 p-0 text-gray-500 hover:text-gray-700"
132132
(onClick)="onView(committee)"
133-
[attr.data-testid]="'committee-members-' + committee.id" />
133+
[attr.data-testid]="'committee-members-' + committee.uid" />
134134
<lfx-button
135135
icon="fa-light fa-ellipsis"
136136
[text]="true"
@@ -139,7 +139,7 @@
139139
severity="secondary"
140140
styleClass="h-8 w-8 p-0 text-gray-500 hover:text-gray-700"
141141
(onClick)="toggleCommitteeActionMenu($event, committee, actionMenu)"
142-
[attr.data-testid]="'committee-more-' + committee.id" />
142+
[attr.data-testid]="'committee-more-' + committee.uid" />
143143
</div>
144144
</td>
145145
</tr>

apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ export class CommitteeTableComponent {
4646
result.push(parent);
4747

4848
// Then add any subcommittees for this parent
49-
const subcommittees = allCommittees.filter((c) => c.parent_uid === parent.id);
49+
const subcommittees = allCommittees.filter((c) => c.parent_uid === parent.uid);
5050
subcommittees.forEach((sub) => {
5151
result.push({ ...sub, level: 1 });
5252
});
5353
});
5454

5555
// Add any orphaned committees (shouldn't happen, but just in case)
56-
const orphaned = allCommittees.filter((c) => c.parent_uid && !allCommittees.find((p) => p.id === c.parent_uid));
56+
const orphaned = allCommittees.filter((c) => c.parent_uid && !allCommittees.find((p) => p.uid === c.parent_uid));
5757
result.push(...orphaned);
5858

5959
return result;

apps/lfx-pcc/src/app/modules/project/dashboard/project-dashboard/project.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,9 @@ export class ProjectComponent {
239239
}
240240

241241
return committees.map((committee) => ({
242-
id: committee.id,
242+
id: committee.uid,
243243
title: committee.name,
244-
url: `/project/${project.slug}/committees/${committee.id}`,
244+
url: `/project/${project.slug}/committees/${committee.uid}`,
245245
status: 'Active',
246246
date: committee.updated_at || committee.created_at,
247247
}));

0 commit comments

Comments
 (0)