Skip to content

Commit

Permalink
Acquisition File Header | psp-6936 (#3507)
Browse files Browse the repository at this point in the history
* Addded last updated by acquisition

* minor fix

* Fixed issues with some pages
  • Loading branch information
FuriousLlama authored Oct 7, 2023
1 parent f6d6e3f commit 977165a
Show file tree
Hide file tree
Showing 27 changed files with 661 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ public IActionResult GetAcquisitionFile(long id)
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);
}

/// <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 @@ public PimsAcquisitionFile GetById(long id)
return acqFile;
}

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

return _acqFileRepository.GetLastUpdateBy(acquisitionFileId);
}

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 @@ const initialState: AcquisitionContainerState = {
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 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer
loading: loadingAcquisitionFileProperties,
},
getAcquisitionFileChecklist: { execute: retrieveAcquisitionFileChecklist },
getLastUpdatedBy: { execute: getLastUpdatedBy, loading: loadingGetLastUpdatedBy },
} = useAcquisitionProvider();

const mapMachine = useMapStateMachine();
Expand Down Expand Up @@ -131,15 +142,42 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer
setStaleFile,
]);

const fetchLastUpdatedBy = React.useCallback(async () => {
var retrieved = await getLastUpdatedBy(acquisitionFileId);
if (retrieved !== undefined) {
setLastUpdatedBy(retrieved);
} else {
setLastUpdatedBy(null);
}
}, [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 @@ export const AcquisitionContainer: React.FunctionComponent<IAcquisitionContainer

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
Loading

0 comments on commit 977165a

Please sign in to comment.