From 6c6a6fb3001b33c0144e4c55b57c4d18cdbed7ee Mon Sep 17 00:00:00 2001 From: Sarah Wang Date: Tue, 10 Mar 2026 01:36:54 -0400 Subject: [PATCH] impl --- server/api/routes/firebaseAPI.ts | 1 + src/App.tsx | 21 +++++++++------- src/pages/Captcha.tsx | 7 +++--- src/pages/artist/PostSurvey.tsx | 41 +++++++++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/server/api/routes/firebaseAPI.ts b/server/api/routes/firebaseAPI.ts index c21fe19..099f6ce 100644 --- a/server/api/routes/firebaseAPI.ts +++ b/server/api/routes/firebaseAPI.ts @@ -26,6 +26,7 @@ router.post("/autosave", async (req, res) => { 5: "brainstorm", 6: "write", 7: "post-survey", + 8: "thank-you", }; const status = data.data?.timeStamps diff --git a/src/App.tsx b/src/App.tsx index f367ef2..4e278ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,13 +44,15 @@ interface DataContextValue { addUserData: (newData: Partial) => void; addRoleSpecificData: (updates: Partial | Partial) => void; addPreSurvey: ( - updates: Partial | Partial + updates: Partial | Partial, ) => void; addPostSurvey: ( - updates: Partial | Partial + updates: Partial | Partial, ) => void; sessionId: string | null; flushSaves: () => Promise; + isTestMode: boolean; + setIsTestMode: (value: boolean) => void; } export const DataContext = createContext(null); @@ -58,13 +60,14 @@ export const DataContext = createContext(null); function App() { const [userData, setUserData] = useState(null); const [sessionId, setSessionId] = useState(null); + const [isTestMode, setIsTestMode] = useState(false); const saveTimerRef = useRef(null); usePreventRefresh( - "To make sure your session counts, please avoid refreshing the page. Do you still want to refresh?" + "To make sure your session counts, please avoid refreshing the page. Do you still want to refresh?", ); usePreventBack( - "To make sure your session counts, please avoid pressing the back button." + "To make sure your session counts, please avoid pressing the back button.", ); // clear session storage and set the session ID on first render @@ -108,12 +111,12 @@ function App() { }; const addRoleSpecificData = ( - updates: Partial | Partial + updates: Partial | Partial, ) => { setUserData((prev: any) => { if (!prev || !prev.data) { throw new Error( - "Tried to update data when userData is null or incomplete." + "Tried to update data when userData is null or incomplete.", ); } @@ -130,7 +133,7 @@ function App() { }; const addPreSurvey = ( - updates: Partial | Partial + updates: Partial | Partial, ) => { setUserData((prev: any) => { if (!prev || !prev.data) { @@ -160,7 +163,7 @@ function App() { }; const addPostSurvey = ( - updates: Partial | Partial + updates: Partial | Partial, ) => { setUserData((prev: any) => { if (!prev || !prev.data) { @@ -221,6 +224,8 @@ function App() { addPreSurvey, sessionId, flushSaves, + isTestMode, + setIsTestMode, }} > diff --git a/src/pages/Captcha.tsx b/src/pages/Captcha.tsx index cfa0dc9..dfddad1 100644 --- a/src/pages/Captcha.tsx +++ b/src/pages/Captcha.tsx @@ -8,7 +8,7 @@ import { ArtistCondition } from "../types"; import type { Poem } from "../types"; import { Passages } from "../consts/passages"; -const TEST_CAPTCHA = "*TEST"; +const TEST_CAPTCHA = "TEST"; const getRandomArtistCondition = (): ArtistCondition => { const values = Object.values(ArtistCondition); @@ -22,7 +22,7 @@ const Captcha = () => { if (!context) { throw new Error("Component must be used within a DataContext.Provider"); } - const { userData, addUserData, addRoleSpecificData } = context; + const { userData, addUserData, addRoleSpecificData, setIsTestMode } = context; const [captchaMessage, setCaptchaMessage] = useState(""); const [inputCaptcha, setInputCaptcha] = useState(""); const canvasRef = useRef(null); @@ -39,7 +39,7 @@ const Captcha = () => { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < 4; i++) { captcha_text += c_chars.charAt( - Math.floor(Math.random() * c_chars.length) + Math.floor(Math.random() * c_chars.length), ); } setCaptchaMessage(captcha_text); @@ -138,6 +138,7 @@ const Captcha = () => { }); navigate("/consent"); } else if (inputCaptcha == TEST_CAPTCHA) { + setIsTestMode(true); addUserData({ role: "artist" }); addRoleSpecificData({ condition: ArtistCondition.TOTAL_ACCESS }); addRoleSpecificData({ diff --git a/src/pages/artist/PostSurvey.tsx b/src/pages/artist/PostSurvey.tsx index 62fd0cf..154f488 100644 --- a/src/pages/artist/PostSurvey.tsx +++ b/src/pages/artist/PostSurvey.tsx @@ -14,7 +14,7 @@ const ArtistPostSurvey = () => { throw new Error("Component must be used within a DataContext.Provider"); } - const { userData, addPostSurvey, sessionId } = context; + const { userData, addPostSurvey, sessionId, isTestMode } = context; const navigate = useNavigate(); const poemData = userData?.data && (userData.data as Artist).poem; @@ -81,7 +81,42 @@ const ArtistPostSurvey = () => { postSurvey: ArtistPostSurveyQuestions, postAnswers: answers, }); - submitDb(answers); + + if (isTestMode) { + try { + const testData = { + ...userData, + data: { + ...userData?.data, + surveyResponse: { + ...(userData?.data as Artist).surveyResponse, + postSurvey: ArtistPostSurveyQuestions, + postAnswers: answers, + }, + }, + }; + await fetch("/api/firebase/autosave", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ sessionId, data: testData }), + }); + toaster.create({ + description: "Test session saved (not committed to production).", + type: "success", + duration: 5000, + }); + navigate("/artist/thank-you"); + } catch (error) { + console.error("Error saving test data:", error); + toaster.create({ + description: "There was an error saving. Please try again.", + type: "error", + duration: 5000, + }); + } + } else { + submitDb(answers); + } }; const filteredSurvey: SurveyDefinition = { @@ -89,7 +124,7 @@ const ArtistPostSurvey = () => { sections: ArtistPostSurveyQuestions.sections.filter( (section) => !section.conditions || // no conditions → always include - section.conditions.includes(userData?.data.condition) + section.conditions.includes(userData?.data.condition), ), };