Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Acquisition File Header | psp-6936 #3507

Merged
merged 5 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -83,6 +83,21 @@
return new JsonResult(_mapper.Map<AcquisitionFileModel>(acqFile));
}

/// <summary>
/// Gets the specified acquisition file last updated-by information.
/// </summary>
/// <returns></returns>
[HttpGet("{id:long}/updateInfo")]
[HasPermission(Permissions.AcquisitionFileView)]
[Produces("application/json")]
[ProducesResponseType(typeof(Dal.Entities.Models.LastUpdatedByModel), 200)]
[SwaggerOperation(Tags = new[] { "acquisitionfile" })]
public IActionResult GetLastUpdatedBy(long id)
{
var lastUpdated = _acquisitionService.GetLastUpdateInformation(id);
return new JsonResult(lastUpdated);
}

Check warning on line 99 in source/backend/api/Areas/Acquisition/Controllers/AcquisitionFileController.cs

View check run for this annotation

Codecov / codecov/patch

source/backend/api/Areas/Acquisition/Controllers/AcquisitionFileController.cs#L96-L99

Added lines #L96 - L99 were not covered by tests

/// <summary>
/// Adds the specified acquisition file.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions source/backend/api/Services/AcquisitionFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@
return acqFile;
}

public LastUpdatedByModel GetLastUpdateInformation(long acquisitionFileId)
{
_logger.LogInformation("Retrieving last updated-by information...");
_user.ThrowIfNotAuthorized(Permissions.AcquisitionFileView);

Check warning on line 140 in source/backend/api/Services/AcquisitionFileService.cs

View check run for this annotation

Codecov / codecov/patch

source/backend/api/Services/AcquisitionFileService.cs#L138-L140

Added lines #L138 - L140 were not covered by tests

return _acqFileRepository.GetLastUpdateBy(acquisitionFileId);
}

Check warning on line 143 in source/backend/api/Services/AcquisitionFileService.cs

View check run for this annotation

Codecov / codecov/patch

source/backend/api/Services/AcquisitionFileService.cs#L142-L143

Added lines #L142 - L143 were not covered by tests

public IEnumerable<PimsPropertyAcquisitionFile> GetProperties(long id)
{
_logger.LogInformation("Getting acquisition file with id {id}", id);
Expand Down
2 changes: 2 additions & 0 deletions source/backend/api/Services/IAcquisitionFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface IAcquisitionFileService

PimsAcquisitionFile GetById(long id);

LastUpdatedByModel GetLastUpdateInformation(long acquisitionFileId);

PimsAcquisitionFile Add(PimsAcquisitionFile acquisitionFile, IEnumerable<UserOverrideCode> userOverrides);

PimsAcquisitionFile Update(PimsAcquisitionFile acquisitionFile, IEnumerable<UserOverrideCode> userOverrides);
Expand Down
432 changes: 432 additions & 0 deletions source/backend/dal/Repositories/AcquisitionFileRepository.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public interface IAcquisitionFileRepository : IRepository

PimsAcquisitionFile GetById(long id);

LastUpdatedByModel GetLastUpdateBy(long id);

List<PimsAcquisitionOwner> GetOwnersByAcquisitionFileId(long acquisitionFileId);

List<PimsAcquisitionFilePerson> GetTeamMembers(HashSet<short> regions, long? contractorPersonId = null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
mockAcquisitionFileOwnersResponse,
mockAcquisitionFileResponse,
} from '@/mocks/acquisitionFiles.mock';
import { mockLastUpdatedBy } from '@/mocks/lastUpdatedBy.mock';
import { mockLookups } from '@/mocks/lookups.mock';
import { mapMachineBaseMock } from '@/mocks/mapFSM.mock';
import { mockNotesResponse } from '@/mocks/noteResponses.mock';
Expand Down Expand Up @@ -39,7 +40,6 @@ jest.mock('@/features/documents/hooks/useDocumentGenerationRepository');
}));

jest.mock('@/components/common/mapFSM/MapStateMachineContext');
(useMapStateMachine as jest.Mock).mockImplementation(() => mapMachineBaseMock);

const onClose = jest.fn();

Expand Down Expand Up @@ -96,10 +96,13 @@ describe('AcquisitionContainer component', () => {
};

beforeEach(() => {
(useMapStateMachine as jest.Mock).mockImplementation(() => mapMachineBaseMock);

mockAxios.onGet(new RegExp('users/info/*')).reply(200, {});
mockAxios
.onGet(new RegExp('acquisitionfiles/1/properties'))
.reply(200, mockAcquisitionFileResponse().fileProperties);
mockAxios.onGet(new RegExp('acquisitionfiles/1/updateInfo')).reply(200, mockLastUpdatedBy(1));
mockAxios
.onGet(new RegExp('acquisitionfiles/1/owners'))
.reply(200, mockAcquisitionFileOwnersResponse());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,21 @@
export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainerProps> = props => {
// Load state from props and side-bar context
const { acquisitionFileId, onClose, View } = props;
const { setFile, setFileLoading, staleFile, setStaleFile, file } = useContext(SideBarContext);
const {
setFile,
setFileLoading,
staleFile,
setStaleFile,
file,
setLastUpdatedBy,
lastUpdatedBy,
staleLastUpdatedBy,
} = useContext(SideBarContext);
const [isValid, setIsValid] = useState<boolean>(true);
const withUserOverride = useApiUserOverride<
(userOverrideCodes: UserOverrideCode[]) => Promise<any | void>
>('Failed to update Acquisition File');

const {
getAcquisitionFile: { execute: retrieveAcquisitionFile, loading: loadingAcquisitionFile },
updateAcquisitionProperties,
Expand All @@ -64,6 +74,7 @@
loading: loadingAcquisitionFileProperties,
},
getAcquisitionFileChecklist: { execute: retrieveAcquisitionFileChecklist },
getLastUpdatedBy: { execute: getLastUpdatedBy, loading: loadingGetLastUpdatedBy },
} = useAcquisitionProvider();

const mapMachine = useMapStateMachine();
Expand Down Expand Up @@ -131,15 +142,42 @@
setStaleFile,
]);

const fetchLastUpdatedBy = React.useCallback(async () => {
var retrieved = await getLastUpdatedBy(acquisitionFileId);
if (retrieved !== undefined) {
setLastUpdatedBy(retrieved);
} else {
setLastUpdatedBy(null);

Check warning on line 150 in source/frontend/src/features/mapSideBar/acquisition/AcquisitionContainer.tsx

View check run for this annotation

Codecov / codecov/patch

source/frontend/src/features/mapSideBar/acquisition/AcquisitionContainer.tsx#L149-L150

Added lines #L149 - L150 were not covered by tests
}
}, [acquisitionFileId, getLastUpdatedBy, setLastUpdatedBy]);

React.useEffect(() => {
if (
lastUpdatedBy === undefined ||
acquisitionFileId !== lastUpdatedBy?.parentId ||
staleLastUpdatedBy
) {
fetchLastUpdatedBy();
}
}, [fetchLastUpdatedBy, lastUpdatedBy, acquisitionFileId, staleLastUpdatedBy]);

useEffect(() => {
if (acquisitionFile === undefined || acquisitionFileId !== acquisitionFile.id || staleFile) {
fetchAcquisitionFile();
}
}, [acquisitionFile, fetchAcquisitionFile, acquisitionFileId, staleFile]);

useEffect(
() => setFileLoading(loadingAcquisitionFile || loadingAcquisitionFileProperties),
[loadingAcquisitionFile, setFileLoading, loadingAcquisitionFileProperties],
() =>
setFileLoading(
loadingAcquisitionFile || loadingAcquisitionFileProperties || loadingGetLastUpdatedBy,
),
[
loadingAcquisitionFile,
setFileLoading,
loadingAcquisitionFileProperties,
loadingGetLastUpdatedBy,
],
);

const close = useCallback(() => onClose && onClose(), [onClose]);
Expand Down Expand Up @@ -207,6 +245,7 @@

const onSuccess = () => {
fetchAcquisitionFile();
fetchLastUpdatedBy();
mapMachine.refreshMapProperties();
setIsEditing(false);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
mockAcquisitionFileResponse,
} from '@/mocks/acquisitionFiles.mock';
import { getMockApiInterestHolders } from '@/mocks/interestHolders.mock';
import { mockLastUpdatedBy } from '@/mocks/lastUpdatedBy.mock';
import { mockLookups } from '@/mocks/lookups.mock';
import { mapMachineBaseMock } from '@/mocks/mapFSM.mock';
import { rest, server } from '@/mocks/msw/server';
Expand Down Expand Up @@ -89,6 +90,9 @@ describe('AcquisitionView component', () => {
...mockAcquisitionFileResponse(),
fileType: FileTypes.Acquisition,
}}
lastUpdatedBy={{
...mockLastUpdatedBy(1),
}}
>
<Route path="/mapview/sidebar/acquisition/:id">
<AcquisitionView {...props} />
Expand Down Expand Up @@ -149,7 +153,7 @@ describe('AcquisitionView component', () => {
expect(getByText('1-12345-01 - Test ACQ File')).toBeVisible();
expect(getByText(prettyFormatUTCDate(testAcquisitionFile.appCreateTimestamp))).toBeVisible();
expect(
getByText(prettyFormatUTCDate(testAcquisitionFile.appLastUpdateTimestamp)),
getByText(prettyFormatUTCDate(mockLastUpdatedBy(1).appLastUpdateTimestamp)),
).toBeVisible();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const AcquisitionView: React.FunctionComponent<IAcquisitionViewProps> = (
const location = useLocation();
const history = useHistory();
const match = useRouteMatch();
const { file } = useContext(SideBarContext);
const { file, lastUpdatedBy } = useContext(SideBarContext);
if (!!file && file?.fileType !== FileTypes.Acquisition) {
throw Error('Context file is not an acquisition file');
}
Expand Down Expand Up @@ -130,7 +130,9 @@ export const AcquisitionView: React.FunctionComponent<IAcquisitionViewProps> = (
className="mr-2"
/>
}
header={<AcquisitionHeader acquisitionFile={acquisitionFile} />}
header={
<AcquisitionHeader acquisitionFile={acquisitionFile} lastUpdatedBy={lastUpdatedBy} />
}
footer={
isEditing && (
<SidebarFooter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ exports[`AcquisitionView component renders as expected 1`] = `
>
Last updated:
<strong>
Jul 27, 2022
Oct 6, 2023
</strong>
by
<span
Expand All @@ -230,7 +230,7 @@ exports[`AcquisitionView component renders as expected 1`] = `
id="userNameTooltip"
>
<strong>
admin
MARODRIG
</strong>
</span>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ const mockAxios = new MockAdapter(axios);
describe('AcquisitionHeader component', () => {
// render component under test
const setup = (props: IAcquisitionHeaderProps, renderOptions: RenderOptions = {}) => {
const utils = render(<AcquisitionHeader acquisitionFile={props.acquisitionFile} />, {
...renderOptions,
});
const utils = render(
<AcquisitionHeader
acquisitionFile={props.acquisitionFile}
lastUpdatedBy={props.lastUpdatedBy}
/>,
{
...renderOptions,
},
);

return { ...utils };
};
Expand All @@ -29,13 +35,21 @@ describe('AcquisitionHeader component', () => {
});

it('renders as expected when no data is provided', () => {
const { asFragment } = setup({});
const { asFragment } = setup({ lastUpdatedBy: null });
expect(asFragment()).toMatchSnapshot();
});

it('renders as expected when an acquisition file is provided', async () => {
const testAcquisitionFile = mockAcquisitionFileResponse();
const { getByText } = setup({ acquisitionFile: testAcquisitionFile });
const { getByText } = setup({
acquisitionFile: testAcquisitionFile,
lastUpdatedBy: {
parentId: testAcquisitionFile.id || 0,
appLastUpdateUserid: testAcquisitionFile.appLastUpdateUserid || '',
appLastUpdateUserGuid: testAcquisitionFile.appLastUpdateUserGuid || '',
appLastUpdateTimestamp: testAcquisitionFile.appLastUpdateTimestamp || '',
},
});

expect(getByText('1-12345-01 - Test ACQ File')).toBeVisible();
expect(getByText(prettyFormatUTCDate(testAcquisitionFile.appCreateTimestamp))).toBeVisible();
Expand All @@ -46,15 +60,15 @@ describe('AcquisitionHeader component', () => {

it('renders the file number and name concatenated', async () => {
const testAcquisitionFile = mockAcquisitionFileResponse();
const { getByText } = setup({ acquisitionFile: testAcquisitionFile });
const { getByText } = setup({ acquisitionFile: testAcquisitionFile, lastUpdatedBy: null });

expect(getByText('File:')).toBeVisible();
expect(getByText('1-12345-01 - Test ACQ File')).toBeVisible();
});

it('renders the file Project Number and name concatenated', async () => {
const testAcquisitionFile = mockAcquisitionFileResponse();
const { getByText } = setup({ acquisitionFile: testAcquisitionFile });
const { getByText } = setup({ acquisitionFile: testAcquisitionFile, lastUpdatedBy: null });

expect(getByText('Ministry project:')).toBeVisible();
expect(
Expand All @@ -66,7 +80,7 @@ describe('AcquisitionHeader component', () => {

it('renders the file Product code and description concatenated', async () => {
const testAcquisitionFile = mockAcquisitionFileResponse();
const { getByText } = setup({ acquisitionFile: testAcquisitionFile });
const { getByText } = setup({ acquisitionFile: testAcquisitionFile, lastUpdatedBy: null });

expect(getByText('Ministry product:')).toBeVisible();
expect(getByText('00048 - MISCELLANEOUS CLAIMS')).toBeVisible();
Expand All @@ -81,9 +95,28 @@ describe('AcquisitionHeader component', () => {
productId: null,
projectId: null,
},
lastUpdatedBy: null,
});

expect(getByText('Ministry product:')).toBeVisible();
expect(getByTestId('acq-header-product-val')).toHaveTextContent('');
});

it('renders the last-update-time when provided', async () => {
const testDate = new Date().toISOString();
const testAcquisitionFile = mockAcquisitionFileResponse();
const { getByText } = setup({
acquisitionFile: testAcquisitionFile,
lastUpdatedBy: {
parentId: testAcquisitionFile.id || 0,
appLastUpdateUserid: 'Test User Id',
appLastUpdateUserGuid: 'TEST GUID',
appLastUpdateTimestamp: testDate,
},
});

expect(getByText('1-12345-01 - Test ACQ File')).toBeVisible();
expect(getByText(prettyFormatUTCDate(testAcquisitionFile.appCreateTimestamp))).toBeVisible();
expect(getByText(prettyFormatUTCDate(testDate))).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import styled from 'styled-components';
import { HeaderField } from '@/components/common/HeaderField/HeaderField';
import { UserNameTooltip } from '@/components/common/UserNameTooltip';
import { Api_AcquisitionFile } from '@/models/api/AcquisitionFile';
import { Api_LastUpdatedBy } from '@/models/api/File';
import { prettyFormatUTCDate } from '@/utils';

export interface IAcquisitionHeaderProps {
acquisitionFile?: Api_AcquisitionFile;
lastUpdatedBy: Api_LastUpdatedBy | null;
}

export const AcquisitionHeader: React.FunctionComponent<
React.PropsWithChildren<IAcquisitionHeaderProps>
> = ({ acquisitionFile }) => {
> = ({ acquisitionFile, lastUpdatedBy }) => {
const leftColumnWidth = '7';
const leftColumnLabel = '3';

Expand Down Expand Up @@ -71,10 +73,10 @@ export const AcquisitionHeader: React.FunctionComponent<
<Col className="text-right">
<StyleSmallText>
Last updated:{' '}
<strong>{prettyFormatUTCDate(acquisitionFile?.appLastUpdateTimestamp)}</strong> by{' '}
<strong>{prettyFormatUTCDate(lastUpdatedBy?.appLastUpdateTimestamp)}</strong> by{' '}
<UserNameTooltip
userName={acquisitionFile?.appLastUpdateUserid}
userGuid={acquisitionFile?.appLastUpdateUserGuid}
userName={lastUpdatedBy?.appLastUpdateUserid}
userGuid={lastUpdatedBy?.appLastUpdateUserGuid}
/>
</StyleSmallText>
</Col>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ export const AcquisitionRouter: React.FC<IAcquisitionRouterProps> = props => {
acquisitionFileId={props.acquisitionFile.id || -1}
View={UpdateAgreementsForm}
formikRef={props.formikRef}
onSuccess={() => props.setIsEditing(false)}
onSuccess={props.onSuccess}
/>
</Route>
<Route exact path={`${stripTrailingSlash(path)}/${FileTabType.STAKEHOLDERS}`}>
<UpdateStakeHolderContainer
View={UpdateStakeHolderForm}
formikRef={props.formikRef}
acquisitionFile={props.acquisitionFile}
onSuccess={() => props.setIsEditing(false)}
onSuccess={props.onSuccess}
/>
</Route>
{/* Ignore property-related routes (which are handled in separate FilePropertyRouter) */}
Expand Down
Loading
Loading