-
+
+ Select
+
+
+
+
+
+
+
+
+
+
+ `;
+}
+
+const setupContinueNavigationHandler = () => {
+ const continueBtn = document.querySelector('.continueButton');
+ const selectButton = document.querySelector('.selectButton');
+ if (!continueBtn || !selectButton) return;
+
+ continueBtn.addEventListener('click', () => {
+ const selectedButtonType = selectButton.getAttribute('data-tool');
+ if (!selectedButtonType) return;
+
+ if (selectedButtonType === 'verificationCorrections') {
+ window.location.hash = '#verificationCorrectionsTool';
+ } else if (selectedButtonType === 'surveyReset') {
+ window.location.hash = '#surveyResetTool';
+ } else if (selectedButtonType === 'incentiveEligibility') {
+ window.location.hash = '#incentiveEligibilityTool';
+ }
+ });
+}
+
+const setupDropdownSelectionHandler = () => {
+ // get dropdown menu options element
+ const dropdownMenu = document.getElementById('dropdownToolsMenu');
+ const dropdownOptions = dropdownMenu.querySelectorAll('.dropdown-item');
+ const selectButton = document.querySelector('.selectButton');
+ const continueButton = document.querySelector('.continueButton');
+
+ if (!dropdownMenu || !dropdownOptions || !selectButton || !continueButton) return;
+
+ for (let option of dropdownOptions) {
+ option.addEventListener('click', (e) => {
+ const selectedText = e.target.textContent.trim();
+ const selectedToolType = e.target.getAttribute('data-tool');
+
+ if (selectedToolType) {
+ selectButton.textContent = selectedText;
+ continueButton.classList.remove('disabled');
+ selectButton.setAttribute('data-tool', selectedToolType);
+ } else {
+ selectButton.textContent = 'Select';
+ continueButton.classList.add('disabled');
+ selectButton.setAttribute('data-tool', '');
+ }
+ });
+ }
+};
\ No newline at end of file
diff --git a/src/dataCorrectionsTool/incentiveEligibilityTool.js b/src/dataCorrectionsTool/incentiveEligibilityTool.js
new file mode 100644
index 0000000..e4a65d1
--- /dev/null
+++ b/src/dataCorrectionsTool/incentiveEligibilityTool.js
@@ -0,0 +1,375 @@
+import fieldMapping from '../fieldToConceptIdMapping.js';
+import { dashboardNavBarLinks, removeActiveClass } from '../navigationBar.js';
+import { renderParticipantHeader } from '../participantHeader.js';
+import { handleBackToToolSelect, displayDataCorrectionsNavbar, setActiveDataCorrectionsTab } from './dataCorrectionsHelpers.js';
+import { showAnimation, hideAnimation, baseAPI, getIdToken, triggerNotificationBanner, formatUTCDate } from '../utils.js';
+import { findParticipant } from '../participantLookup.js';
+
+let participantPaymentRound = null;
+let isEligibleForIncentiveUpdate = null;
+let selectedDateOfEligibility = null; // YYYY-MM-DD
+
+const conceptIdToPaymentRoundMapping = {
+ 266600170: 'baseline',
+}
+
+export const setupIncentiveEligibilityToolPage = (participant) => {
+ if (participant !== undefined) {
+ const isParent = localStorage.getItem('isParent');
+ document.getElementById('navBarLinks').innerHTML = dashboardNavBarLinks(isParent);
+ removeActiveClass('nav-link', 'active');
+ document.getElementById('participantVerificationBtn').classList.add('active');
+ mainContent.innerHTML = renderIncentiveEligibilityToolContent(participant);
+ handlePaymentRoundSelect(participant);
+ handleBackToToolSelect();
+ clearPaymentRoundSelect();
+ toggleSubmitButton();
+ setupModalContent();
+ setActiveDataCorrectionsTab();
+ handleDatePickerSelection();
+ }
+};
+
+
+const renderIncentiveEligibilityToolContent = (participant) => {
+ return `
+
+
+ ${renderParticipantHeader(participant)}
+ ${displayDataCorrectionsNavbar()}
+
+
+
+
+
+
Data Corrections Tool
+
+ Note: This tool should only be used to make corrections to participant data post-verification. All changes need to be approved by the CCC before being applied to the participant record via this tool.
+
+
+
+
+
+
+
+
+
+ To update the incentive eligibility for this participant, please choose the payment round and set the date of eligibility below. By clicking submit, the incentive eligibility status will be updated from 'not eligible' to 'eligible'.
+
+
+
+
Payment Round:
+
+
+ Select
+
+
+
+
+
+
+
+
+
+
Current Incentive Eligibility Status:
+
Incentive Eligibility Status:
+
Date of Eligibility:
+
+
+
+
+
+
+
+
+
+ <- Back
+ Clear
+
+
+ Submit
+
+
+
+
+
+
+
+
+
+ `;
+};
+
+
+/**
+ * Handles the payment round selection dropdown conetent
+*/
+const handlePaymentRoundSelect = (participant) => {
+ const paymentRoundElement = document.getElementById('dropdownPaymentMenu');
+ const dropdownPaymentOptions = paymentRoundElement.children;
+ const incentiveStatusText = document.getElementById('incentiveStatusText');
+ const isIncentiveEligibleNote = document.getElementById('isIncentiveEligibleNote');
+ const selectButton = document.querySelector('.selectButton');
+ const participantConnectId = participant?.["Connect_ID"];
+ const query = `connectId=${participantConnectId}`
+
+ if (!paymentRoundElement ||
+ !dropdownPaymentOptions ||
+ !incentiveStatusText ||
+ !isIncentiveEligibleNote ||
+ !selectButton) return;
+
+ const { paymentRound, baselinePayment, eligiblePayment, norcPaymentEligibility, no } = fieldMapping;
+
+ for (let option of dropdownPaymentOptions) {
+ option.addEventListener('click', async (e) => { // TODO: Add gaurd to prevent multiple event listeners from being added
+ participantPaymentRound = e.target.dataset.payment;
+ if (participantPaymentRound === fieldMapping['baseline'].toString()) {
+ selectButton.textContent = e.target.textContent;
+ try {
+ showAnimation();
+ const response = await findParticipant(query);
+ hideAnimation();
+ const participantData = response.data[0];
+ localStorage.setItem('participant', JSON.stringify(participantData));
+
+ const isNORCPaymentEligible = participantData?.[paymentRound]?.[baselinePayment]?.[norcPaymentEligibility] === no;
+ const isIncentiveEligible = participantData?.[paymentRound]?.[baselinePayment]?.[eligiblePayment] === no;
+ const isEligibleForIncentiveUpdate = isNORCPaymentEligible && isIncentiveEligible;
+
+ if (isEligibleForIncentiveUpdate) {
+ toggleSubmitButton(isEligibleForIncentiveUpdate);
+ displaySetDateOfEligibilityContent();
+ setIncentiveEligibleInputDefaultValue();
+ handleParticipantPaymentTextContent(participantData, isEligibleForIncentiveUpdate);
+ confirmIncentiveEligibilityUpdate(participant);
+ dateOfEligibilityInput.disabled = false;
+ } else {
+ toggleSubmitButton();
+ handleParticipantPaymentTextContent(participantData, isEligibleForIncentiveUpdate);
+ }
+ setupModalContent(participantPaymentRound);
+ } catch (error) {
+ console.error(`Failed to fetch participant data for Connect ID ${participantConnectId}: `, error);
+ }
+ } else {
+ toggleSubmitButton();
+ participantPaymentRound = null;
+ isEligibleForIncentiveUpdate = null;
+ selectButton.textContent = e.target.textContent;
+ removeSetDateOfEligibilityContent();
+ isIncentiveEligibleNote.innerHTML = ``;
+ handleParticipantPaymentTextContent(participant, isEligibleForIncentiveUpdate);
+ }
+ });
+ }
+};
+
+const handleParticipantPaymentTextContent = (participant, isEligibleForIncentiveUpdate) => {
+ const incentiveStatusText = document.getElementById('incentiveStatusText');
+ const isIncentiveEligibleNote = document.getElementById('isIncentiveEligibleNote');
+ const dateOfEligibilityText = document.getElementById('dateOfEligibilityText');
+ if (!incentiveStatusText || !isIncentiveEligibleNote || !dateOfEligibilityText) return;
+
+ const { paymentRound, baseline, eligiblePaymentRoundTimestamp } = fieldMapping;
+
+ if (isEligibleForIncentiveUpdate) {
+ incentiveStatusText.textContent = 'Incentive Eligibility Status: Not Eligible';
+ dateOfEligibilityText.textContent = 'Date of Eligibility: N/A';
+ isIncentiveEligibleNote.innerHTML = ``;
+
+ } else if (isEligibleForIncentiveUpdate === false) {
+ incentiveStatusText.textContent = 'Incentive Eligibility Status: Eligibile';
+ dateOfEligibilityText.textContent = `Date of Eligibility: ${formatUTCDate(participant?.[paymentRound]?.[baseline]?.[eligiblePaymentRoundTimestamp])}`; // TODO: Add flexibility for other payment rounds
+ isIncentiveEligibleNote.innerHTML = ` This participant is already incentive eligible. The eligibility status cannot be updated. `;
+
+ } else {
+ incentiveStatusText.textContent = 'Incentive Eligibility Status: ';
+ dateOfEligibilityText.textContent = 'Date of Eligibility:';
+ isIncentiveEligibleNote.innerHTML = ``;
+ }
+};
+
+const setIncentiveEligibleInputDefaultValue = () => {
+ const dateOfEligibilityInput = document.getElementById('dateOfEligibilityInput');
+ if (dateOfEligibilityInput) {
+ const currentDate = new Date().toLocaleDateString("en-CA", {timeZone:"America/New_York"}); // MM/DD/YYYY
+ dateOfEligibilityInput.value = currentDate;
+ dateOfEligibilityInput.max = currentDate;
+ }
+};
+
+const convertToISO8601 = (dateString) => {
+ const date = new Date(dateString);
+ return date.toISOString();
+};
+
+/**
+ * Clears the payment round selection back to default and clears the text content
+ */
+const clearPaymentRoundSelect = () => {
+ const clearButton = document.getElementById('clearPaymentRoundButton');
+ const isIncentiveEligibleNote = document.getElementById('isIncentiveEligibleNote');
+ const selectButton = document.querySelector('.selectButton');
+ if (!clearButton || !isIncentiveEligibleNote || !selectButton) return;
+
+ clearButton.addEventListener('click', () => {
+ setParticipantPaymentRound();
+ removeSetDateOfEligibilityContent();
+ });
+};
+
+const setParticipantPaymentRound = () => {
+ const clearButton = document.getElementById('clearPaymentRoundButton');
+ const isIncentiveEligibleNote = document.getElementById('isIncentiveEligibleNote');
+ const selectButton = document.querySelector('.selectButton');
+ const incentiveStatusText = document.getElementById('incentiveStatusText');
+ const dateOfEligibilityText = document.getElementById('dateOfEligibilityText');
+ if (!clearButton || !isIncentiveEligibleNote || !selectButton || !incentiveStatusText || !dateOfEligibilityText) return;
+
+ isIncentiveEligibleNote.textContent = '';
+ dateOfEligibilityText.textContent = 'Date of Eligibility:';
+ selectButton.textContent = ' Select ';
+ participantPaymentRound = null;
+ setIncentiveEligibleInputDefaultValue();
+ incentiveStatusText.textContent = 'Incentive Eligibility Status: ';
+};
+
+const toggleSubmitButton = (isEligibleForIncentiveUpdate) => {
+ const submitButton = document.getElementById('submitButton');
+ if (!submitButton) return;
+ if (isEligibleForIncentiveUpdate) {
+ submitButton.removeAttribute('disabled');
+ } else {
+ submitButton.disabled = true;
+ }
+};
+
+const confirmIncentiveEligibilityUpdate = (participant) => {
+ const confirmButton = document.getElementById('confirmUpdateEligibility');
+ const { paymentRound, baseline, eligiblePaymentRoundTimestamp } = fieldMapping;
+
+ if (confirmButton && dateOfEligibilityInput) {
+ confirmButton.addEventListener('click', async (e) => {
+ const confirmUpdateEligibilityButton = document.getElementById('confirmUpdateEligibility');
+ const selectedDateValue = selectedDateOfEligibility ? convertToISO8601(selectedDateOfEligibility) : convertToISO8601(dateOfEligibilityInput.value);
+ if (confirmUpdateEligibilityButton) {
+ try {
+ const updateResponse = await updateParticipantIncentiveEligibility(participant, participantPaymentRound, selectedDateValue);
+ const currentParticipantData = updateResponse.data;
+
+ if (updateResponse.code === 200) {
+ triggerNotificationBanner("Participant incentive eligibility status updated successfully!", "success" ,14000);
+ localStorage.setItem('participant', JSON.stringify(currentParticipantData));
+ document.getElementById('incentiveStatusText').textContent = 'Incentive Eligibility Status: Eligible';
+ document.getElementById('isIncentiveEligibleNote').innerHTML = ` This participant is already incentive eligible. The eligibility status cannot be updated. `;
+ document.getElementById('dateOfEligibilityText').textContent = `Date of Eligibility: ${formatUTCDate(currentParticipantData?.[paymentRound]?.[baseline]?.[eligiblePaymentRoundTimestamp])}`; // TODO: Add flexibility for other payment rounds
+ removeSetDateOfEligibilityContent();
+ toggleSubmitButton();
+ }
+ } catch (error) {
+ console.error("Failed to update participant incentive eligibility: ", error);
+ triggerNotificationBanner(`${error.message}`, 'danger', 14000);
+ }
+ }
+ });
+ }
+}
+
+const setupModalContent = (participantPaymentRound) => {
+ const paymentRoundType = conceptIdToPaymentRoundMapping[participantPaymentRound];
+ const modalBody = document.querySelector('.modal-body');
+ if (!modalBody) return;
+ modalBody.textContent = `Are you sure you want to update the participant's incentive eligibility status for ${paymentRoundType}?`;
+};
+
+/**
+ * Handles user input choosing from the date picker and sets selectedDateOfEligibility value
+ *
+*/
+const handleDatePickerSelection = () => {
+ const datePicker = document.getElementById("dateOfEligibilityInput");
+ if (datePicker) {
+ datePicker.addEventListener("change", (e) => {
+ selectedDateOfEligibility = e.target.value; // YYYY-MM-DD
+ })
+ }
+};
+
+const displaySetDateOfEligibilityContent = () => {
+ const setDateOfEligibilityContainer = document.getElementById('setDateOfEligibilityContainer');
+ if (setDateOfEligibilityContainer) {
+ setDateOfEligibilityContainer.innerHTML = `Set Date of Eligibility:
+ `;
+ }
+};
+
+const removeSetDateOfEligibilityContent = () => {
+ const setDateOfEligibilityContainer = document.getElementById('setDateOfEligibilityContainer');
+ if (setDateOfEligibilityContainer) {
+ setDateOfEligibilityContainer.innerHTML = '';
+ }
+};
+
+/**
+ * Update participant incentive eligibility ands returns updated data on success
+ * Async function to update participant incentive eligibility
+ * @param {Object} participant - Participant object
+ * @param {String} selectedPaymentRound - Selected payment round
+ * @param {String} selectedDateValue - Selected date of eligibility
+ * @returns {Promise<{
+ * code: number,
+ * data: Object,
+ * message?: string
+ * }>} Response object - { code: 200, data: {100767870,...} }
+*/
+const updateParticipantIncentiveEligibility = async (participant, selectedPaymentRound, selectedDateValue) => {
+ const connectId = participant['Connect_ID'];
+
+ try {
+ const idToken = await getIdToken();
+ const response = await fetch(`${baseAPI}/dashboard?api=updateParticipantIncentiveEligibility`, {
+ method: "POST",
+ headers: {
+ Authorization: "Bearer " + idToken,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ connectId: connectId,
+ currentPaymentRound: selectedPaymentRound,
+ dateOfEligibilityInput: selectedDateValue // ISO8601 date format
+ }),
+ });
+ if (!response.ok) {
+ const error = (response.status + " Error" + ": " + (await response.json()).message);
+ throw new Error(error);
+ }
+
+ const responseObj = await response.json();
+ return responseObj;
+ } catch (error) {
+ console.error("Failed to update participant incentive eligibility: ", error);
+ throw error;
+ }
+};
\ No newline at end of file
diff --git a/src/dataCorrectionsTool/surveyResetTool.js b/src/dataCorrectionsTool/surveyResetTool.js
new file mode 100644
index 0000000..2a35d4e
--- /dev/null
+++ b/src/dataCorrectionsTool/surveyResetTool.js
@@ -0,0 +1,307 @@
+import fieldMapping from '../fieldToConceptIdMapping.js';
+import { dashboardNavBarLinks, removeActiveClass } from '../navigationBar.js';
+import { renderParticipantHeader } from '../participantHeader.js';
+import { findParticipant } from '../participantLookup.js';
+import { baseAPI, getIdToken, hideAnimation, showAnimation } from '../utils.js';
+import { handleBackToToolSelect, displayDataCorrectionsNavbar, setActiveDataCorrectionsTab } from './dataCorrectionsHelpers.js';
+import { triggerNotificationBanner } from '../utils.js';
+
+let selectedSurvey = null;
+
+const statusMapping = {
+ "972455046": "Not Started",
+ "615768760": "Started",
+ "231311385": "Completed",
+};
+
+const surveyModalBody = {
+ [fieldMapping.ssnStatusFlag]: "Are you sure you want to reset the survey status for the SSN survey?",
+};
+
+export const setupSurveyResetToolPage = (participant) => {
+ if (participant !== undefined) {
+ const isParent = localStorage.getItem('isParent');
+ document.getElementById('navBarLinks').innerHTML = dashboardNavBarLinks(isParent);
+ removeActiveClass('nav-link', 'active');
+ document.getElementById('participantVerificationBtn').classList.add('active');
+ mainContent.innerHTML = renderDataCorrectionsSelectionContent(participant);
+ handleSurveyTypeChange(participant);
+ handleBackToToolSelect();
+ clearSurveySelection();
+ submitSurveyStatusReset();
+ disableSubmitButton();
+ setActiveDataCorrectionsTab();
+ }
+};
+
+const renderDataCorrectionsSelectionContent = (participant) => {
+ return `
+
+
+ ${renderParticipantHeader(participant)}
+ ${displayDataCorrectionsNavbar()}
+
+
+
+
+
+
Data Corrections Tool
+
+ Note: This tool should only be used to make corrections to participant data post-verification. All changes need to be approved by the CCC before being applied to the participant record via this tool.
+
+
+
+
+
+
+
+
+ Please select the survey to be reset below. A survey reset means that the survey data and answers will be permanently deleted , and the survey status flag will be reset to 'not started'.
+
+
+
Update Survey:
+
+
+ Select
+
+
+
+
+
Current Survey Status:
+
Survey Name:
+
Survey Status:
+
+
+
+
+
+
+
+
+ <- Back
+ Clear
+
+
+ Submit
+
+
+
+
+
+
+
+
+
+ `;
+};
+
+
+const handleSurveyTypeChange = (participant) => {
+ const surveyDropdown = document.getElementById('dropdownSurveyMenu');
+ const dropdownSurveyOptions = surveyDropdown?.children;
+ const selectButton = document.querySelector('.selectButton');
+ if (!surveyDropdown || !dropdownSurveyOptions || !selectButton) return;
+
+ const participantConnectId = participant['Connect_ID'];
+ const { ssnStatusFlag, notStarted } = fieldMapping;
+ let query;
+
+ for (let option of dropdownSurveyOptions) {
+ option.addEventListener('click', async (e) => { // TODO: Add gaurd to prevent multiple event listeners from being added
+ selectedSurvey = Number(e.target.dataset.survey);
+
+ if (selectedSurvey === ssnStatusFlag) {
+ selectButton.textContent = e.target.textContent;
+ try {
+ query = `connectId=${participantConnectId}`
+ showAnimation();
+ const response = await findParticipant(query);
+ hideAnimation();
+ const participantData = response.data[0];
+ localStorage.setItem('participant', JSON.stringify(participantData));
+
+ if (participantData[ssnStatusFlag] === notStarted) {
+ displayAlreadyResetNote();
+ disableSubmitButton();
+ } else {
+ removeAlreadyResetNote();
+ enableSubmitButton();
+ }
+ updateSurveyStatusTextContent(participantData, selectedSurvey);
+
+ } catch (error) {
+ console.error(`Failed to fetch participant data for Connect ID ${participantConnectId}: `, error);
+ }
+ } else {
+ selectButton.textContent = e.target.textContent;
+ selectedSurvey = null;
+ updateSurveyStatusTextContent(participant, selectedSurvey);
+ removeAlreadyResetNote();
+ }
+ });
+ }
+};
+
+const updateSurveyStatusTextContent = (participant, selectedSurvey, statusCode) => {
+ const surveyNameElement = document.getElementById('surveyNameText');
+ const surveyStatusElement = document.getElementById('surveyStatusText');
+ const { surveyStatus, ssnStatusFlag } = fieldMapping;
+
+ const participantSurveyStatus = {
+ [ssnStatusFlag]: participant[ssnStatusFlag],
+ };
+
+ if (selectedSurvey === ssnStatusFlag) {
+ surveyNameElement.textContent = 'Survey Name: SSN Survey';
+ surveyStatusElement.textContent = `Survey Status: ${statusMapping[participantSurveyStatus[ssnStatusFlag]] || ''} `;
+
+ if (statusCode === 200) {
+ surveyStatusElement.textContent = `Survey Status: ${statusMapping[surveyStatus["notStarted"]]}`;
+ }
+ } else if (selectedSurvey === null) {
+ surveyNameElement.textContent = 'Survey Name: ';
+ surveyStatusElement.textContent = 'Survey Status: ';
+ disableSubmitButton();
+ }
+};
+
+/**
+ * Clears the survey status selection back to default and clears the text content
+ */
+const clearSurveySelection = () => {
+ const clearButton = document.getElementById('clearSurveySelect');
+ if (!clearButton) return;
+
+ clearButton.addEventListener('click', () => {
+ const surveyNameElement = document.getElementById('surveyNameText');
+ const surveyStatusElement = document.getElementById('surveyStatusText');
+ const selectButton = document.querySelector('.selectButton');
+ const submitButton = document.getElementById('submitButton');
+ if (!surveyNameElement || !surveyStatusElement || !selectButton || !submitButton) return;
+
+ surveyNameElement.textContent = 'Survey Name: ';
+ surveyStatusElement.textContent = 'Survey Status: ';
+ selectButton.textContent = 'Select';
+ selectedSurvey = null;
+ disableSubmitButton();
+ removeAlreadyResetNote();
+ });
+};
+
+const submitSurveyStatusReset = () => {
+ const submitButton = document.getElementById('submitButton');
+ const confirmResetButton = document.getElementById('confirmResetButton');
+ const surveyStatusElement = document.getElementById('surveyStatusText');
+ if (!submitButton || !confirmResetButton || !surveyStatusElement) return;
+
+ submitButton.addEventListener('click', async () => {
+ if (selectedSurvey === null) return;
+ setupModalContent(selectedSurvey);
+ });
+
+ if (confirmResetButton) {
+ confirmResetButton.addEventListener('click', async () => {
+ try {
+ const response = await resetParticipantSurvey(selectedSurvey);
+
+ if (response.code === 200 || response.data) {
+ localStorage.setItem('participant', JSON.stringify(response.data));
+ updateSurveyStatusTextContent(response.data, selectedSurvey, response.code);
+ displayAlreadyResetNote();
+ triggerNotificationBanner("Survey has been successfully reset!", "success", 10000);
+ disableSubmitButton();
+ }
+ } catch (error) {
+ console.error(`Failed to reset survey: ${error.message}`);
+ triggerNotificationBanner(`${error.message}`, "danger", 10000);
+ }
+ });
+ }
+};
+
+const displayAlreadyResetNote = () => {
+ const isSurveyAlreadyResetNote = document.getElementById('isSurveyAlreadyResetNote');
+ if (!isSurveyAlreadyResetNote) return;
+ isSurveyAlreadyResetNote.innerHTML = ` The survey is "Not Started". There is no survey data to be reset. `;
+}
+
+const removeAlreadyResetNote = () => {
+ const isSurveyAlreadyResetNote = document.getElementById('isSurveyAlreadyResetNote');
+ if (!isSurveyAlreadyResetNote) return;
+ isSurveyAlreadyResetNote.innerHTML = ``;
+}
+
+const setupModalContent = (survey) => {
+ const modalBody = document.querySelector('.modal-body');
+ if (!modalBody) return;
+ modalBody.textContent = surveyModalBody[survey];
+}
+
+const disableSubmitButton = () => {
+ const submitButton = document.getElementById('submitButton');
+ if (!submitButton) return;
+ submitButton.disabled = true;
+}
+
+const enableSubmitButton = () => {
+ const submitButton = document.getElementById('submitButton');
+ if (!submitButton) return;
+ submitButton.disabled = false;
+}
+
+/**
+ * Reset the participant survey status for the selected survey
+ * @param {number} selectedSurvey - the survey to reset, Ex. selectedSurvey = ssnStatusFlag (126331570)
+ * @param {object} participant - the participant object
+ * @returns {Promise} - the updated participant object
+ *
+*/
+const resetParticipantSurvey = async (selectedSurvey) => {
+ const participant = JSON.parse(localStorage.getItem('participant'));
+ const connectId = participant['Connect_ID'];
+
+ try {
+ const idToken = await getIdToken();
+ const response = await fetch(`${baseAPI}/dashboard?api=resetParticipantSurvey`, {
+ method: "POST",
+ headers: {
+ Authorization: "Bearer " + idToken,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ connectId: connectId,
+ survey: selectedSurvey
+ }),
+ });
+ if (!response.ok) {
+ const error = (response.status + " Error" + ": " + (await response.json()).message);
+ throw new Error(error);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Failed to reset participant survey: ", error);
+ throw error;
+ }
+};
\ No newline at end of file
diff --git a/src/dataCorrectionsTool.js b/src/dataCorrectionsTool/verificationCorrectionsTool.js
similarity index 90%
rename from src/dataCorrectionsTool.js
rename to src/dataCorrectionsTool/verificationCorrectionsTool.js
index 6a2f323..d4411ba 100644
--- a/src/dataCorrectionsTool.js
+++ b/src/dataCorrectionsTool/verificationCorrectionsTool.js
@@ -1,36 +1,40 @@
-import fieldMapping from './fieldToConceptIdMapping.js';
-import { dashboardNavBarLinks, removeActiveClass } from './navigationBar.js';
-import { showAnimation, hideAnimation, baseAPI, getIdToken, getDataAttributes, triggerNotificationBanner } from './utils.js';
-import { renderParticipantHeader } from './participantHeader.js';
-import { keyToVerificationStatus, keyToDuplicateType, recruitmentType, updateRecruitmentType } from './idsToName.js';
-import { appState } from './stateManager.js';
-import { findParticipant } from './participantLookup.js';
+import fieldMapping from '../fieldToConceptIdMapping.js';
+import { dashboardNavBarLinks, removeActiveClass } from '../navigationBar.js';
+import { showAnimation, hideAnimation, baseAPI, getIdToken, getDataAttributes, triggerNotificationBanner } from '../utils.js';
+import { renderParticipantHeader } from '../participantHeader.js';
+import { keyToVerificationStatus, keyToDuplicateType, recruitmentType, updateRecruitmentType } from '../idsToName.js';
+import { appState } from '../stateManager.js';
+import { findParticipant } from '../participantLookup.js';
+import { handleBackToToolSelect, displayDataCorrectionsNavbar, setActiveDataCorrectionsTab } from './dataCorrectionsHelpers.js';
-export const renderDataCorrectionsToolPage = (participant) => {
+export const setupVerificationCorrectionsPage = (participant) => {
if (participant !== undefined) {
const isParent = localStorage.getItem('isParent')
document.getElementById('navBarLinks').innerHTML = dashboardNavBarLinks(isParent);
removeActiveClass('nav-link', 'active');
document.getElementById('participantVerificationBtn').classList.add('active');
- mainContent.innerHTML = renderVerificationTool(participant);
+ mainContent.innerHTML = renderVerificationCorrections(participant);
let selectedResponse = {};
dropdownTrigger('dropdownVerification', 'dropdownMenuButtonVerificationOptns', selectedResponse);
dropdownTrigger('dropdownDuplicateType', 'dropdownMenuButtonDuplicateTypeOptns',selectedResponse);
dropdownTrigger('dropdownUpdateRecruitType', 'dropdownMenuButtonUpdateRecruitTypeOptns', selectedResponse);
viewOptionsSelected(participant);
resetChanges(participant);
+ handleBackToToolSelect();
+ setActiveDataCorrectionsTab();
}
}
-export const renderVerificationTool = (participant) => {
+export const renderVerificationCorrections = (participant) => {
let template = ``;
template = `
${renderParticipantHeader(participant)}
-
-
-
`
-
+
template += `
@@ -112,7 +120,7 @@ export const renderVerificationTool = (participant) => {
`
return template;
-}
+};
const dropdownTrigger = (buttonId, menuId, response) => {
let keyName = 'Select'
@@ -260,11 +268,11 @@ const clickHandler = async (selectedOptions) => {
console.error('An error occurred:', error);
triggerNotificationBanner('Try again later.', 'danger');
}
- }
+};
const reloadVerificationToolPage = (participant, message, type) => {
showAnimation();
- renderDataCorrectionsToolPage(participant);
+ setupVerificationCorrectionsPage(participant);
triggerNotificationBanner(message, type);
hideAnimation();
}
diff --git a/src/fieldToConceptIdMapping.js b/src/fieldToConceptIdMapping.js
index d5b437c..dd5af93 100644
--- a/src/fieldToConceptIdMapping.js
+++ b/src/fieldToConceptIdMapping.js
@@ -56,6 +56,12 @@ export default
"city":703385619,
"state":634434746,
"zip":892050548,
+ "physicalAddress1":207908218,
+ "physicalAddress2":224392018,
+ "physicalCity":451993790,
+ "physicalState":187799450,
+ "physicalZip":449168732,
+ "isPOBox":250235523,
"healthcareProvider":827220437,
"ssnOnFile": 454067894,
"timeProfileSubmitted": 430551721,
@@ -94,9 +100,9 @@ export default
'prefPhone': 127547625,
- "notStarted": 0,
- "started": 1,
- "submitted": 2,
+ "notStarted": 972455046,
+ "started": 615768760,
+ "submitted": 231311385,
"notYetEligible1": 789467219,
"notStarted1": 972455046,
@@ -192,18 +198,23 @@ export default
'experienceSurveyStartDate': 263355177,
'experienceCompleteDate': 199471989,
+ 'cancerScreeningHistorySurveyStatus': 176068627,
+ 'cancerScreeningHistorySurveyStartDate': 609630315,
+ 'cancerScreeningHistorySurveyCompletedDate': 389890053,
+
"baselineEMR": 209454331,
"baselineEMRflag": 101170406,
"baselineEMRpushDate": 143523420,
"baselinePaymentFlag": 117044491,
"paymentRoundup": 130371375,
+ "paymentRound": 130371375,
"baselinePayment": 266600170,
"eligiblePayment": 731498909,
"paymentIssued": 648936790,
"datePaymentIssued": 297462035,
- "norcIssuePayment": 222373868,
- "baselinePaymentDate": 787567527,
+ "norcPaymentEligibility": 222373868,
+ "eligiblePaymentRoundTimestamp": 787567527,
"refusedBaselinePayment": 648228701,
"refusedBaselinePaymentDate": 438636757,
@@ -335,6 +346,7 @@ export default
'refusedAllFutureQualityOfLifeSurveys': 688142378,
'refusedExperienceSurvey': 101763809,
'refusedAllFutureExperienceSurveys': 525277409,
+ 'refusedCancerScreeningHistorySurvey': 671903816,
'refusedAllFutureActivities': 906417725,
'revokeHIPAA': 773707518,
@@ -495,6 +507,7 @@ export default
'refAllFutureQualityOfLifeSurveysTimeStamp': 953225775,
'refExperienceSurveyTimeStamp': 688172931,
'refAllFutureExperienceSurveysTimeStamp': 182718292,
+ 'refCancerScreeningHistorySurveyTimeStamp': 957406695,
// remaining withdrawal
'contactSuspended': 726389747,
@@ -536,4 +549,11 @@ export default
shipped: 277438316,
received: 375535639
},
+
+
+ surveyStatus: {
+ notStarted: 972455046,
+ started: 615768760,
+ completed: 231311385
+ }
};
diff --git a/src/navigationBar.js b/src/navigationBar.js
index 5919b59..624f995 100644
--- a/src/navigationBar.js
+++ b/src/navigationBar.js
@@ -45,7 +45,7 @@ export const dashboardNavBarLinks = (isParent) => {
${(helpDesk === 'true' || coordinatingCenter === 'true') ?
(`
- Data Corrections Tool
+ Data Corrections Tool
`) : (``) }
${(isParent !== 'true' || coordinatingCenter === 'true') ?
(`
diff --git a/src/participantCommons.js b/src/participantCommons.js
index e2dac2b..88546ab 100644
--- a/src/participantCommons.js
+++ b/src/participantCommons.js
@@ -21,10 +21,10 @@ export const renderTable = (data, source) => {
fieldMapping.zipCodeMatch, fieldMapping.siteMatch, fieldMapping.ageMatch, fieldMapping.cancerStatusMatch, fieldMapping.updateRecruitType,
fieldMapping.preConsentOptOut, fieldMapping.datePreConsentOptOut, fieldMapping.maxNumContactsReached, fieldMapping.signInMechansim, fieldMapping.consentFirstName,
fieldMapping.consentMiddleName, fieldMapping.consentLastName, fieldMapping.accountName,fieldMapping.accountPhone, fieldMapping.accountEmail, fieldMapping.prefName,
- fieldMapping.address1, fieldMapping.address2, fieldMapping.city, fieldMapping.state, fieldMapping.zip, fieldMapping.email, fieldMapping.email1,
+ fieldMapping.address1, fieldMapping.address2, fieldMapping.city, fieldMapping.state, fieldMapping.zip, fieldMapping.physicalAddress1, fieldMapping.physicalAddress2, fieldMapping.physicalCity, fieldMapping.physicalState, fieldMapping.physicalZip, fieldMapping.email, fieldMapping.email1,
fieldMapping.email2, fieldMapping.cellPhone, fieldMapping.homePhone, fieldMapping.otherPhone, fieldMapping.previousCancer, fieldMapping.allBaselineSurveysCompleted,
fieldMapping.preferredLanguage, fieldMapping.participationStatus, fieldMapping.bohStatusFlag1, fieldMapping.mreStatusFlag1, fieldMapping.sasStatusFlag1, fieldMapping.lawStausFlag1,
- fieldMapping.ssnFullflag, fieldMapping.ssnPartialFlag , fieldMapping.refusedSurvey, fieldMapping.refusedBlood, fieldMapping.refusedUrine, fieldMapping.refusedMouthwash, fieldMapping.refusedSpecimenSurveys, fieldMapping.refusedFutureSamples, fieldMapping.refusedQualityOfLifeSurvey, fieldMapping.refusedAllFutureQualityOfLifeSurveys,
+ fieldMapping.ssnFullflag, fieldMapping.ssnPartialFlag , fieldMapping.refusedSurvey, fieldMapping.refusedBlood, fieldMapping.refusedUrine, fieldMapping.refusedMouthwash, fieldMapping.refusedSpecimenSurveys, fieldMapping.refusedFutureSamples, fieldMapping.refusedQualityOfLifeSurvey, fieldMapping.refusedAllFutureQualityOfLifeSurveys, fieldMapping.refusedCancerScreeningHistorySurvey,
fieldMapping.refusedExperienceSurvey, fieldMapping.refusedAllFutureExperienceSurveys, fieldMapping.refusedFutureSurveys, fieldMapping.refusedAllFutureActivities, fieldMapping.revokeHIPAA, fieldMapping.dateHipaaRevokeRequested, fieldMapping.dateHIPAARevoc, fieldMapping.withdrawConsent, fieldMapping.dateWithdrewConsentRequested,
fieldMapping.participantDeceased, fieldMapping.dateOfDeath, fieldMapping.destroyData, fieldMapping.dateDataDestroyRequested, fieldMapping.dateDataDestroy, fieldMapping.suspendContact
];
@@ -579,7 +579,7 @@ const tableTemplate = (data, showButtons) => {
)
: (x === (fieldMapping.refusedSurvey).toString() || x === (fieldMapping.refusedBlood).toString() || x === (fieldMapping.refusedUrine).toString() ||
x === (fieldMapping.refusedMouthwash).toString() || x === (fieldMapping.refusedSpecimenSurveys).toString() || x === (fieldMapping.refusedFutureSamples).toString() ||
- x === (fieldMapping.refusedFutureSurveys).toString() || x === (fieldMapping.refusedQualityOfLifeSurvey).toString() || x === (fieldMapping.refusedAllFutureQualityOfLifeSurveys).toString() ||
+ x === (fieldMapping.refusedFutureSurveys).toString() || x === (fieldMapping.refusedQualityOfLifeSurvey).toString() || x === (fieldMapping.refusedAllFutureQualityOfLifeSurveys).toString() || x === fieldMapping.refusedCancerScreeningHistorySurvey.toString() ||
x === (fieldMapping.refusedExperienceSurvey).toString() || x === (fieldMapping.refusedAllFutureExperienceSurveys).toString()) ?
(
(participant[fieldMapping.refusalOptions]?.[x] === fieldMapping.yes ?
diff --git a/src/participantDetails.js b/src/participantDetails.js
index 95a7025..3f78c34 100644
--- a/src/participantDetails.js
+++ b/src/participantDetails.js
@@ -358,7 +358,7 @@ const renderFormInModal = (participant, changedOption, conceptId, participantKey
const renderText = textFieldMappingsArray.includes(parseInt(conceptId));
const renderDay = conceptId == fieldMapping.birthDay;
const renderMonth = conceptId == fieldMapping.birthMonth;
- const renderState = conceptId == fieldMapping.state;
+ const renderState = conceptId == fieldMapping.state || conceptId == fieldMapping.physicalState;
const renderSuffix = conceptId == fieldMapping.suffix;
const renderLanguage = conceptId == fieldMapping.preferredLanguage;
const elementId = `fieldModified${conceptId}`;
diff --git a/src/participantDetailsHelpers.js b/src/participantDetailsHelpers.js
index 1e8b38b..112d8e7 100644
--- a/src/participantDetailsHelpers.js
+++ b/src/participantDetailsHelpers.js
@@ -265,6 +265,48 @@ export const getImportantRows = (participant, changedOption) => {
validationType: 'zip',
isRequired: true
},
+ { field: fieldMapping.isPOBox,
+ label: 'Mailing address is PO Box',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'permissionSelector',
+ isRequired: false
+ },
+ { field: fieldMapping.physicalAddress1,
+ label: 'Physical Address Line 1 (if different from Mailing address)',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'address',
+ isRequired: true
+ },
+ { field: fieldMapping.physicalAddress2,
+ label: 'Physical Address Line 2 (if different from Mailing address)',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'address',
+ isRequired: false
+ },
+ { field: fieldMapping.physicalCity,
+ label: 'Physical City (if different from Mailing address)',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'text',
+ isRequired: true
+ },
+ { field: fieldMapping.physicalState,
+ label: 'Physical State (if different from Mailing address)',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'state',
+ isRequired: true
+ },
+ { field: fieldMapping.physicalZip,
+ label: 'Physical Zip (if different from Mailing address)',
+ editable: !isParticipantDataDestroyed,
+ display: true,
+ validationType: 'zip',
+ isRequired: true
+ },
{ field: fieldMapping.birthMonth,
label: 'Birth Month',
editable: !isParticipantDataDestroyed && !isParticipantVerified,
@@ -915,7 +957,7 @@ const getUITextForUpdatedValue = (newValue, conceptIdArray) => {
return suffixToTextMap.get(parseInt(newValue));
} else if (conceptIdArray.toString().includes(fieldMapping.preferredLanguage.toString())) {
return languageToTextMap.get(parseInt(newValue));
- } else if (conceptIdArray.some(id => [fieldMapping.canWeText.toString(), fieldMapping.voicemailMobile.toString(), fieldMapping.voicemailHome.toString(), fieldMapping.voicemailOther.toString()].includes(id.toString()))) {
+ } else if (conceptIdArray.some(id => [fieldMapping.canWeText.toString(), fieldMapping.voicemailMobile.toString(), fieldMapping.voicemailHome.toString(), fieldMapping.voicemailOther.toString(), fieldMapping.isPOBox.toString()].includes(id.toString()))) {
return newValue === fieldMapping.yes.toString() ? "Yes" : "No";
} else {
return newValue;
diff --git a/src/participantHeader.js b/src/participantHeader.js
index bd8abc9..62bbf7b 100644
--- a/src/participantHeader.js
+++ b/src/participantHeader.js
@@ -1,9 +1,9 @@
import fieldMapping from './fieldToConceptIdMapping.js';
-import { humanReadableMDY } from './utils.js';
+import { formatUTCDate } from './utils.js';
import { keyToNameObj } from './idsToName.js';
export const renderParticipantHeader = (participant) => {
- const readableVerificationDate = humanReadableMDY(participant[fieldMapping.verficationDate]);
+ const readableVerificationDate = formatUTCDate(participant[fieldMapping.verficationDate]);
let verificationHtmlStr = "";
if (participant[fieldMapping.verifiedFlag] === fieldMapping.verified) {
verificationHtmlStr = `Verified : ${readableVerificationDate}`;
@@ -25,7 +25,7 @@ export const renderParticipantHeader = (participant) => {
DOB Yr : ${participant[fieldMapping.birthYear] || ""}
${
participant[fieldMapping.consentFlag] === fieldMapping.yes
- ? `Consented : ${humanReadableMDY(participant[fieldMapping.consentDate])}`
+ ? `Consented : ${formatUTCDate(participant[fieldMapping.consentDate])}`
: "Not Consented : N/A"
}
${verificationHtmlStr}
@@ -69,7 +69,7 @@ export const getParticipantStatus = (participant) => {
export const getParticipantSuspendedDate = (participant) => {
if (participant[fieldMapping.suspendContact] !== "" && participant[fieldMapping.suspendContact] !== undefined ) {
- let suspendContactRequestedFrom = humanReadableMDY(participant[fieldMapping.startDateSuspendedContact]);
+ let suspendContactRequestedFrom = formatUTCDate(participant[fieldMapping.startDateSuspendedContact]);
let suspendedDate = participant[fieldMapping.suspendContact];
return `Suspended Contact : From: ${suspendContactRequestedFrom} To: ${suspendedDate}`;
}
diff --git a/src/participantLookup.js b/src/participantLookup.js
index e590cd0..c411825 100644
--- a/src/participantLookup.js
+++ b/src/participantLookup.js
@@ -155,7 +155,6 @@ const addEventSearch = () => {
if (params.size === 0) {
return alert('Please enter at least one field to search');
};
-
performSearch(params.toString(), sitePref, "search-failed");
})
};
diff --git a/src/participantSummary.js b/src/participantSummary.js
index 643c6de..e1d8cbe 100644
--- a/src/participantSummary.js
+++ b/src/participantSummary.js
@@ -4,23 +4,23 @@ import fieldMapping from './fieldToConceptIdMapping.js';
import { userProfile, verificationStatus, baselineBOHSurvey, baselineMRESurvey,baselineSASSurvey,
baselineLAWSurvey, baselineSSN, baselineCOVIDSurvey, baselineBloodSample, baselineUrineSample, baselineBiospecSurvey, baselineMenstrualSurvey,
baselineMouthwashSample, baselineBloodUrineSurvey, baselineMouthwashSurvey, baselinePromisSurvey, baselineEMR, baselinePayment,
- baselineExperienceSurvey} from './participantSummaryRow.js';
-import { humanReadableMDY, conceptToSiteMapping, pdfCoordinatesMap } from './utils.js';
+ baselineExperienceSurvey, cancerScreeningHistorySurvey} from './participantSummaryRow.js';
+import { formatUTCDate, conceptToSiteMapping, pdfCoordinatesMap } from './utils.js';
-const { PDFDocument, StandardFonts } = PDFLib
+const { PDFDocument, StandardFonts } = PDFLib;
document.body.scrollTop = document.documentElement.scrollTop = 0;
export const renderParticipantSummary = (participant) => {
- const isParent = localStorage.getItem('isParent')
+ const isParent = localStorage.getItem('isParent');
document.getElementById('navBarLinks').innerHTML = dashboardNavBarLinks(isParent);
removeActiveClass('nav-link', 'active');
document.getElementById('participantSummaryBtn').classList.add('active');
if (participant !== null) {
- mainContent.innerHTML = render(participant);
- downloadCopyHandler(participant)
+ document.querySelector("#mainContent").innerHTML = render(participant);
+ downloadCopyHandler(participant);
}
-}
+};
export const render = (participant) => {
if (!participant) {
@@ -102,6 +102,9 @@ export const render = (participant) => {
${baselineExperienceSurvey(participant)}
+
+ ${cancerScreeningHistorySurvey(participant)}
+
${baselineBloodSample(participant)}
@@ -139,7 +142,7 @@ const downloadCopyHandler = (participant) => {
const lang = versionArray[3];
a.addEventListener('click', () => {
try {
- renderDownload(participant, humanReadableMDY(participant[fieldMapping.consentDate]), `./forms/Consent/${conceptToSiteMapping[participant[fieldMapping.healthcareProvider]]}_consent_${(version || defaultVersion)}${(lang ? '_'+lang : '')}.pdf`,
+ renderDownload(participant, formatUTCDate(participant[fieldMapping.consentDate]), `./forms/Consent/${conceptToSiteMapping[participant[fieldMapping.healthcareProvider]]}_consent_${(version || defaultVersion)}${(lang ? '_'+lang : '')}.pdf`,
getHealthcareProviderCoordinates(conceptToSiteMapping[participant[fieldMapping.healthcareProvider]], 'consent', version || defaultVersion, lang || defaultLang));
} catch (error) {
console.error(error);
@@ -154,7 +157,7 @@ const downloadCopyHandler = (participant) => {
const lang = versionArray[3];
b.addEventListener('click', () => {
try {
- renderDownload(participant, humanReadableMDY(participant[fieldMapping.hippaDate]), `./forms/HIPAA/${conceptToSiteMapping[participant[fieldMapping.healthcareProvider]]}_HIPAA_${(version || defaultVersion)}${(lang ? '_'+lang : '')}.pdf`,
+ renderDownload(participant, formatUTCDate(participant[fieldMapping.hippaDate]), `./forms/HIPAA/${conceptToSiteMapping[participant[fieldMapping.healthcareProvider]]}_HIPAA_${(version || defaultVersion)}${(lang ? '_'+lang : '')}.pdf`,
getHealthcareProviderCoordinates(conceptToSiteMapping[participant[fieldMapping.healthcareProvider]], 'hipaa', version || defaultVersion, lang || defaultLang));
} catch (error) {
console.error(error);
@@ -168,7 +171,7 @@ const downloadCopyHandler = (participant) => {
const version = versionArray[2] || 'V1.0';
const lang = versionArray[3];
c.addEventListener('click', () => {
- renderDownload(participant, humanReadableMDY(participant[fieldMapping.dateHIPAARevoc]), `./forms/HIPAA Revocation/HIPAA_Revocation_${version}${(lang ? '_'+lang : '')}.pdf`, getRevocationCoordinates('HIPAA',version,lang || 'Eng'), 'hipaarevoc');
+ renderDownload(participant, formatUTCDate(participant[fieldMapping.dateHIPAARevoc]), `./forms/HIPAA Revocation/HIPAA_Revocation_${version}${(lang ? '_'+lang : '')}.pdf`, getRevocationCoordinates('HIPAA',version,lang || 'Eng'), 'hipaarevoc');
})
}
const d = document.getElementById('downloadCopyDataDestroy');
@@ -177,7 +180,7 @@ const downloadCopyHandler = (participant) => {
const version = versionArray[2] || 'V1.0';
const lang = versionArray[3];
d.addEventListener('click', () => {
- renderDownload(participant, humanReadableMDY(participant[fieldMapping.dateDataDestroy]), `./forms/Data Destruction/Data_Destruction_${version}${(lang ? '_'+lang : '')}.pdf`, getRevocationCoordinates('Data',version,lang || 'Eng'), 'datadestruction');
+ renderDownload(participant, formatUTCDate(participant[fieldMapping.dateDataDestroy]), `./forms/Data Destruction/Data_Destruction_${version}${(lang ? '_'+lang : '')}.pdf`, getRevocationCoordinates('Data',version,lang || 'Eng'), 'datadestruction');
})
}
@@ -308,7 +311,7 @@ const consentHandler = (participant) => {
Agreement
Consent
Signed
- ${participant[fieldMapping.consentDate] && humanReadableMDY(participant[fieldMapping.consentDate])}
+ ${participant[fieldMapping.consentDate] && formatUTCDate(participant[fieldMapping.consentDate])}
${participant[fieldMapping.consentVersion]}
N/A
Download Link
@@ -338,7 +341,7 @@ const hippaHandler = (participant) => {
Agreement
HIPAA
Signed
- ${participant[fieldMapping.hippaDate] && humanReadableMDY(participant[fieldMapping.hippaDate])}
+ ${participant[fieldMapping.hippaDate] && formatUTCDate(participant[fieldMapping.hippaDate])}
${participant[fieldMapping.hipaaVersion]}
N/A
Download Link
@@ -367,7 +370,7 @@ const hipaaRevocation = (participant) => {
Agreement
HIPAA Revoc Form
Signed
- ${(participant[fieldMapping.dateHIPAARevoc] !== undefined) ? humanReadableMDY(participant[fieldMapping.dateHIPAARevoc]) : `N/A`}
+ ${(participant[fieldMapping.dateHIPAARevoc] !== undefined) ? formatUTCDate(participant[fieldMapping.dateHIPAARevoc]) : `N/A`}
${(participant[fieldMapping.versionHIPAARevoc] !== undefined) ? participant[fieldMapping.versionHIPAARevoc] : `N/A`}
N/A
Download Link
@@ -377,7 +380,7 @@ const hipaaRevocation = (participant) => {
Agreement
HIPAA Revoc Form
Not Signed
- ${(participant[fieldMapping.dateHIPAARevoc] !== undefined) ? humanReadableMDY(participant[fieldMapping.dateHIPAARevoc]) : `N/A`}
+ ${(participant[fieldMapping.dateHIPAARevoc] !== undefined) ? formatUTCDate(participant[fieldMapping.dateHIPAARevoc]) : `N/A`}
${(participant[fieldMapping.versionHIPAARevoc] !== undefined) ? participant[fieldMapping.versionHIPAARevoc] : `N/A`}
N/A
Download Link `
@@ -406,7 +409,7 @@ const dataDestroy = (participant) => {
Agreement
Data Destroy Form
Signed
- ${(participant[fieldMapping.dateDataDestroy] !== undefined) ? humanReadableMDY(participant[fieldMapping.dateDataDestroy]) : `N/A`}
+ ${(participant[fieldMapping.dateDataDestroy] !== undefined) ? formatUTCDate(participant[fieldMapping.dateDataDestroy]) : `N/A`}
${(participant[fieldMapping.versionDataDestroy] !== undefined) ? participant[fieldMapping.versionDataDestroy] : `N/A` }
N/A
Download Link
@@ -416,7 +419,7 @@ const dataDestroy = (participant) => {
Agreement
Data Destroy Form
Not Signed
- ${(participant[fieldMapping.dateDataDestroy] !== undefined) ? humanReadableMDY(participant[fieldMapping.dateDataDestroy]) : `N/A`}
+ ${(participant[fieldMapping.dateDataDestroy] !== undefined) ? formatUTCDate(participant[fieldMapping.dateDataDestroy]) : `N/A`}
${(participant[fieldMapping.versionDataDestroy] !== undefined) ? participant[fieldMapping.versionDataDestroy] : `N/A` }
N/A
Download Link
diff --git a/src/participantSummaryRow.js b/src/participantSummaryRow.js
index 2e93a94..7df88ff 100644
--- a/src/participantSummaryRow.js
+++ b/src/participantSummaryRow.js
@@ -1,4 +1,4 @@
-import { humanReadableMDY } from './utils.js';
+import { formatUTCDate } from './utils.js';
import fieldMapping from './fieldToConceptIdMapping.js';
export const userProfile = (participant) => {
@@ -8,7 +8,7 @@ export const userProfile = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Enrollment", "N/A", "User Profile", "N/A", "N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.userProfileFlag] === fieldMapping.yes) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Enrollment", "N/A", "User Profile", "Submitted",
- humanReadableMDY(participant[fieldMapping.userProfileDateTime]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.userProfileDateTime]), "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Enrollment", "N/A", "User Profile", "Not Submitted", "N/A", "N/A", "N/A", "N/A");
}
@@ -23,19 +23,19 @@ export const verificationStatus = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Enrollment", "N/A", "Verification", "N/A", "N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.verifiedFlag] === fieldMapping.verified) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Enrollment", "N/A", "Verification Status", "Verified",
- humanReadableMDY(participant[fieldMapping.verficationDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.verficationDate]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.verifiedFlag] === fieldMapping.cannotBeVerified) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Enrollment", "N/A", "Verification Status", "Can't be Verified",
- humanReadableMDY(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.verifiedFlag] === fieldMapping.notYetVerified) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Enrollment", "N/A", "Verification Status", "Not yet Verified",
"N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.verifiedFlag] === fieldMapping.duplicate) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Enrollment", "N/A", "Verification Status", "Duplicate",
- humanReadableMDY(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Enrollment", "N/A", "Verification Status", "Outreach Timed Out",
- humanReadableMDY(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.verficationDate]) || "N/A", "N/A", "N", "N/A");
}
return template;
@@ -173,10 +173,10 @@ export const baselineBOHSurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "BOH", "N/A", "N/A", "N/A", "Y", "N/A");
} else if (participant[fieldMapping.bohStatusFlag1] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "BOH", "Submitted",
- humanReadableMDY(participant[fieldMapping.bohCompletedDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.bohCompletedDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.bohStatusFlag1] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "BOH", "Started",
- humanReadableMDY(participant[fieldMapping.bohStartDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.bohStartDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.bohStatusFlag1] === fieldMapping.notStarted1) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "BOH", "Not Started", "N/A", "N/A", "N", "N/A");
} else {
@@ -194,10 +194,10 @@ export const baselineMRESurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "MRE", "N/A", "N/A", "N/A", "Y", "N/A");
} else if (participant[fieldMapping.mreStatusFlag1] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "MRE", "Submitted",
- humanReadableMDY(participant[fieldMapping.mreCompletedDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.mreCompletedDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.mreStatusFlag1] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "MRE", "Started",
- humanReadableMDY(participant[fieldMapping.mreStartDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.mreStartDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.mreStatusFlag1] === fieldMapping.notStarted1) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "MRE", "Not Started", "N/A", "N/A", "N", "N/A");
} else {
@@ -215,10 +215,10 @@ export const baselineSASSurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "SAS", "N/A", "N/A", "N/A", "Y", "N/A");
} else if (participant[fieldMapping.sasStatusFlag1] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "SAS", "Submitted",
- humanReadableMDY(participant[fieldMapping.sasCompletedDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.sasCompletedDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.sasStatusFlag1] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "SAS", "Started",
- humanReadableMDY(participant[fieldMapping.sasStartDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.sasStartDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.sasStatusFlag1] === fieldMapping.notStarted1) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "SAS", "Not Started", "N/A", "N/A", "N", "N/A");
} else {
@@ -236,10 +236,10 @@ export const baselineLAWSurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "LAW", "N/A", "N/A", "N/A", "Y", "N/A");
} else if (participant[fieldMapping.lawStausFlag1] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "LAW", "Submitted",
- humanReadableMDY(participant[fieldMapping.lawCompletedDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.lawCompletedDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.lawStausFlag1] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "LAW", "Started",
- humanReadableMDY(participant[fieldMapping.lawStartDate1]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.lawStartDate1]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.lawStausFlag1] === fieldMapping.notStarted1) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "LAW", "Not Started", "N/A", "N/A", "N", "N/A");
} else {
@@ -267,10 +267,10 @@ export const baselineCOVIDSurvey = (participant) => {
if (participant[fieldMapping.covidFlag] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "COVID", "Submitted",
- humanReadableMDY(participant[fieldMapping.covidCompletedDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.covidCompletedDate]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.covidFlag] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "COVID", "Started",
- humanReadableMDY(participant[fieldMapping.covidStartDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.covidStartDate]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.covidFlag] === fieldMapping.notStarted1) {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "COVID", "Not Started", "N/A", "N/A", "N", "N/A");
} else {
@@ -291,10 +291,10 @@ export const baselineBiospecSurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Blood/Urine/Mouthwash", "N/A", "N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.combinedBoodUrineMouthwashSurvey] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "Blood/Urine/Mouthwash", "Submitted",
- humanReadableMDY(participant[fieldMapping.combinedBoodUrineMouthwashSurveyCompleteDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.combinedBoodUrineMouthwashSurveyCompleteDate]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.combinedBoodUrineMouthwashSurvey] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "Blood/Urine/Mouthwash", "Started",
- humanReadableMDY(participant[fieldMapping.combinedBoodUrineMouthwashSurveyStartDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.combinedBoodUrineMouthwashSurveyStartDate]), "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Blood/Urine/Mouthwash", "Not Started", "N/A", "N/A", "N", "N/A");
}
@@ -312,10 +312,10 @@ export const baselineBloodUrineSurvey = (participant) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Clinical Blood/Urine", "N/A", "N/A", "N/A", "N", "N/A");
} else if (participant[fieldMapping.bloodUrineSurveyFlag] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "Clinical Blood/Urine", "Submitted",
- humanReadableMDY(participant[fieldMapping.bloodUrineSurveyCompletedDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.bloodUrineSurveyCompletedDate]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.bloodUrineSurveyFlag] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "Clinical Blood/Urine", "Started",
- humanReadableMDY(participant[fieldMapping.bloodUrineSurveyStartedDate]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.bloodUrineSurveyStartedDate]), "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Clinical Blood/Urine", "Not Started", "N/A", "N/A", "N", "N/A");
}
@@ -333,10 +333,10 @@ export const baselineMouthwashSurvey = (participantModule) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Home Mouthwash", "N/A", "N/A", "N/A", "N", "N/A");
} else if (participantModule[fieldMapping.mouthwashSurveyFlag] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "Home Mouthwash", "Submitted",
- humanReadableMDY(participantModule[fieldMapping.mouthwashSurveyCompletedDate]), "N/A", "N", "N/A");
+ formatUTCDate(participantModule[fieldMapping.mouthwashSurveyCompletedDate]), "N/A", "N", "N/A");
} else if (participantModule[fieldMapping.mouthwashSurveyFlag] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "Home Mouthwash", "Started",
- humanReadableMDY(participantModule[fieldMapping.mouthwashSurveyStartedDate]), "N/A", "N", "N/A");
+ formatUTCDate(participantModule[fieldMapping.mouthwashSurveyStartedDate]), "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Home Mouthwash", "Not Started", "N/A", "N/A", "N", "N/A");
}
@@ -380,15 +380,27 @@ export const baselineExperienceSurvey = (participant) => {
return getTemplateRow(icon, color, timeline, category, item, itemStatus, date, setting, refused, extra);
};
+export const cancerScreeningHistorySurvey = (data) => {
+ const refusedAllFutureSurveys = data[fieldMapping.refusalOptions]?.[fieldMapping.refusedFutureSurveys];
+ const refusedAllFutureActivities = data[fieldMapping.refusedAllFutureActivities];
+ const refusedCancerScreeningHistorySurvey = data[fieldMapping.refusalOptions]?.[fieldMapping.refusedCancerScreeningHistorySurvey];
+ const refused = refusedAllFutureSurveys === fieldMapping.yes || refusedAllFutureActivities === fieldMapping.yes || refusedCancerScreeningHistorySurvey === fieldMapping.yes ? "Y" : "N";
+ let { icon, color, itemStatus, date } = getSurveyStatus(data, fieldMapping.cancerScreeningHistorySurveyStatus, fieldMapping.cancerScreeningHistorySurveyStartDate, fieldMapping.cancerScreeningHistorySurveyCompletedDate);
+
+ if (!data[fieldMapping.cancerScreeningHistorySurveyStatus]) itemStatus = "Not Eligible";
+
+ return getTemplateRow(icon, color, "Follow-Up 9-mo", "Survey", "Cancer Screening History", itemStatus, date, "N/A", refused, "N/A");
+};
+
export const baselineMenstrualSurvey = (participant) => {
let template = ``;
if (participant[fieldMapping.menstrualFlag] === fieldMapping.submitted1) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Survey", "Menstrual Cycle", "Submitted",
- humanReadableMDY(participant[fieldMapping.menstrualDateTimeCompleted]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.menstrualDateTimeCompleted]), "N/A", "N", "N/A");
} else if (participant[fieldMapping.menstrualFlag] === fieldMapping.started1) {
template += getTemplateRow("fa fa-hashtag fa-2x", "color: orange", "Baseline", "Survey", "Menstrual Cycle", "Started",
- humanReadableMDY(participant[fieldMapping.menstrualDateTimeStart]), "N/A", "N", "N/A");
+ formatUTCDate(participant[fieldMapping.menstrualDateTimeStart]), "N/A", "N", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Survey", "Menstrual Cycle", "Not Started", "N/A", "N/A", "N", "N/A");
}
@@ -404,7 +416,7 @@ export const baselineEMR = (participantModule) => {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "EMR", "N/A", "Not Pushed", "N/A", "N/A", "N", "N/A");
} else if (baselineEMR[fieldMapping.baselineEMRflag] === fieldMapping.yes) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "EMR", "N/A", "Pushed",
- humanReadableMDY(baselineEMR[fieldMapping.baselineEMRpushDate]), "N/A", "N/A", "N/A");
+ formatUTCDate(baselineEMR[fieldMapping.baselineEMRpushDate]), "N/A", "N/A", "N/A");
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "EMR", "N/A", "Not Pushed", "N/A", "N/A", "N/A", "N/A");
}
@@ -422,8 +434,8 @@ export const baselinePayment = (participantModule) => {
participantModule[fieldMapping.paymentRoundup][fieldMapping.baselinePayment][fieldMapping.eligiblePayment] === fieldMapping.yes
) {
template += getTemplateRow("fa fa-check fa-2x", "color: green", "Baseline", "Payment", "N/A", "Eligible",
- humanReadableMDY(participantModule[fieldMapping.paymentRoundup][fieldMapping.baselinePayment][fieldMapping.baselinePaymentDate]),
- "N/A", "N/A", checkIncentiveIssued(participantModule)
+ formatUTCDate(participantModule[fieldMapping.paymentRoundup][fieldMapping.baselinePayment][fieldMapping.eligiblePaymentRoundTimestamp]),
+ "N/A", "N/A", checkIncentiveIssued(participantModule)
);
} else {
template += getTemplateRow("fa fa-times fa-2x", "color: red", "Baseline", "Payment", "N/A", "Not Eligible", "N/A", "N/A", "N/A", checkIncentiveIssued(participantModule)
@@ -440,14 +452,14 @@ const getSurveyStatus = (participant, surveyFlag, startDate, completeDate) => {
icon: "fa fa-check fa-2x",
color: "color: green",
itemStatus: "Submitted",
- date: humanReadableMDY(participant[completeDate]),
+ date: formatUTCDate(participant[completeDate]),
};
case fieldMapping.started1:
return {
icon: "fa fa-hashtag fa-2x",
color: "color: orange",
itemStatus: "Started",
- date: humanReadableMDY(participant[startDate]),
+ date: formatUTCDate(participant[startDate]),
};
case fieldMapping.notYetEligible1:
return {
@@ -478,9 +490,9 @@ const getSurveyStatus = (participant, surveyFlag, startDate, completeDate) => {
const checkIncentiveIssued = (participantModule) => {
return participantModule[fieldMapping.paymentRoundup] &&
(participantModule[fieldMapping.paymentRoundup][fieldMapping.biospecimenFollowUp][fieldMapping.paymentIssued] === (fieldMapping.yes)) ?
- `Issued on ${humanReadableMDY(participantModule[fieldMapping.paymentRoundup][fieldMapping.biospecimenFollowUp][fieldMapping.datePaymentIssued])}`:
+ `Issued on ${formatUTCDate(participantModule[fieldMapping.paymentRoundup][fieldMapping.biospecimenFollowUp][fieldMapping.datePaymentIssued])}`:
(participantModule[fieldMapping.paymentRoundup]?.[fieldMapping.biospecimenFollowUp]?.[fieldMapping.refusedBaselinePayment] === (fieldMapping.yes)) ?
- `Declined on ${humanReadableMDY(participantModule[fieldMapping.paymentRoundup][fieldMapping.biospecimenFollowUp][fieldMapping.refusedBaselinePaymentDate])}`:
+ `Declined on ${formatUTCDate(participantModule[fieldMapping.paymentRoundup][fieldMapping.biospecimenFollowUp][fieldMapping.refusedBaselinePaymentDate])}`:
`N/A`
}
@@ -517,11 +529,11 @@ const setSampleDateTime = (biospecimenRow, biospecimenFlag, researchDateTime, cl
(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][biospecimenFlag]) === (fieldMapping.biospecimenResearch) ?
(
- biospecimenSampleDateTime += humanReadableMDY(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][researchDateTime])
+ biospecimenSampleDateTime += formatUTCDate(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][researchDateTime])
) :
(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][biospecimenFlag]) === (fieldMapping.biospecimenClinical) ?
(
- biospecimenSampleDateTime += humanReadableMDY(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][clinicalDateTime])
+ biospecimenSampleDateTime += formatUTCDate(biospecimenRow[fieldMapping.biospecimenCollectionDetail][fieldMapping.biospecimenFollowUp][clinicalDateTime])
) : ``
)
return biospecimenSampleDateTime;
diff --git a/src/participantWithdrawal.js b/src/participantWithdrawal.js
index db6bbe1..71740c3 100644
--- a/src/participantWithdrawal.js
+++ b/src/participantWithdrawal.js
@@ -87,6 +87,8 @@ const getParticipantSelectedRefusals = (participant) => {
strArray.push("Refused all future QOL surveys (but willing to do other future surveys)");
if (refusalOptions[fieldMapping.refusedExperienceSurvey] === fieldMapping.yes)
strArray.push("Refused 2024 Connect Experience Survey (but willing to do other future surveys)");
+ if (refusalOptions[fieldMapping.refusedCancerScreeningHistorySurvey] === fieldMapping.yes)
+ strArray.push("Refused Cancer Screening History Survey (willing to do specimens)");
if (refusalOptions[fieldMapping.refusedAllFutureExperienceSurveys] === fieldMapping.yes)
strArray.push("Refused all future Connect Experience surveys (but willing to do other future surveys)");
if (refusalOptions[fieldMapping.refusedFutureSurveys] === fieldMapping.yes)
diff --git a/src/participantWithdrawalForm.js b/src/participantWithdrawalForm.js
index cea796b..965e8b0 100644
--- a/src/participantWithdrawalForm.js
+++ b/src/participantWithdrawalForm.js
@@ -82,6 +82,12 @@ export const renderParticipantWithdrawalLandingPage = () => {
2024 Connect Experience Survey (but willing to do other future surveys)
+
+
+ Cancer Screening History Survey (but willing to do other future surveys)
+
+
@@ -504,6 +510,9 @@ const sendResponses = async (finalOptions, retainOptions, requestedHolder, sourc
else if (parseInt(x.dataset.optionkey) === fieldMapping.refusedAllFutureExperienceSurveys) {
setRefusalTimeStamp(sendRefusalData, x.dataset.optionkey, fieldMapping.refAllFutureExperienceSurveysTimeStamp);
}
+ else if (parseInt(x.dataset.optionkey) === fieldMapping.refusedCancerScreeningHistorySurvey) {
+ setRefusalTimeStamp(sendRefusalData, x.dataset.optionkey, fieldMapping.refCancerScreeningHistorySurveyTimeStamp);
+ }
else {
sendRefusalData[x.dataset.optionkey] = fieldMapping.yes
}
diff --git a/src/utils.js b/src/utils.js
index ef174a3..2f780b2 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -22,6 +22,23 @@ export const humanReadableY = () => {
return currentYear;
} // 2021
+/**
+ * Convert ISO8601 date to human readable date
+ * @param {String} participantDate - ISO8601 date string
+ * @param {boolean} formatToYearMonthDay - Optional flag to format date to YYYY-MM-DD
+ * @returns {String} - Human readable date string (MM/DD/YYYY) or YYYY-MM-DD (true)
+ *
+*/
+export const formatUTCDate = (participantDate, formatToYearMonthDay) => {
+ if (!participantDate) return 'N/A';
+ const date = new Date(participantDate);
+ const year = date.getUTCFullYear();
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
+ const day = String(date.getUTCDate()).padStart(2, "0");
+
+ return formatToYearMonthDay ? `${year}-${month}-${day}` : `${month}/${day}/${year}`;
+};
+
// Function prevents the user from internal navigation if unsaved changes are present
export const internalNavigatorHandler = (counter) => {
setTimeout(() => {
@@ -133,7 +150,7 @@ export const conceptToSiteMapping = {
13: 'NCI'
}
-export const triggerNotificationBanner = (message, type) => {
+export const triggerNotificationBanner = (message, type, timeout) => {
const alertList = document.getElementById("alert_placeholder");
if (alertList) {
alertList.innerHTML = `
@@ -143,6 +160,17 @@ export const triggerNotificationBanner = (message, type) => {
×
`;
+
+ if (!timeout) return;
+ setTimeout(() => {
+ const alertElement = alertList.querySelector('.alert');
+ if (alertElement) {
+ alertElement.classList.remove('show');
+ alertElement.addEventListener('transitionend', () => {
+ alertElement.remove();
+ });
+ }
+ }, timeout);
}
}