-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding debounce so saving isn't as agressive! Adding download of repo…
…rt, closes #2
- Loading branch information
Showing
11 changed files
with
278 additions
and
126 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,31 @@ | ||
import React from 'react'; | ||
import { useModal } from './context/modal-context' | ||
import axios from 'axios'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { useSelector } from 'react-redux'; | ||
|
||
export default function CheckpointCTAs() { | ||
const { setModal } = useModal() | ||
const navigate = useNavigate(); | ||
const assessmentId = useSelector((state) => state.checkpoints.activeAssessment.id); // Get assessmentId from Redux state | ||
|
||
// if ( !activeCheckpoint || activeCheckpoint.length === 0) return <p></p>; | ||
const handleViewReport = () => { | ||
navigate(`/assessment/${assessmentId}/report`); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div className="checkpoint-ctas" > | ||
<button className="view-report-cta" onClick={() => { | ||
axios.get('/json/report.json').then(res => { | ||
const modalData = { | ||
type: "report", | ||
content: res.data[0] | ||
}; | ||
setModal(modalData); | ||
}); | ||
}} | ||
> | ||
<div className="cta-inner"> | ||
<div className="cta-title">View your report</div> | ||
<div className="cta-content"> | ||
|
||
<div className="cta-text">You can review your report at anytime. However, the more checkpoints you tackle, the more useful the report will be.</div> | ||
|
||
</div> | ||
|
||
<div className="button button-white"> | ||
Read the report | ||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||
<line x1="10.1424" y1="0.431885" x2="10.1425" y2="19.5679" stroke="#2254F4" strokeWidth="2.2849"/> | ||
<line x1="0.429688" y1="9.85706" x2="19.5657" y2="9.85706" stroke="#2254F4" strokeWidth="2.2849"/> | ||
</svg> | ||
|
||
</div> | ||
<div className="viewReport"> | ||
<button className="view-report-cta" onClick={handleViewReport}> | ||
<div className="cta-inner"> | ||
<div className="cta-title">View your report</div> | ||
<div className="cta-content"> | ||
<div className="cta-image"></div> | ||
<div className="cta-text"> | ||
<div className="text">View your report in many formats so you can easily reuse it.</div> | ||
<div className="buttons-container"> | ||
<button className="button button-white">View Report</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</button> | ||
</div> | ||
|
||
|
||
</> | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// src/DownloadReportButton.js | ||
|
||
import React from 'react'; | ||
import axiosInstance from './axiosInstance'; | ||
|
||
export default function DownloadReportButton({ assessmentId }) { | ||
const handleDownloadReport = async (format) => { | ||
try { | ||
const response = await axiosInstance.get(`/assessments/${assessmentId}/report`, { | ||
headers: { | ||
Accept: format === 'csv' ? 'text/csv' : 'application/json' | ||
}, | ||
responseType: 'blob' // Important for downloading files | ||
}); | ||
|
||
// Create a link element to trigger the download | ||
const url = window.URL.createObjectURL(new Blob([response.data])); | ||
const link = document.createElement('a'); | ||
link.href = url; | ||
link.setAttribute('download', `assessment_report_${assessmentId}.${format}`); | ||
document.body.appendChild(link); | ||
link.click(); | ||
link.remove(); | ||
} catch (error) { | ||
console.error(`Error downloading the ${format} report:`, error); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="checkpoint-ctas"> | ||
<button className="view-report-cta"> | ||
<div className="cta-inner"> | ||
<div className="cta-title">Download your report</div> | ||
<div className="cta-content"> | ||
<div className="cta-image"></div> | ||
<div className="cta-text"> | ||
<div className="text">Download your report in many formats so you can easily reuse it.</div> | ||
<div className="buttons-container"> | ||
<button className="button button-white" onClick={() => handleDownloadReport('json')}>Download JSON</button> | ||
<button className="button button-white" onClick={() => handleDownloadReport('csv')}>Download CSV</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,77 @@ | ||
import React, { useState } from 'react'; | ||
import { useSelector, useDispatch } from 'react-redux' | ||
import React, { useState, useEffect, useCallback } from 'react'; | ||
import { useSelector, useDispatch } from 'react-redux'; | ||
import { debounce } from 'lodash'; | ||
import SaveAndContinue from './SaveAndContinue'; | ||
import SaveAndContinueDisabled from './SaveAndContinueDisabled'; | ||
|
||
|
||
import { | ||
updateCheckpointAnswers, | ||
} from "./checkpointsSlice"; | ||
|
||
import { updateCheckpointAnswers } from './checkpointsSlice'; | ||
|
||
export default function MitigateForm(props) { | ||
const dispatch = useDispatch(); | ||
const activeCheckpointAnswer = useSelector((state) => state.checkpoints.activeCheckpointAnswer); | ||
|
||
const { fields } = props; | ||
|
||
const form_data = {}; | ||
|
||
fields.forEach((field, i) => { | ||
const field_name = field.name; | ||
let val = ""; | ||
if (typeof(activeCheckpointAnswer.form_data) !== "undefined" && typeof(activeCheckpointAnswer.form_data[field_name]) !== "undefined") { | ||
let val = ''; | ||
if (typeof activeCheckpointAnswer.form_data !== 'undefined' && typeof activeCheckpointAnswer.form_data[field_name] !== 'undefined') { | ||
val = activeCheckpointAnswer.form_data[field_name]; | ||
} | ||
|
||
form_data[field_name] = val; | ||
}); | ||
|
||
const [formData, setformData] = useState(form_data); | ||
|
||
|
||
const [formData, setFormData] = useState(form_data); | ||
|
||
// Function to dispatch the updateCheckpointAnswers action | ||
const debouncedDispatch = useCallback( | ||
debounce((answer) => { | ||
dispatch(updateCheckpointAnswers(answer)); | ||
}, 1000), // Adjust the debounce delay as needed (1000ms = 1 second) | ||
[] | ||
); | ||
|
||
const handleChange = (name, value) => { | ||
|
||
const new_form_data = {...formData}; | ||
|
||
const new_form_data = { ...formData }; | ||
new_form_data[name] = value; | ||
|
||
const answer = {...activeCheckpointAnswer}; | ||
const answer = { ...activeCheckpointAnswer }; | ||
answer.form_data = new_form_data; | ||
dispatch(updateCheckpointAnswers(answer)) | ||
setformData(new_form_data) | ||
|
||
} | ||
|
||
return ( | ||
|
||
<div> | ||
// Update local state immediately | ||
setFormData(new_form_data); | ||
|
||
{fields.map((field, i) => { | ||
return ( | ||
<div | ||
key={i} | ||
className="form-element" | ||
> | ||
<label>{field.label}</label> | ||
<textarea | ||
name={field.name} | ||
value={formData[field.name]} | ||
onChange={(e) => handleChange(e.target.name, e.target.value)} | ||
></textarea> | ||
</div> | ||
// Dispatch the debounced action | ||
debouncedDispatch(answer); | ||
}; | ||
|
||
); | ||
})} | ||
|
||
{ | ||
( | ||
!activeCheckpointAnswer.option.explain_risk | ||
|| | ||
typeof(activeCheckpointAnswer.form_data) !== "undefined" && activeCheckpointAnswer.form_data.mitigating_actions.length | ||
) ? <SaveAndContinue /> : <SaveAndContinueDisabled /> | ||
} | ||
useEffect(() => { | ||
// Cleanup function to cancel any pending debounced actions when the component unmounts | ||
return () => { | ||
debouncedDispatch.cancel(); | ||
}; | ||
}, [debouncedDispatch]); | ||
|
||
return ( | ||
<div> | ||
{fields.map((field, i) => ( | ||
<div key={i} className="form-element"> | ||
<label>{field.label}</label> | ||
<textarea | ||
name={field.name} | ||
value={formData[field.name]} | ||
onChange={(e) => handleChange(e.target.name, e.target.value)} | ||
></textarea> | ||
</div> | ||
))} | ||
{(!activeCheckpointAnswer.option.explain_risk || | ||
(typeof activeCheckpointAnswer.form_data !== 'undefined' && | ||
activeCheckpointAnswer.form_data.mitigating_actions.length)) ? ( | ||
<SaveAndContinue /> | ||
) : ( | ||
<SaveAndContinueDisabled /> | ||
)} | ||
</div> | ||
); | ||
|
||
|
||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.