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 (
+
+ {
+ reprocessedState.setDate(date)
+ reprocessedState.setModalVisible(true)
+ }}
+ >
+ Reprocessed ⓘ
+
+
+ )
+}
+
+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 }) => {
{`Section ${section} - ${label}`}
{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 (
<>
{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 }) => (
<>
@@ -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 (