Skip to content

Commit a7e1a3b

Browse files
authored
upload other attachemnts to activity without doing spatial works (#597)
1 parent 6cf56f0 commit a7e1a3b

File tree

4 files changed

+115
-28
lines changed

4 files changed

+115
-28
lines changed

client/wfprev-war/src/main/angular/src/app/components/add-attachment/add-attachment.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe('AddAttachmentComponent', () => {
137137
it('should return correct file types for "Other"', () => {
138138
component.attachmentType = 'Other';
139139
const result = component.getAcceptedFileTypes();
140-
expect(result).toBe('.pdf,.doc,.docx,.jpg,.png');
140+
expect(result).toBe('');
141141
});
142142

143143
it('should return an empty string for unknown attachmentType', () => {

client/wfprev-war/src/main/angular/src/app/components/add-attachment/add-attachment.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ export class AddAttachmentComponent {
7575
getAcceptedFileTypes(): string {
7676
switch (this.attachmentType) {
7777
case 'Gross Project Area Boundary':
78+
case 'Activity Polygon':
7879
return '.kml,.kmz,.shp,.gdb,.zip';
7980
case 'Other':
80-
return '.pdf,.doc,.docx,.jpg,.png';
81+
return '';
8182
default:
8283
return '';
8384
}

client/wfprev-war/src/main/angular/src/app/components/edit-project/project-details/project-files/project-files.component.spec.ts

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ describe('ProjectFilesComponent', () => {
9999
component.ngOnInit();
100100
expect(component.loadProjectAttachments).toHaveBeenCalled();
101101
});
102+
it('should call loadActivityAttachments when activityGuid and fiscalGuid are present', () => {
103+
component.activityGuid = 'activity-guid';
104+
component.fiscalGuid = 'fiscal-guid';
105+
106+
spyOn(component, 'loadActivityAttachments');
107+
108+
component.ngOnInit();
109+
110+
expect(component.loadActivityAttachments).toHaveBeenCalled();
111+
});
102112
});
103113

104114
describe('loadProjectAttachments', () => {
@@ -198,7 +208,7 @@ describe('ProjectFilesComponent', () => {
198208
it('should open file upload modal and call uploadFile if a file is selected', () => {
199209
const mockFile = new File(['content'], 'test-file.txt', { type: 'text/plain' });
200210
mockDialog.open.and.returnValue({
201-
afterClosed: () => of({ file: mockFile }),
211+
afterClosed: () => of({ file: mockFile, type: 'Activity Polygon' }),
202212
} as any);
203213

204214
spyOn(component, 'uploadFile').and.stub();
@@ -208,7 +218,7 @@ describe('ProjectFilesComponent', () => {
208218
width: '1000px',
209219
data: { indicator: 'project-files' },
210220
});
211-
expect(component.uploadFile).toHaveBeenCalledWith(mockFile);
221+
expect(component.uploadFile).toHaveBeenCalledWith(mockFile, 'Activity Polygon');
212222
});
213223

214224
it('should not call uploadFile if modal is closed without a file', () => {
@@ -245,17 +255,17 @@ describe('ProjectFilesComponent', () => {
245255

246256
spyOn(component, 'uploadAttachment').and.stub();
247257

248-
component.uploadFile(mockFile);
258+
component.uploadFile(mockFile, 'Activity Polygon');
249259

250260
expect(mockProjectService.uploadDocument).toHaveBeenCalledWith({ file: mockFile });
251-
expect(component.uploadAttachment).toHaveBeenCalledWith(mockFile, response);
261+
expect(component.uploadAttachment).toHaveBeenCalledWith(mockFile, response, 'Activity Polygon');
252262
});
253263

254264
it('should handle file upload error', () => {
255265
const mockFile = new File(['content'], 'test-file.txt', { type: 'text/plain' });
256266
mockProjectService.uploadDocument.and.returnValue(throwError(() => new Error('Upload failed')));
257267

258-
component.uploadFile(mockFile);
268+
component.uploadFile(mockFile, 'Activity Polygon');
259269

260270
expect(mockProjectService.uploadDocument).toHaveBeenCalledWith({ file: mockFile });
261271
expect(mockSnackbar.open).toHaveBeenCalledWith(
@@ -285,7 +295,7 @@ describe('ProjectFilesComponent', () => {
285295
spyOn(component, 'updateProjectBoundary').and.stub();
286296
spyOn(component, 'loadProjectAttachments').and.stub();
287297

288-
component.uploadAttachment(mockFile, response);
298+
component.uploadAttachment(mockFile, response, 'Activity Polygon');
289299

290300
expect(mockAttachmentService.createProjectAttachment).toHaveBeenCalledWith(
291301
mockProjectGuid,
@@ -314,12 +324,30 @@ describe('ProjectFilesComponent', () => {
314324
throwError(() => new Error('Failed to create attachment'))
315325
);
316326

317-
component.uploadAttachment(mockFile, response);
327+
component.uploadAttachment(mockFile, response, 'Activity Polygon');
318328

319329
expect(mockAttachmentService.createProjectAttachment).toHaveBeenCalled();
320330
expect(console.log).toHaveBeenCalledWith('Failed to upload attachment: ', jasmine.any(Error));
321331
});
322332

333+
it('should call finishWithoutGeometry if type is "Other"', async () => {
334+
const mockFile = new File(['test'], 'test-file.txt', { type: 'text/plain' });
335+
const response = { fileId: 'test-file-id' };
336+
const uploadResponse = { uploadedByUserId: 'tester' };
337+
338+
component.projectGuid = 'project-guid';
339+
component.fiscalGuid = 'fiscal-guid';
340+
component.activityGuid = 'activity-guid';
341+
342+
spyOn(component as any, 'finishWithoutGeometry');
343+
mockAttachmentService.createActivityAttachment.and.returnValue(of(uploadResponse));
344+
345+
await component.uploadAttachment(mockFile, response, 'Other');
346+
347+
expect(mockAttachmentService.createActivityAttachment).toHaveBeenCalled();
348+
expect(component.uploadedBy).toBe('tester');
349+
expect(component.finishWithoutGeometry).toHaveBeenCalled();
350+
});
323351
});
324352

325353
describe('updateProjectBoundary', () => {
@@ -543,7 +571,7 @@ describe('ProjectFilesComponent', () => {
543571
it('should show error if uploaded file has no extension', () => {
544572
const mockFile = new File(['content'], 'file.', { type: 'text/plain' });
545573

546-
component.uploadAttachment(mockFile, { fileId: 'some-id' });
574+
component.uploadAttachment(mockFile, { fileId: 'some-id' }, 'Activity Polygon');
547575

548576
expect(mockSnackbar.open).toHaveBeenCalledWith(
549577
'The spatial file was not uploaded because the file format is not accepted.',
@@ -620,15 +648,15 @@ describe('ProjectFilesComponent', () => {
620648
const description = 'my description';
621649

622650
mockDialog.open.and.returnValue({
623-
afterClosed: () => of({ file: mockFile, description }),
651+
afterClosed: () => of({ file: mockFile, description, type: 'Activity Polygon' }),
624652
} as any);
625653

626654
spyOn(component, 'uploadFile').and.stub();
627655

628656
component.openFileUploadModal();
629657

630658
expect(component.attachmentDescription).toBe(description);
631-
expect(component.uploadFile).toHaveBeenCalledWith(mockFile);
659+
expect(component.uploadFile).toHaveBeenCalledWith(mockFile, 'Activity Polygon');
632660
});
633661
});
634662

@@ -796,7 +824,7 @@ describe('ProjectFilesComponent', () => {
796824
mockAttachmentService.createActivityAttachment.and.returnValue(of(mockResponse));
797825
mockSpatialService.extractCoordinates.and.returnValue(Promise.resolve(mockCoordinates));
798826

799-
await component.uploadAttachment(mockFile, { fileId: 'file-xyz' });
827+
await component.uploadAttachment(mockFile, { fileId: 'file-xyz' }, 'Activity Polygon');
800828

801829
expect(mockAttachmentService.createActivityAttachment).toHaveBeenCalledWith(
802830
'project-guid',
@@ -815,5 +843,44 @@ describe('ProjectFilesComponent', () => {
815843
expect(component.updateActivityBoundary).toHaveBeenCalledWith(mockFile, mockCoordinates);
816844
});
817845

846+
describe('finishWithoutGeometry', () => {
847+
beforeEach(() => {
848+
spyOn(component.filesUpdated, 'emit');
849+
spyOn(component, 'loadActivityAttachments');
850+
spyOn(component, 'loadProjectAttachments');
851+
});
852+
853+
it('should show snackbar, call loadActivityAttachments and emit event when isActivityContext is true', () => {
854+
component.fiscalGuid = 'fiscal-guid';
855+
component.activityGuid = 'activity-guid';
856+
857+
(component as any).finishWithoutGeometry();
858+
859+
expect(mockSnackbar.open).toHaveBeenCalledWith(
860+
'File uploaded successfully.',
861+
'Close',
862+
jasmine.objectContaining({ duration: 5000, panelClass: 'snackbar-success' })
863+
);
864+
expect(component.loadActivityAttachments).toHaveBeenCalled();
865+
expect(component.loadProjectAttachments).not.toHaveBeenCalled();
866+
expect(component.filesUpdated.emit).toHaveBeenCalled();
867+
});
868+
869+
it('should show snackbar, call loadProjectAttachments and emit event when isActivityContext is false', () => {
870+
component.fiscalGuid = '';
871+
component.activityGuid = '';
872+
873+
(component as any).finishWithoutGeometry();
874+
875+
expect(mockSnackbar.open).toHaveBeenCalledWith(
876+
'File uploaded successfully.',
877+
'Close',
878+
jasmine.objectContaining({ duration: 5000, panelClass: 'snackbar-success' })
879+
);
880+
expect(component.loadProjectAttachments).toHaveBeenCalled();
881+
expect(component.loadActivityAttachments).not.toHaveBeenCalled();
882+
expect(component.filesUpdated.emit).toHaveBeenCalled();
883+
});
884+
});
818885

819886
});

client/wfprev-war/src/main/angular/src/app/components/edit-project/project-details/project-files/project-files.component.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table';
66
import { catchError, map, throwError } from 'rxjs';
77
import { AddAttachmentComponent } from 'src/app/components/add-attachment/add-attachment.component';
88
import { ConfirmationDialogComponent } from 'src/app/components/confirmation-dialog/confirmation-dialog.component';
9-
import { ActivityBoundary, FileAttachment, Project, ProjectBoundary, ProjectFile } from 'src/app/components/models';
9+
import { ActivityBoundary, FileAttachment, ProjectBoundary, ProjectFile } from 'src/app/components/models';
1010
import { AttachmentService } from 'src/app/services/attachment-service';
1111
import { ProjectService } from 'src/app/services/project-services';
1212
import { SpatialService } from 'src/app/services/spatial-services';
@@ -38,7 +38,7 @@ export class ProjectFilesComponent implements OnInit {
3838
public readonly dialog: MatDialog,
3939
public attachmentService: AttachmentService,
4040
public spatialService: SpatialService,
41-
private route: ActivatedRoute,
41+
private readonly route: ActivatedRoute,
4242
) { }
4343

4444
messages = Messages;
@@ -119,14 +119,14 @@ export class ProjectFilesComponent implements OnInit {
119119

120120
loadActivityAttachments(): void {
121121
if (!this.fiscalGuid || !this.activityGuid) return;
122-
this.projectGuid = this.route.snapshot?.queryParamMap?.get('projectGuid') || '';
122+
this.projectGuid = this.route.snapshot?.queryParamMap?.get('projectGuid') ?? '';
123123
// Find projectPlanFiscalGuid from the activityGuid
124124
this.attachmentService.getActivityAttachments(this.projectGuid, this.fiscalGuid, this.activityGuid).subscribe({
125125
next: (response) => {
126126
if (response?._embedded?.fileAttachment && Array.isArray(response._embedded.fileAttachment)) {
127127
const fileAttachments = response._embedded.fileAttachment.sort((a: FileAttachment, b: FileAttachment) => {
128-
const timeA = new Date(a.uploadedByTimestamp || 0).getTime();
129-
const timeB = new Date(b.uploadedByTimestamp || 0).getTime();
128+
const timeA = new Date(a.uploadedByTimestamp ?? 0).getTime();
129+
const timeB = new Date(b.uploadedByTimestamp ?? 0).getTime();
130130
return timeB - timeA; // latest first
131131
});
132132
this.projectFiles = fileAttachments;
@@ -155,19 +155,20 @@ export class ProjectFilesComponent implements OnInit {
155155

156156
dialogRef.afterClosed().subscribe(result => {
157157
if (result?.file) {
158-
this.uploadFile(result.file);
158+
const selectedType = result.type;
159+
this.uploadFile(result.file, selectedType);
159160
}
160161
if (result?.description) {
161162
this.attachmentDescription = result.description;
162163
}
163164
})
164165
}
165166

166-
uploadFile(file: File): void {
167+
uploadFile(file: File, type: string): void {
167168
this.projectService.uploadDocument({ file }).subscribe({
168169
next: (response) => {
169170
if (response) {
170-
this.uploadAttachment(file, response);
171+
this.uploadAttachment(file, response, type);
171172
}
172173
},
173174
error: () => {
@@ -179,7 +180,7 @@ export class ProjectFilesComponent implements OnInit {
179180
});
180181
}
181182

182-
uploadAttachment(file: File, response: any): void {
183+
uploadAttachment(file: File, response: any, type: string): void {
183184
const fileExtension = file.name.split('.').pop()?.toLowerCase();
184185
if (!fileExtension) {
185186
this.snackbarService.open('The spatial file was not uploaded because the file format is not accepted.', 'Close', {
@@ -206,11 +207,15 @@ export class ProjectFilesComponent implements OnInit {
206207
if (response) {
207208
this.uploadedBy = response?.uploadedByUserId;
208209

209-
this.spatialService.extractCoordinates(file).then(response => {
210-
if (response) {
211-
this.updateActivityBoundary(file, response)
212-
}
213-
})
210+
if (type === 'Other') {
211+
this.finishWithoutGeometry();
212+
} else{
213+
this.spatialService.extractCoordinates(file).then(response => {
214+
if (response) {
215+
this.updateActivityBoundary(file, response)
216+
}
217+
})
218+
}
214219
}
215220
}
216221
})
@@ -235,6 +240,20 @@ export class ProjectFilesComponent implements OnInit {
235240
}
236241
}
237242

243+
finishWithoutGeometry() {
244+
this.snackbarService.open('File uploaded successfully.', 'Close', {
245+
duration: 5000,
246+
panelClass: 'snackbar-success',
247+
});
248+
249+
if (this.isActivityContext) {
250+
this.loadActivityAttachments();
251+
} else {
252+
this.loadProjectAttachments();
253+
}
254+
this.filesUpdated.emit();
255+
}
256+
238257
updateProjectBoundary(file: File, response: Position[][][]) {
239258
const now = new Date();
240259
const futureDate = new Date(now);
@@ -433,7 +452,7 @@ export class ProjectFilesComponent implements OnInit {
433452
const url = window.URL.createObjectURL(blob);
434453
const link = document.createElement('a');
435454
link.href = url;
436-
link.download = file.documentPath || 'downloaded-file'; // fallback filename
455+
link.download = file.documentPath ?? 'downloaded-file'; // fallback filename
437456
document.body.appendChild(link);
438457
link.click();
439458
document.body.removeChild(link);

0 commit comments

Comments
 (0)