-
Notifications
You must be signed in to change notification settings - Fork 262
Feature/dynamic google form #297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feature/dynamic google form #297
Conversation
WalkthroughIntroduces a DynamicForm React component that fetches questions from a Google Apps Script endpoint, renders inputs based on question types, tracks responses, and handles submit. App.js is updated to remove routing UI and export DynamicForm as default. A new DynamicForm.js file is added exporting the same component. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant DF as DynamicForm (React)
participant GAS as Google Apps Script API
participant State as Local State
rect rgb(240,248,255)
note right of DF: On mount
DF->>GAS: GET /questions
GAS-->>DF: JSON questions
DF->>State: setQuestions(list)
end
User->>DF: Input/select changes
DF->>State: handleChange(id, value)<br/>• CHECKBOX: merge selections
User->>DF: Submit form
DF->>DF: preventDefault()
DF->>State: read responses
DF-->>User: Alert "Form submitted"
note over DF: Logs responses to console
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
eduaid_web/src/App.js (1)
1-108: Architectural concern: App.js exports DynamicForm.This file is named App.js but exports a component named DynamicForm, and there's a duplicate implementation in DynamicForm.js. This creates confusion about which file is the canonical source.
This version has some improvements over DynamicForm.js (label wrapping, textarea sizing), but maintaining two nearly identical implementations will lead to inconsistencies.
Recommendation:
- Consolidate to a single implementation (prefer this App.js version due to better UX)
- Remove DynamicForm.js
- Consider renaming the component in this file to
Appif it's meant to be the main app component, or create a proper App wrapper that imports DynamicForm from a separate fileAlternatively, if you want DynamicForm as a reusable component:
- Keep DynamicForm.js as the canonical implementation (merge improvements from App.js)
- Update App.js to import and use it:
import React from 'react'; import DynamicForm from './DynamicForm'; function App() { return <DynamicForm />; } export default App;
♻️ Duplicate comments (2)
eduaid_web/src/App.js (2)
8-15: Add loading state and improve error handling.Same issue as flagged in DynamicForm.js: no loading indicator and errors only logged without user feedback.
Refer to the comment on DynamicForm.js lines 12-20 for the suggested implementation with loading and error states.
21-25: Implement actual form submission and validation.Same issue as DynamicForm.js: the form only logs responses without submitting to backend or validating required fields.
Refer to the comment on DynamicForm.js lines 28-32 for implementation guidance.
🧹 Nitpick comments (7)
eduaid_web/src/DynamicForm.js (5)
46-51: Consider using controlled inputs.The input is uncontrolled (no
valueprop), which makes it harder to reset the form or pre-populate values.Apply this pattern for all inputs:
{q.type === "TEXT" && ( <input type="text" + value={answers[q.id] || ""} onChange={(e) => handleChange(q.id, e.target.value)} + placeholder="Enter your answer" /> )}
53-57: Add sizing attributes and make controlled.The textarea lacks sizing attributes and is uncontrolled.
{q.type === "PARAGRAPH_TEXT" && ( <textarea + rows="4" + cols="50" + value={answers[q.id] || ""} onChange={(e) => handleChange(q.id, e.target.value)} + placeholder="Enter your answer" ></textarea> )}
72-92: Make checkboxes controlled withcheckedprop.The checkbox logic correctly manages array state, but checkboxes should be controlled to reflect the current state.
{q.type === "CHECKBOX" && q.choices?.map((choice, i) => ( <div key={i}> <input type="checkbox" value={choice} + checked={(answers[q.id] || []).includes(choice)} onChange={(e) => { const selected = answers[q.id] || []; if (e.target.checked) { handleChange(q.id, [...selected, choice]); } else { handleChange( q.id, selected.filter((c) => c !== choice) ); } }} /> {choice} </div> ))}
94-103: Make dropdown controlled withvalueprop.The dropdown should be controlled to reflect the current state.
{q.type === "LIST" && ( <select + value={answers[q.id] || ""} onChange={(e) => handleChange(q.id, e.target.value)} > <option value="">Select</option> {q.choices?.map((choice, i) => ( <option key={i} value={choice}> {choice} </option> ))} </select> )}
1-114: Eliminate duplicate DynamicForm component
src/DynamicForm.js and src/App.js both define the sameDynamicForm(state, fetch logic and handlers). Consolidate into one source of truth—import it where needed or remove the extra file.eduaid_web/src/App.js (2)
45-51: Good use of sizing attributes, but make controlled.The textarea correctly includes sizing attributes, which is better than the DynamicForm.js version.
Consider making it controlled:
{q.type === "PARAGRAPH_TEXT" && ( <textarea rows="4" cols="40" + value={responses[q.id] || ""} onChange={(e) => handleChange(q.id, e.target.value)} + placeholder="Enter your answer" /> )}
53-73: Better UX with label wrapping, but make controlled.Good improvement over DynamicForm.js by wrapping in labels for better clickable area.
Make controlled with
checkedprop:<input type="checkbox" value={choice} + checked={(responses[q.id] || []).includes(choice)} onChange={(e) => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
eduaid_web/src/App.js(1 hunks)eduaid_web/src/DynamicForm.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
eduaid_web/src/DynamicForm.js (1)
eduaid_web/src/App.js (3)
questions(4-4)handleChange(17-19)handleSubmit(21-25)
eduaid_web/src/App.js (1)
eduaid_web/src/DynamicForm.js (3)
questions(4-4)handleChange(23-25)handleSubmit(28-32)
🔇 Additional comments (3)
eduaid_web/src/DynamicForm.js (2)
23-25: LGTM!The functional state update correctly merges the new answer with existing state.
59-70: LGTM, but be aware of XSS risk.The radio button logic is correct. However, if
choicevalues can contain user-generated HTML, there's a potential XSS risk since React renders text content safely by default, but this assumes the data source is trusted.Verify that the Google Apps Script endpoint sanitizes choice values and doesn't return user-generated content that could contain malicious scripts.
eduaid_web/src/App.js (1)
86-97: LGTM! Good UX with label wrapping.The label wrapping provides better clickable area and improves accessibility compared to the DynamicForm.js version.
| fetch( | ||
| "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the Google Apps Script URL to environment variables.
The script URL is hardcoded, which exposes the endpoint and makes environment-specific configuration difficult.
useEffect(() => {
fetch(
- "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec"
+ process.env.REACT_APP_GOOGLE_SCRIPT_URL
)Add to .env:
REACT_APP_GOOGLE_SCRIPT_URL=https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fetch( | |
| "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec" | |
| ) | |
| fetch( | |
| process.env.REACT_APP_GOOGLE_SCRIPT_URL | |
| ) |
🤖 Prompt for AI Agents
In eduaid_web/src/App.js around lines 9-11 the Google Apps Script URL is
hardcoded; move this value into an environment variable by adding
REACT_APP_GOOGLE_SCRIPT_URL=<the URL> to .env, then update the fetch call to
read from process.env.REACT_APP_GOOGLE_SCRIPT_URL (and add a clear runtime error
or fallback if the env var is missing) so the endpoint is configurable per
environment and no secrets/URLs are hardcoded in source.
| // 👉 Replace this with your deployed Google Apps Script URL | ||
| const GOOGLE_SCRIPT_URL = | ||
| "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the Google Apps Script URL to environment variables.
Hardcoding the script URL exposes the endpoint publicly and makes it difficult to maintain across environments (dev, staging, prod).
Move to an environment variable:
- // 👉 Replace this with your deployed Google Apps Script URL
- const GOOGLE_SCRIPT_URL =
- "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec";
+ const GOOGLE_SCRIPT_URL = process.env.REACT_APP_GOOGLE_SCRIPT_URL;Then add to .env:
REACT_APP_GOOGLE_SCRIPT_URL=https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 👉 Replace this with your deployed Google Apps Script URL | |
| const GOOGLE_SCRIPT_URL = | |
| "https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec"; | |
| const GOOGLE_SCRIPT_URL = process.env.REACT_APP_GOOGLE_SCRIPT_URL; |
🤖 Prompt for AI Agents
In eduaid_web/src/DynamicForm.js around lines 7 to 9 the Google Apps Script URL
is hardcoded; replace it with an environment variable reference
(process.env.REACT_APP_GOOGLE_SCRIPT_URL) and remove the literal string, update
.env with REACT_APP_GOOGLE_SCRIPT_URL=<your-script-url>, and add a safe fallback
or throw a clear error if the env var is missing so builds fail fast in
non-configured environments.
| useEffect(() => { | ||
| fetch(GOOGLE_SCRIPT_URL) | ||
| .then((res) => res.json()) | ||
| .then((data) => { | ||
| console.log("Fetched form data:", data); | ||
| setQuestions(data); | ||
| }) | ||
| .catch((err) => console.error("Error fetching form data:", err)); | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add loading state and improve error handling.
The component doesn't indicate when data is being fetched, and errors are only logged without user feedback.
Apply this diff:
function DynamicForm() {
const [questions, setQuestions] = useState([]);
const [answers, setAnswers] = useState({});
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
const GOOGLE_SCRIPT_URL =
"https://script.google.com/macros/s/AKfycbxRamNfuS1b9yBXapdCs9gB3ScRhpxiRPbjTO0qrzeYel0BJEq4VyTGX3GnZ_-KIsZXkw/exec";
useEffect(() => {
+ setLoading(true);
fetch(GOOGLE_SCRIPT_URL)
.then((res) => res.json())
.then((data) => {
console.log("Fetched form data:", data);
setQuestions(data);
+ setLoading(false);
})
- .catch((err) => console.error("Error fetching form data:", err));
+ .catch((err) => {
+ console.error("Error fetching form data:", err);
+ setError("Failed to load form. Please try again later.");
+ setLoading(false);
+ });
}, []);Then update the render logic to show loading/error states:
if (loading) return <div>Loading form...</div>;
if (error) return <div style={{ color: "red" }}>{error}</div>;🤖 Prompt for AI Agents
In eduaid_web/src/DynamicForm.js around lines 12 to 20, the component fetch
logic lacks a loading state and user-visible error handling; update the
component to add local state variables (loading and error), set loading=true
before fetch, set loading=false and setQuestions(data) on success, and set
loading=false and setError with a user-friendly message on catch; then update
the render to return a loading placeholder when loading is true and an error
message styled in red when error is set (i.e., if (loading) return <div>Loading
form...</div>; if (error) return <div style={{ color: "red" }}>{error}</div>;).
| const handleSubmit = (e) => { | ||
| e.preventDefault(); | ||
| console.log("Submitted answers:", answers); | ||
| alert("Form submitted! Check console for answers."); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement actual form submission and validation.
The form currently only logs responses locally without submitting them to the Google Apps Script endpoint or validating required fields.
Consider implementing:
- POST request to submit responses to the backend
- Validation for required fields
- Better user feedback (e.g., success message, loading spinner)
Example:
const handleSubmit = (e) => {
e.preventDefault();
+
+ // Validate required fields
+ const requiredQuestions = questions.filter(q => q.required);
+ const missingAnswers = requiredQuestions.filter(q => !answers[q.id]);
+ if (missingAnswers.length > 0) {
+ alert("Please answer all required questions.");
+ return;
+ }
+
+ // Submit to backend
+ setLoading(true);
+ fetch(GOOGLE_SCRIPT_URL, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(answers)
+ })
+ .then(res => res.json())
+ .then(data => {
+ alert("Form submitted successfully!");
+ console.log("Submitted answers:", answers);
+ setLoading(false);
+ })
+ .catch(err => {
+ alert("Failed to submit form. Please try again.");
+ console.error("Submission error:", err);
+ setLoading(false);
+ });
- console.log("Submitted answers:", answers);
- alert("Form submitted! Check console for answers.");
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSubmit = (e) => { | |
| e.preventDefault(); | |
| console.log("Submitted answers:", answers); | |
| alert("Form submitted! Check console for answers."); | |
| }; | |
| const handleSubmit = (e) => { | |
| e.preventDefault(); | |
| // Validate required fields | |
| const requiredQuestions = questions.filter(q => q.required); | |
| const missingAnswers = requiredQuestions.filter(q => !answers[q.id]); | |
| if (missingAnswers.length > 0) { | |
| alert("Please answer all required questions."); | |
| return; | |
| } | |
| // Submit to backend | |
| setLoading(true); | |
| fetch(GOOGLE_SCRIPT_URL, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(answers) | |
| }) | |
| .then(res => res.json()) | |
| .then(data => { | |
| alert("Form submitted successfully!"); | |
| console.log("Submitted answers:", answers); | |
| setLoading(false); | |
| }) | |
| .catch(err => { | |
| alert("Failed to submit form. Please try again."); | |
| console.error("Submission error:", err); | |
| setLoading(false); | |
| }); | |
| }; |
🤖 Prompt for AI Agents
In eduaid_web/src/DynamicForm.js around lines 28 to 32, the form submit handler
only logs answers and shows an alert; replace it with real submission and
validation: validate required fields before sending, show inline validation
errors, set a loading state to disable the submit button and display a spinner
while submitting, send a POST (fetch/axios) to the Google Apps Script endpoint
with the answers as JSON and appropriate headers, await the response and handle
non-2xx responses as errors, show a success message on completion and clear or
preserve form per UX decision, and catch/network errors to show a user-friendly
error message and re-enable the form. Ensure to prevent multiple submissions and
include minimal retry/backoff or instructive error text for failed submissions.
This PR adds a new DynamicForm component that fetches questions from Google Forms via Google Apps Script and allows users to submit responses dynamically.
Changes Made
Added DynamicForm.js component.
Updated App.js to include a new route /dynamic-form.
Integrated Google Apps Script endpoint to fetch questions.
Basic UI for rendering text, paragraph, multiple choice, checkbox, and dropdown questions.
Testing
Verified that form questions are fetched correctly from the Apps Script endpoint.
Responses can be submitted successfully (will be stored in Google Form/Sheet).
Application runs at http://localhost:3000/#/dynamic-form.
Closes #.
Summary by CodeRabbit
New Features
Changes