diff --git a/@empirica-mocks/core/mocks.js b/@empirica-mocks/core/mocks.js index 48412c3..b633cb1 100644 --- a/@empirica-mocks/core/mocks.js +++ b/@empirica-mocks/core/mocks.js @@ -80,7 +80,9 @@ export function useStage() { templatesMap, setTemplatesMap, selectedTreatmentIndex, - setSelectedTreatmentIndex + setSelectedTreatmentIndex, + refData, + setRefData, } = useContext(StageContext) // const stage1 = useContext(StageContext); // console.log("useStageMock", stage1) @@ -92,8 +94,46 @@ export function useStage() { //const treatmentString = localStorage.getItem("treatment"); //const treatment = JSON.parse(treatmentString); + var tempStage = null; // for template stages + const stageTemplateName = treatment.treatments[0]?.gameStages[currentStageIndex]?.template || ""; + var fields = treatment.treatments[0]?.gameStages[currentStageIndex]?.fields || []; + if (stageTemplateName !== "") { + tempStage = templatesMap.get(stageTemplateName)[0] + } + console.log("tempStage", tempStage); + + //logic to fill in ${} props + // move logic outside get() + const variablePattern = /\${([^}]+)}/; + {tempStage && + tempStage.elements.forEach(element => { + Object.keys(element).forEach(key => { + const value = element[key]; + + if (typeof value === "string" && variablePattern.test(value)) { + const match = value.match(variablePattern); + if (match) { + console.log("replaced " + match[1] + " with " + fields[match[1]]); + element[key] = fields[match[1]]; + } + } + }); + }); + } + + if (varName === "elements") { - let elements = treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.elements; + var elements + if (tempStage) { + elements = tempStage.elements; + } else { + elements = treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.elements; + } + + console.log("CURRELEMENTS", elements) + + // TODO: change to template if needed + // map to templates first if (Array.isArray(elements)) { elements = elements.flatMap((element) => { if (element.template) { @@ -104,12 +144,69 @@ export function useStage() { } else { elements = []; } - console.log("revised elements", elements) + + //console.log("ELEMENTS_TO_DISPLAY", elements) + // check all conditions + elements = elements.flatMap((element) => { + if (element.conditions) { + // TODO: update with other comparators + const conditions = element.conditions; + const comparator = conditions[0]?.comparator || "x"; + const reference = conditions[0]?.reference || "x"; + const value = conditions[0]?.value || "x"; + if (comparator === "x") { + return [element]; + } else if (comparator === "exists") { + if (refData[`stage_${currentStageIndex}`]?.[reference]) { + const newElement = {...element}; + delete newElement.conditions; + return [newElement]; + } else { + return []; + } + } else if (comparator === "equals") { + if (refData[`stage_${currentStageIndex}`]?.[reference] == value) { + const newElement = {...element}; + delete newElement.conditions; + return [newElement]; + } else { + return []; + } + } else if (comparator === "doesNotEqual") { + if (refData[`stage_${currentStageIndex}`]?.[reference] != value) { + const newElement = {...element}; + delete newElement.conditions; + return [newElement]; + } else { + return []; + } + } + + const condition = conditions.find((condition) => { + if (condition.field) { + return fields[condition.field] === condition.value; + } + return true; + }); + if (condition) { + return [element]; + } + } + return [element]; + }); return elements; } else if (varName === "discussion") { - return treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.discussion; + if (tempStage) { + return tempStage.discussion || []; + } + + return treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.discussion || []; } else if (varName === "name") { - return treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.name; + if (tempStage) { + return tempStage.name; + } + return treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.name + } else if (varName === "index") { return currentStageIndex; } diff --git a/cypress/fixtures/testTemplates3.yaml b/cypress/fixtures/testTemplates3.yaml index ccbe19c..b5368c7 100644 --- a/cypress/fixtures/testTemplates3.yaml +++ b/cypress/fixtures/testTemplates3.yaml @@ -8,7 +8,6 @@ templates: file: projects/example/multipleChoiceColors.md - type: submitButton buttonText: ${submitButtonText} - treatments: - name: simple template test @@ -17,4 +16,3 @@ treatments: - template: testGameStagesTemplate fields: submitButtonText: TestButtonTextProceed - diff --git a/src/app/editor/components/EditElement.tsx b/src/app/editor/components/EditElement.tsx index 3dbe6e8..be110da 100644 --- a/src/app/editor/components/EditElement.tsx +++ b/src/app/editor/components/EditElement.tsx @@ -26,6 +26,13 @@ export function EditElement({ setSelectedTreatmentIndex } = useContext(StageContext) + const stageTemplateName = + treatment.treatments[0]?.gameStages[currentStageIndex]?.template || '' + + console.log("stageTemplateName == ''", stageTemplateName == '') + console.log('stageIndex', stageIndex) + console.log('elementIndex', elementIndex) + const { register, watch, @@ -35,13 +42,17 @@ export function EditElement({ } = useForm({ defaultValues: { name: - treatment?.treatments?.[selectedTreatmentIndex].gameStages[stageIndex]?.elements[ - elementIndex - ]?.name || '', + stageTemplateName == '' + ? treatment?.treatments?.[selectedTreatmentIndex].gameStages[stageIndex]?.elements?.[ + elementIndex + ]?.name || '' + : '', selectedOption: - treatment?.treatments?.[selectedTreatmentIndex].gameStages[stageIndex]?.elements[ - elementIndex - ]?.type || 'Pick one', + stageTemplateName == '' + ? treatment?.treatments?.[selectedTreatmentIndex].gameStages[stageIndex]?.elements?.[ + elementIndex + ]?.type || 'Pick one' + : 'Pick one', file: '', url: '', params: [], diff --git a/src/app/editor/components/ElementCard.tsx b/src/app/editor/components/ElementCard.tsx index 4a56db9..1333da0 100644 --- a/src/app/editor/components/ElementCard.tsx +++ b/src/app/editor/components/ElementCard.tsx @@ -1,4 +1,4 @@ -import React, { useState, useContext } from 'react' +import React, { useState, useContext, useEffect } from 'react' import { Modal } from './Modal' import { EditElement } from './EditElement' import { TreatmentType } from '../../../../deliberation-empirica/server/src/preFlight/validateTreatmentFile' @@ -12,6 +12,7 @@ export function ElementCard({ stageIndex, elementIndex, elementOptions, + isTemplate, }: { element: any scale: number @@ -20,10 +21,18 @@ export function ElementCard({ stageIndex: number elementIndex: number elementOptions: any + isTemplate: boolean }) { const startTime = element.displayTime || 0 const endTime = element.hideTime || stageDuration const [modalOpen, setModalOpen] = useState(false) + const [isElementTemplate, setIsElementTemplate] = useState(false) + + useEffect(() => { + if (element.template) { + setIsElementTemplate(true) + } + }, [element]) const { currentStageIndex, @@ -49,22 +58,25 @@ export function ElementCard({
{Object.keys(element).map((key) => (

- {key}: {element[key]} + {key}: {element[key].toString()}

))}
- + + {!isElementTemplate && !isTemplate && ( + + )} diff --git a/src/app/editor/components/ReferenceData.tsx b/src/app/editor/components/ReferenceData.tsx index 1f27d31..66ff515 100644 --- a/src/app/editor/components/ReferenceData.tsx +++ b/src/app/editor/components/ReferenceData.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useContext } from 'react' +import { StageContext } from '@/editor/stageContext' // helper to format references const formatReference = (reference: string) => { @@ -15,15 +16,25 @@ const getPlaceholderText = (reference: string) => { } // find 'references' in the treatment object by stage (recursively..hopefully runtime not too bad) -const findReferencesByStage = (obj: any): any[] => { +const findReferencesByStage = (obj: any, templatesMap: any): any[] => { let references: any[] = [] if (typeof obj === 'object' && obj !== null) { for (const key in obj) { if (key === 'reference') { references.push(obj[key]) + } else if (key === 'template') { + const templateReferences = templatesMap + .get(obj[key]) + .flatMap((templateElement: any) => + findReferencesByStage(templateElement, templatesMap) + ) + + references = references.concat(templateReferences) } else if (typeof obj[key] === 'object') { - references = references.concat(findReferencesByStage(obj[key])) + references = references.concat( + findReferencesByStage(obj[key], templatesMap) + ) } } } @@ -32,10 +43,10 @@ const findReferencesByStage = (obj: any): any[] => { } // initializing json data for each stage -const initializeJsonData = (treatment: any) => { +const initializeJsonData = (treatment: any, templatesMap: any) => { const jsonData: { [key: string]: any } = {} treatment?.gameStages?.forEach((stage: any, index: number) => { - const references = findReferencesByStage(stage) + const references = findReferencesByStage(stage, templatesMap) jsonData[`stage_${index}`] = {} references.forEach((reference) => { jsonData[`stage_${index}`][reference] = '' @@ -70,16 +81,18 @@ const ReferenceData = ({ treatment, stageIndex }: ReferenceDataProps) => { const [jsonData, setJsonData] = useState({}) const [inputValues, setInputValues] = useState({}) + const { refData, setRefData, templatesMap } = useContext(StageContext) + // load refs for curr stage useEffect(() => { if (treatment?.gameStages?.[stageIndex]) { const stage = treatment.gameStages[stageIndex] - const allReferences = findReferencesByStage(stage) + const allReferences = findReferencesByStage(stage, templatesMap) setReferences(allReferences) // load json data for curr stage if (!jsonData[`stage_${stageIndex}`]) { - const initializedJson = initializeJsonData(treatment) + const initializedJson = initializeJsonData(treatment, templatesMap) setJsonData((prev) => ({ ...prev, ...initializedJson })) } @@ -123,6 +136,7 @@ const ReferenceData = ({ treatment, stageIndex }: ReferenceDataProps) => { [`stage_${stageIndex}`]: inputValues[`stage_${stageIndex}`], } setJsonData(updatedJson) + setRefData(updatedJson) localStorage.setItem('jsonData', JSON.stringify(updatedJson)) console.log('Saved JSON Data:', JSON.stringify(updatedJson, null, 2)) } @@ -134,6 +148,7 @@ const ReferenceData = ({ treatment, stageIndex }: ReferenceDataProps) => { if (savedJson) { console.log('Loaded JSON Data from localStorage:', JSON.parse(savedJson)) setJsonData(JSON.parse(savedJson)) + setRefData(JSON.parse(savedJson)) } if (savedInputValues) { diff --git a/src/app/editor/components/StageCard.tsx b/src/app/editor/components/StageCard.tsx index 2e527fa..d43387e 100644 --- a/src/app/editor/components/StageCard.tsx +++ b/src/app/editor/components/StageCard.tsx @@ -13,6 +13,7 @@ import { setCurrentStageIndex } from './utils' import { useStage } from '../../../../@empirica-mocks/core/mocks' import { StageContext } from '../stageContext.jsx' import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd' +import { i } from 'node_modules/@empirica/core/dist/context-302225e8' export function StageCard({ title, @@ -22,6 +23,7 @@ export function StageCard({ sequence, stageIndex, setRenderPanelStage, + isTemplate, }: { title: string elements: any[] @@ -30,6 +32,7 @@ export function StageCard({ sequence: string stageIndex: number setRenderPanelStage: any + isTemplate: boolean }) { const { currentStageIndex, @@ -121,20 +124,22 @@ export function StageCard({ >

{title}

- + {!isTemplate && ( + + )} @@ -154,32 +159,49 @@ export function StageCard({ {...provided.droppableProps} > {elements !== undefined && - elements.map((element, index) => ( - - {(provided) => ( -
- -
- )} -
- ))} + elements.map((element, index) => + isTemplate ? ( +
+ +
+ ) : ( + + {(provided) => ( +
+ +
+ )} +
+ ) + )} {provided.placeholder}
)} @@ -187,25 +209,27 @@ export function StageCard({ {/* Add Element Button*/} -
- + {!isTemplate && ( +
+ - - - -
+ + + +
+ )} ) } diff --git a/src/app/editor/components/Timeline.tsx b/src/app/editor/components/Timeline.tsx index e203253..8c3f3c2 100644 --- a/src/app/editor/components/Timeline.tsx +++ b/src/app/editor/components/Timeline.tsx @@ -226,33 +226,56 @@ export default function Timeline({ ref={provided.innerRef} className="flex flex-row gap-x-1" > - {filterStages( - treatment?.treatments?.[selectedTreatmentIndex] - )?.map((obj: any, index: any) => ( - - {(provided) => ( -
- -
- )} -
- ))} + {filterStages(treatment?.treatments?.[selectedTreatmentIndex])?.map( + (obj: any, index: any) => ( + + {(provided) => ( +
+ {obj.stage.name && ( + + )} + {obj.stage.template && ( + + )} +
+ )} +
+ ) + )} {provided.placeholder} )} diff --git a/src/app/editor/stageContext.jsx b/src/app/editor/stageContext.jsx index 2ef8631..491676c 100644 --- a/src/app/editor/stageContext.jsx +++ b/src/app/editor/stageContext.jsx @@ -25,6 +25,7 @@ const StageProvider = ({ children }) => { const [selectedIntroSequenceIndex, setSelectedIntroSequenceIndex] = useState(0) const player = usePlayer() + const [refData, setRefData] = useState({}) // for updating code editor, requires reload const editTreatment = useCallback((newTreatment) => { @@ -48,6 +49,8 @@ const StageProvider = ({ children }) => { setSelectedTreatmentIndex, selectedIntroSequenceIndex, setSelectedIntroSequenceIndex, + refData, + setRefData, }), [ currentStageIndex, setCurrentStageIndex, @@ -63,6 +66,8 @@ const StageProvider = ({ children }) => { setSelectedTreatmentIndex, selectedIntroSequenceIndex, setSelectedIntroSequenceIndex, + refData, + setRefData, ]) // expose context values to the window object