diff --git a/client/src/components/exercise/lab15/Main.js b/client/src/components/exercise/lab15/Main.js index c2c39f533..81f0af49c 100644 --- a/client/src/components/exercise/lab15/Main.js +++ b/client/src/components/exercise/lab15/Main.js @@ -32,8 +32,8 @@ const Main = () => { - + diff --git a/client/src/components/exercise/lab15/pages/ModelRepair.js b/client/src/components/exercise/lab15/pages/ModelRepair.js index 6065e4923..66ed0a8e4 100644 --- a/client/src/components/exercise/lab15/pages/ModelRepair.js +++ b/client/src/components/exercise/lab15/pages/ModelRepair.js @@ -34,7 +34,7 @@ const ModelRepair = () => { }, ]} navigateNext={() => { - navigate(`${EXERCISE_PATH}/model-with-grades`); + navigate(`${EXERCISE_PATH}/prompt-builder`); }} repairComplete /> diff --git a/client/src/components/exercise/lab15/pages/PromptBuilder.js b/client/src/components/exercise/lab15/pages/PromptBuilder.js index 4e6292d20..45f820de8 100644 --- a/client/src/components/exercise/lab15/pages/PromptBuilder.js +++ b/client/src/components/exercise/lab15/pages/PromptBuilder.js @@ -1,48 +1,142 @@ import { navigate } from "@reach/router"; -import React, { useState } from "react"; + +import LabButton from "src/components/all-components/LabButton"; +import Quiz from "src/components/quiz/components/Quiz"; +import { GCSE_SECTIONS } from "src/constants/lab15"; +import { + PROMPT_BUILDER_DESCRIPTION, + PROMPT_BUILDER_HEADING, + PROMPT_COMPLETE_FAILED_MESSAGE, + PROMPT_COMPLETE_PASSED_MESSAGE, + PROMPT_QUESTION_TEMPLATE, + STAGE_OPTIONS, +} from "src/constants/lab15/PromptBuilderConfig"; import PromptViewer from "../components/PromptViewer"; -import { GCSE_SECTIONS } from "../../../../constants/lab15"; -import LabButton from "../../../all-components/LabButton"; - -const ModelRepair = () => { - const [sectionValues] = useState({ - values: GCSE_SECTIONS.reduce((acc, section) => { - acc[section.key] = null; - return acc; - }, {}), - activeIndex: 0, - lockedKeys: [], - justLockedKey: null, - }); - - const activeSection = GCSE_SECTIONS[sectionValues.activeIndex]; - const activeKey = activeSection?.key ?? null; +import usePromptBuilderStageManager from "../components/PromptStateManager"; + +const PromptBuilder = () => { + const { + stages, + currentStage, + currentStageIndex, + selections, + lockedKeys, + justLockedKey, + totalScore, + passingScore, + canMoveToNextStage, + allStagesAnswered, + hasMetPassingScore, + selectStageOption, + lockCurrentStage, + clearJustLockedKey, + nextStage, + previousStage, + } = usePromptBuilderStageManager(); + + const activeOptions = STAGE_OPTIONS[currentStage] || []; + const isFinalStage = currentStageIndex === stages.length - 1; + + const handleNext = () => { + if (!canMoveToNextStage) return; + clearJustLockedKey(); + requestAnimationFrame(() => { + lockCurrentStage(); + }); + + if (!isFinalStage) { + nextStage(); + } + }; + + const handleBack = () => { + if (currentStageIndex === 0) return; + previousStage(); + }; + + const handleAnswerSelected = (event) => { + const optionIndex = Number(event.target.value); + const selectedOption = activeOptions[optionIndex]; + if (!selectedOption) return; + + selectStageOption(currentStage, selectedOption.text, selectedOption.score); + }; + + const quizAnswerOptions = activeOptions.map((option, index) => ({ + type: String(index), + content: option.text, + })); + + const questionLabel = + GCSE_SECTIONS[currentStageIndex]?.label || GCSE_SECTIONS[0].label; + + const sectionValues = { + values: selections, + lockedKeys, + justLockedKey, + }; + const activeKey = currentStage; return (
-

Exercise Start

+

{PROMPT_BUILDER_HEADING}

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam. + {PROMPT_BUILDER_DESCRIPTION}

+
- {/* Left Panel */}
-
- {/* QUIZ COMPONENT TO BE ADDED */} +
+

+ {currentStage} +

+
+ Score: {totalScore}/{passingScore} +
+ + {}} + nextQuestion={handleNext} + lastQuestion={handleBack} + onAnswerSelected={handleAnswerSelected} + onComplete={() => { + clearJustLockedKey(); + requestAnimationFrame(() => { + lockCurrentStage(); + }); + }} + questionId={currentStageIndex + 1} + question={PROMPT_QUESTION_TEMPLATE.replace( + "{label}", + questionLabel, + )} + questionTotal={stages.length} + /> + + {allStagesAnswered && ( +
+
+ {hasMetPassingScore + ? PROMPT_COMPLETE_PASSED_MESSAGE + : PROMPT_COMPLETE_FAILED_MESSAGE} +
+
+ )}
- {/* Divider */} +
navigate("/Lab15/Exercise/model-repair")} + onClick={() => navigate("/Lab15/Exercise/model-with-grades")} />
); }; -export default ModelRepair; +export default PromptBuilder; diff --git a/client/src/constants/lab15/PromptBuilderConfig.js b/client/src/constants/lab15/PromptBuilderConfig.js new file mode 100644 index 000000000..00a02906c --- /dev/null +++ b/client/src/constants/lab15/PromptBuilderConfig.js @@ -0,0 +1,60 @@ +export const STAGE_OPTIONS = { + goal: [ + { + id: "goal-1", + text: "Help me with science.", + score: 1, + }, + { + id: "goal-2", + text: "Explain Newton's First Law in simple terms for a middle school student.", + score: 3, + }, + ], + context: [ + { + id: "context-1", + text: "I need this quickly.", + score: 1, + }, + { + id: "context-2", + text: "This is for a class recap and I struggled with motion and forces this week.", + score: 3, + }, + ], + sources: [ + { + id: "sources-1", + text: "Use anything online.", + score: 1, + }, + { + id: "sources-2", + text: "Use our textbook chapter on Forces and cite one trustworthy educational source.", + score: 3, + }, + ], + expectations: [ + { + id: "expectations-1", + text: "Make it short.", + score: 1, + }, + { + id: "expectations-2", + text: "Answer in 4 bullet points and include one real-world example.", + score: 3, + }, + ], +}; + +export const PROMPT_BUILDER_HEADING = "Exercise Start"; +export const PROMPT_BUILDER_DESCRIPTION = + "Build your prompt section by section using GCSE. Choose the strongest option in each category to improve your score."; +export const PROMPT_COMPLETE_PASSED_MESSAGE = + "Prompt complete. You passed the quality threshold."; +export const PROMPT_COMPLETE_FAILED_MESSAGE = + "Prompt complete, but score is below passing. Improve your choices."; +export const PROMPT_QUESTION_TEMPLATE = + "Choose the best {label} statement for your prompt.";