diff --git a/backend/lcfs/tests/compliance_report/test_compliance_report_views.py b/backend/lcfs/tests/compliance_report/test_compliance_report_views.py index 9741e568d..aa5ca7675 100644 --- a/backend/lcfs/tests/compliance_report/test_compliance_report_views.py +++ b/backend/lcfs/tests/compliance_report/test_compliance_report_views.py @@ -12,6 +12,7 @@ from lcfs.web.api.compliance_report.schema import ( ComplianceReportUpdateSchema, ComplianceReportSummaryUpdateSchema, + ChainedComplianceReportSchema, ) from lcfs.services.s3.client import DocumentService @@ -226,7 +227,9 @@ async def test_get_compliance_report_by_id_success( ) as mock_validate_organization_access: set_mock_user(fastapi_app, [RoleEnum.GOVERNMENT]) - mock_compliance_report = compliance_report_base_schema() + mock_compliance_report = ChainedComplianceReportSchema( + report=compliance_report_base_schema(), chain=[] + ) mock_get_compliance_report_by_id.return_value = mock_compliance_report mock_validate_organization_access.return_value = None @@ -240,7 +243,9 @@ async def test_get_compliance_report_by_id_success( expected_response = json.loads(mock_compliance_report.json(by_alias=True)) assert response.json() == expected_response - mock_get_compliance_report_by_id.assert_called_once_with(1, False) + mock_get_compliance_report_by_id.assert_called_once_with( + 1, False, get_chain=True + ) mock_validate_organization_access.assert_called_once_with(1) diff --git a/backend/lcfs/tests/organization/test_organization_views.py b/backend/lcfs/tests/organization/test_organization_views.py index a98a1a306..3f8cacafc 100644 --- a/backend/lcfs/tests/organization/test_organization_views.py +++ b/backend/lcfs/tests/organization/test_organization_views.py @@ -16,6 +16,7 @@ from lcfs.web.api.organization.validation import OrganizationValidation from lcfs.web.api.compliance_report.services import ComplianceReportServices +from lcfs.web.api.compliance_report.schema import ChainedComplianceReportSchema @pytest.mark.anyio @@ -160,7 +161,8 @@ async def test_export_transactions_for_org_success( ): set_mock_user(fastapi_app, [RoleEnum.SUPPLIER]) - mock_transactions_services.export_transactions.return_value = {"streaming": True} + mock_transactions_services.export_transactions.return_value = { + "streaming": True} fastapi_app.dependency_overrides[TransactionsService] = ( lambda: mock_transactions_services @@ -188,7 +190,8 @@ async def test_create_transfer_success( set_mock_user(fastapi_app, [RoleEnum.SUPPLIER]) organization_id = 1 - url = fastapi_app.url_path_for("create_transfer", organization_id=organization_id) + url = fastapi_app.url_path_for( + "create_transfer", organization_id=organization_id) payload = {"from_organization_id": 1, "to_organization_id": 2} @@ -226,7 +229,8 @@ async def test_update_transfer_success( ): set_mock_user(fastapi_app, [RoleEnum.SUPPLIER]) - url = fastapi_app.url_path_for("update_transfer", organization_id=1, transfer_id=1) + url = fastapi_app.url_path_for( + "update_transfer", organization_id=1, transfer_id=1) payload = {"from_organization_id": 1, "to_organization_id": 2} @@ -274,7 +278,8 @@ async def test_create_compliance_report_success( "create_compliance_report", organization_id=organization_id ) - payload = {"compliance_period": "2024", "organization_id": 1, "status": "status"} + payload = {"compliance_period": "2024", + "organization_id": 1, "status": "status"} mock_organization_validation.create_compliance_report.return_value = None mock_compliance_report_services.create_compliance_report.return_value = { @@ -346,7 +351,8 @@ async def test_get_all_org_reported_years_success( ): set_mock_user(fastapi_app, [RoleEnum.SUPPLIER]) - url = fastapi_app.url_path_for("get_all_org_reported_years", organization_id=1) + url = fastapi_app.url_path_for( + "get_all_org_reported_years", organization_id=1) mock_compliance_report_services.get_all_org_reported_years.return_value = [ {"compliance_period_id": 1, "description": "2024"} @@ -379,20 +385,23 @@ async def test_get_compliance_report_by_id_success( ) # Mock the compliance report service's method - mock_compliance_report_services.get_compliance_report_by_id.return_value = { - "compliance_report_id": 1, - "compliance_period_id": 1, - "compliance_period": {"compliance_period_id": 1, "description": "2024"}, - "organization_id": 1, - "organization": {"organization_id": 1, "name": "org1"}, - "current_status_id": 1, - "current_status": {"compliance_report_status_id": 1, "status": "status"}, - "summary": {"summary_id": 1, "is_locked": False}, - "compliance_report_group_uuid": "uuid", - "version": 0, - "supplemental_initiator": SupplementalInitiatorType.SUPPLIER_SUPPLEMENTAL, - "has_supplemental": False, - } + mock_compliance_report_services.get_compliance_report_by_id.return_value = ChainedComplianceReportSchema( + report={ + "compliance_report_id": 1, + "compliance_period_id": 1, + "compliance_period": {"compliance_period_id": 1, "description": "2024"}, + "organization_id": 1, + "organization": {"organization_id": 1, "name": "org1"}, + "current_status_id": 1, + "current_status": {"compliance_report_status_id": 1, "status": "status"}, + "summary": {"summary_id": 1, "is_locked": False}, + "compliance_report_group_uuid": "uuid", + "version": 0, + "supplemental_initiator": SupplementalInitiatorType.SUPPLIER_SUPPLEMENTAL, + "has_supplemental": False, + }, + chain=[] + ) # Create a mock for the validation service mock_compliance_report_validation = AsyncMock() @@ -412,7 +421,7 @@ async def test_get_compliance_report_by_id_success( # Assertions assert response.status_code == 200 mock_compliance_report_services.get_compliance_report_by_id.assert_awaited_once_with( - 1, apply_masking=True + 1, apply_masking=True, get_chain=True ) mock_compliance_report_validation.validate_organization_access.assert_awaited_once_with( 1 diff --git a/backend/lcfs/web/api/compliance_report/repo.py b/backend/lcfs/web/api/compliance_report/repo.py index 71a26cb3d..194afb8d0 100644 --- a/backend/lcfs/web/api/compliance_report/repo.py +++ b/backend/lcfs/web/api/compliance_report/repo.py @@ -29,7 +29,6 @@ ) from lcfs.web.api.compliance_report.schema import ( ComplianceReportBaseSchema, - ComplianceReportSummarySchema, ComplianceReportSummaryUpdateSchema, ) from lcfs.db.models.compliance.ComplianceReportHistory import ComplianceReportHistory @@ -435,34 +434,61 @@ async def get_compliance_report_by_id(self, report_id: int, is_model: bool = Fal """ Retrieve a compliance report from the database by ID """ - result = ( - ( - await self.db.execute( - select(ComplianceReport) - .options( - joinedload(ComplianceReport.organization), - joinedload(ComplianceReport.compliance_period), - joinedload(ComplianceReport.current_status), - joinedload(ComplianceReport.summary), - joinedload(ComplianceReport.history).joinedload( - ComplianceReportHistory.status - ), - joinedload(ComplianceReport.history).joinedload( - ComplianceReportHistory.user_profile - ), - joinedload(ComplianceReport.transaction), - ) - .where(ComplianceReport.compliance_report_id == report_id) - ) + result = await self.db.execute( + select(ComplianceReport) + .options( + joinedload(ComplianceReport.organization), + joinedload(ComplianceReport.compliance_period), + joinedload(ComplianceReport.current_status), + joinedload(ComplianceReport.summary), + joinedload(ComplianceReport.history).joinedload( + ComplianceReportHistory.status + ), + joinedload(ComplianceReport.history).joinedload( + ComplianceReportHistory.user_profile + ), + joinedload(ComplianceReport.transaction), ) - .unique() - .scalars() - .first() + .where(ComplianceReport.compliance_report_id == report_id) ) + + compliance_report = result.scalars().unique().first() + + if not compliance_report: + return None + if is_model: - return result - else: - return ComplianceReportBaseSchema.model_validate(result) + return compliance_report + + return ComplianceReportBaseSchema.model_validate(compliance_report) + + @repo_handler + async def get_compliance_report_chain(self, group_uuid: str): + result = await self.db.execute( + select(ComplianceReport) + .options( + joinedload(ComplianceReport.organization), + joinedload(ComplianceReport.compliance_period), + joinedload(ComplianceReport.current_status), + joinedload(ComplianceReport.summary), + joinedload(ComplianceReport.history).joinedload( + ComplianceReportHistory.status + ), + joinedload(ComplianceReport.history).joinedload( + ComplianceReportHistory.user_profile + ), + joinedload(ComplianceReport.transaction), + ) + .where(ComplianceReport.compliance_report_group_uuid == group_uuid) + .order_by(ComplianceReport.version.desc()) # Ensure ordering by version + ) + + compliance_reports = result.scalars().unique().all() + + return [ + ComplianceReportBaseSchema.model_validate(report) + for report in compliance_reports + ] @repo_handler async def get_fuel_type(self, fuel_type_id: int) -> FuelType: diff --git a/backend/lcfs/web/api/compliance_report/schema.py b/backend/lcfs/web/api/compliance_report/schema.py index d427ee7d5..0f157be8b 100644 --- a/backend/lcfs/web/api/compliance_report/schema.py +++ b/backend/lcfs/web/api/compliance_report/schema.py @@ -160,6 +160,11 @@ class ComplianceReportBaseSchema(BaseSchema): has_supplemental: bool +class ChainedComplianceReportSchema(BaseSchema): + report: ComplianceReportBaseSchema + chain: Optional[List[ComplianceReportBaseSchema]] = [] + + class ComplianceReportCreateSchema(BaseSchema): compliance_period: str organization_id: int diff --git a/backend/lcfs/web/api/compliance_report/services.py b/backend/lcfs/web/api/compliance_report/services.py index b5490755b..31993bc75 100644 --- a/backend/lcfs/web/api/compliance_report/services.py +++ b/backend/lcfs/web/api/compliance_report/services.py @@ -52,7 +52,8 @@ async def create_compliance_report( report_data.status ) if not draft_status: - raise DataNotFoundException(f"Status '{report_data.status}' not found.") + raise DataNotFoundException( + f"Status '{report_data.status}' not found.") # Generate a new group_uuid for the new report series group_uuid = str(uuid.uuid4()) @@ -193,6 +194,7 @@ def _mask_report_status(self, reports: List) -> List: ComplianceReportStatusEnum.Submitted.value ) report.current_status.compliance_report_status_id = None + masked_reports.append(report) else: masked_reports.append(report) @@ -201,22 +203,45 @@ def _mask_report_status(self, reports: List) -> List: @service_handler async def get_compliance_report_by_id( - self, report_id: int, apply_masking: bool = False - ) -> ComplianceReportBaseSchema: + self, report_id: int, apply_masking: bool = False, get_chain: bool = False + ): """Fetches a specific compliance report by ID.""" report = await self.repo.get_compliance_report_by_id(report_id) if report is None: raise DataNotFoundException("Compliance report not found.") + validated_report = ComplianceReportBaseSchema.model_validate(report) masked_report = ( self._mask_report_status([validated_report])[0] if apply_masking else validated_report ) + history_masked_report = self._mask_report_status_for_history( masked_report, apply_masking ) + if get_chain: + compliance_report_chain = await self.repo.get_compliance_report_chain( + report.compliance_report_group_uuid + ) + + if apply_masking: + # Apply masking to each report in the chain + masked_chain = self._mask_report_status( + compliance_report_chain) + # Apply history masking to each report in the chain + masked_chain = [ + self._mask_report_status_for_history(report, apply_masking) + for report in masked_chain + ] + compliance_report_chain = masked_chain + + return { + "report": history_masked_report, + "chain": compliance_report_chain, + } + return history_masked_report def _mask_report_status_for_history( diff --git a/backend/lcfs/web/api/compliance_report/views.py b/backend/lcfs/web/api/compliance_report/views.py index c25a8568f..de43c0c26 100644 --- a/backend/lcfs/web/api/compliance_report/views.py +++ b/backend/lcfs/web/api/compliance_report/views.py @@ -19,7 +19,9 @@ ComplianceReportBaseSchema, ComplianceReportListSchema, ComplianceReportSummarySchema, - ComplianceReportUpdateSchema, ComplianceReportSummaryUpdateSchema, + ChainedComplianceReportSchema, + ComplianceReportUpdateSchema, + ComplianceReportSummaryUpdateSchema, ) from lcfs.web.api.compliance_report.services import ComplianceReportServices from lcfs.web.api.compliance_report.summary_service import ( @@ -66,12 +68,12 @@ async def get_compliance_reports( pagination.filters.append( FilterModel(field="status", filter="Draft", filter_type="text", type="notEqual") ) - return await service.get_compliance_reports_paginated(pagination) + return await service.get_compliance_reports_paginated(pagination) @router.get( "/{report_id}", - response_model=ComplianceReportBaseSchema, + response_model=ChainedComplianceReportSchema, status_code=status.HTTP_200_OK, ) @view_handler([RoleEnum.GOVERNMENT]) @@ -80,12 +82,16 @@ async def get_compliance_report_by_id( report_id: int, service: ComplianceReportServices = Depends(), validate: ComplianceReportValidation = Depends(), -) -> ComplianceReportBaseSchema: +) -> ChainedComplianceReportSchema: await validate.validate_organization_access(report_id) mask_statuses = not user_has_roles(request.user, [RoleEnum.GOVERNMENT]) - return await service.get_compliance_report_by_id(report_id, mask_statuses) + result = await service.get_compliance_report_by_id( + report_id, mask_statuses, get_chain=True + ) + + return result @router.get( @@ -128,6 +134,7 @@ async def update_compliance_report_summary( report_id, summary_data ) + @view_handler(["*"]) @router.put( "/{report_id}", diff --git a/backend/lcfs/web/api/organization/views.py b/backend/lcfs/web/api/organization/views.py index eb6244051..e175bf756 100644 --- a/backend/lcfs/web/api/organization/views.py +++ b/backend/lcfs/web/api/organization/views.py @@ -33,6 +33,7 @@ ComplianceReportCreateSchema, ComplianceReportListSchema, CompliancePeriodSchema, + ChainedComplianceReportSchema ) from lcfs.web.api.compliance_report.services import ComplianceReportServices from .services import OrganizationService @@ -55,7 +56,8 @@ async def get_org_users( request: Request, organization_id: int, - status: str = Query(default="Active", description="Active or Inactive users list"), + status: str = Query( + default="Active", description="Active or Inactive users list"), pagination: PaginationRequestSchema = Body(..., embed=False), response: Response = None, org_service: OrganizationService = Depends(), @@ -264,7 +266,7 @@ async def get_compliance_reports( ) -> ComplianceReportListSchema: organization_id = request.user.organization.organization_id return await report_service.get_compliance_reports_paginated( - pagination, organization_id, bceid_user = True + pagination, organization_id, bceid_user=True ) @@ -288,7 +290,7 @@ async def get_all_org_reported_years( @router.get( "/{organization_id}/reports/{report_id}", - response_model=ComplianceReportBaseSchema, + response_model=ChainedComplianceReportSchema, status_code=status.HTTP_200_OK, ) @view_handler([RoleEnum.SUPPLIER]) @@ -299,10 +301,10 @@ async def get_compliance_report_by_id( report_id: int = None, report_service: ComplianceReportServices = Depends(), report_validate: ComplianceReportValidation = Depends(), -) -> ComplianceReportBaseSchema: +) -> ChainedComplianceReportSchema: """ Endpoint to get information of a user by ID This endpoint returns the information of a user by ID, including their roles and organization. """ await report_validate.validate_organization_access(report_id) - return await report_service.get_compliance_report_by_id(report_id, apply_masking=True) + return await report_service.get_compliance_report_by_id(report_id, apply_masking=True, get_chain=True) diff --git a/frontend/src/hooks/useComplianceReports.js b/frontend/src/hooks/useComplianceReports.js index 0a79e4023..c28f04fd4 100644 --- a/frontend/src/hooks/useComplianceReports.js +++ b/frontend/src/hooks/useComplianceReports.js @@ -55,7 +55,7 @@ export const useGetComplianceReport = (orgID, reportID, options) => { return useQuery({ queryKey: ['compliance-report', reportID], queryFn: async () => { - return (await client.get(path)) + return (await client.get(path)).data }, ...options }) @@ -132,22 +132,22 @@ export const useComplianceReportDocuments = (parentID, options) => { } export const useCreateSupplementalReport = (reportID, options) => { - const client = useApiService(); - const queryClient = useQueryClient(); - const path = apiRoutes.createSupplementalReport.replace(':reportID', reportID); + const client = useApiService() + const queryClient = useQueryClient() + const path = apiRoutes.createSupplementalReport.replace(':reportID', reportID) return useMutation({ mutationFn: () => client.post(path), onSuccess: (data) => { - queryClient.invalidateQueries(['compliance-reports']); + queryClient.invalidateQueries(['compliance-reports']) if (options && options.onSuccess) { - options.onSuccess(data); + options.onSuccess(data) } }, onError: (error) => { if (options && options.onError) { - options.onError(error); + options.onError(error) } - }, - }); -}; \ No newline at end of file + } + }) +} diff --git a/frontend/src/views/ComplianceReports/EditViewComplianceReport.jsx b/frontend/src/views/ComplianceReports/EditViewComplianceReport.jsx index 28cc739ed..e05a6c55a 100644 --- a/frontend/src/views/ComplianceReports/EditViewComplianceReport.jsx +++ b/frontend/src/views/ComplianceReports/EditViewComplianceReport.jsx @@ -92,9 +92,9 @@ export const EditViewComplianceReport = () => { complianceReportId ) - const currentStatus = reportData?.data?.currentStatus?.status + const currentStatus = reportData?.report.currentStatus?.status const { data: orgData, isLoading } = useOrganization( - reportData?.data?.organizationId + reportData?.report.organizationId ) const { mutate: updateComplianceReport } = useUpdateComplianceReport( complianceReportId, @@ -126,7 +126,7 @@ export const EditViewComplianceReport = () => { t, setModalData, updateComplianceReport, - reportData, + isGovernmentUser, isSigningAuthorityDeclared }), @@ -136,7 +136,7 @@ export const EditViewComplianceReport = () => { t, setModalData, updateComplianceReport, - reportData, + isGovernmentUser, isSigningAuthorityDeclared ] @@ -182,7 +182,7 @@ export const EditViewComplianceReport = () => { {compliancePeriod + ' ' + t('report:complianceReport')} -{' '} - {reportData?.data?.nickname} + {reportData?.report.nickname} { )} {!location.state?.newReport && ( @@ -228,7 +229,10 @@ export const EditViewComplianceReport = () => { )} {!isGovernmentUser && ( - + )} {/* Internal Comments */} {isGovernmentUser && ( diff --git a/frontend/src/views/ComplianceReports/__tests__/EditViewComplianceReports.test.jsx b/frontend/src/views/ComplianceReports/__tests__/EditViewComplianceReports.test.jsx index 48700858c..cb50dbee6 100644 --- a/frontend/src/views/ComplianceReports/__tests__/EditViewComplianceReports.test.jsx +++ b/frontend/src/views/ComplianceReports/__tests__/EditViewComplianceReports.test.jsx @@ -91,10 +91,11 @@ describe('EditViewComplianceReport', () => { }, complianceReport: { data: { - data: { + report: { organizationId: '123', currentStatus: { status: COMPLIANCE_REPORT_STATUSES.DRAFT } - } + }, + chain: [] }, isLoading: false, isError: false @@ -192,9 +193,10 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.SUBMITTED } - } + }, + chain: [] } }, currentUser: { @@ -214,11 +216,12 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.RECOMMENDED_BY_ANALYST } - } + }, + chain: [] } }, currentUser: { @@ -241,11 +244,12 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.RECOMMENDED_BY_MANAGER } - } + }, + chain: [] } }, currentUser: { @@ -268,9 +272,10 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.ASSESSED } - } + }, + chain: [] } }, currentUser: { @@ -290,9 +295,10 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.SUBMITTED } - } + }, + chain: [] } }, currentUser: { data: { isGovernmentUser: false }, hasRoles: () => false } @@ -340,7 +346,10 @@ describe('EditViewComplianceReport', () => { setupMocks({ complianceReport: { data: { - data: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.DRAFT } } + report: { + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.DRAFT } + }, + chain: [] } } }) @@ -366,10 +375,20 @@ describe('EditViewComplianceReport', () => { vi.mocked(useComplianceReportsHook.useGetComplianceReport).mockReturnValue({ data: { - data: { + report: { currentStatus: { status: COMPLIANCE_REPORT_STATUSES.ASSESSED }, history: historyMock - } + }, + chain: [ + { + history: historyMock, + version: 0, + compliancePeriod: { + description: '2024' + }, + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.SUBMITTED } + } + ] }, isLoading: false, isError: false @@ -400,5 +419,4 @@ describe('EditViewComplianceReport', () => { expect(screen.getByLabelText('scroll to top')).toBeInTheDocument() }) }) - }) diff --git a/frontend/src/views/ComplianceReports/components/AssessmentCard.jsx b/frontend/src/views/ComplianceReports/components/AssessmentCard.jsx index 339b22133..8668d0778 100644 --- a/frontend/src/views/ComplianceReports/components/AssessmentCard.jsx +++ b/frontend/src/views/ComplianceReports/components/AssessmentCard.jsx @@ -1,39 +1,70 @@ -import { useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import { List, ListItemText, Stack, Typography } from '@mui/material' +import BCButton from '@/components/BCButton' +import BCTypography from '@/components/BCTypography' import BCWidgetCard from '@/components/BCWidgetCard/BCWidgetCard' -import { timezoneFormatter } from '@/utils/formatters' +import { Role } from '@/components/Role' +import { StyledListItem } from '@/components/StyledListItem' +import { roles } from '@/constants/roles' import { COMPLIANCE_REPORT_STATUSES } from '@/constants/statuses' +import { useCreateSupplementalReport } from '@/hooks/useComplianceReports' +import { useCurrentUser } from '@/hooks/useCurrentUser' import { constructAddress } from '@/utils/constructAddress' -import BCButton from '@/components/BCButton' +import { timezoneFormatter } from '@/utils/formatters' import AssignmentIcon from '@mui/icons-material/Assignment' -import { useCreateSupplementalReport } from '@/hooks/useComplianceReports' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import { List, ListItemText, Stack, Typography, styled } from '@mui/material' +import MuiAccordion from '@mui/material/Accordion' +import MuiAccordionDetails from '@mui/material/AccordionDetails' +import MuiAccordionSummary, { + accordionSummaryClasses +} from '@mui/material/AccordionSummary' import Box from '@mui/material/Box' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' -import { StyledListItem } from '@/components/StyledListItem' -import { roles } from '@/constants/roles' -import { Role } from '@/components/Role' import { FEATURE_FLAGS, isFeatureEnabled } from '@/constants/config.js' -export const AssessmentCard = ({ - orgData, - hasMet, - history, - hasSupplemental, - isGovernmentUser, - currentStatus, - complianceReportId, - alertRef -}) => { - const { t } = useTranslation(['report']) - const navigate = useNavigate() +const Accordion = styled((props) => ( + +))(() => ({ + border: `none`, + '&::before': { + display: 'none' + } +})) + +const AccordionSummary = styled((props) => ( + } + {...props} + /> +))(() => ({ + minHeight: 'unset', + padding: 0, + flexDirection: 'row-reverse', + [`& .${accordionSummaryClasses.content}`]: { + margin: 0 + }, + [`& .${accordionSummaryClasses.expanded}`]: { + margin: 0 + } +})) +const AccordionDetails = styled(MuiAccordionDetails)(() => ({ + paddingLeft: '1rem', + paddingTop: 0, + paddingBottom: 0 +})) + +const HistoryCard = ({ report }) => { + const { data: currentUser } = useCurrentUser() + const isGovernmentUser = currentUser?.isGovernmentUser + const { t } = useTranslation(['report']) const filteredHistory = useMemo(() => { - if (!history || history.length === 0) { + if (!report.history || report.history.length === 0) { return [] } // Sort the history array by date in descending order - return [...history] + return [...report.history] .sort((a, b) => { return new Date(b.createDate) - new Date(a.createDate) }) @@ -46,12 +77,61 @@ export const AssessmentCard = ({ } return item }) - .filter( - (item) => - item.status.status !== COMPLIANCE_REPORT_STATUSES.DRAFT || - hasSupplemental - ) - }, [history, isGovernmentUser, hasSupplemental]) + .filter((item) => item.status.status !== COMPLIANCE_REPORT_STATUSES.DRAFT) + }, [isGovernmentUser, report.history]) + return ( + + } + aria-controls="panel1-content" + > + + {report.version === 0 + ? `${report.compliancePeriod.description} Compliance Report` + : report.nickname} + : {report.currentStatus.status} + + + + + {filteredHistory.map((item, index) => ( + + + + + + ))} + + + + ) +} + +export const AssessmentCard = ({ + orgData, + hasMet, + hasSupplemental, + isGovernmentUser, + currentStatus, + complianceReportId, + alertRef, + chain +}) => { + const { t } = useTranslation(['report']) + const navigate = useNavigate() const { mutate: createSupplementalReport, isLoading } = useCreateSupplementalReport(complianceReportId, { @@ -166,7 +246,7 @@ export const AssessmentCard = ({ )} - {!!filteredHistory.length && ( + {!!chain.length && ( <> {t('report:reportHistory')} - - {filteredHistory.map((item, index) => ( - - - - - - ))} - + {chain.map((report) => ( + + ))} )} + {isFeatureEnabled(FEATURE_FLAGS.SUPPLEMENTAL_REPORTING) && currentStatus === COMPLIANCE_REPORT_STATUSES.ASSESSED && ( diff --git a/frontend/src/views/ComplianceReports/components/__tests__/AssessmentCard.test.jsx b/frontend/src/views/ComplianceReports/components/__tests__/AssessmentCard.test.jsx index dbecd910e..8cfda28ad 100644 --- a/frontend/src/views/ComplianceReports/components/__tests__/AssessmentCard.test.jsx +++ b/frontend/src/views/ComplianceReports/components/__tests__/AssessmentCard.test.jsx @@ -77,12 +77,12 @@ describe('AssessmentCard', () => { , { wrapper } ) @@ -96,12 +96,12 @@ describe('AssessmentCard', () => { , { wrapper } ) @@ -112,16 +112,27 @@ describe('AssessmentCard', () => { }) it('renders report history when history is available', async () => { + const mockChain = [ + { + history: mockHistory, + version: 0, + compliancePeriod: { + description: '2024' + }, + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.SUBMITTED } + } + ] + render( , { wrapper } ) @@ -136,7 +147,8 @@ describe('AssessmentCard', () => { ).toBeInTheDocument() }) }) - it('filters out DRAFT status from history except when hasSupplemental is true', async () => { + + it('filters out DRAFT status from history', async () => { const historyWithDraft = [ ...mockHistory, { @@ -146,31 +158,14 @@ describe('AssessmentCard', () => { } ] - render( - , - { wrapper } - ) - await waitFor(() => { - expect(screen.getByText(/Alice Wong/)).toBeInTheDocument() - }) - }) - - it('filters out DRAFT status from history', async () => { - const historyWithDraft = [ - ...mockHistory, + const mockChain = [ { - status: { status: COMPLIANCE_REPORT_STATUSES.DRAFT }, - createDate: '2024-08-01', - userProfile: { firstName: 'Alice', lastName: 'Wong' } + history: historyWithDraft, + version: 0, + compliancePeriod: { + description: '2024' + }, + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.SUBMITTED } } ] @@ -178,12 +173,12 @@ describe('AssessmentCard', () => { , { wrapper } ) @@ -201,16 +196,26 @@ describe('AssessmentCard', () => { }) it('changes status to "AssessedBy" when the user is not a government user', async () => { + const mockChain = [ + { + history: mockHistory, + version: 0, + compliancePeriod: { + description: '2024' + }, + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.ASSESSED } + } + ] render( , { wrapper } ) @@ -222,16 +227,26 @@ describe('AssessmentCard', () => { }) it('displays organization information', async () => { + const mockChain = [ + { + history: mockHistory, + version: 0, + compliancePeriod: { + description: '2024' + }, + currentStatus: { status: COMPLIANCE_REPORT_STATUSES.ASSESSED } + } + ] render( , { wrapper } )