Skip to content

Commit c85341d

Browse files
committed
fix(admin): allow creating public notice when forest client has no prior notice
1 parent 103aa1c commit c85341d

2 files changed

Lines changed: 50 additions & 25 deletions

File tree

admin/src/app/foms/public-notice/public-notice-edit.component.spec.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ describe('PublicNoticeEditComponent', () => {
120120
});
121121

122122
it('keeps the fetched notice for later update/delete calls', () => {
123-
expect(component.publicNoticeResponse.id).toBe(55);
124-
expect(component.publicNoticeResponse.revisionCount).toBe(3);
123+
expect(component.publicNoticeResponse!.id).toBe(55);
124+
expect(component.publicNoticeResponse!.revisionCount).toBe(3);
125125
});
126126

127127
it('derives maxPostDate from the project commenting open date', () => {
@@ -150,7 +150,28 @@ describe('PublicNoticeEditComponent', () => {
150150
});
151151

152152
it('drops the inherited post date so operation years are not carried over', () => {
153-
expect(component.publicNoticeResponse.postDate).toBeUndefined();
153+
expect(component.publicNoticeResponse!.postDate).toBeUndefined();
154+
});
155+
});
156+
157+
describe('when the project and forest client have no prior public notice at all', () => {
158+
beforeEach(async () => {
159+
findLatestMock.mockReturnValue(asyncOf(null));
160+
await createComponent({ publicNoticeId: undefined, editMode: true });
161+
});
162+
163+
it('queries the latest notice for the forest client', () => {
164+
expect(findLatestMock).toHaveBeenCalledWith(99);
165+
expect(findOneMock).not.toHaveBeenCalled();
166+
});
167+
168+
it('is treated as a new notice and renders an empty form', () => {
169+
expect(component.isNewForm).toBe(true);
170+
expect(component.isAddNewNotice()).toBe(true);
171+
expect(component.publicNoticeResponse).toBeNull();
172+
expect(component.formReady()).toBe(true);
173+
expect(component.publicNoticeFormGroup).toBeDefined();
174+
expect(fixture.nativeElement.querySelector('form#publicNoticeForm')).not.toBeNull();
154175
});
155176
});
156177

admin/src/app/foms/public-notice/public-notice-edit.component.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class PublicNoticeEditComponent {
4848
project: ProjectResponse;
4949
readonly projectId = computed(() => Number(this.appId()));
5050
isNewForm: boolean;
51-
publicNoticeResponse: PublicNoticeResponse;
51+
publicNoticeResponse: PublicNoticeResponse | null;
5252
publicNoticeFormGroup: IFormGroup<PublicNoticeForm>;
5353
addressLimit: number = 500;
5454
businessHoursLimit: number = 100;
@@ -80,10 +80,10 @@ export class PublicNoticeEditComponent {
8080
}
8181

8282
effect(() => {
83-
const publicNotice = this.publicNoticeResource.value();
84-
if (!publicNotice) {
83+
if (!this.publicNoticeResource.hasValue()) {
8584
return;
8685
}
86+
const publicNotice = this.publicNoticeResource.value() ?? null;
8787
// Only the fetched notice re-triggers this; everything the initializer reads besides it is
8888
// route-constant for the lifetime of one activation.
8989
untracked(() => this.buildForm(publicNotice));
@@ -95,15 +95,15 @@ export class PublicNoticeEditComponent {
9595
* `isNewForm` and the post-date bounds must be settled before `processBeforeFormGroupInitialized()`
9696
* adjusts the response, which must in turn happen before the form group is created from it.
9797
*/
98-
private buildForm(publicNotice: PublicNoticeResponse) {
98+
private buildForm(publicNotice: PublicNoticeResponse | null) {
9999
const projectDetail = this.projectDetail();
100100
this.project = projectDetail;
101101
this.isNewForm = !projectDetail.publicNoticeId;
102102
this.publicNoticeResponse = publicNotice;
103103
this.maxPostDate = DateTime.fromISO(this.project.commentingOpenDate).toJSDate();
104104
this.processBeforeFormGroupInitialized()
105105

106-
const publicNoticeForm = new PublicNoticeForm(this.publicNoticeResponse);
106+
const publicNoticeForm = new PublicNoticeForm(this.publicNoticeResponse ?? undefined);
107107
this.publicNoticeFormGroup = this.formBuilder.formGroup(publicNoticeForm) as IFormGroup<PublicNoticeForm>;
108108
this.onSameAsReviewIndToggled();
109109
if (!this.editMode()) {
@@ -118,26 +118,30 @@ export class PublicNoticeEditComponent {
118118
if (this.isNewForm) {
119119
// Don't inherit operation years from previous public notice from the forest client.
120120
// Cast to Partial so the (non-optional in the generated type) postDate can be deleted.
121-
delete (this.publicNoticeResponse as Partial<PublicNoticeResponse>)?.postDate;
121+
if (this.publicNoticeResponse) {
122+
delete (this.publicNoticeResponse as Partial<PublicNoticeResponse>).postDate;
123+
}
122124
}
123125
else { // a case there was public notice saved for the project.
124126
// This is a tricky case. "bsDatepicker" when (minDate=maxDate) and when previous field date falls
125127
// outside of the date range, "bsDatepicker" has problem initializing it and even if you trying picking from UI.
126128
// So, specifically set it here for corner cases.
127129
const pnPostDate = this.publicNoticeResponse?.postDate;
128-
const startOfPnPostDate = DateTime.fromISO(pnPostDate).startOf('day');
129-
const startOfCommentingOpenDate = DateTime.fromISO(this.project.commentingOpenDate).startOf('day');
130-
const startOfMinPostDate = DateTime.fromJSDate(this.minPostDate).startOf('day');
131-
const startOfMaxPostDate = DateTime.fromJSDate(this.maxPostDate).startOf('day');
132-
if (pnPostDate && startOfMinPostDate <= startOfCommentingOpenDate) {
133-
if ((startOfPnPostDate < startOfMinPostDate) || (startOfPnPostDate > startOfMaxPostDate)){
134-
// startOfMinPostDate is derived from a valid date, so toISODate() is non-null here.
135-
this.publicNoticeResponse.postDate = startOfMinPostDate.toISODate()!;
130+
if (pnPostDate && this.publicNoticeResponse) {
131+
const startOfPnPostDate = DateTime.fromISO(pnPostDate).startOf('day');
132+
const startOfCommentingOpenDate = DateTime.fromISO(this.project.commentingOpenDate).startOf('day');
133+
const startOfMinPostDate = DateTime.fromJSDate(this.minPostDate).startOf('day');
134+
const startOfMaxPostDate = DateTime.fromJSDate(this.maxPostDate).startOf('day');
135+
if (startOfMinPostDate <= startOfCommentingOpenDate) {
136+
if ((startOfPnPostDate < startOfMinPostDate) || (startOfPnPostDate > startOfMaxPostDate)){
137+
// startOfMinPostDate is derived from a valid date, so toISODate() is non-null here.
138+
this.publicNoticeResponse.postDate = startOfMinPostDate.toISODate()!;
139+
}
140+
}
141+
else if (startOfMinPostDate > startOfCommentingOpenDate) {
142+
// Clear the post date; postDate is non-optional in the generated type, so cast to Partial.
143+
(this.publicNoticeResponse as Partial<PublicNoticeResponse>).postDate = undefined;
136144
}
137-
}
138-
else if (pnPostDate && (startOfMinPostDate > startOfCommentingOpenDate)) {
139-
// Clear the post date; postDate is non-optional in the generated type, so cast to Partial.
140-
(this.publicNoticeResponse as Partial<PublicNoticeResponse>).postDate = undefined;
141145
}
142146
}
143147
}
@@ -186,13 +190,13 @@ export class PublicNoticeEditComponent {
186190

187191
async deletePublicNotice() {
188192
const dialogRef = this.modalSvc.openConfirmationDialog(
189-
`You are about to delete Online Public Notice <strong>#${this.publicNoticeResponse.id}</strong>. Are you sure?`,
193+
`You are about to delete Online Public Notice <strong>#${this.publicNoticeResponse!.id}</strong>. Are you sure?`,
190194
'Delete Online Public Notice');
191195

192196
dialogRef.afterClosed().subscribe(async (confirm) => {
193197
if (confirm) {
194198
await lastValueFrom(
195-
this.publicNoticeService.publicNoticeControllerRemove(this.publicNoticeResponse.id)
199+
this.publicNoticeService.publicNoticeControllerRemove(this.publicNoticeResponse!.id)
196200
);
197201
this.router.navigate(['/a', this.projectId()]);
198202
}
@@ -231,7 +235,7 @@ export class PublicNoticeEditComponent {
231235
}
232236
else {
233237
body = this.publicNoticeFormGroup.value as Partial<PublicNoticeUpdateRequest>;
234-
body.revisionCount = this.publicNoticeResponse.revisionCount;
238+
body.revisionCount = this.publicNoticeResponse!.revisionCount;
235239
}
236240

237241
body.projectId = this.project.id;
@@ -246,7 +250,7 @@ export class PublicNoticeEditComponent {
246250
return this.publicNoticeService.publicNoticeControllerCreate(body);
247251
}
248252
else {
249-
return this.publicNoticeService.publicNoticeControllerUpdate(this.publicNoticeResponse.id, body);
253+
return this.publicNoticeService.publicNoticeControllerUpdate(this.publicNoticeResponse!.id, body);
250254
}
251255
}
252256

0 commit comments

Comments
 (0)