Skip to content
Snippets Groups Projects
Commit 4dbc8269 authored by Saket Agrahari's avatar Saket Agrahari
Browse files

COMP-213: progress bar

parent 6406643a
No related branches found
No related tags found
1 merge request!46COMP-213: progress bar
import React from "react";
function ProgressBar({
completionPercentage,
unansweredPercentage,
pages,
pageTitle,
}) {
const progressBarContainerStyle: React.CSSProperties = {
display: "flex",
flexWrap: "wrap",
height: "10px",
margin: "5px",
width: `${100 / pages}%`,
};
const progressBarFillStyle: React.CSSProperties = {
height: "100%",
transition: "width 0.3s ease",
};
const progressBarFillStyleCopy: React.CSSProperties = {
...progressBarFillStyle,
width: `${completionPercentage}%`,
backgroundColor: "#1ab394",
};
const unansweredProgressBarFillStyle: React.CSSProperties = {
...progressBarFillStyle,
width: `${unansweredPercentage}%`,
backgroundColor: "#9d9d9d",
};
const pageTitleStyle: React.CSSProperties ={
width: "100%",
textAlign: "center"
}
return (
<div style={progressBarContainerStyle}>
<div style={progressBarFillStyleCopy} />
<div style={unansweredProgressBarFillStyle} />
<div style={pageTitleStyle}>{pageTitle}</div>
</div>
);
}
export default ProgressBar;
...@@ -3,11 +3,17 @@ import { Model, Serializer, ComputedUpdater, Question } from "survey-core"; ...@@ -3,11 +3,17 @@ import { Model, Serializer, ComputedUpdater, Question } from "survey-core";
import { Survey } from "survey-react-ui"; import { Survey } from "survey-react-ui";
import "survey-core/modern.min.css"; import "survey-core/modern.min.css";
import './survey.scss'; import './survey.scss';
import ProgressBar from "./ProgressBar";
Serializer.addProperty("itemvalue", "customDescription:text"); Serializer.addProperty("itemvalue", "customDescription:text");
Serializer.addProperty("question", "hideCheckboxLabels:boolean"); Serializer.addProperty("question", "hideCheckboxLabels:boolean");
interface Progress {
completionPercentage: number;
unansweredPercentage: number;
totalPages: number;
pageTitle: string;
}
enum VerificationStatus { enum VerificationStatus {
New = "new", // a question that was not answered last year New = "new", // a question that was not answered last year
Answered = "answered", // a question that was not answered last year but has an answer now Answered = "answered", // a question that was not answered last year but has an answer now
...@@ -18,9 +24,9 @@ enum VerificationStatus { ...@@ -18,9 +24,9 @@ enum VerificationStatus {
function SurveyComponent() { function SurveyComponent() {
const [surveyModel, setSurveyModel] = useState<Model>();
const [surveyModel, setSurveyModel] = useState<Model>(); const [progress, setProgress] = useState<Progress[]>([]);
const verificationStatus = useRef<Map<string, VerificationStatus>>(new Map()); const verificationStatus = useRef<Map<string, VerificationStatus>>(new Map());
function setVerifyButton(question: Question, state: VerificationStatus) { function setVerifyButton(question: Question, state: VerificationStatus) {
...@@ -52,9 +58,9 @@ function SurveyComponent() { ...@@ -52,9 +58,9 @@ function SurveyComponent() {
} }
} }
// const surveyComplete = useCallback((sender) => { // const surveyComplete = useCallback((sender) => {
// console.log(sender.data); // console.log(sender.data);
// }, []); // }, []);
async function getModel() async function getModel()
{ {
...@@ -67,10 +73,9 @@ function SurveyComponent() { ...@@ -67,10 +73,9 @@ function SurveyComponent() {
const survey = new Model(json['model']); const survey = new Model(json['model']);
if (json['data'] !== null) if (json['data'] !== null) {
{ survey.data = json['data'];
survey.data = json['data']; }
}
// TODO also use data and page info // TODO also use data and page info
...@@ -103,25 +108,24 @@ function SurveyComponent() { ...@@ -103,25 +108,24 @@ function SurveyComponent() {
innerCss: "sv-btn sv-btn--navigation sv-footer__complete-btn" innerCss: "sv-btn sv-btn--navigation sv-footer__complete-btn"
}); });
survey.onComplete.add((sender, options) => { survey.onComplete.add((sender, options) => {
console.log(sender.data);
console.log(sender.data)
options.showSaveInProgress();
options.showSaveInProgress(); const xhr = new XMLHttpRequest();
const xhr = new XMLHttpRequest(); xhr.open("POST", "/api/survey/save");
xhr.open("POST", "/api/survey/save"); xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); xhr.onload = xhr.onerror = function () {
xhr.onload = xhr.onerror = function () { if (xhr.status == 200) {
if (xhr.status == 200) { // Display the "Success" message (pass a string value to display a custom message)
// Display the "Success" message (pass a string value to display a custom message) options.showSaveSuccess();
options.showSaveSuccess(); // Alternatively, you can clear all messages:
// Alternatively, you can clear all messages: // options.clearSaveMessages();
// options.clearSaveMessages(); } else {
} else { // Display the "Error" message (pass a string value to display a custom message)
// Display the "Error" message (pass a string value to display a custom message) options.showSaveError();
options.showSaveError(); }
} };
};
const saveData = { const saveData = {
data: sender.data, data: sender.data,
...@@ -133,9 +137,8 @@ function SurveyComponent() { ...@@ -133,9 +137,8 @@ function SurveyComponent() {
survey.onPartialSend.add((sender, options) => { survey.onPartialSend.add((sender, options) => {
console.log(sender.data) console.log(sender.data)
// TODO same as above // TODO same as above
});
});
survey.onAfterRenderQuestion.add(function(survey, options){ survey.onAfterRenderQuestion.add(function(survey, options){
const status = verificationStatus.current.get(options.question.name); const status = verificationStatus.current.get(options.question.name);
...@@ -160,38 +163,90 @@ function SurveyComponent() { ...@@ -160,38 +163,90 @@ function SurveyComponent() {
} }
}); });
survey.onMatrixAfterCellRender.add((survey, options) => { survey.onMatrixAfterCellRender.add((survey, options) => {
// get the customDescription for matrix rows and set it in the title // get the customDescription for matrix rows and set it in the title
// attribute so that it shows up as a hover popup // attribute so that it shows up as a hover popup
// NB I would have preferred using onAfterRenderQuestion, but unfortunately that is // NB I would have preferred using onAfterRenderQuestion, but unfortunately that is
// not always triggered on re-renders (specifically when extra column become visble or invisible) // not always triggered on re-renders (specifically when extra column become visble or invisible)
if (options.column['indexValue'] == 0 && 'item' in options.row) { if (options.column['indexValue'] == 0 && 'item' in options.row) {
const item = options.row['item'] as object; const item = options.row['item'] as object;
if (item['customDescription'] !== undefined) { if (item['customDescription'] !== undefined) {
options.htmlElement.parentElement?.children[0].setAttribute("title", item['customDescription']); options.htmlElement.parentElement?.children[0].setAttribute(
} "title",
} item['customDescription']
);
}
}
});
survey.onCurrentPageChanged.add((sender) => {
console.log("sender--> " + sender);
calculateProgress(sender);
});
setSurveyModel(survey);
}
const filterCallback = (question) => {
return question.value !== null && question.value !== undefined;
};
const calculateProgress = (survey) => {
// console.log("survey--> "+ survey);
if (survey && survey.pages) {
console.log("survey.page--> " + survey.pages);
const progressArray: Progress[] = [];
survey.pages.forEach((page) => {
const sectionQuestions = page.questions.filter(
(question) => question.startWithNewLine
);
const questionCount = sectionQuestions.length;
const answeredCount = sectionQuestions.filter(filterCallback).length;
const unansweredCount = questionCount - answeredCount;
const completionPercentage = answeredCount / questionCount;
progressArray.push({
completionPercentage: completionPercentage * 100,
unansweredPercentage: (unansweredCount / questionCount) * 100,
totalPages: survey.pages.length,
pageTitle: page.title,
}); });
});
setSurveyModel(survey); setProgress(progressArray);
} }
};
useEffect(() => { useEffect(() => {
getModel(); getModel();
}, []); }, []);
if (surveyModel) useEffect(() => {
{ if (surveyModel) {
return (<Survey model={surveyModel} />); calculateProgress(surveyModel);
} }
else }, [surveyModel]);
{
return (<span>loading...</span>); if (surveyModel) {
} return (
<div className="survey-container">
<div className="survey-progress">
{progress.map((sectionProgress, index) => (
<ProgressBar
key={index}
completionPercentage={sectionProgress.completionPercentage}
unansweredPercentage={sectionProgress.unansweredPercentage}
pages={sectionProgress.totalPages}
pageTitle={sectionProgress.pageTitle}
/>
))}
</div>
<Survey model={surveyModel} />
</div>
);
} else {
return <span>loading...</span>;
}
} }
export default SurveyComponent; export default SurveyComponent;
\ No newline at end of file
...@@ -24,4 +24,8 @@ ...@@ -24,4 +24,8 @@
.sv-action-bar-item.verification.verification-ok:hover { .sv-action-bar-item.verification.verification-ok:hover {
cursor: auto; cursor: auto;
background-color: transparent; background-color: transparent;
}
.survey-progress {
display: flex;
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment