From 740e69918e629c25d12f0a683a5de1e213d2f301 Mon Sep 17 00:00:00 2001 From: Evan Ping Date: Tue, 25 Jun 2024 14:16:16 -0400 Subject: [PATCH 01/20] implemented react-hook-form to addpopup --- cypress/e2e/test.cy.ts | 15 +- deliberation-empirica | 2 +- package-lock.json | 358 +++++++++++++----------- package.json | 1 + src/app/editor/components/AddPopup.tsx | 368 ++++++++++--------------- 5 files changed, 345 insertions(+), 399 deletions(-) diff --git a/cypress/e2e/test.cy.ts b/cypress/e2e/test.cy.ts index 193a1b5..cea233e 100644 --- a/cypress/e2e/test.cy.ts +++ b/cypress/e2e/test.cy.ts @@ -22,10 +22,11 @@ describe('test spec', () => { // add first element to stage 1 cy.get('[data-cy="add-element-button-0"]').click() cy.get('[data-cy="add-popup-name-addElement-0-"]').type("Element 1") - cy.get('[data-cy="add-popup-type-addElement-0-"]').select("prompt") + cy.get('[data-cy="add-popup-type-addElement-0-"]').select("survey") + cy.get('[data-cy="add-popup-onSubmit-addElement-0-"]').type("Thanks!") cy.get('[data-cy="add-popup-save-addElement-0-"]').click() - cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") cy.get('[data-cy="element-0-0"]').contains("Element 1").should("be.visible") // add second element to stage 1 @@ -35,7 +36,7 @@ describe('test spec', () => { cy.get('[data-cy="add-popup-fileAddress-addElement-0-"]').type("file/address") cy.get('[data-cy="add-popup-save-addElement-0-"]').click() - cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") cy.get('[data-cy="element-0-1"]').contains("Prompt").should("be.visible") cy.get('[data-cy="element-0-1"]').contains("file/address").should("be.visible") @@ -61,17 +62,17 @@ describe('test spec', () => { // edit first element cy.get('[data-cy="edit-element-button-0-0"]').click() cy.get('[data-cy="add-popup-name-editElement-0-0"]').type(" Edited") - // cy.get('[data-cy="add-popup-onSubmit-editElement-0-0"]').clear().type("Thanks!") + cy.get('[data-cy="add-popup-onSubmit-editElement-0-0"]').clear().type("Thanks!") cy.get('[data-cy="add-popup-save-editElement-0-0"]').click() - cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") cy.get('[data-cy="element-0-0"]').contains("Element 1 Edited").should("be.visible") // delete second element cy.get('[data-cy="edit-element-button-0-1"]').click() cy.get('[data-cy="add-popup-delete-editElement-0-1"]').click() - cy.get('[data-cy="stage-0"]').should("not.contain", "Element 2") + cy.get('[data-cy="element-0-1"]').should("not.exist") // add fourth element to second stage via code editor cy.get('[data-cy="code-editor"]').type(" - name: Element 4\n type: prompt\nfile: file/address") @@ -100,6 +101,6 @@ describe('test spec', () => { cy.get('[data-cy="edit-stage-button-2"]').click() cy.get('[data-cy="add-popup-delete-editStage-2-"]').click() - cy.get('[data-cy="timeline"]').should("not.contain", "Stage 3") + cy.get('[data-cy="stage-2"]').should("not.exist") }) }) \ No newline at end of file diff --git a/deliberation-empirica b/deliberation-empirica index ebc13a6..546dbb7 160000 --- a/deliberation-empirica +++ b/deliberation-empirica @@ -1 +1 @@ -Subproject commit ebc13a6421b3737fdcbdd81a436a573b3059a1eb +Subproject commit 546dbb771b533e3203561d8a57e56c75612a2161 diff --git a/package-lock.json b/package-lock.json index 39c2acc..e3d9976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react": "^18", "react-debounce-input": "^3.3.0", "react-dom": "^18", + "react-hook-form": "^7.52.0", "react-markdown": "^9.0.1", "react-player": "^2.16.0", "react-resizable-layout": "^0.7.2", @@ -388,6 +389,126 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", + "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", + "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", + "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", + "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", + "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", + "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", + "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", + "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7325,6 +7446,21 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "node_modules/react-hook-form": { + "version": "7.52.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", + "integrity": "sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -9669,126 +9805,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", - "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", - "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", - "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", - "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", - "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", - "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", - "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", - "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } }, "dependencies": { @@ -10067,6 +10083,54 @@ "integrity": "sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==", "optional": true }, + "@next/swc-darwin-x64": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", + "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", + "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", + "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", + "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", + "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", + "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", + "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", + "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", + "optional": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -15093,6 +15157,12 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "react-hook-form": { + "version": "7.52.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", + "integrity": "sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -16830,54 +16900,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" - }, - "@next/swc-darwin-x64": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", - "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", - "optional": true - }, - "@next/swc-linux-arm64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", - "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", - "optional": true - }, - "@next/swc-linux-arm64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", - "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", - "optional": true - }, - "@next/swc-linux-x64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", - "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", - "optional": true - }, - "@next/swc-linux-x64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", - "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", - "optional": true - }, - "@next/swc-win32-arm64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", - "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", - "optional": true - }, - "@next/swc-win32-ia32-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", - "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", - "optional": true - }, - "@next/swc-win32-x64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", - "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", - "optional": true } } } diff --git a/package.json b/package.json index a51eea1..63d5586 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "react": "^18", "react-debounce-input": "^3.3.0", "react-dom": "^18", + "react-hook-form": "^7.52.0", "react-markdown": "^9.0.1", "react-player": "^2.16.0", "react-resizable-layout": "^0.7.2", diff --git a/src/app/editor/components/AddPopup.tsx b/src/app/editor/components/AddPopup.tsx index cadbb2f..b3e9c7e 100644 --- a/src/app/editor/components/AddPopup.tsx +++ b/src/app/editor/components/AddPopup.tsx @@ -1,6 +1,7 @@ import React from "react"; import { useState } from "react"; import { stringify } from "yaml"; +import { useForm } from "react-hook-form"; export default function AddPopup({ type, @@ -17,36 +18,71 @@ export default function AddPopup({ stageIndex: any; elementIndex: any; }) { + var currComponent; + if (type === "editElement") { + currComponent = treatment.gameStages[stageIndex].elements[elementIndex]; + } else if (type === "editStage") { + currComponent = treatment.gameStages[stageIndex]; + } + + const { register, watch } = useForm({ + defaultValues: { + name: + currComponent !== undefined && currComponent.name !== undefined + ? currComponent.name + : "", + duration: + currComponent !== undefined && currComponent.duration !== undefined + ? currComponent.duration + : "", + selectedOption: + currComponent !== undefined && currComponent.type !== undefined + ? currComponent.type + : "Pick one", + file: "", + url: "", + params: "", + onSubmit: "", + style: "", + buttonText: "", + startTime: "", + endTime: "", + }, + }); + function handleSave(saveType: string) { const updatedTreatment = { ...treatment }; if (saveType === "addStage") { const inputs = { - name: nameValue, - duration: parseInt(durationValue), + name: watch("name"), + duration: parseInt(watch("duration")), elements: [], }; updatedTreatment?.gameStages?.push(inputs); } else if (saveType === "editStage") { const stageElmts = updatedTreatment.gameStages[stageIndex].elements; const inputs = { - name: nameValue, - duration: parseInt(durationValue), + name: watch("name"), + duration: parseInt(watch("duration")), elements: [], }; updatedTreatment.gameStages[stageIndex] = inputs; updatedTreatment.gameStages[stageIndex].elements = stageElmts; } else if (saveType === "addElement" || saveType === "editElement") { const inputs: { name: any; type: any; [key: string]: any } = { - name: nameValue, - type: selectedOption, + name: watch("name"), + type: watch("selectedOption"), }; - for (const key in elementValues) { - const value = elementValues[key as keyof typeof elementValues]; - if (value != "") { - inputs[key as keyof typeof elementValues] = value; - } - } + if (watch("file") !== "") inputs.file = watch("file"); + if (watch("url") !== "") inputs.url = watch("url"); + if (watch("params") !== "") inputs.params = watch("params"); + if (watch("onSubmit") !== "") inputs.onSubmit = watch("onSubmit"); + if (watch("style") !== "") inputs.style = watch("style"); + if (watch("buttonText") !== "") inputs.buttonText = watch("buttonText"); + if (watch("startTime") !== "") inputs.startTime = watch("startTime"); + if (watch("endTime") !== "") inputs.endTime = watch("endTime"); + if (saveType === "addElement") { updatedTreatment?.gameStages[stageIndex]?.elements?.push(inputs); } else if (type === "editElement") { @@ -74,10 +110,6 @@ export default function AddPopup({ updatedTreatment.gameStages = newStages; } - setSelectedOption(null); - setNameValue(""); - setDurationValue(""); - setElementValues(defaultElementValues); setTreatment(updatedTreatment); /* @@ -91,190 +123,89 @@ export default function AddPopup({ window.location.reload(); } - var currComponent; - if (type === "editElement") { - currComponent = treatment.gameStages[stageIndex].elements[elementIndex]; - } else if (type === "editStage") { - currComponent = treatment.gameStages[stageIndex]; - } - - const [selectedOption, setSelectedOption] = useState( - currComponent !== undefined && currComponent.type !== undefined - ? currComponent.type - : "Pick one" - ); - const [nameValue, setNameValue] = useState( - currComponent !== undefined && currComponent.name !== undefined - ? currComponent.name - : "" - ); - const [durationValue, setDurationValue] = useState( - currComponent !== undefined && currComponent.duration !== undefined - ? currComponent.duration - : "" - ); - - const handleNameChange = (event: any) => { - setNameValue(event.target.value); - }; - - const handleDurationChange = (event: any) => { - setDurationValue(event.target.value); - }; - - const handleSelectChange = (event: any) => { - const selectedValues = event.target.selectedOptions[0].value; - setSelectedOption(selectedValues); - setElementValues(defaultElementValues); - }; - - const defaultElementValues = { - file: "", - url: "", - params: "", - onSubmit: "", - style: "", - buttonText: "", - startTime: "", - endTime: "", - }; - const [elementValues, setElementValues] = useState<{ - file: string; - url: string; - params: string; - onSubmit: string; - style: string; - buttonText: string; - startTime: string; - endTime: string; - }>(defaultElementValues); - - const handleInputChange = (property: any, newValue: any) => { - setElementValues((prevValues) => ({ - ...prevValues, - [property]: newValue, - })); - }; - - const handleFileChange = (newValue: any) => - handleInputChange("file", newValue); - const handleURLChange = (newValue: any) => handleInputChange("url", newValue); - const handleParamsChange = (newValue: any) => - handleInputChange("params", newValue); - const handleOnSubmitChange = (newValue: any) => - handleInputChange("onSubmit", newValue); - const handleStyleChange = (newValue: any) => - handleInputChange("style", newValue); - const handleButtonTextChange = (newValue: any) => - handleInputChange("buttonText", newValue); - const handleStartTimeChange = (newValue: any) => - handleInputChange("startTime", newValue); - const handleEndTimeChange = (newValue: any) => - handleInputChange("endTime", newValue); - - // DEFAULT QUESTIONS - //console.log(stageIndex) + // FORM QUESTIONS stageIndex = stageIndex !== undefined ? stageIndex : ""; elementIndex = elementIndex !== undefined ? elementIndex : ""; const htmlElements = []; - questions.forEach((q: any, index: any) => { - const question = q.question; - const responseType = q.responseType; - const options = q.options || []; - htmlElements.push( -
- {question === "Name" && ( -
- -
- )} - {question === "Duration" && ( -
- -
- )} - {question === "Type" && ( -
- + htmlElements.push( + +
+ +
- // ELEMENT ATTRIBUTE QUESTIONS - htmlElements.push( -
- {(selectedOption === "prompt" || selectedOption === "audioElement") && ( + {(type === "editStage" || type === "addStage") && ( +
+ +
+ )} + + {(type === "editElement" || type === "addElement") && ( +
+ +
+ )} + + {(watch("selectedOption") === "prompt" || + watch("selectedOption") === "audioElement") && (
)} - {selectedOption === "kitchenTimer" && ( + + {watch("selectedOption") === "kitchenTimer" && (
handleStartTimeChange(e.target.value)} />
@@ -314,77 +241,74 @@ export default function AddPopup({ {"End Time"}
handleEndTimeChange(e.target.value)} /> )} - {(selectedOption === "qualtrics" || - selectedOption === "trainingVideo") && ( + + {(watch("selectedOption") === "qualtrics" || + watch("selectedOption") === "trainingVideo") && (
)} - {selectedOption === "qualtrics" && ( + + {watch("selectedOption") === "qualtrics" && (
)} - {selectedOption === "separator" && ( + + {watch("selectedOption") === "separator" && (
)} - {(selectedOption === "survey" || - selectedOption === "qualtrics" || - selectedOption === "submitButton") && ( + + {(watch("selectedOption") === "survey" || + watch("selectedOption") === "qualtrics" || + watch("selectedOption") === "submitButton") && (
)} - {selectedOption === "submitButton" && ( + + {watch("selectedOption") === "submitButton" && (
@@ -432,6 +352,8 @@ export default function AddPopup({ header = "Edit Stage"; } + //console.log(watch()); // WATCH ALL INPUTS + return (

{header}

@@ -444,8 +366,8 @@ export default function AddPopup({ style={{ margin: "10px" }} onClick={() => handleSave(type)} disabled={ - (durationValue === "" || nameValue === "") && - (selectedOption === "Pick one" || nameValue === "") + (watch("duration") === "" || watch("name") === "") && + (watch("selectedOption") === "Pick one" || watch("name") === "") } > Save From 04886f9bf575f1beba60552ec4cb0351b7d1a71d Mon Sep 17 00:00:00 2001 From: Evan Ping Date: Tue, 25 Jun 2024 15:03:09 -0400 Subject: [PATCH 02/20] synced cypress test with main --- cypress/e2e/test.cy.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cypress/e2e/test.cy.ts b/cypress/e2e/test.cy.ts index cea233e..193a1b5 100644 --- a/cypress/e2e/test.cy.ts +++ b/cypress/e2e/test.cy.ts @@ -22,11 +22,10 @@ describe('test spec', () => { // add first element to stage 1 cy.get('[data-cy="add-element-button-0"]').click() cy.get('[data-cy="add-popup-name-addElement-0-"]').type("Element 1") - cy.get('[data-cy="add-popup-type-addElement-0-"]').select("survey") - cy.get('[data-cy="add-popup-onSubmit-addElement-0-"]').type("Thanks!") + cy.get('[data-cy="add-popup-type-addElement-0-"]').select("prompt") cy.get('[data-cy="add-popup-save-addElement-0-"]').click() - cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") cy.get('[data-cy="element-0-0"]').contains("Element 1").should("be.visible") // add second element to stage 1 @@ -36,7 +35,7 @@ describe('test spec', () => { cy.get('[data-cy="add-popup-fileAddress-addElement-0-"]').type("file/address") cy.get('[data-cy="add-popup-save-addElement-0-"]').click() - cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") cy.get('[data-cy="element-0-1"]').contains("Prompt").should("be.visible") cy.get('[data-cy="element-0-1"]').contains("file/address").should("be.visible") @@ -62,17 +61,17 @@ describe('test spec', () => { // edit first element cy.get('[data-cy="edit-element-button-0-0"]').click() cy.get('[data-cy="add-popup-name-editElement-0-0"]').type(" Edited") - cy.get('[data-cy="add-popup-onSubmit-editElement-0-0"]').clear().type("Thanks!") + // cy.get('[data-cy="add-popup-onSubmit-editElement-0-0"]').clear().type("Thanks!") cy.get('[data-cy="add-popup-save-editElement-0-0"]').click() - cy.get('[data-cy="element-0-0"]').contains("Survey").should("be.visible") + cy.get('[data-cy="element-0-0"]').contains("Prompt").should("be.visible") cy.get('[data-cy="element-0-0"]').contains("Element 1 Edited").should("be.visible") // delete second element cy.get('[data-cy="edit-element-button-0-1"]').click() cy.get('[data-cy="add-popup-delete-editElement-0-1"]').click() - cy.get('[data-cy="element-0-1"]').should("not.exist") + cy.get('[data-cy="stage-0"]').should("not.contain", "Element 2") // add fourth element to second stage via code editor cy.get('[data-cy="code-editor"]').type(" - name: Element 4\n type: prompt\nfile: file/address") @@ -101,6 +100,6 @@ describe('test spec', () => { cy.get('[data-cy="edit-stage-button-2"]').click() cy.get('[data-cy="add-popup-delete-editStage-2-"]').click() - cy.get('[data-cy="stage-2"]').should("not.exist") + cy.get('[data-cy="timeline"]').should("not.contain", "Stage 3") }) }) \ No newline at end of file From f6f52caecd3cca6bccde437990b3017176432325 Mon Sep 17 00:00:00 2001 From: Christa Date: Wed, 26 Jun 2024 13:51:37 -0400 Subject: [PATCH 03/20] Update .gitmodules configuration --- src/editor/schemas | 1 + 1 file changed, 1 insertion(+) create mode 160000 src/editor/schemas diff --git a/src/editor/schemas b/src/editor/schemas new file mode 160000 index 0000000..546dbb7 --- /dev/null +++ b/src/editor/schemas @@ -0,0 +1 @@ +Subproject commit 546dbb771b533e3203561d8a57e56c75612a2161 From 5ebe8f5a1883e7a761cb4b51844e30b307d227ba Mon Sep 17 00:00:00 2001 From: Christa Date: Fri, 28 Jun 2024 02:24:31 -0400 Subject: [PATCH 04/20] finished validation for stage --- @empirica-mocks/core/mocks.js | 2 +- package-lock.json | 15 + package.json | 1 + src/app/editor/components/AddPopup.tsx | 7 +- src/app/editor/components/EditElement.tsx | 346 ++++++++++++++++++++++ src/app/editor/components/EditStage.tsx | 142 +++++++++ src/app/editor/components/ElementCard.tsx | 40 ++- src/app/editor/components/Modal.tsx | 18 ++ src/app/editor/components/StageCard.tsx | 70 +++-- src/app/editor/components/Timeline.tsx | 28 +- src/editor/schemas | 1 - tsconfig.json | 3 +- 12 files changed, 621 insertions(+), 52 deletions(-) create mode 100644 src/app/editor/components/EditElement.tsx create mode 100644 src/app/editor/components/EditStage.tsx create mode 100644 src/app/editor/components/Modal.tsx delete mode 160000 src/editor/schemas diff --git a/@empirica-mocks/core/mocks.js b/@empirica-mocks/core/mocks.js index 6b88a6b..4f338f2 100644 --- a/@empirica-mocks/core/mocks.js +++ b/@empirica-mocks/core/mocks.js @@ -1,6 +1,6 @@ export function usePlayer() { // This is a mock function that returns a mock player object - console.log("loaded usePlayer() from react-mocks.js"); + // console.log("loaded usePlayer() from react-mocks.js"); const player = { isMock: true, introDone: true, diff --git a/package-lock.json b/package-lock.json index e3d9976..27ec6ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@empirica/core": "^1.11.0", "@hello-pangea/dnd": "^16.6.0", + "@hookform/resolvers": "^3.6.0", "@uiw/react-textarea-code-editor": "^2.1.9", "@watts-lab/surveys": "^1.13.4", "next": "^13.5.6", @@ -279,6 +280,14 @@ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@hookform/resolvers": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.6.0.tgz", + "integrity": "sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -10001,6 +10010,12 @@ "use-memo-one": "^1.1.3" } }, + "@hookform/resolvers": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.6.0.tgz", + "integrity": "sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", diff --git a/package.json b/package.json index 63d5586..50b3de3 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "@empirica/core": "^1.11.0", "@hello-pangea/dnd": "^16.6.0", + "@hookform/resolvers": "^3.6.0", "@uiw/react-textarea-code-editor": "^2.1.9", "@watts-lab/surveys": "^1.13.4", "next": "^13.5.6", diff --git a/src/app/editor/components/AddPopup.tsx b/src/app/editor/components/AddPopup.tsx index b3e9c7e..228fdd2 100644 --- a/src/app/editor/components/AddPopup.tsx +++ b/src/app/editor/components/AddPopup.tsx @@ -2,6 +2,7 @@ import React from "react"; import { useState } from "react"; import { stringify } from "yaml"; import { useForm } from "react-hook-form"; +import { TreatmentType } from "@/deliberation-empirica/server/src/preFlight/validateTreatmentFile"; export default function AddPopup({ type, @@ -13,7 +14,7 @@ export default function AddPopup({ }: { type: string; questions: any; - treatment: any; + treatment: TreatmentType; setTreatment: any; stageIndex: any; elementIndex: any; @@ -32,11 +33,11 @@ export default function AddPopup({ ? currComponent.name : "", duration: - currComponent !== undefined && currComponent.duration !== undefined + currComponent !== undefined && "duration" in currComponent && currComponent.duration !== undefined ? currComponent.duration : "", selectedOption: - currComponent !== undefined && currComponent.type !== undefined + currComponent !== undefined && "type" in currComponent && currComponent.type !== undefined ? currComponent.type : "Pick one", file: "", diff --git a/src/app/editor/components/EditElement.tsx b/src/app/editor/components/EditElement.tsx new file mode 100644 index 0000000..b6c6878 --- /dev/null +++ b/src/app/editor/components/EditElement.tsx @@ -0,0 +1,346 @@ +import React, {useState} from "react"; +import { useForm } from "react-hook-form"; +import { TreatmentType, ElementType, elementSchema } from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as zod from 'zod'; + +export function EditElement({ + treatment, + editTreatment, + stageIndex, + elementIndex, +}: { + treatment: TreatmentType; + editTreatment: Function; + stageIndex: number; + elementIndex: number; +}) { + + const [isValid, setIsValid] = useState(true) + var element = treatment.gameStages[stageIndex].elements[elementIndex]; // if elementIndex is undefine, is the whole thing undefined? or does it error? + + + const { register, watch } = useForm({ + defaultValues: { + name: element?.name || "Enter Name", + type: element?.type || "Pick one", + file: element?.file || "", + url: element?.url || "", + params: element?.params || "", + style: element?.style || "", + buttonText: element?.buttonText || "", + startTime: element?.startTime || "", + endTime: element?.endTime || "", + }, + resolver: zodResolver(elementSchema), + }); + + function saveEdits() { + const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy + if (stageIndex === undefined) { + throw new Error("No stage index given") + } + + const parseResult = elementSchema.safeParse({ + name: watch("name"), + type: watch("type"), + file: watch("file"), + url: watch("url"), + params: watch("params"), + style: watch("style"), + buttonText: watch("buttonText"), + startTime: watch("startTime"), + endTime: watch("endTime") + }) + if (!parseResult.success) { + console.log(parseResult.error) + window.alert( parseResult.error ) + setIsValid(false); + return; + } + + setIsValid(true); + const newElement = parseResult.data + if (elementIndex === undefined) { // create new element + updatedTreatment.gameStages[stageIndex].elements.push(newElement) + } else { + updatedTreatment.gameStages[stageIndex].elements[elementIndex] = newElement + } + editTreatment(updatedTreatment); + } + + function deleteElement() { + const confirm = window.confirm( + "Are you sure you want to delete the element?" + ); + if (confirm) { + const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy + updatedTreatment.gameStages[stageIndex].elements.splice(elementIndex, 1); // delete in place + editTreatment(updatedTreatment); + } + } + +// function handleSave(saveType: string) { +// const updatedTreatment = { ...treatment }; + +// if (saveType === "addStage") { +// const inputs = { +// name: watch("name"), +// duration: parseInt(watch("duration")), +// elements: [], +// }; +// updatedTreatment?.gameStages?.push(inputs); +// } else if (saveType === "editStage") { +// const stageElmts = updatedTreatment.gameStages[stageIndex].elements; +// const inputs = { +// name: watch("name"), +// duration: parseInt(watch("duration")), +// elements: [], +// }; +// updatedTreatment.gameStages[stageIndex] = inputs; +// updatedTreatment.gameStages[stageIndex].elements = stageElmts; +// } else if (saveType === "addElement" || saveType === "editElement") { +// const inputs: { name: any; type: any; [key: string]: any } = { +// name: watch("name"), +// type: watch("selectedOption"), +// }; +// if (watch("file") !== "") inputs.file = watch("file"); +// if (watch("url") !== "") inputs.url = watch("url"); +// if (watch("params") !== "") inputs.params = watch("params"); +// if (watch("onSubmit") !== "") inputs.onSubmit = watch("onSubmit"); +// if (watch("style") !== "") inputs.style = watch("style"); +// if (watch("buttonText") !== "") inputs.buttonText = watch("buttonText"); +// if (watch("startTime") !== "") inputs.startTime = watch("startTime"); +// if (watch("endTime") !== "") inputs.endTime = watch("endTime"); + +// if (saveType === "addElement") { +// updatedTreatment?.gameStages[stageIndex]?.elements?.push(inputs); +// } else if (type === "editElement") { +// updatedTreatment.gameStages[stageIndex].elements[elementIndex] = inputs; +// } +// } else if (saveType === "deleteElement") { +// const oldElements = { +// ...updatedTreatment.gameStages[stageIndex].elements, +// }; +// var newElements: any[] = []; +// Object.keys(oldElements).forEach((key) => { +// if (key != elementIndex) { +// newElements.push(oldElements[key]); +// } +// }); +// updatedTreatment.gameStages[stageIndex].elements = newElements; +// } else if (saveType === "deleteStage") { +// const oldStages = { ...updatedTreatment.gameStages }; +// var newStages: any[] = []; +// Object.keys(oldStages).forEach((key) => { +// if (key != stageIndex) { +// newStages.push(oldStages[key]); +// } +// }); +// updatedTreatment.gameStages = newStages; +// } + +// setTreatment(updatedTreatment); + +// /* +// const addElementDialog = document.getElementById('add-element'); +// if (addElementDialog) { +// addElementDialog.close(); +// } +// */ + +// localStorage.setItem("code", stringify(updatedTreatment)); +// window.location.reload(); +// } + + // FORM QUESTIONS + stageIndex = stageIndex !== undefined ? stageIndex : ""; + + elementIndex = elementIndex !== undefined ? elementIndex : ""; + + const htmlElements = []; + htmlElements.push( + +
+ +
+ + +
+ +
+ + {(watch("type") === "prompt" || + watch("type") === "audioElement") && ( +
+ +
+ )} + + {watch("selectedOption") === "kitchenTimer" && ( +
+
+ +
+
+ +
+
+ )} + + {(watch("selectedOption") === "qualtrics" || + watch("selectedOption") === "trainingVideo") && ( +
+ +
+ )} + + {watch("selectedOption") === "qualtrics" && ( +
+ +
+ )} + + {watch("selectedOption") === "separator" && ( +
+ +
+ )} + + + {watch("selectedOption") === "submitButton" && ( +
+ +
+ )} + + ); + + + return ( +
+

{elementIndex !== undefined ? "Edit Element" : "Add Element"}

+ {htmlElements} + + + {/* Todo: Do we want to hide the delete button when creating a new element? */} + + + +
+ ); +} diff --git a/src/app/editor/components/EditStage.tsx b/src/app/editor/components/EditStage.tsx new file mode 100644 index 0000000..5305251 --- /dev/null +++ b/src/app/editor/components/EditStage.tsx @@ -0,0 +1,142 @@ +import React from "react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { TreatmentType, stageSchema, StageType} from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as zod from 'zod'; + +export function EditStage({ + treatment, + editTreatment, + stageIndex, // a number if the stage already exists, otherwise undefined +}: { + treatment: TreatmentType; + editTreatment: (treatment : TreatmentType) => void; + stageIndex: number; +}) { + + const { register, watch, handleSubmit, setValue, formState: {isValid, errors} } = useForm( + stageIndex !== undefined + ? { + defaultValues: { + name: treatment.gameStages[stageIndex].name, + duration: treatment.gameStages[stageIndex].duration, + }, + resolver: zodResolver(stageSchema), + mode: "onChange" + } + : {} + ); + + //console.log(typeof editTreatment); + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value, type } = e.target; + const parsedValue = type === "number" ? parseFloat(value) : value; + setValue(name as keyof StageType, parsedValue); + }; + + async function saveEdits() { + try { + const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy + console.log(typeof watch("duration")); + if (isValid){ + if (stageIndex === undefined) { // create new stage + updatedTreatment?.gameStages?.push({ + name: watch("name"), + duration: watch("duration"), + // todo: add discussion component + elements: [], + }); + } else { // modify existing stage + updatedTreatment.gameStages[stageIndex].name = watch("name"); + updatedTreatment.gameStages[stageIndex].duration = watch("duration"); + // todo: add discussion component + } + console.log(typeof editTreatment); + editTreatment(updatedTreatment); + } else { + throw new Error("Form is not valid"); + } + } catch (error) { + console.error(error); + } +} + + function deleteStage() { + const confirm = window.confirm( + "Are you sure you want to delete the stage and all its contents" + ); + if (confirm) { + const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy + updatedTreatment.gameStages.splice(stageIndex, 1); // delete in place + editTreatment(updatedTreatment); + } + } + + // ----------- Form Questions ----------------- + + const htmlElements = []; + htmlElements.push( +
+
+ +
+ +
+ +
+
+ ); + + //console.log(watch()); // WATCH ALL INPUTS + + return ( +
+

{stageIndex !== undefined ? "Edit Stage": "Add Stage" }

+ {htmlElements} + + + +{/* Todo: Do we want to hide the delete button when creating a new stage? */} + +
+ ); +} diff --git a/src/app/editor/components/ElementCard.tsx b/src/app/editor/components/ElementCard.tsx index ad0fb83..295491e 100644 --- a/src/app/editor/components/ElementCard.tsx +++ b/src/app/editor/components/ElementCard.tsx @@ -1,6 +1,9 @@ -import React from "react"; +import React, {useState} from "react"; import { Element } from "./Element"; import AddPopup from "./AddPopup"; +import { Modal } from "./Modal" +import { EditElement } from "./EditElement" +import { TreatmentType } from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; export function ElementCard({ element, @@ -10,7 +13,7 @@ export function ElementCard({ stageIndex, elementIndex, treatment, - setTreatment, + editTreatment, elementOptions, }: { element: any; @@ -20,12 +23,15 @@ export function ElementCard({ stageIndex: number; elementIndex: number; treatment: any; - setTreatment: any; + editTreatment: (treatment : TreatmentType) => void; elementOptions: any; }) { const startTime = element.displayTime || 0; const endTime = element.hideTime || stageDuration; + const [modalOpen, setModalOpen] = useState(false); + + const editModalId = `modal-stage${stageIndex}-element-${elementIndex}` return (
( - document.getElementById( - "stage" + stageIndex + "element" + elementIndex + document.getElementById(editModalId ) as HTMLDialogElement | null )?.showModal() } > Edit - -
-
- {/* if there is a button in form, it will close the modal */} - -
- + -
-
+ /> + +
); } diff --git a/src/app/editor/components/Modal.tsx b/src/app/editor/components/Modal.tsx new file mode 100644 index 0000000..f90a7d4 --- /dev/null +++ b/src/app/editor/components/Modal.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +export function Modal({ id, children }) { + + return( + +
+
+ {/* if there is a button in form, it will close the modal */} + +
+ {children} +
+
+ ) +} diff --git a/src/app/editor/components/StageCard.tsx b/src/app/editor/components/StageCard.tsx index 6d79f73..faa3da3 100644 --- a/src/app/editor/components/StageCard.tsx +++ b/src/app/editor/components/StageCard.tsx @@ -1,26 +1,32 @@ "use client"; import AddPopup from "./AddPopup"; -import React from "react"; +import React, { useState } from "react"; import { ElementCard } from "./ElementCard"; import { cn } from "@/app/components/utils"; +import { Modal } from "./Modal"; +import { EditStage } from "./EditStage"; +import { EditElement } from "./EditElement"; +import { TreatmentType, DurationType} from "../../../../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; -export function StageCard({ +export function StageCard({ title, elements, duration, scale, treatment, - setTreatment, + setTreatment, //Todo: get rid of this entirely + editTreatment, sequence, stageIndex, setRenderPanelStage, }: { title: string; elements: any[]; - duration: number; + duration: DurationType; scale: number; treatment: any; setTreatment: any; + editTreatment: (treatment: TreatmentType) => void; sequence: string; stageIndex: number; setRenderPanelStage: any; @@ -44,7 +50,7 @@ export function StageCard({ ]; const addStageOptions = [ { question: "Name", responseType: "text" }, - { question: "Duration", responseType: "text" }, + { question: "Duration", responseType: "number"}, { question: "Discussion", responseType: "text" }, ]; @@ -59,6 +65,8 @@ export function StageCard({ }); } + const newElementModalId = `modal-stage${stageIndex}-element-new` + return ( // TODO: reorder elements with drag and drop
- ( - document.getElementById( - "editStage" + stageIndex - ) as HTMLDialogElement | null - )?.showModal() - } + onClick={() => document.getElementById("modal-stage" + stageIndex)?.showModal()} > Edit - + + + + + + {/*
- {/* if there is a button in form, it will close the modal */} + {/* if there is a button in form, it will close the modal @@ -104,7 +117,7 @@ export function StageCard({ elementIndex={""} />
-
+
*/}
@@ -131,17 +144,36 @@ export function StageCard({ onClick={() => ( document.getElementById( - "stage" + stageIndex + newElementModalId ) as HTMLDialogElement | null )?.showModal() } > + - + + + + + + + + + + {/*
- {/* if there is a button in form, it will close the modal */} + {/* if there is a button in form, it will close the modal @@ -155,7 +187,7 @@ export function StageCard({ elementIndex={""} />
-
+
*/}
diff --git a/src/app/editor/components/Timeline.tsx b/src/app/editor/components/Timeline.tsx index 43e2129..cd2309b 100644 --- a/src/app/editor/components/Timeline.tsx +++ b/src/app/editor/components/Timeline.tsx @@ -5,6 +5,10 @@ import { StageCard } from "./StageCard"; import AddPopup from "./AddPopup"; import TimelineTools from "./TimelineTools"; import TimePicker from "./TimePicker"; +import { stringify } from "yaml"; +import { Modal } from "./Modal" +import { EditStage } from "./EditStage"; +import { TreatmentType } from "../../../../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; export default function Timeline({ setRenderPanelStage, @@ -14,6 +18,13 @@ export default function Timeline({ const [scale, setScale] = useState(1); // pixels per second const [treatment, setTreatment] = useState(null); + function editTreatment(newTreatment: TreatmentType) { + setTreatment(newTreatment) + localStorage.setItem("code", stringify(newTreatment)); + window.location.reload(); + } + // Todo: think about using 'useContext' here instead of passing editTreatment all the way down + useEffect(() => { // Access localStorage only on the client side if (typeof window !== "undefined") { @@ -54,6 +65,7 @@ export default function Timeline({ scale={scale} treatment={treatment} setTreatment={setTreatment} + editTreatment={editTreatment} sequence={"gameStage"} stageIndex={index} setRenderPanelStage={setRenderPanelStage} @@ -66,17 +78,25 @@ export default function Timeline({ onClick={() => ( document.getElementById( - "add-stage" + "modal-add-stage" ) as HTMLDialogElement | null )?.showModal() } > + - + + + + {/*
- {/* if there is a button in form, it will close the modal */} + {/* if there is a button in form, it will close the modal @@ -90,7 +110,7 @@ export default function Timeline({ elementIndex={""} />
-
+
*/} diff --git a/src/editor/schemas b/src/editor/schemas deleted file mode 160000 index 546dbb7..0000000 --- a/src/editor/schemas +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 546dbb771b533e3203561d8a57e56c75612a2161 diff --git a/tsconfig.json b/tsconfig.json index dcdcdfa..9e6c212 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ ], "paths": { "@/*": ["./src/*"], - "@/api/*": ["./src/app/api/*"] + "@/api/*": ["./src/app/api/*"], + "@deliberation-empirica": ["./deliberation-empirica"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], From c12a80f0b92eded97271754bea883e13969da0f9 Mon Sep 17 00:00:00 2001 From: Christa Date: Fri, 28 Jun 2024 13:02:35 -0400 Subject: [PATCH 05/20] updated validation for element --- src/app/editor/components/EditElement.tsx | 104 +++++++++++++--------- src/app/editor/components/EditStage.tsx | 5 -- 2 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/app/editor/components/EditElement.tsx b/src/app/editor/components/EditElement.tsx index b6c6878..07af316 100644 --- a/src/app/editor/components/EditElement.tsx +++ b/src/app/editor/components/EditElement.tsx @@ -16,57 +16,75 @@ export function EditElement({ elementIndex: number; }) { - const [isValid, setIsValid] = useState(true) - var element = treatment.gameStages[stageIndex].elements[elementIndex]; // if elementIndex is undefine, is the whole thing undefined? or does it error? + //const [isValid, setIsValid] = useState(true) + //var element: ElementType = treatment.gameStages[stageIndex].elements[elementIndex]; // if elementIndex is undefine, is the whole thing undefined? or does it error? - const { register, watch } = useForm({ + const { register, watch, handleSubmit,setValue, formState: {isValid, errors} } = useForm( + elementIndex !== undefined + ?{ defaultValues: { - name: element?.name || "Enter Name", - type: element?.type || "Pick one", - file: element?.file || "", - url: element?.url || "", - params: element?.params || "", - style: element?.style || "", - buttonText: element?.buttonText || "", - startTime: element?.startTime || "", - endTime: element?.endTime || "", + name: treatment.gameStages[stageIndex].elements[elementIndex].name, + type: treatment.gameStages[stageIndex].elements[elementIndex].type, + file: treatment.gameStages[stageIndex].elements[elementIndex].file, }, resolver: zodResolver(elementSchema), - }); + mode: "onChange" + } + : {} +); function saveEdits() { + try { const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy - if (stageIndex === undefined) { - throw new Error("No stage index given") + if (isValid) { + if (stageIndex === undefined) { + throw new Error("No stage index given") + } + if (elementIndex === undefined) { + updatedTreatment.gameStages[stageIndex].elements.push({ + name: watch("name"), + type: watch("type"), + file: watch("file") + //TODO: to add more fields here + }); + } else { + updatedTreatment.gameStages[stageIndex].elements[elementIndex].name = watch("name"); + updatedTreatment.gameStages[stageIndex].elements[elementIndex].type = watch("type"); + updatedTreatment.gameStages[stageIndex].elements[elementIndex].file = watch("file"); + // TODO: add more fields here + } + editTreatment(updatedTreatment); + } else { + throw new Error("Form is not valid"); } + } catch (error) { + console.error(error); + } + + - const parseResult = elementSchema.safeParse({ - name: watch("name"), - type: watch("type"), - file: watch("file"), - url: watch("url"), - params: watch("params"), - style: watch("style"), - buttonText: watch("buttonText"), - startTime: watch("startTime"), - endTime: watch("endTime") - }) - if (!parseResult.success) { - console.log(parseResult.error) - window.alert( parseResult.error ) - setIsValid(false); - return; - } + // const parseResult = elementSchema.safeParse({ + // name: watch("name"), + // type: watch("type"), + // file: watch("file"), + // url: watch("url"), + // params: watch("params"), + // style: watch("style"), + // buttonText: watch("buttonText"), + // startTime: watch("startTime"), + // endTime: watch("endTime") + // }) + // if (!parseResult.success) { + // console.log(parseResult.error) + // window.alert( parseResult.error ) + // setIsValid(false); + // return; + // } - setIsValid(true); - const newElement = parseResult.data - if (elementIndex === undefined) { // create new element - updatedTreatment.gameStages[stageIndex].elements.push(newElement) - } else { - updatedTreatment.gameStages[stageIndex].elements[elementIndex] = newElement - } - editTreatment(updatedTreatment); + //setIsValid(true); + + } function deleteElement() { @@ -154,9 +172,9 @@ export function EditElement({ // } // FORM QUESTIONS - stageIndex = stageIndex !== undefined ? stageIndex : ""; + // stageIndex = stageIndex !== undefined ? stageIndex : ""; - elementIndex = elementIndex !== undefined ? elementIndex : ""; + // elementIndex = elementIndex !== undefined ? elementIndex : ""; const htmlElements = []; htmlElements.push( @@ -172,6 +190,7 @@ export function EditElement({ placeholder="Enter text here." className="input input-bordered w-full max-w-xs" /> + {errors.name && {errors.name.message}} @@ -212,6 +231,7 @@ export function EditElement({ placeholder="Enter number here." className="input input-bordered w-full max-w-xs" /> + {errors.file && {errors.file.message}} )} diff --git a/src/app/editor/components/EditStage.tsx b/src/app/editor/components/EditStage.tsx index 5305251..c2e0429 100644 --- a/src/app/editor/components/EditStage.tsx +++ b/src/app/editor/components/EditStage.tsx @@ -29,11 +29,6 @@ export function EditStage({ ); //console.log(typeof editTreatment); - const handleInputChange = (e: React.ChangeEvent) => { - const { name, value, type } = e.target; - const parsedValue = type === "number" ? parseFloat(value) : value; - setValue(name as keyof StageType, parsedValue); - }; async function saveEdits() { try { From bbf8913dfda67eaf2b79a8ef0cd80783263cfcf4 Mon Sep 17 00:00:00 2001 From: Christa Date: Mon, 1 Jul 2024 11:36:59 -0400 Subject: [PATCH 06/20] improved validation --- .prettierrc | 8 +- src/app/editor/components/EditElement.tsx | 301 +++++++++------------- src/app/editor/components/EditStage.tsx | 9 +- 3 files changed, 140 insertions(+), 178 deletions(-) diff --git a/.prettierrc b/.prettierrc index 9e26dfe..f9fafb6 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1 +1,7 @@ -{} \ No newline at end of file +{ + "semi": false, + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false +} \ No newline at end of file diff --git a/src/app/editor/components/EditElement.tsx b/src/app/editor/components/EditElement.tsx index 07af316..925b116 100644 --- a/src/app/editor/components/EditElement.tsx +++ b/src/app/editor/components/EditElement.tsx @@ -1,8 +1,12 @@ -import React, {useState} from "react"; +import React, { useState } from "react"; import { useForm } from "react-hook-form"; -import { TreatmentType, ElementType, elementSchema } from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; -import { zodResolver } from '@hookform/resolvers/zod'; -import * as zod from 'zod'; +import { + TreatmentType, + ElementType, + elementSchema, +} from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as zod from "zod"; export function EditElement({ treatment, @@ -11,58 +15,69 @@ export function EditElement({ elementIndex, }: { treatment: TreatmentType; - editTreatment: Function; + editTreatment: (treatment : TreatmentType) => void; stageIndex: number; elementIndex: number; }) { - //const [isValid, setIsValid] = useState(true) //var element: ElementType = treatment.gameStages[stageIndex].elements[elementIndex]; // if elementIndex is undefine, is the whole thing undefined? or does it error? - - const { register, watch, handleSubmit,setValue, formState: {isValid, errors} } = useForm( - elementIndex !== undefined - ?{ - defaultValues: { - name: treatment.gameStages[stageIndex].elements[elementIndex].name, - type: treatment.gameStages[stageIndex].elements[elementIndex].type, - file: treatment.gameStages[stageIndex].elements[elementIndex].file, - }, - resolver: zodResolver(elementSchema), - mode: "onChange" - } - : {} -); + const { + register, + watch, + handleSubmit, + setValue, + formState: { isValid, errors }, + } = useForm( + elementIndex !== undefined + ? { + defaultValues: { + name: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.name, + type: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.type, + file: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.file, + }, + resolver: zodResolver(elementSchema), + mode: "onChange", + } + : {} + ); + + console.log(typeof editTreatment); // Here it's a function function saveEdits() { + //console.log(typeof editTreatment); // here it's undefined try { - const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy - if (isValid) { + const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy + if (isValid) { + //console.log("HEEERREEE") if (stageIndex === undefined) { - throw new Error("No stage index given") + throw new Error("No stage index given"); } if (elementIndex === undefined) { - updatedTreatment.gameStages[stageIndex].elements.push({ - name: watch("name"), - type: watch("type"), - file: watch("file") - //TODO: to add more fields here - }); - } else { - updatedTreatment.gameStages[stageIndex].elements[elementIndex].name = watch("name"); - updatedTreatment.gameStages[stageIndex].elements[elementIndex].type = watch("type"); - updatedTreatment.gameStages[stageIndex].elements[elementIndex].file = watch("file"); - // TODO: add more fields here - } + updatedTreatment.gameStages[stageIndex].elements.push({ + name: watch("name"), + type: watch("type"), + file: watch("file"), + //TODO: to add more fields here + }); + } else { + updatedTreatment.gameStages[stageIndex].elements[elementIndex].name = + watch("name"); + updatedTreatment.gameStages[stageIndex].elements[elementIndex].type = + watch("type"); + updatedTreatment.gameStages[stageIndex].elements[elementIndex].file = + watch("file"); + // TODO: add more fields here + } + console.log(typeof editTreatment); editTreatment(updatedTreatment); - } else { + } else { throw new Error("Form is not valid"); + } + } catch (error) { + console.error(error); } - } catch (error) { - console.error(error); } - - // const parseResult = elementSchema.safeParse({ // name: watch("name"), @@ -77,15 +92,12 @@ export function EditElement({ // }) // if (!parseResult.success) { // console.log(parseResult.error) - // window.alert( parseResult.error ) + // window.alert( parseResult.error ) // setIsValid(false); // return; // } - - //setIsValid(true); - - } + //setIsValid(true); function deleteElement() { const confirm = window.confirm( @@ -98,87 +110,10 @@ export function EditElement({ } } -// function handleSave(saveType: string) { -// const updatedTreatment = { ...treatment }; - -// if (saveType === "addStage") { -// const inputs = { -// name: watch("name"), -// duration: parseInt(watch("duration")), -// elements: [], -// }; -// updatedTreatment?.gameStages?.push(inputs); -// } else if (saveType === "editStage") { -// const stageElmts = updatedTreatment.gameStages[stageIndex].elements; -// const inputs = { -// name: watch("name"), -// duration: parseInt(watch("duration")), -// elements: [], -// }; -// updatedTreatment.gameStages[stageIndex] = inputs; -// updatedTreatment.gameStages[stageIndex].elements = stageElmts; -// } else if (saveType === "addElement" || saveType === "editElement") { -// const inputs: { name: any; type: any; [key: string]: any } = { -// name: watch("name"), -// type: watch("selectedOption"), -// }; -// if (watch("file") !== "") inputs.file = watch("file"); -// if (watch("url") !== "") inputs.url = watch("url"); -// if (watch("params") !== "") inputs.params = watch("params"); -// if (watch("onSubmit") !== "") inputs.onSubmit = watch("onSubmit"); -// if (watch("style") !== "") inputs.style = watch("style"); -// if (watch("buttonText") !== "") inputs.buttonText = watch("buttonText"); -// if (watch("startTime") !== "") inputs.startTime = watch("startTime"); -// if (watch("endTime") !== "") inputs.endTime = watch("endTime"); - -// if (saveType === "addElement") { -// updatedTreatment?.gameStages[stageIndex]?.elements?.push(inputs); -// } else if (type === "editElement") { -// updatedTreatment.gameStages[stageIndex].elements[elementIndex] = inputs; -// } -// } else if (saveType === "deleteElement") { -// const oldElements = { -// ...updatedTreatment.gameStages[stageIndex].elements, -// }; -// var newElements: any[] = []; -// Object.keys(oldElements).forEach((key) => { -// if (key != elementIndex) { -// newElements.push(oldElements[key]); -// } -// }); -// updatedTreatment.gameStages[stageIndex].elements = newElements; -// } else if (saveType === "deleteStage") { -// const oldStages = { ...updatedTreatment.gameStages }; -// var newStages: any[] = []; -// Object.keys(oldStages).forEach((key) => { -// if (key != stageIndex) { -// newStages.push(oldStages[key]); -// } -// }); -// updatedTreatment.gameStages = newStages; -// } - -// setTreatment(updatedTreatment); - -// /* -// const addElementDialog = document.getElementById('add-element'); -// if (addElementDialog) { -// addElementDialog.close(); -// } -// */ - -// localStorage.setItem("code", stringify(updatedTreatment)); -// window.location.reload(); -// } - // FORM QUESTIONS - // stageIndex = stageIndex !== undefined ? stageIndex : ""; - - // elementIndex = elementIndex !== undefined ? elementIndex : ""; - const htmlElements = []; htmlElements.push( - +
- -
- -
+
+ +
- {(watch("type") === "prompt" || - watch("type") === "audioElement") && ( + {(watch("type") === "prompt" || watch("type") === "audioElement") && (
)} @@ -245,7 +188,9 @@ export function EditElement({ @@ -258,7 +203,9 @@ export function EditElement({ @@ -276,7 +223,9 @@ export function EditElement({ @@ -292,7 +241,9 @@ export function EditElement({ @@ -308,7 +259,9 @@ export function EditElement({ @@ -316,7 +269,6 @@ export function EditElement({ )} - {watch("selectedOption") === "submitButton" && (
@@ -335,32 +289,33 @@ export function EditElement({ ); - return (

{elementIndex !== undefined ? "Edit Element" : "Add Element"}

{htmlElements} - + {/* Todo: Do we want to hide the delete button when creating a new element? */} - - - +
); } diff --git a/src/app/editor/components/EditStage.tsx b/src/app/editor/components/EditStage.tsx index c2e0429..0bf15ef 100644 --- a/src/app/editor/components/EditStage.tsx +++ b/src/app/editor/components/EditStage.tsx @@ -28,13 +28,14 @@ export function EditStage({ : {} ); - //console.log(typeof editTreatment); - + console.log(typeof editTreatment); async function saveEdits() { try { const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy - console.log(typeof watch("duration")); + console.log("Hereeee"); + console.log(typeof editTreatment); if (isValid){ + console.log("Form is valid"); if (stageIndex === undefined) { // create new stage updatedTreatment?.gameStages?.push({ name: watch("name"), @@ -47,7 +48,7 @@ export function EditStage({ updatedTreatment.gameStages[stageIndex].duration = watch("duration"); // todo: add discussion component } - console.log(typeof editTreatment); + console.log(typeof editTreatment); editTreatment(updatedTreatment); } else { throw new Error("Form is not valid"); From a621ebcc2d7d30fbcce7564b3017a365a9d304b6 Mon Sep 17 00:00:00 2001 From: Christa Date: Tue, 2 Jul 2024 11:25:27 -0400 Subject: [PATCH 07/20] fixed bugs related editTreatment --- src/app/editor/components/EditElement.tsx | 205 ++++++++++------------ src/app/editor/components/EditStage.tsx | 134 +++++++------- src/app/editor/components/ElementCard.tsx | 67 ++++--- src/app/editor/components/StageCard.tsx | 121 ++++++------- src/app/editor/components/Timeline.tsx | 2 +- 5 files changed, 256 insertions(+), 273 deletions(-) diff --git a/src/app/editor/components/EditElement.tsx b/src/app/editor/components/EditElement.tsx index 925b116..560973a 100644 --- a/src/app/editor/components/EditElement.tsx +++ b/src/app/editor/components/EditElement.tsx @@ -1,12 +1,12 @@ -import React, { useState } from "react"; -import { useForm } from "react-hook-form"; +import React, { useState } from 'react' +import { useForm } from 'react-hook-form' import { TreatmentType, ElementType, elementSchema, -} from "@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile"; -import { zodResolver } from "@hookform/resolvers/zod"; -import * as zod from "zod"; +} from '@/../deliberation-empirica/server/src/preFlight/validateTreatmentFile' +import { zodResolver } from '@hookform/resolvers/zod' +import * as zod from 'zod' export function EditElement({ treatment, @@ -14,14 +14,11 @@ export function EditElement({ stageIndex, elementIndex, }: { - treatment: TreatmentType; - editTreatment: (treatment : TreatmentType) => void; - stageIndex: number; - elementIndex: number; + treatment: TreatmentType + editTreatment: (treatment: TreatmentType) => void + stageIndex: number + elementIndex: number }) { - //const [isValid, setIsValid] = useState(true) - //var element: ElementType = treatment.gameStages[stageIndex].elements[elementIndex]; // if elementIndex is undefine, is the whole thing undefined? or does it error? - const { register, watch, @@ -32,97 +29,73 @@ export function EditElement({ elementIndex !== undefined ? { defaultValues: { - name: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.name, - type: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.type, - file: treatment.gameStages[stageIndex]?.elements?.[elementIndex]?.file, + name: treatment.gameStages[stageIndex]?.elements?.[elementIndex] + ?.name, + type: treatment.gameStages[stageIndex]?.elements?.[elementIndex] + ?.type, + file: treatment.gameStages[stageIndex]?.elements?.[elementIndex] + ?.file, }, resolver: zodResolver(elementSchema), - mode: "onChange", + mode: 'onChange', } : {} - ); - + ) - console.log(typeof editTreatment); // Here it's a function function saveEdits() { - //console.log(typeof editTreatment); // here it's undefined try { - const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy - if (isValid) { - //console.log("HEEERREEE") - if (stageIndex === undefined) { - throw new Error("No stage index given"); - } - if (elementIndex === undefined) { - updatedTreatment.gameStages[stageIndex].elements.push({ - name: watch("name"), - type: watch("type"), - file: watch("file"), - //TODO: to add more fields here - }); - } else { - updatedTreatment.gameStages[stageIndex].elements[elementIndex].name = - watch("name"); - updatedTreatment.gameStages[stageIndex].elements[elementIndex].type = - watch("type"); - updatedTreatment.gameStages[stageIndex].elements[elementIndex].file = - watch("file"); - // TODO: add more fields here - } - console.log(typeof editTreatment); - editTreatment(updatedTreatment); + const updatedTreatment = JSON.parse(JSON.stringify(treatment)) // deep copy + + if (stageIndex === undefined) { + throw new Error('No stage index given') + } + + if (elementIndex === undefined) { + updatedTreatment.gameStages[stageIndex].elements.push({ + name: watch('name'), + type: watch('type'), + file: watch('file'), + //TODO: to add more fields here + }) } else { - throw new Error("Form is not valid"); + updatedTreatment.gameStages[stageIndex].elements[elementIndex].name = + watch('name') + updatedTreatment.gameStages[stageIndex].elements[elementIndex].type = + watch('type') + updatedTreatment.gameStages[stageIndex].elements[elementIndex].file = + watch('file') + // TODO: add more fields here } + editTreatment(updatedTreatment) } catch (error) { - console.error(error); + console.error(error) } } - // const parseResult = elementSchema.safeParse({ - // name: watch("name"), - // type: watch("type"), - // file: watch("file"), - // url: watch("url"), - // params: watch("params"), - // style: watch("style"), - // buttonText: watch("buttonText"), - // startTime: watch("startTime"), - // endTime: watch("endTime") - // }) - // if (!parseResult.success) { - // console.log(parseResult.error) - // window.alert( parseResult.error ) - // setIsValid(false); - // return; - // } - - //setIsValid(true); - function deleteElement() { const confirm = window.confirm( - "Are you sure you want to delete the element?" - ); + 'Are you sure you want to delete the element?' + ) if (confirm) { - const updatedTreatment = JSON.parse(JSON.stringify(treatment)); // deep copy - updatedTreatment.gameStages[stageIndex].elements.splice(elementIndex, 1); // delete in place - editTreatment(updatedTreatment); + const updatedTreatment = JSON.parse(JSON.stringify(treatment)) // deep copy + updatedTreatment.gameStages[stageIndex].elements.splice(elementIndex, 1) // delete in place + editTreatment(updatedTreatment) } } // FORM QUESTIONS - const htmlElements = []; + const htmlElements = [] htmlElements.push( -
+