import React, { useState, useEffect } from "react"; import "../styles/addrubric.css"; const AddRubric = ({ getRubrics, handleAddRubricModal, duplicatedRubricData }) => { // IMPORTANT: rubricData contains all the data collected from each modal const [rubricData, setRubricData] = useState({}); const [errorMessage, setErrorMessage] = useState({}); // (Modal 1) Create Rubric Levels const [deleteColumnHovered, setDeleteColumnsHovered] = useState([]); const [showCreateLevelsModal, setShowCreateLevelsModal] = useState(true); // (Modal 2) Create Rubric Criteria const [deleteRowHovered, setDeleteRowsHovered] = useState([]); const [showCreateCriteriaModal, setShowCreateCriteriaModal] = useState(false); // (Modal 3) Preview Rubric const [showPreviewModal, setShowPreviewModal] = useState(false); // (Modal 1) Rubric Levels // Add a new column to the rubric const handleAddColumn = () => { const updatedData = { ...rubricData, levels: [...rubricData["levels"], { name: "", score: "" }] }; setRubricData(updatedData); setDeleteColumnsHovered([...deleteColumnHovered, false]); } // Delete a column from the rubric const handleDeleteColumn = (indexToRemove) => { const newErrorMessage = { ...errorMessage }; const currentErrorMessage = Object.entries(errorMessage) if (currentErrorMessage.length > 0 && !newErrorMessage["rubric-name"]) { const currentLevelNumber = parseInt(currentErrorMessage[0][0].split("level-")[1]) const newLevelNumber = currentLevelNumber - 1 if (indexToRemove <= currentLevelNumber) { setErrorMessage({ ["level-" + newLevelNumber.toString()]: currentErrorMessage[0][1] }) if (currentLevelNumber === indexToRemove) { setErrorMessage({}) } } } // Remove the column from the rubric const updatedData = { ...rubricData, levels: rubricData.levels.filter((_, index) => index !== indexToRemove) }; // Remove the column from the error message const updatedTopics = rubricData.topics.map((topic) => ({ ...topic, responses: topic.responses.filter((_, index) => index !== indexToRemove) })); updatedData["topics"] = updatedTopics setRubricData(updatedData); // Remove the column from the delete button states const updatedColumnDisplay = deleteColumnHovered.filter((_, index) => index !== indexToRemove); setDeleteColumnsHovered(updatedColumnDisplay); }; // Set the delete button states for columns const handleDeleteColumnsHovered = (action, index) => { if (action === "hovered") { const updatedData = [...deleteColumnHovered]; updatedData[index] = true; setDeleteColumnsHovered(updatedData); } else { const updatedData = [...deleteColumnHovered]; updatedData[index] = false; setDeleteColumnsHovered(updatedData); } } /** * Handles the change of the name of a level in the rubric. * @param index * @param value * @returns {void} */ const handleLevelNameChange = (index, value) => { if (errorMessage["level-" + index.toString()] && errorMessage["level-" + index.toString()]["name"]) { setErrorMessage({}); } const updatedData = { ...rubricData }; updatedData["levels"][index]["name"] = value; setRubricData(updatedData); }; /** * Handles the change of the score of a level in the rubric. * @param {number} index The index of the level in the rubric. * @param {number} value The new score of the level. * @returns {void} */ const handleLevelPointsChange = (index, value) => { if (errorMessage["level-" + index.toString()] && !errorMessage["level-" + index.toString()]["name"] && errorMessage["level-" + index.toString()]["level"]) { setErrorMessage({}); } const updatedData = { ...rubricData }; updatedData["levels"][index]["score"] = parseInt(value); setRubricData(updatedData); }; /** * Handles the change of the name of the rubric. * @param value * @returns {void} */ const handleRubricNameChange = (value) => { if (errorMessage["rubric-name"]) { setErrorMessage({}); } const updatedData = { ...rubricData } updatedData["name"] = value; setRubricData(updatedData) }; // (Modal 2) Rubric Criteria /** * Adds a new row to the rubric. */ const handleAddRow = () => { const emptyResponses = Array.from({ length: rubricData.levels.length }, () => ""); const updatedData = { ...rubricData, topics: [...rubricData["topics"], { question: "", responses: emptyResponses, type: "multiple_choice" }] }; setRubricData(updatedData); setDeleteRowsHovered([...deleteRowHovered, false]); }; /** * Handles the change of the name of a criterion in the rubric. * @param index * @param value */ const handleCriterionNameChange = (index, value) => { if (errorMessage["criterion-" + index.toString()] && errorMessage["criterion-" + index.toString()]["name"]) { setErrorMessage({}); } const updatedData = { ...rubricData }; updatedData["topics"][index]["question"] = value; setRubricData(updatedData); }; /** * Handles the change of the response of a criterion in the rubric. * @param criterionIndex * @param levelIndex * @param value */ const handleCriterionResponseChange = (criterionIndex, levelIndex, value) => { if (errorMessage["criterion-" + criterionIndex.toString()] && !errorMessage["criterion-" + criterionIndex.toString()]["name"] && errorMessage["criterion-" + criterionIndex.toString()]["level-" + levelIndex.toString()]) { setErrorMessage({}); } const updatedData = { ...rubricData }; updatedData["topics"][criterionIndex]["responses"][levelIndex] = value; setRubricData(updatedData); }; /** * Deletes a row from the rubric. * @param indexToRemove */ const handleDeleteRow = (indexToRemove) => { const currentErrorMessage = Object.entries(errorMessage) if (currentErrorMessage.length > 0) { const currenCriterionNumber = parseInt(currentErrorMessage[0][0].split("criterion-")[1]) const newCriterionNumber = currenCriterionNumber - 1 if (indexToRemove <= currenCriterionNumber) { setErrorMessage({ ["criterion-" + newCriterionNumber.toString()]: currentErrorMessage[0][1] }) if (currenCriterionNumber === indexToRemove) { setErrorMessage({}) } } } const updatedData = { ...rubricData, topics: rubricData["topics"].filter((_, index) => index !== indexToRemove) }; const updatedRowDisplay = deleteRowHovered.filter((_, index) => index !== indexToRemove); setRubricData(updatedData); setDeleteRowsHovered(updatedRowDisplay); }; /** * Sets the delete button states for rows. * @param action * @param index */ const handleDeleteRowsHovered = (action, index) => { if (action === "hovered") { const updatedData = [...deleteRowHovered]; updatedData[index] = true; setDeleteRowsHovered(updatedData); } else { const updatedData = [...deleteRowHovered]; updatedData[index] = false; setDeleteRowsHovered(updatedData); } }; /** * Handles the click of the back button. */ const handleBackButton = () => { if (showCreateCriteriaModal) { setShowCreateLevelsModal(true); setShowCreateCriteriaModal(false); setShowPreviewModal(false); } else if (showPreviewModal) { setShowCreateLevelsModal(false); setShowCreateCriteriaModal(true); setShowPreviewModal(false); } } /** * Handles the click of the next button. */ const handleNextButton = async () => { if (showCreateLevelsModal) { let errors = await fetchRubricErrors("rubricInitialize.php"); if (errors.length === 0) { setShowCreateLevelsModal(false); setShowCreateCriteriaModal(true); setShowPreviewModal(false); } } else if (showCreateCriteriaModal) { let errors = await fetchRubricErrors("rubricSetCriterions.php"); if (errors.length === 0) { setShowCreateLevelsModal(false); setShowCreateCriteriaModal(false); setShowPreviewModal(true); } } else { let errors = await fetchSaveRubric(); setShowPreviewModal(false); getRubrics(); handleAddRubricModal(false); } }; // Fetches /** * Fetches the errors from the API. * @param filename */ const fetchRubricErrors = async (filename) => { try { const response = await fetch( process.env.REACT_APP_API_URL + filename, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(rubricData) } ); const result = await response.json(); if (result["errors"] && Object.keys(result["errors"]).length > 0) { const [errorKey, errorValue] = Object.entries(result["errors"])[0]; setErrorMessage({ [errorKey]: errorValue }); } return result["errors"]; } catch (error) { console.error(error); } }; /** * Fetches the save rubric from the API. */ const fetchSaveRubric = async () => { try { const response = await fetch( process.env.REACT_APP_API_URL + "rubricConfirm.php", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ "save-rubric": 1 }), } ); return } catch (error) { console.error(error); } }; useEffect(() => { if (duplicatedRubricData) { // + Duplicate Rubric setRubricData(duplicatedRubricData) } else { // + Add Rubric // Set Delete Button States for Columns and Rows const defaultDeleteColumnsHovered = [] const defaultDeleteRowsHovered = [] // Set Levels const defaultLevelAmount = 4; const defaultLevels = []; for (let i = 0; i < defaultLevelAmount; i++) { defaultLevels.push({ name: "", score: "" }); defaultDeleteColumnsHovered.push(false); } setDeleteColumnsHovered(defaultDeleteColumnsHovered); // Set Criterions const defaultCriterionAmount = 4; const defaultCriterions = []; for (let i = 0; i < defaultCriterionAmount; i++) { const emptyResponses = Array.from({ length: defaultLevelAmount }, () => ""); defaultCriterions.push({ question: "", responses: emptyResponses, type: "multiple_choice" }); defaultDeleteRowsHovered.push(false); } setDeleteRowsHovered(defaultDeleteRowsHovered) // Set Rubric Data setRubricData({ name: "", levels: defaultLevels, topics: defaultCriterions }) } }, []) /** * Handles the click of the close button. */ return (
{rubricData["levels"].map((_, index) => ( |
|
))}
|
Criterion | {rubricData["levels"] && rubricData["levels"].map((level, index) => (
handleLevelNameChange(index, e.target.value)}
placeholder="Name"
required
type="text"
value={level["name"]}
/>
handleLevelPointsChange(index, e.target.value)}
placeholder={index}
required
type="number"
value={level["score"]}
/>
pts
{errorMessage["level-" + index.toString()] && (
{errorMessage["level-" + index.toString()]["name"] ? (
errorMessage["level-" + index.toString()]["name"]
) :
errorMessage["level-" + index.toString()]["level"]
}
|
))}
{rubricData["levels"] && rubricData["levels"].length < 5 ? (
) : null} |
{rubricData["levels"] && rubricData["levels"].map((_, levelIndex) => ( | ))} {rubricData["levels"] && rubricData["levels"].length < 5 ? ( | ) : null} |
Criterion | {rubricData["levels"] && rubricData["levels"].map((level, index) => ({`${level.name} (${level.score} pts)`} | ))} {rubricData["topics"] && rubricData["topics"].length > 1 && ()} |
{rubricData["levels"] && rubricData["levels"].map((_, levelIndex) => ( | ))} {rubricData["topics"] && rubricData["topics"].length > 1 ? ( | |
{rubricData["levels"] && rubricData["levels"].map((level, levelIndex) => ( | ))} |
Criterion | {rubricData["levels"] && rubricData["levels"].map((level, index) => ({`${level.name} (${level.score} pts)`} | ))}
{criterion.question} | {rubricData["levels"] && rubricData["levels"].map((_, levelIndex) => ({criterion["responses"][levelIndex]} | ))}