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 */}
- {/* Right Panel */}
+
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.";