diff --git a/tdrs-backend/tdpservice/data_files/serializers.py b/tdrs-backend/tdpservice/data_files/serializers.py index a279d3d5a..2460c1366 100644 --- a/tdrs-backend/tdpservice/data_files/serializers.py +++ b/tdrs-backend/tdpservice/data_files/serializers.py @@ -78,7 +78,7 @@ def get_has_error(self, obj): def get_latest_reparse_file_meta(self, instance): """Return related reparse_file_metas, ordered by finished_at decending.""" - reparse_file_metas = instance.reparse_file_metas.all().order_by('-finished_at') + reparse_file_metas = instance.reparse_file_metas.all().exclude(finished_at=None).order_by('-finished_at') if reparse_file_metas.count() > 0: return ReparseFileMetaSerializer(reparse_file_metas.first(), many=False, read_only=True).data return None diff --git a/tdrs-frontend/src/assets/Reports.scss b/tdrs-frontend/src/assets/Reports.scss index 58b89ce6c..946e9ece6 100644 --- a/tdrs-frontend/src/assets/Reports.scss +++ b/tdrs-frontend/src/assets/Reports.scss @@ -42,6 +42,20 @@ cursor: pointer; } +.reprocessed { + background-color: transparent; + border: none; + color: #264A64; + text-align: left; + text-decoration: underline; + margin: 0; + padding: 0; +} + +.reprocessed:hover { + cursor: pointer; +} + .usa-table caption { width: 100%; } \ No newline at end of file diff --git a/tdrs-frontend/src/components/Modal/Modal.jsx b/tdrs-frontend/src/components/Modal/Modal.jsx index eb2b0d87c..323639f9b 100644 --- a/tdrs-frontend/src/components/Modal/Modal.jsx +++ b/tdrs-frontend/src/components/Modal/Modal.jsx @@ -24,18 +24,17 @@ const Modal = ({ title, message, buttons = [], isVisible = false }) => { (b) => b.key === selectedButtonKey ) + const btnIdxMinOne = Math.max(0, buttons.length - 1) if (shiftKey) { // go backward + const selectedIdxMinOne = Math.max(0, selectedButtonIndex - 1) nextButtonIndex = - selectedButtonIndex >= 0 - ? selectedButtonIndex - 1 - : buttons.length - 1 + selectedButtonIndex >= 0 ? selectedIdxMinOne : btnIdxMinOne } else { nextButtonIndex = - selectedButtonIndex < buttons.length - 1 ? selectedButtonIndex + 1 : 0 + selectedButtonIndex < btnIdxMinOne ? selectedButtonIndex + 1 : 0 } } - const nextButtonKey = buttons[nextButtonIndex].key const nextButton = modalRef.current.querySelector( `button[buttonkey="${nextButtonKey}"]` @@ -45,7 +44,6 @@ const Modal = ({ title, message, buttons = [], isVisible = false }) => { const onKeyDown = (e) => { const { key, shiftKey } = e - switch (key) { case 'Tab': onTabPressed(shiftKey) diff --git a/tdrs-frontend/src/components/Reports/Reports.jsx b/tdrs-frontend/src/components/Reports/Reports.jsx index 0ac0f3d98..19e0f3ec8 100644 --- a/tdrs-frontend/src/components/Reports/Reports.jsx +++ b/tdrs-frontend/src/components/Reports/Reports.jsx @@ -17,6 +17,7 @@ import { fetchSttList } from '../../actions/sttList' import Modal from '../Modal' import SegmentedControl from '../SegmentedControl' import SubmissionHistory from '../SubmissionHistory' +import ReprocessedModal from '../SubmissionHistory/ReprocessedModal' import { selectPrimaryUserRole } from '../../selectors/auth' /** @@ -55,6 +56,9 @@ function Reports() { const [formValidation, setFormValidationState] = useState({}) const [touched, setTouched] = useState({}) + const [reprocessedModalVisible, setReprocessedModalVisible] = useState(false) + const [reprocessedDate, setReprocessedDate] = useState('') + const quarters = { Q1: 'Quarter 1 (October - December)', Q2: 'Quarter 2 (January - March)', @@ -472,6 +476,10 @@ function Reports() { stt: stt, file_type: fileTypeInputValue, }} + reprocessedState={{ + setModalVisible: setReprocessedModalVisible, + setDate: setReprocessedDate, + }} /> )} @@ -499,6 +507,11 @@ function Reports() { }, ]} /> + ) } diff --git a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx index 56b1b5ecc..13dfe1d2d 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx @@ -8,6 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' +import { ReprocessedButton } from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -24,15 +25,20 @@ const MonthSubRow = ({ data }) => ) -const CaseAggregatesRow = ({ file }) => { +const CaseAggregatesRow = ({ file, reprocessedState }) => { const dispatch = useDispatch() - + const reprocessedDate = formatDate(getReprocessedDate(file)) return ( <> {formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && <>} + {hasReparsed(file) && ( + + )} @@ -75,7 +81,7 @@ const CaseAggregatesRow = ({ file }) => { ) } -export const CaseAggregatesTable = ({ files }) => ( +export const CaseAggregatesTable = ({ files, reprocessedState }) => ( <> @@ -107,7 +113,11 @@ export const CaseAggregatesTable = ({ files }) => ( {files.map((file) => ( - + ))} diff --git a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx new file mode 100644 index 000000000..5d1a7eaaa --- /dev/null +++ b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx @@ -0,0 +1,53 @@ +import React, { useState } from 'react' +import Modal from '../Modal' + +const Message = ({ date }) => { + return ( + <> + We've reprocessed your submission with updated validation criteria, based + on system improvements, to improve accuracy of error reports. No changes + have been made to your original data submission. +
+
+ Data was reprocessed on: {date} + + ) +} + +export const ReprocessedButton = ({ date, reprocessedState }) => { + return ( +
+ +
+ ) +} + +const ReprocessedModal = ({ date, isVisible, setModalVisible }) => { + const message = + return ( + { + setModalVisible(false) + }, + }, + ]} + /> + ) +} + +export default ReprocessedModal diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx index b768fb7cd..c10e8daec 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx @@ -9,7 +9,12 @@ import { useState } from 'react' import { CaseAggregatesTable } from './CaseAggregatesTable' import { TotalAggregatesTable } from './TotalAggregatesTable' -const SectionSubmissionHistory = ({ section, label, files }) => { +const SectionSubmissionHistory = ({ + section, + label, + files, + reprocessedState, +}) => { const pageSize = 5 const [resultsPage, setResultsPage] = useState(1) @@ -30,7 +35,10 @@ const SectionSubmissionHistory = ({ section, label, files }) => { {files && files.length > 0 ? ( - + ) : ( No data available. )} @@ -57,9 +65,13 @@ SectionSubmissionHistory.propTypes = { year: PropTypes.string, }), files: PropTypes.array, + reprocessedState: PropTypes.shape({ + setModalVisible: PropTypes.func, + setDate: PropTypes.func, + }), } -const SubmissionHistory = ({ filterValues }) => { +const SubmissionHistory = ({ filterValues, reprocessedState }) => { const dispatch = useDispatch() const [hasFetchedFiles, setHasFetchedFiles] = useState(false) const { files } = useSelector((state) => state.reports) @@ -95,6 +107,7 @@ const SubmissionHistory = ({ filterValues }) => { label={section} filterValues={filterValues} files={files.filter((f) => f.section.includes(section))} + reprocessedState={reprocessedState} /> ) })} diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index eda2d13b8..34bebed53 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -22,10 +22,22 @@ describe('SubmissionHistory', () => { file_type: 'TANF', } - const setup = (store = appStore, filterValues = defaultFilterValues) => + const defaultReprocessedState = { + setDate: () => {}, + setModalVisible: () => {}, + } + + const setup = ( + store = appStore, + filterValues = defaultFilterValues, + reprocessedState = defaultReprocessedState + ) => render( - + ) @@ -474,4 +486,42 @@ describe('SubmissionHistory', () => { } } ) + + it('Shows Reprocessed button', () => { + const state = { + reports: { + files: [ + { + id: '123', + fileName: 'test1.txt', + fileType: 'TANF', + quarter: 'Q1', + section: 'Active Case Data', + uuid: '123-4-4-321', + year: '2023', + s3_version_id: '321-0-0-123', + createdAt: '12/12/2012 12:12', + submittedBy: 'test@teamraft.com', + latest_reparse_file_meta: { + finished: true, + success: true, + started_at: '2024-12-09T18:38:14+0000', + finished_at: '2024-12-09T18:38:16+0000', + }, + }, + ], + }, + } + + const store = appConfigureStore(state) + const dispatch = jest.fn(store.dispatch) + store.dispatch = dispatch + + setup(store) + + expect(screen.queryByText('test1.txt')).toBeInTheDocument() + + const reprocessedBtn = screen.queryByText('Reprocessed ⓘ') + expect(reprocessedBtn).toBeInTheDocument() + }) }) diff --git a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx index 2d05fba6c..913ed337b 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx @@ -8,6 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' +import { ReprocessedButton } from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -22,15 +23,20 @@ const MonthSubRow = ({ data }) => ) -const TotalAggregatesRow = ({ file }) => { +const TotalAggregatesRow = ({ file, reprocessedState }) => { const dispatch = useDispatch() - + const reprocessedDate = formatDate(getReprocessedDate(file)) return ( <> @@ -95,7 +101,11 @@ export const TotalAggregatesTable = ({ files }) => ( {files.map((file) => ( - + ))} diff --git a/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx b/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx index 5bb71d0e4..2301485fe 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx @@ -31,7 +31,8 @@ export const hasReparsed = (f) => f.latest_reparse_file_meta.finished_at && f.latest_reparse_file_meta.finished_at !== null -export const getReprocessedDate = (f) => f.latest_reparse_file_meta.finished_at +export const getReprocessedDate = (f) => + f?.latest_reparse_file_meta?.finished_at export const getErrorReportStatus = (file) => { if (
{`Section ${section} - ${label}`}
{formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && <>} + {hasReparsed(file) && ( + + )} @@ -69,7 +75,7 @@ const TotalAggregatesRow = ({ file }) => { ) } -export const TotalAggregatesTable = ({ files }) => ( +export const TotalAggregatesTable = ({ files, reprocessedState }) => ( <>