Skip to content

Conversation

@plutov
Copy link
Owner

@plutov plutov commented Aug 4, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new React-based UI with client-side routing using React Router.
    • Added dedicated pages for surveys, survey responses, and a not-found (404) page.
    • Implemented a new HTML entry point and Nginx configuration for static site serving.
  • Improvements

    • Switched the frontend build system from Next.js to Vite for faster development and builds.
    • Simplified environment variable management for the UI.
    • Updated Docker and deployment configuration to serve the UI via Nginx on port 80.
    • Enhanced ESLint and TypeScript configurations for improved code quality and compatibility.
    • Updated documentation to reflect the new tech stack and deployment steps.
  • Bug Fixes

    • Improved error handling and loading states on survey and response pages.
  • Chores

    • Updated dependencies and build scripts for React, React Router, Tailwind CSS, and related tooling.
    • Adjusted .gitignore and example environment files to match the new build output and configuration.

@coderabbitai
Copy link

coderabbitai bot commented Aug 4, 2025

Walkthrough

This update transitions the frontend from a Next.js-based setup to a Vite-powered React application using React Router for client-side routing. It introduces new configuration files for Vite and Nginx, updates environment variable usage, revises Docker and deployment configurations, and adapts documentation and code to reflect the new stack. Several new React route components are added.

Changes

Cohort / File(s) Change Summary
Frontend Build & Deployment Config
.gitignore, ui/Dockerfile, ui/nginx.conf, ui/index.html, ui/vite.config.ts, ui/postcss.config.js, ui/package.json, ui/tsconfig.json, ui/.env.example, compose.yaml
Migrated frontend build and deployment from Next.js to Vite/React with React Router. Added Nginx config for static serving. Updated Dockerfile to use Nginx. Adjusted environment variables and build output paths. Updated package scripts, dependencies, and TypeScript config for Vite.
Documentation
README.md
Updated references from Next.js to React Router UI and adjusted tech stack documentation.
Linting Configuration
ui/.eslintrc.json
Expanded and restructured ESLint config for React, TypeScript, React hooks, and Prettier compatibility.
API Integration
ui/src/lib/api.ts
Updated API host resolution to use Vite environment variable. Refactored for consistency and added logging.
App Routing & Layout
ui/src/main.tsx, ui/src/routes/layout.tsx, ui/src/routes/not-found.tsx
Introduced main React entry point, root layout, and not-found page using React Router.
Survey App Pages
ui/src/routes/app.tsx, ui/src/routes/app.surveys.$surveyUuid.responses.tsx, ui/src/routes/survey.$urlSlug.tsx
Added new route components for main app page, survey responses by UUID, and individual surveys by slug, handling loading and error states.
Survey Question Component
ui/src/components/app/survey/SurveyQuestions.tsx
Applied block scoping to switch cases, minor formatting, and import adjustments for clarity and consistency.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Nginx
    participant ReactApp
    participant API

    Browser->>Nginx: Request / (or /survey/:slug)
    Nginx->>Browser: Serve index.html and static assets
    Browser->>ReactApp: Load JS bundle, mount #root
    ReactApp->>API: Fetch surveys or survey details (using VITE_API_URL)
    API-->>ReactApp: Return survey data
    ReactApp-->>Browser: Render app UI with routing
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related issues

  • Replace Nextjs with Vue #44: This PR directly addresses the goal of replacing Next.js with React Router and related changes in the UI codebase.

Poem

🐇✨
Goodbye, Next.js, you’ve run your course,
React Router now steers our force!
Vite brings the speed, Nginx serves with pride,
Surveys and routes, all neatly supplied.
With configs anew and docs up to date,
This bunny hops forward—React’s looking great!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/react-router-v2

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (6)
ui/.env.example (1)

1-1: Environment variable simplification looks good, but add a trailing newline.

The consolidation to a single VITE_API_URL variable aligns well with the Vite migration and simplifies the configuration.

Apply this diff to add the missing blank line at the end:

-VITE_API_URL=http://localhost:9900
+VITE_API_URL=http://localhost:9900
+
ui/src/routes/app.surveys.$surveyUuid.responses.tsx (1)

26-66: Consider extracting data fetching logic.

The fetchSurveyData function is quite long and handles multiple concerns. Consider extracting it into a custom hook or separate utility function for better reusability and testability.

ui/nginx.conf (1)

41-44: Consider adding Content Security Policy.

While the basic security headers are good, consider adding a Content Security Policy (CSP) header for enhanced security against XSS attacks.

Add this line within the server block:

 # Security headers
 add_header X-Frame-Options DENY;
 add_header X-Content-Type-Options nosniff;
 add_header X-XSS-Protection "1; mode=block";
+add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; img-src 'self' data: https:;";
ui/src/lib/api.ts (1)

7-11: Remove debug console.log statements from production code.

The console.log statement on line 10 should be removed or conditionally included only in development environments, as it can expose internal API URLs in production logs.

-    if (!host) {
-      // Use environment variable or fallback to default API URL
-      host = import.meta.env.VITE_API_URL || 'http://localhost:9900'
-      console.log('Using API host:', host)
-    }
+    if (!host) {
+      // Use environment variable or fallback to default API URL
+      host = import.meta.env.VITE_API_URL || 'http://localhost:9900'
+    }
ui/src/routes/survey.$urlSlug.tsx (1)

32-32: Consider extracting document title management to a custom hook.

Direct manipulation of document.title works but could be extracted to a reusable useDocumentTitle hook for better separation of concerns.

// Custom hook example
function useDocumentTitle(title: string) {
  useEffect(() => {
    document.title = title
  }, [title])
}

// Usage in component
useDocumentTitle(notFound ? 'Survey not found' : survey?.config.title || 'Loading...')

Also applies to: 41-41

ui/src/routes/app.tsx (1)

35-43: Consider extracting loading spinner to a reusable component.

The loading spinner implementation is duplicated between this component and SurveyPage. Consider creating a shared LoadingSpinner component.

// components/ui/LoadingSpinner.tsx
export function LoadingSpinner() {
  return (
    <div className="flex justify-center items-center h-64">
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
    </div>
  )
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d55c0b0 and 453bfcc.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • ui/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • .gitignore (1 hunks)
  • README.md (2 hunks)
  • compose.yaml (1 hunks)
  • ui/.env.example (1 hunks)
  • ui/.eslintrc.json (1 hunks)
  • ui/Dockerfile (1 hunks)
  • ui/index.html (1 hunks)
  • ui/nginx.conf (1 hunks)
  • ui/package.json (2 hunks)
  • ui/postcss.config.js (1 hunks)
  • ui/src/components/app/survey/SurveyQuestions.tsx (7 hunks)
  • ui/src/lib/api.ts (1 hunks)
  • ui/src/main.tsx (1 hunks)
  • ui/src/routes/app.surveys.$surveyUuid.responses.tsx (1 hunks)
  • ui/src/routes/app.tsx (1 hunks)
  • ui/src/routes/layout.tsx (1 hunks)
  • ui/src/routes/not-found.tsx (1 hunks)
  • ui/src/routes/survey.$urlSlug.tsx (1 hunks)
  • ui/tsconfig.json (1 hunks)
  • ui/vite.config.ts (1 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
ui/.env.example

[warning] 1-1: [EndingBlankLine] No blank line at the end of the file

🔇 Additional comments (37)
.gitignore (1)

9-9: LGTM! Correctly ignoring Vite build output.

The addition of ui/dist is appropriate for the migration to Vite, which outputs built assets to the dist directory by default. This aligns with ignoring other build artifacts.

ui/postcss.config.js (1)

1-6: LGTM! Correctly updated to ES module syntax for Vite compatibility.

The migration from CommonJS to ES module syntax is necessary for Vite compatibility while maintaining the same PostCSS configuration.

README.md (2)

288-290: LGTM! Documentation correctly updated for the tech stack migration.

The deployment section accurately reflects the new architecture components.


319-319: LGTM! Tech stack section correctly updated.

The reference to React Router instead of Next.js accurately reflects the migration.

ui/src/routes/not-found.tsx (1)

1-21: LGTM! Well-structured 404 page component.

The NotFoundPage component is cleanly implemented with:

  • Proper document title setting
  • Semantic HTML structure
  • Consistent Tailwind CSS styling
  • Appropriate use of the AppLayout wrapper

The component follows React best practices and integrates well with the new React Router architecture.

ui/src/routes/layout.tsx (1)

1-22: LGTM! Clean root layout implementation.

The component correctly implements a root layout for React Router with proper document head management. The meta description handling logic appropriately checks for existing tags before creating new ones.

ui/vite.config.ts (1)

1-29: Vite configuration and alias usage verified and approved

  • The components and lib aliases are actively used across your .tsx and .ts files.
  • The styles alias is applied via bare CSS imports (e.g. import 'styles/app.css').
  • The @ alias is correctly pointed to ./src and ready for future use.
  • Server, build, and define settings align with the React Router migration.

All looks good—approving the changes.

compose.yaml (2)

22-22: Port mapping correctly updated for Nginx.

The port change from 3000 to 80 correctly reflects the migration from Node.js server to Nginx static file serving.


24-24: No change needed to VITE_API_URL

The VITE_API_URL is consumed client-side (in ui/src/lib/api.ts and your route components), so at runtime it’s the browser making the fetch requests. In Docker-Compose you’ve mapped the host’s port 9900 to the API container, so from the browser http://localhost:9900 correctly reaches your backend. Using http://api:8080 would only resolve inside the Docker network—not from the user’s browser—so leave the existing setting as is.

ui/index.html (2)

1-17: Well-structured HTML entry point.

The HTML file correctly sets up the Vite React application with proper meta tags, favicon links, and module script loading. The dark theme default and Tailwind utility classes are appropriate.


5-11: All referenced favicon assets are present

Verified that the following files exist under ui/public and match the HTML references:

  • favicon.ico
  • favicon-32x32.png
  • favicon-16x16.png
  • apple-touch-icon.png
  • site.webmanifest
ui/src/components/app/survey/SurveyQuestions.tsx (4)

18-18: Good formatting improvement.

Adding the trailing comma to the import improves consistency and makes future additions cleaner.


234-263: Excellent addition of explicit block scoping.

Adding braces around the Rating case block improves code clarity and prevents potential variable scoping issues in switch statements.


429-450: Consistent block scoping improvements.

The explicit braces added to Rating, Ranking, and YesNo case blocks maintain consistency and improve code readability throughout the switch statement.


470-478: Clean multiline formatting.

The form element reformatting to multiline improves readability and follows consistent formatting patterns.

ui/Dockerfile (1)

19-33: LGTM! Clean migration to static file serving.

The transition from Node.js runtime to Nginx for serving the static React SPA is well-implemented. The configuration correctly:

  • Uses a lightweight nginx:alpine base
  • Cleans up default Nginx files
  • Copies build artifacts from the correct Vite output directory (/app/dist)
  • Includes custom Nginx configuration
  • Exposes the standard HTTP port 80
ui/src/main.tsx (2)

13-37: Well-structured React Router configuration.

The router setup follows React Router best practices with:

  • Proper nested routing structure
  • Error boundary with NotFoundPage
  • Catch-all route for unmatched paths
  • Clear separation of concerns with dedicated route components

39-44: Root element verified in index.html

The search confirms that ui/index.html contains an element with id="root". No further changes are needed.

ui/src/routes/app.surveys.$surveyUuid.responses.tsx (1)

71-79: LGTM! Good loading state implementation.

The loading spinner with proper styling and layout structure provides good user experience during data fetching.

ui/.eslintrc.json (1)

1-19: Well-configured ESLint setup for Vite + React.

The configuration properly transitions from Next.js to a Vite-based setup with:

  • Appropriate environment settings for browser and ES2020
  • Standard recommended rule sets
  • React refresh plugin for development
  • Proper ignore patterns for build artifacts
ui/nginx.conf (2)

30-33: LGTM! Proper SPA routing configuration.

The try_files directive correctly handles client-side routing by falling back to index.html for all unmatched routes, which is essential for React Router.


35-39: Good static asset caching strategy.

The one-year cache duration with immutable headers is appropriate for fingerprinted static assets in a modern build system like Vite.

ui/src/lib/api.ts (2)

48-137: LGTM! Consistent formatting improvements.

The formatting and indentation improvements enhance code readability while maintaining the original functionality.


150-150: LGTM! Consistent environment variable migration.

The migration from process.env to import.meta.env.VITE_API_URL is correctly implemented across the API functions.

Also applies to: 157-157

ui/src/routes/survey.$urlSlug.tsx (2)

9-48: LGTM! Well-structured React component with proper error handling.

The component follows React best practices with proper state management, error handling, and loading states. The useEffect dependency array is correctly set with [urlSlug].


50-50: Environment variable access is correct.

The use of import.meta.env.VITE_API_URL is appropriate for the Vite migration.

ui/package.json (4)

4-4: LGTM! Correct ES module specification for Vite.

Adding "type": "module" is appropriate for the Vite + React setup and aligns with modern ES module standards.


6-10: LGTM! Proper Vite script configuration.

The migration from Next.js scripts to Vite commands is correctly implemented with appropriate ESLint configuration.


19-22: LGTM! Appropriate dependency updates for React Router migration.

The React and React DOM version upgrades to 18.3.1 and addition of React Router 7.7.1 support the new architecture.


34-47: LGTM! Comprehensive Vite ecosystem integration.

The devDependencies updates correctly include Vite, its React plugin, and updated TypeScript/ESLint tooling for the new setup.

ui/tsconfig.json (5)

4-10: LGTM! Appropriate target and library updates for modern environment.

The ES2020 target and capitalized library references align with modern TypeScript and Vite best practices.


17-18: LGTM! Correct module resolution for Vite.

The "bundler" module resolution and allowImportingTsExtensions setting are appropriate for Vite's module handling.


22-22: LGTM! Correct JSX setting for React 18.

The "react-jsx" JSX setting is appropriate for React 18 and Vite, replacing Next.js's "preserve" setting.


24-24: LGTM! Proper Vite client types inclusion.

Adding "vite/client" types replaces the Next.js plugin and provides proper Vite environment typings.


31-33: LGTM! Updated include patterns for new project structure.

The include patterns correctly target the new Vite-based project structure with src directory and vite.config.ts.

ui/src/routes/app.tsx (2)

8-33: LGTM! Well-structured component with proper React patterns.

The component correctly implements React hooks, error handling, and loading states. The useEffect dependency array is appropriately empty for mount-only execution.


53-53: LGTM! Consistent environment variable usage.

The use of import.meta.env.VITE_API_URL is consistent with the Vite migration and other components.

Comment on lines +3 to +5
if (init) {
init['cache'] = 'no-store'
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure cache setting is applied consistently.

The cache setting is now only applied when init is provided. This could cause caching issues for API calls that don't provide an init parameter.

-    if (init) {
-      init['cache'] = 'no-store'
-    }
+    if (!init) {
+      init = {}
+    }
+    init['cache'] = 'no-store'
📝 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.

Suggested change
if (init) {
init['cache'] = 'no-store'
}
if (!init) {
init = {}
}
init['cache'] = 'no-store'
🤖 Prompt for AI Agents
In ui/src/lib/api.ts around lines 3 to 5, the cache setting 'no-store' is only
applied when the init parameter is provided, which can lead to inconsistent
caching behavior. Modify the code to ensure that the cache setting is always
applied by initializing the init object if it is undefined, then setting
init['cache'] to 'no-store' consistently for all API calls.

apiURL
)
}
if (payload! instanceof FormData) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix syntax error in instanceof check.

The negation operator placement is incorrect and will cause a runtime error.

-  if (payload! instanceof FormData) {
+  if (!(payload instanceof FormData)) {
📝 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.

Suggested change
if (payload! instanceof FormData) {
if (!(payload instanceof FormData)) {
🤖 Prompt for AI Agents
In ui/src/lib/api.ts at line 233, the syntax for the instanceof check is
incorrect due to the misplaced negation operator. Fix this by properly grouping
the expression with parentheses so that the negation applies to the entire
instanceof check, for example, use !(payload instanceof FormData) instead of
payload! instanceof FormData.

Comment on lines +45 to +50
const apiURL = import.meta.env.VITE_API_URL || ''
const surveySessionsResp = await getSurveySessions(
survey.uuid,
`limit=${SurveySessionsLimit}&offset=0&sort_by=created_at&order=desc`,
apiURL
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistent API URL usage.

The component fetches the API URL from environment variables twice (lines 45 and 89) but only passes it to getSurveySessions. For consistency, either:

  1. Pass apiURL to all API calls, or
  2. Handle API URL resolution within the API functions themselves

Apply this diff to use consistent API URL handling:

-        const apiURL = import.meta.env.VITE_API_URL || ''
-        const surveySessionsResp = await getSurveySessions(
-          survey.uuid,
-          `limit=${SurveySessionsLimit}&offset=0&sort_by=created_at&order=desc`,
-          apiURL
-        )
+        const surveySessionsResp = await getSurveySessions(
+          survey.uuid,
+          `limit=${SurveySessionsLimit}&offset=0&sort_by=created_at&order=desc`
+        )
📝 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.

Suggested change
const apiURL = import.meta.env.VITE_API_URL || ''
const surveySessionsResp = await getSurveySessions(
survey.uuid,
`limit=${SurveySessionsLimit}&offset=0&sort_by=created_at&order=desc`,
apiURL
)
const surveySessionsResp = await getSurveySessions(
survey.uuid,
`limit=${SurveySessionsLimit}&offset=0&sort_by=created_at&order=desc`
)
🤖 Prompt for AI Agents
In ui/src/routes/app.surveys.$surveyUuid.responses.tsx around lines 45 to 50,
the API URL is fetched from environment variables and passed only to
getSurveySessions, while other API calls fetch the URL separately. To fix this
inconsistency, refactor the code to either pass the apiURL variable to all API
calls uniformly or modify the API functions to internally resolve the API URL,
ensuring consistent usage across the component.

Comment on lines +15 to +17
define: {
'process.env': {},
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential issue with process.env override.

Defining process.env as an empty object will prevent access to all environment variables including VITE_API_URL mentioned in the compose.yaml. This could break API communication.

Consider removing this definition or using a more targeted approach:

-  define: {
-    'process.env': {},
-  },

If you need to define specific variables, use individual definitions:

+  define: {
+    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
+  },
📝 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.

Suggested change
define: {
'process.env': {},
},
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
🤖 Prompt for AI Agents
In ui/vite.config.ts around lines 15 to 17, defining 'process.env' as an empty
object overrides all environment variables, blocking access to needed ones like
VITE_API_URL. To fix this, remove the 'process.env' definition entirely or
replace it with individual environment variable definitions that explicitly set
only the required variables, preserving access to others.

@plutov plutov closed this Aug 5, 2025
@plutov plutov deleted the feat/react-router-v2 branch August 5, 2025 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants