Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions server/api/routes/firebaseAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 13 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,30 @@ interface DataContextValue {
addUserData: (newData: Partial<UserData>) => void;
addRoleSpecificData: (updates: Partial<Artist> | Partial<Audience>) => void;
addPreSurvey: (
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>,
) => void;
addPostSurvey: (
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>,
) => void;
sessionId: string | null;
flushSaves: () => Promise<void>;
isTestMode: boolean;
setIsTestMode: (value: boolean) => void;
}

export const DataContext = createContext<DataContextValue | null>(null);

function App() {
const [userData, setUserData] = useState<UserData | null>(null);
const [sessionId, setSessionId] = useState<string | null>(null);
const [isTestMode, setIsTestMode] = useState<boolean>(false);
const saveTimerRef = useRef<number | null>(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
Expand Down Expand Up @@ -108,12 +111,12 @@ function App() {
};

const addRoleSpecificData = (
updates: Partial<Artist> | Partial<Audience>
updates: Partial<Artist> | Partial<Audience>,
) => {
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.",
);
}

Expand All @@ -130,7 +133,7 @@ function App() {
};

const addPreSurvey = (
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>,
) => {
setUserData((prev: any) => {
if (!prev || !prev.data) {
Expand Down Expand Up @@ -160,7 +163,7 @@ function App() {
};

const addPostSurvey = (
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>
updates: Partial<ArtistSurvey> | Partial<AudienceSurvey>,
) => {
setUserData((prev: any) => {
if (!prev || !prev.data) {
Expand Down Expand Up @@ -221,6 +224,8 @@ function App() {
addPreSurvey,
sessionId,
flushSaves,
isTestMode,
setIsTestMode,
}}
>
<Provider>
Expand Down
7 changes: 4 additions & 3 deletions src/pages/Captcha.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<HTMLCanvasElement>(null);
Expand All @@ -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);
Expand Down Expand Up @@ -138,6 +138,7 @@ const Captcha = () => {
});
navigate("/consent");
} else if (inputCaptcha == TEST_CAPTCHA) {
setIsTestMode(true);
addUserData({ role: "artist" });
addRoleSpecificData({ condition: ArtistCondition.TOTAL_ACCESS });
addRoleSpecificData({
Expand Down
41 changes: 38 additions & 3 deletions src/pages/artist/PostSurvey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,15 +81,50 @@ 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 = {
...ArtistPostSurveyQuestions,
sections: ArtistPostSurveyQuestions.sections.filter(
(section) =>
!section.conditions || // no conditions → always include
section.conditions.includes(userData?.data.condition)
section.conditions.includes(userData?.data.condition),
),
};

Expand Down