Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe('AddAttachmentComponent', () => {
it('should return correct file types for "Other"', () => {
component.attachmentType = 'Other';
const result = component.getAcceptedFileTypes();
expect(result).toBe('.pdf,.doc,.docx,.jpg,.png');
expect(result).toBe('');
});

it('should return an empty string for unknown attachmentType', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ export class AddAttachmentComponent {
getAcceptedFileTypes(): string {
switch (this.attachmentType) {
case 'Gross Project Area Boundary':
case 'Activity Polygon':
return '.kml,.kmz,.shp,.gdb,.zip';
case 'Other':
return '.pdf,.doc,.docx,.jpg,.png';
return '';
default:
return '';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ describe('ProjectFilesComponent', () => {
component.ngOnInit();
expect(component.loadProjectAttachments).toHaveBeenCalled();
});
it('should call loadActivityAttachments when activityGuid and fiscalGuid are present', () => {
component.activityGuid = 'activity-guid';
component.fiscalGuid = 'fiscal-guid';

spyOn(component, 'loadActivityAttachments');

component.ngOnInit();

expect(component.loadActivityAttachments).toHaveBeenCalled();
});
});

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

spyOn(component, 'uploadFile').and.stub();
Expand All @@ -208,7 +218,7 @@ describe('ProjectFilesComponent', () => {
width: '1000px',
data: { indicator: 'project-files' },
});
expect(component.uploadFile).toHaveBeenCalledWith(mockFile);
expect(component.uploadFile).toHaveBeenCalledWith(mockFile, 'Activity Polygon');
});

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

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

component.uploadFile(mockFile);
component.uploadFile(mockFile, 'Activity Polygon');

expect(mockProjectService.uploadDocument).toHaveBeenCalledWith({ file: mockFile });
expect(component.uploadAttachment).toHaveBeenCalledWith(mockFile, response);
expect(component.uploadAttachment).toHaveBeenCalledWith(mockFile, response, 'Activity Polygon');
});

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

component.uploadFile(mockFile);
component.uploadFile(mockFile, 'Activity Polygon');

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

component.uploadAttachment(mockFile, response);
component.uploadAttachment(mockFile, response, 'Activity Polygon');

expect(mockAttachmentService.createProjectAttachment).toHaveBeenCalledWith(
mockProjectGuid,
Expand Down Expand Up @@ -314,12 +324,30 @@ describe('ProjectFilesComponent', () => {
throwError(() => new Error('Failed to create attachment'))
);

component.uploadAttachment(mockFile, response);
component.uploadAttachment(mockFile, response, 'Activity Polygon');

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

it('should call finishWithoutGeometry if type is "Other"', async () => {
const mockFile = new File(['test'], 'test-file.txt', { type: 'text/plain' });
const response = { fileId: 'test-file-id' };
const uploadResponse = { uploadedByUserId: 'tester' };

component.projectGuid = 'project-guid';
component.fiscalGuid = 'fiscal-guid';
component.activityGuid = 'activity-guid';

spyOn(component as any, 'finishWithoutGeometry');
mockAttachmentService.createActivityAttachment.and.returnValue(of(uploadResponse));

await component.uploadAttachment(mockFile, response, 'Other');

expect(mockAttachmentService.createActivityAttachment).toHaveBeenCalled();
expect(component.uploadedBy).toBe('tester');
expect(component.finishWithoutGeometry).toHaveBeenCalled();
});
});

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

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

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

mockDialog.open.and.returnValue({
afterClosed: () => of({ file: mockFile, description }),
afterClosed: () => of({ file: mockFile, description, type: 'Activity Polygon' }),
} as any);

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

component.openFileUploadModal();

expect(component.attachmentDescription).toBe(description);
expect(component.uploadFile).toHaveBeenCalledWith(mockFile);
expect(component.uploadFile).toHaveBeenCalledWith(mockFile, 'Activity Polygon');
});
});

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

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

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

describe('finishWithoutGeometry', () => {
beforeEach(() => {
spyOn(component.filesUpdated, 'emit');
spyOn(component, 'loadActivityAttachments');
spyOn(component, 'loadProjectAttachments');
});

it('should show snackbar, call loadActivityAttachments and emit event when isActivityContext is true', () => {
component.fiscalGuid = 'fiscal-guid';
component.activityGuid = 'activity-guid';

(component as any).finishWithoutGeometry();

expect(mockSnackbar.open).toHaveBeenCalledWith(
'File uploaded successfully.',
'Close',
jasmine.objectContaining({ duration: 5000, panelClass: 'snackbar-success' })
);
expect(component.loadActivityAttachments).toHaveBeenCalled();
expect(component.loadProjectAttachments).not.toHaveBeenCalled();
expect(component.filesUpdated.emit).toHaveBeenCalled();
});

it('should show snackbar, call loadProjectAttachments and emit event when isActivityContext is false', () => {
component.fiscalGuid = '';
component.activityGuid = '';

(component as any).finishWithoutGeometry();

expect(mockSnackbar.open).toHaveBeenCalledWith(
'File uploaded successfully.',
'Close',
jasmine.objectContaining({ duration: 5000, panelClass: 'snackbar-success' })
);
expect(component.loadProjectAttachments).toHaveBeenCalled();
expect(component.loadActivityAttachments).not.toHaveBeenCalled();
expect(component.filesUpdated.emit).toHaveBeenCalled();
});
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { catchError, map, throwError } from 'rxjs';
import { AddAttachmentComponent } from 'src/app/components/add-attachment/add-attachment.component';
import { ConfirmationDialogComponent } from 'src/app/components/confirmation-dialog/confirmation-dialog.component';
import { ActivityBoundary, FileAttachment, Project, ProjectBoundary, ProjectFile } from 'src/app/components/models';
import { ActivityBoundary, FileAttachment, ProjectBoundary, ProjectFile } from 'src/app/components/models';
import { AttachmentService } from 'src/app/services/attachment-service';
import { ProjectService } from 'src/app/services/project-services';
import { SpatialService } from 'src/app/services/spatial-services';
Expand Down Expand Up @@ -38,7 +38,7 @@ export class ProjectFilesComponent implements OnInit {
public readonly dialog: MatDialog,
public attachmentService: AttachmentService,
public spatialService: SpatialService,
private route: ActivatedRoute,
private readonly route: ActivatedRoute,
) { }

messages = Messages;
Expand Down Expand Up @@ -119,14 +119,14 @@ export class ProjectFilesComponent implements OnInit {

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

dialogRef.afterClosed().subscribe(result => {
if (result?.file) {
this.uploadFile(result.file);
const selectedType = result.type;
this.uploadFile(result.file, selectedType);
}
if (result?.description) {
this.attachmentDescription = result.description;
}
})
}

uploadFile(file: File): void {
uploadFile(file: File, type: string): void {
this.projectService.uploadDocument({ file }).subscribe({
next: (response) => {
if (response) {
this.uploadAttachment(file, response);
this.uploadAttachment(file, response, type);
}
},
error: () => {
Expand All @@ -179,7 +180,7 @@ export class ProjectFilesComponent implements OnInit {
});
}

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

this.spatialService.extractCoordinates(file).then(response => {
if (response) {
this.updateActivityBoundary(file, response)
}
})
if (type === 'Other') {
this.finishWithoutGeometry();
} else{
this.spatialService.extractCoordinates(file).then(response => {
if (response) {
this.updateActivityBoundary(file, response)
}
})
}
}
}
})
Expand All @@ -235,6 +240,20 @@ export class ProjectFilesComponent implements OnInit {
}
}

finishWithoutGeometry() {
this.snackbarService.open('File uploaded successfully.', 'Close', {
duration: 5000,
panelClass: 'snackbar-success',
});

if (this.isActivityContext) {
this.loadActivityAttachments();
} else {
this.loadProjectAttachments();
}
this.filesUpdated.emit();
}

updateProjectBoundary(file: File, response: Position[][][]) {
const now = new Date();
const futureDate = new Date(now);
Expand Down Expand Up @@ -433,7 +452,7 @@ export class ProjectFilesComponent implements OnInit {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = file.documentPath || 'downloaded-file'; // fallback filename
link.download = file.documentPath ?? 'downloaded-file'; // fallback filename
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
Expand Down
Loading