Skip to content

feat: added contact page#345

Merged
MODSetter merged 5 commits intomainfrom
dev
Oct 1, 2025
Merged

feat: added contact page#345
MODSetter merged 5 commits intomainfrom
dev

Conversation

@MODSetter
Copy link
Owner

@MODSetter MODSetter commented Oct 1, 2025

Description

feat: added contact page

Screenshots

API Changes

  • This PR includes API changes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance improvement (non-breaking change which enhances performance)
  • Documentation update
  • Breaking change (fix or feature that would cause existing functionality to change)

Testing

  • I have tested these changes locally
  • I have added/updated unit tests
  • I have added/updated integration tests

Checklist:

  • My code follows the code style of this project
  • My change requires documentation updates
  • I have updated the documentation accordingly
  • My change requires dependency updates
  • I have updated the dependencies accordingly
  • My code builds clean without any errors or warnings
  • All new and existing tests passed

High-level PR Summary

This PR adds a new contact page functionality to the SurfSense web application. The implementation includes a contact form UI component, backend API route for handling form submissions, database schema and connection setup using Drizzle ORM, and UI integrations in the navbar and homepage. The contact form allows users to submit their name, email, company, and an optional message, which is then stored in a database. The PR also adds a pricing page with a tiered pricing display component, featuring animated transitions and yearly/monthly toggle options. Additionally, there's a dependency change from framer-motion to motion/react across multiple files, along with the addition of several new dependencies for database management.

⏱️ Estimated Review Time: 30-90 minutes

💡 Review Order Suggestion
Order File Path
1 surfsense_web/app/contact/page.tsx
2 surfsense_web/components/contact/contact-form.tsx
3 surfsense_web/app/api/contact/route.ts
4 surfsense_web/app/db/schema.ts
5 surfsense_web/app/db/index.ts
6 surfsense_web/.env.example
7 surfsense_web/components/Navbar.tsx
8 surfsense_web/components/ModernHeroWithGradients.tsx
9 surfsense_web/app/pricing/page.tsx
10 surfsense_web/components/pricing/pricing-section.tsx
11 surfsense_web/components/pricing.tsx
12 surfsense_web/hooks/use-media-query.ts
13 surfsense_web/drizzle.config.ts
14 surfsense_web/package.json
⚠️ Inconsistent Changes Detected
File Path Warning
surfsense_web/components/chat/AnimatedEmptyState.tsx Removed a seemingly unrelated state variable reference sidebarState without using an alternative, potentially causing functionality issues

Need help? Join our Discord

Analyze latest changes

Summary by CodeRabbit

  • New Features
    • Contact page with a validated contact form, submission handling, and user feedback.
    • Pricing page with plan cards and a monthly/annual toggle that triggers confetti on annual.
    • Added “Contact Us” buttons in header and hero for quick access.
  • Refactor
    • Switched animation imports across the app to a new motion entrypoint (no UI behavior changes).
  • Chores
    • Added database configuration, ORM tooling and related environment variables.
    • Updated project scripts and dependencies to support DB, pricing UI, and animations.
  • UI
    • Added a reusable Switch component for toggles.

@vercel
Copy link

vercel bot commented Oct 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
surf-sense-frontend Ready Ready Preview Comment Oct 1, 2025 5:28am

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

Walkthrough

Adds a contact feature (client form, POST API, DB schema and Drizzle wiring), a Pricing component/page and media-query hook, a Radix Switch UI, and migrates many animation imports from framer-motion to motion/react. Also updates environment/example and package scripts/deps for DB tooling.

Changes

Cohort / File(s) Summary
Environment & Tooling
surfsense_web/.env.example, surfsense_web/drizzle.config.ts, surfsense_web/package.json
Restores NEXT_PUBLIC_ETL_SERVICE; adds optional DATABASE_URL; adds Drizzle config and DB scripts; introduces dependencies for Drizzle, Postgres, motion, canvas-confetti, number-flow, Radix Switch and related tooling.
Database Layer
surfsense_web/app/db/index.ts, surfsense_web/app/db/schema.ts
New Drizzle Postgres client export (db) and usersTable schema (id, name, email unique, company, message).
Contact API
surfsense_web/app/api/contact/route.ts
New POST handler: Zod validation, insert into usersTable, returns 201 with inserted record; 400 for validation errors; 500 for other failures.
Contact UI & Links
surfsense_web/app/contact/page.tsx, surfsense_web/components/contact/contact-form.tsx, surfsense_web/components/ModernHeroWithGradients.tsx, surfsense_web/components/Navbar.tsx
Adds contact page and client form (React Hook Form + Zod + fetch to /api/contact), toasts, decorative grid; adds Contact buttons/links (hero and navbar mobile/desktop).
Pricing Feature
surfsense_web/components/pricing.tsx, surfsense_web/components/pricing/pricing-section.tsx, surfsense_web/app/pricing/page.tsx, surfsense_web/hooks/use-media-query.ts
Adds Pricing component with monthly/annual toggle (confetti on annual), demo PricingBasic, pricing page, and useMediaQuery hook.
Motion import migration
Many files under surfsense_web/app/... and surfsense_web/components/... (e.g., app/dashboard/..., app/login/*, app/onboard/*, components/onboard/*, components/settings/*, components/ui/*, components/search-space-form.tsx)
Replaces imports from framer-motion to motion/react (AnimatePresence, motion, Variants, hooks/types) across many components; no other logic changes.
AnimatedEmptyState tweak
surfsense_web/components/chat/AnimatedEmptyState.tsx
Switches to motion/react for in-view utilities and removes useSidebar usage.
UI Utilities
surfsense_web/components/ui/switch.tsx, surfsense_web/components/ui/spotlight.tsx, surfsense_web/components/ui/tilt.tsx
Adds Radix-based Switch component; migrates Spotlight/Tilt motion imports to motion/react.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant CF as ContactForm (client)
  participant API as /api/contact (Next.js)
  participant DB as Drizzle / Postgres

  U->>CF: Fill form & submit
  CF->>API: POST /api/contact {name,email,company,message}
  API->>API: Validate with Zod
  alt Valid
    API->>DB: insert into usersTable
    DB-->>API: inserted record
    API-->>CF: 201 {success:true, data}
    CF-->>U: Show success toast
  else Invalid
    API-->>CF: 400 {success:false, errors}
    CF-->>U: Show field errors
  else Server error
    API-->>CF: 500 {success:false}
    CF-->>U: Show error toast
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit taps keys with caffeinated cheer,
Forms and pricing sprout, the DB draws near,
Drizzle pours rows where messages land,
Motion hops paths on a new import strand,
“Contact us” I nibble — send it, dear band! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat: added contact page” clearly describes a significant new feature introduced by this pull request, namely the contact page and its associated form, even though the changeset also includes backend API support, database schema setup, and other updates. It is specific and concise, referring to a real aspect of the change without being misleading or generic.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

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

Review by RecurseML

🔍 Review performed on 5acc05a..1a15309

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (5)

surfsense_web/components/contact/contact-form.tsx
surfsense_web/components/pricing.tsx
surfsense_web/components/pricing/pricing-section.tsx
surfsense_web/app/api/contact/route.ts
surfsense_web/hooks/use-media-query.ts

⏭️ Files skipped (56)
  Locations  
surfsense_web/.env.example
surfsense_web/app/contact/page.tsx
surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx
surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx
surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx
surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
surfsense_web/app/dashboard/api-key/api-key-client.tsx
surfsense_web/app/dashboard/page.tsx
surfsense_web/app/dashboard/searchspaces/page.tsx
surfsense_web/app/db/index.ts
surfsense_web/app/db/schema.ts
surfsense_web/app/login/GoogleLoginButton.tsx
surfsense_web/app/login/LocalLoginForm.tsx
surfsense_web/app/login/page.tsx
surfsense_web/app/onboard/page.tsx
surfsense_web/app/pricing/page.tsx
surfsense_web/app/register/page.tsx
surfsense_web/components/ModernHeroWithGradients.tsx
surfsense_web/components/Navbar.tsx
surfsense_web/components/chat/AnimatedEmptyState.tsx
surfsense_web/components/onboard/add-provider-step.tsx
surfsense_web/components/onboard/assign-roles-step.tsx
surfsense_web/components/onboard/completion-step.tsx
surfsense_web/components/search-space-form.tsx
surfsense_web/components/settings/llm-role-manager.tsx
surfsense_web/components/settings/model-config-manager.tsx
surfsense_web/components/theme/theme-toggle.tsx
surfsense_web/components/ui/spotlight.tsx
surfsense_web/components/ui/switch.tsx
surfsense_web/components/ui/tilt.tsx
surfsense_web/drizzle.config.ts
surfsense_web/package.json
surfsense_web/pnpm-lock.yaml
surfsense_web/public/contact/world.svg

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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx (1)

443-480: Key uniqueness may be unstable for files with duplicate names.

The key key={${file.name}-${index}} on line 445 could be unstable if users upload multiple files with the same name (e.g., multiple document.pdf files from different folders). This could cause React reconciliation issues if a user removes one file and adds another with the same name.

Consider using a more stable unique identifier. If files don't have a unique ID, you could generate one when files are added:

 const onDrop = useCallback((acceptedFiles: File[]) => {
-  setFiles((prevFiles) => [...prevFiles, ...acceptedFiles]);
+  setFiles((prevFiles) => [
+    ...prevFiles,
+    ...acceptedFiles.map((file, idx) => ({
+      file,
+      id: `${Date.now()}-${idx}-${file.name}`,
+    })),
+  ]);
 }, []);

Then update the file type and mapping:

const [files, setFiles] = useState<Array<{ file: File; id: string }>>([]);

// In the map:
{files.map((item) => (
  <motion.div
    key={item.id}
    // ... use item.file for file properties
  >
surfsense_web/components/chat/AnimatedEmptyState.tsx (1)

7-7: Remove unused import.

The useSidebar import is no longer used in this component after the sidebar state was removed from the effect logic.

Apply this diff:

-import { useSidebar } from "@/components/ui/sidebar";
 import { cn } from "@/lib/utils";
surfsense_web/components/ui/spotlight.tsx (1)

48-60: Fix event listener cleanup - memory leak.

Lines 57-58 create new arrow functions when removing event listeners, so the original listeners from lines 52-53 are never removed, causing a memory leak. Event listeners must use the same function reference for both add and remove.

Apply this diff to fix the cleanup:

+	const handleMouseEnter = useCallback(() => setIsHovered(true), []);
+	const handleMouseLeave = useCallback(() => setIsHovered(false), []);
+
 	useEffect(() => {
 		if (!parentElement) return;
 
 		parentElement.addEventListener("mousemove", handleMouseMove);
-		parentElement.addEventListener("mouseenter", () => setIsHovered(true));
-		parentElement.addEventListener("mouseleave", () => setIsHovered(false));
+		parentElement.addEventListener("mouseenter", handleMouseEnter);
+		parentElement.addEventListener("mouseleave", handleMouseLeave);
 
 		return () => {
 			parentElement.removeEventListener("mousemove", handleMouseMove);
-			parentElement.removeEventListener("mouseenter", () => setIsHovered(true));
-			parentElement.removeEventListener("mouseleave", () => setIsHovered(false));
+			parentElement.removeEventListener("mouseenter", handleMouseEnter);
+			parentElement.removeEventListener("mouseleave", handleMouseLeave);
 		};
-	}, [parentElement, handleMouseMove]);
+	}, [parentElement, handleMouseMove, handleMouseEnter, handleMouseLeave]);
♻️ Duplicate comments (18)
surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx (1)

4-4: Verify the motion/react package configuration.

The import has been updated from framer-motion to motion/react. This change follows the same migration pattern across multiple files in this PR.

See the verification script in the review comment for surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx to confirm the motion package configuration.

surfsense_web/components/onboard/add-provider-step.tsx (1)

4-4: Verify the motion/react package configuration.

The import has been updated from framer-motion to motion/react. This change follows the same migration pattern across multiple files in this PR.

See the verification script in the review comment for surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx to confirm the motion package configuration.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx (1)

4-4: Verify the motion/react package configuration.

The import has been updated from framer-motion to motion/react. This change follows the same migration pattern across multiple files in this PR.

See the verification script in the review comment for surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx to confirm the motion package configuration.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx (1)

4-4: Verify the motion/react package configuration.

The import has been updated from framer-motion to motion/react. This change follows the same migration pattern across multiple files in this PR.

See the verification script in the review comment for surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx to confirm the motion package configuration.

surfsense_web/components/onboard/assign-roles-step.tsx (1)

4-4: Verify the motion/react import path is valid.

Same concern as in confluence-connector/page.tsx: ensure motion/react is a valid import path for your environment. See the verification script in the previous file's review.

surfsense_web/components/theme/theme-toggle.tsx (1)

4-4: Verify the motion/react import path is valid.

Same import migration concern. Refer to the verification script in the confluence-connector/page.tsx review.

surfsense_web/components/search-space-form.tsx (1)

5-5: Verify the motion/react import path is valid.

This file imports both motion and the Variants type from motion/react. Ensure both exports are available. Refer to the verification script in the confluence-connector/page.tsx review.

surfsense_web/app/login/LocalLoginForm.tsx (1)

2-2: Verify the motion/react import path is valid.

This file imports both AnimatePresence and motion from motion/react. Ensure both exports are available from this path, as AnimatePresence is a core animation primitive. Refer to the verification script in the confluence-connector/page.tsx review.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx (1)

9-9: Same import migration as other files - see verification request.

This file has the identical import path change from framer-motion to motion/react. Please see the verification request in surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx at line 18 to confirm this is a valid migration.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx (1)

4-4: Same import migration - verification pending.

This file has the same import path change from framer-motion to motion/react. Refer to the verification request in surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx at line 18.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx (1)

4-4: Same import migration - verification pending.

This file has the same import path change. See verification request in surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx at line 18.

surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx (1)

3-3: Same import migration - verification pending.

This file has the same import path change. See verification request in surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx at line 18.

surfsense_web/app/dashboard/page.tsx (1)

3-3: Verify the "motion/react" import path is correct.

The import source for both motion and Variants has changed from framer-motion to motion/react. As noted in the learnings, motion as a standalone package is ambiguous and may not be the official package name.

Ensure this import path is valid and that type exports like Variants are available from motion/react. The verification script in the previous file will confirm the package configuration.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx (1)

3-3: Verify the "motion/react" import path is correct.

Same import path change as other files in this PR. Confirm this resolves correctly per the verification script provided earlier.

surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx (1)

3-3: Verify the "motion/react" import path is correct.

Same import path change. Ensure consistency across the codebase and verify this path resolves correctly.

surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx (1)

3-3: Verify the "motion/react" import path is correct.

Same import path change as the rest of this PR. Confirm the package resolution is correct per earlier verification script.

surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx (1)

3-3: Verify the "motion/react" import source.

Same concern as in DocumentsFilters.tsx: the import source motion/react should be verified. See the verification script and comments in DocumentsFilters.tsx review.

Based on learnings.

surfsense_web/components/ui/spotlight.tsx (1)

2-2: Verify the "motion/react" import source.

Same concern as in other files: the import source motion/react should be verified. See the verification script and comments in DocumentsFilters.tsx review.

Based on learnings.

🧹 Nitpick comments (7)
surfsense_web/app/pricing/page.tsx (2)

1-1: Remove unused React import.

In React 19 and Next.js 15, explicit React imports are no longer required for JSX. This import can be removed.

-import React from 'react'
 import PricingBasic from '@/components/pricing/pricing-section'

4-10: Add metadata export for SEO.

Next.js pages should export metadata for proper SEO, Open Graph tags, and page titles.

Add metadata export at the top of the file:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Pricing - SurfSense',
  description: 'Choose the right plan for your needs',
}
surfsense_web/components/pricing/pricing-section.tsx (1)

64-72: Clean wrapper component.

The PricingBasic component properly delegates to the shared Pricing component with appropriate props.

For improved type safety, consider defining an explicit type:

+import { Pricing, type PricingProps } from "@/components/pricing";
+
+type Plan = PricingProps['plans'][number];
+
-const demoPlans = [
+const demoPlans: Plan[] = [
surfsense_web/components/pricing.tsx (2)

95-129: Use a stable plan key instead of the index

key={index} will reshuffle animations/state if the plans array order changes. Prefer a deterministic identifier such as plan.name or plan.href to keep the motion cards stable between renders.

As per coding guidelines
Apply this diff:

-				{plans.map((plan, index) => (
+				{plans.map((plan, index) => (
 					<motion.div
-						key={index}
+						key={plan.name}

169-175: Switch feature list to a stable key

Similarly, using the array index for feature items risks React reusing DOM nodes incorrectly. If each feature string is unique, use it directly as the key for deterministic reconciliation.

As per coding guidelines
Apply this diff:

-								{plan.features.map((feature, idx) => (
-									<li key={idx} className="flex items-start gap-2">
+								{plan.features.map((feature) => (
+									<li key={feature} className="flex items-start gap-2">
surfsense_web/package.json (1)

55-55: Standardize on a single PostgreSQL client.

schema.ts (drizzle-orm/pg-core) uses node-postgres, while index.ts (drizzle-orm/postgres-js + postgres) uses postgres.js. Consolidate on one client to reduce bundle size and eliminate confusion—either remove pg/@types/pg and drizzle-orm/pg-core, or remove postgres and drizzle-orm/postgres-js and switch fully to pg-core.

surfsense_web/components/settings/llm-role-manager.tsx (1)

400-404: Consider using stable content-based keys instead of array index.

The code uses the array index (idx) as the key for mapping role.characteristics. While this works because characteristics is a static array from a constant, using the characteristic string itself as the key would be more robust and align better with React best practices.

Apply this diff to use content-based keys:

-{role.characteristics.map((char, idx) => (
-  <Badge key={idx} variant="outline" className="text-xs">
+{role.characteristics.map((char) => (
+  <Badge key={char} variant="outline" className="text-xs">
     {char}
   </Badge>
 ))}

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5acc05a and 1a15309.

⛔ Files ignored due to path filters (2)
  • surfsense_web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • surfsense_web/public/contact/world.svg is excluded by !**/*.svg
📒 Files selected for processing (59)
  • surfsense_web/.env.example (1 hunks)
  • surfsense_web/app/api/contact/route.ts (1 hunks)
  • surfsense_web/app/contact/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/api-key/api-key-client.tsx (1 hunks)
  • surfsense_web/app/dashboard/page.tsx (1 hunks)
  • surfsense_web/app/dashboard/searchspaces/page.tsx (1 hunks)
  • surfsense_web/app/db/index.ts (1 hunks)
  • surfsense_web/app/db/schema.ts (1 hunks)
  • surfsense_web/app/login/GoogleLoginButton.tsx (1 hunks)
  • surfsense_web/app/login/LocalLoginForm.tsx (1 hunks)
  • surfsense_web/app/login/page.tsx (1 hunks)
  • surfsense_web/app/onboard/page.tsx (1 hunks)
  • surfsense_web/app/pricing/page.tsx (1 hunks)
  • surfsense_web/app/register/page.tsx (1 hunks)
  • surfsense_web/components/ModernHeroWithGradients.tsx (2 hunks)
  • surfsense_web/components/Navbar.tsx (3 hunks)
  • surfsense_web/components/chat/AnimatedEmptyState.tsx (1 hunks)
  • surfsense_web/components/contact/contact-form.tsx (1 hunks)
  • surfsense_web/components/onboard/add-provider-step.tsx (1 hunks)
  • surfsense_web/components/onboard/assign-roles-step.tsx (1 hunks)
  • surfsense_web/components/onboard/completion-step.tsx (1 hunks)
  • surfsense_web/components/pricing.tsx (1 hunks)
  • surfsense_web/components/pricing/pricing-section.tsx (1 hunks)
  • surfsense_web/components/search-space-form.tsx (1 hunks)
  • surfsense_web/components/settings/llm-role-manager.tsx (1 hunks)
  • surfsense_web/components/settings/model-config-manager.tsx (1 hunks)
  • surfsense_web/components/theme/theme-toggle.tsx (1 hunks)
  • surfsense_web/components/ui/spotlight.tsx (1 hunks)
  • surfsense_web/components/ui/switch.tsx (1 hunks)
  • surfsense_web/components/ui/tilt.tsx (1 hunks)
  • surfsense_web/drizzle.config.ts (1 hunks)
  • surfsense_web/hooks/use-media-query.ts (1 hunks)
  • surfsense_web/package.json (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.rules/require_unique_id_props.mdc)

**/*.{jsx,tsx}: When mapping arrays to React elements in JSX/TSX, each rendered element must include a unique key prop
Keys used for React list items should be stable, predictable, and unique among siblings

Files:

  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx
  • surfsense_web/components/theme/theme-toggle.tsx
  • surfsense_web/components/settings/model-config-manager.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx
  • surfsense_web/app/register/page.tsx
  • surfsense_web/components/ui/spotlight.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx
  • surfsense_web/components/chat/AnimatedEmptyState.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx
  • surfsense_web/app/dashboard/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx
  • surfsense_web/components/onboard/completion-step.tsx
  • surfsense_web/app/dashboard/searchspaces/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx
  • surfsense_web/app/onboard/page.tsx
  • surfsense_web/app/pricing/page.tsx
  • surfsense_web/app/contact/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
  • surfsense_web/components/onboard/assign-roles-step.tsx
  • surfsense_web/components/pricing/pricing-section.tsx
  • surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx
  • surfsense_web/components/contact/contact-form.tsx
  • surfsense_web/components/onboard/add-provider-step.tsx
  • surfsense_web/app/dashboard/api-key/api-key-client.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx
  • surfsense_web/components/pricing.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx
  • surfsense_web/components/ModernHeroWithGradients.tsx
  • surfsense_web/components/ui/switch.tsx
  • surfsense_web/components/settings/llm-role-manager.tsx
  • surfsense_web/components/ui/tilt.tsx
  • surfsense_web/app/login/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx
  • surfsense_web/app/login/LocalLoginForm.tsx
  • surfsense_web/components/search-space-form.tsx
  • surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
  • surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
  • surfsense_web/app/login/GoogleLoginButton.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx
  • surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx
  • surfsense_web/components/Navbar.tsx
**/.env.*

📄 CodeRabbit inference engine (.rules/no_env_files_in_repo.mdc)

Do not commit variant environment files like .env.* (e.g., .env.local, .env.production)

Files:

  • surfsense_web/.env.example
**/.env.example

📄 CodeRabbit inference engine (.rules/no_env_files_in_repo.mdc)

Provide a .env.example file with placeholder values instead of real secrets

Files:

  • surfsense_web/.env.example
🧬 Code graph analysis (4)
surfsense_web/app/api/contact/route.ts (2)
surfsense_web/app/db/index.ts (1)
  • db (6-6)
surfsense_web/app/db/schema.ts (1)
  • usersTable (3-9)
surfsense_web/app/contact/page.tsx (1)
surfsense_web/components/contact/contact-form.tsx (1)
  • ContactFormGridWithDetails (24-204)
surfsense_web/components/pricing/pricing-section.tsx (1)
surfsense_web/components/pricing.tsx (1)
  • Pricing (33-202)
surfsense_web/components/pricing.tsx (1)
surfsense_web/hooks/use-media-query.ts (1)
  • useMediaQuery (8-37)
🪛 dotenv-linter (3.3.0)
surfsense_web/.env.example

[warning] 3-3: [UnorderedKey] The NEXT_PUBLIC_ETL_SERVICE key should go before the NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE key

(UnorderedKey)


[warning] 3-3: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


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

(EndingBlankLine)


[warning] 5-5: [UnorderedKey] The DATABASE_URL key should go before the NEXT_PUBLIC_ETL_SERVICE key

(UnorderedKey)

🪛 GitHub Actions: Code Quality Checks
surfsense_web/app/contact/page.tsx

[error] 1-1: Prettier formatting check failed. The formatter would rewrite content in this file. Run the formatter (e.g., 'prettier --write') to fix code style issues.

surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx

[error] 3-3: The imports and exports are not sorted. Organize imports (Biome) to fix order.

🔇 Additional comments (31)
surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx (1)

4-4: Verified motion/react import. The motion package (v12.23.22) defines a ./react export, so import { motion } from "motion/react" is valid.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx (1)

4-4: Import path change is part of project-wide migration.

This file follows the same motion/react import migration pattern flagged in surfsense_web/app/login/page.tsx. Verification of the import path validity applies here as well.

surfsense_web/components/onboard/completion-step.tsx (2)

4-4: Import path change is part of project-wide migration.

This file follows the same motion/react import migration pattern. See verification request in surfsense_web/app/login/page.tsx.


55-90: LGTM: React key prop correctly applied.

The .map() rendering of configuration entries correctly includes a stable key={role} prop on line 66, following React best practices and coding guidelines.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx (1)

4-4: Import path change is part of project-wide migration.

This file follows the same motion/react import migration pattern. See verification request in surfsense_web/app/login/page.tsx.

surfsense_web/app/onboard/page.tsx (2)

3-3: Import path change is part of project-wide migration.

This file follows the same motion/react import migration pattern. See verification request in surfsense_web/app/login/page.tsx.


129-158: LGTM: React key prop correctly applied.

The Array.from().map() rendering of step indicators correctly includes a stable key={stepNum} prop on line 135, following React best practices and coding guidelines.

surfsense_web/app/login/page.tsx (1)

3-3: Import path and dependency correct. The "motion/react" import is the documented entry point for Motion for React, and the project’s package.json includes "motion": "^12.23.22", matching the official upgrade guide (motion.dev).

surfsense_web/components/onboard/assign-roles-step.tsx (2)

132-209: LGTM: Proper key props on mapped elements.

The mapped motion.div elements correctly use key={key} (line 139), providing stable and unique keys for React reconciliation.


228-237: LGTM: Proper key props on progress indicators.

The progress indicator divs correctly use key={key} (line 229), ensuring proper React list rendering.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx (1)

4-4: Accept motion/react import path
The standalone motion@^12.23.22 package exports "./react", so the import is valid.

surfsense_web/components/chat/AnimatedEmptyState.tsx (1)

109-117: Effect behavior changed: no longer reacts to sidebar state.

The effect now runs only once on mount (empty dependency array), dispatching SIDEBAR_CHANGED immediately and stabilizing layout after a timeout. Previously, this effect likely reacted to sidebar state changes. Ensure this behavior change is intentional and that animations still work correctly when the sidebar is toggled.

The simplified logic looks correct, but verify that the animation timing still works as expected when users interact with the sidebar.

surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx (1)

4-4: Approve motion/react import usage

Project’s package.json includes a “motion” dependency and the Motion documentation confirms “motion/react” is the correct entry point for the new Motion package, so no changes are needed.

surfsense_web/app/register/page.tsx (1)

3-3: Import migration looks consistent.

The change from framer-motion to motion/react aligns with the project-wide migration. Ensure the verification from tilt.tsx confirms the package is properly configured.

surfsense_web/components/settings/model-config-manager.tsx (1)

17-17: LGTM!

Consistent with the project-wide migration to motion/react.

surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx (1)

5-5: LGTM!

The migration includes both runtime and type imports (motion, Variants), consistent with the project-wide update.

surfsense_web/app/login/GoogleLoginButton.tsx (1)

3-3: LGTM!

Consistent with the project-wide migration to motion/react.

surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx (1)

18-18: Confirm motion/react is valid: surfsense_web/package.json lists "motion": "^12.23.22" alongside framer-motion, and the lock file shows its sub-packages (motion-dom, motion-utils), so importing from motion/react resolves correctly.

surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx (1)

4-4: No changes needed for motion/react import
The motion@12.23.22 package defines a “./react” export, so import { motion } from "motion/react" resolves correctly.

surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx (3)

159-183: LGTM: Correct key usage in list rendering.

The key={value} prop uses the unique type string, which is stable and unique among siblings.


218-228: LGTM: Correct key usage in list rendering.

The key={key} prop uses the column identifier from the tuple, which is stable and unique.


3-3: Approve motion/react import
motion/react is a valid entrypoint in motion@^12.23.22 per its package exports.

surfsense_web/components/ModernHeroWithGradients.tsx (2)

2-2: LGTM: IconMail import added.

The IconMail icon is correctly imported and used in the new Contact Us button.


57-63: LGTM: Contact Us button implemented consistently.

The new Contact Us button follows the same pattern and styling as the existing Discord and GitHub buttons, providing a cohesive user experience.

surfsense_web/.env.example (1)

4-5: LGTM! Proper placeholder pattern for optional database configuration.

The DATABASE_URL is correctly marked as OPTIONAL with a clear placeholder format. The connection string template makes it easy for developers to understand what values to substitute.

surfsense_web/components/pricing/pricing-section.tsx (1)

5-62: Well-structured pricing data.

The pricing tiers are clearly defined with consistent schema. The Enterprise tier appropriately links to the new contact page.

surfsense_web/package.json (2)

16-19: LGTM! Database tooling scripts properly configured.

The Drizzle Kit workflow scripts are correctly set up for schema generation, migration, push, and studio access.


25-25: LGTM! Utility dependencies added appropriately.

The new utility packages (@number-flow/react, canvas-confetti, dotenv) are at reasonable versions for their intended use cases.

Also applies to: 50-50, 54-54

surfsense_web/components/ui/switch.tsx (2)

1-11: LGTM! Component structure follows best practices.

The component correctly uses the forwardRef pattern with proper TypeScript typing, extracting the correct ref and props types from the Radix primitives.


12-29: LGTM! Switch component properly implemented with accessibility features.

The implementation correctly:

  • Composes Radix primitives with custom styling
  • Includes focus-visible ring for keyboard navigation
  • Handles disabled states appropriately
  • Uses data-attributes for checked/unchecked styling
  • Sets displayName for better debugging
  • Applies smooth transitions for the thumb animation

Note: Accessible labels should be provided by consuming components using this switch.

surfsense_web/components/settings/llm-role-manager.tsx (1)

15-15: Import from “motion/react” is correct. Verified that the installed motion package (v12.23.x) provides React bindings via

import { motion } from "motion/react"

per the official Motion docs and npm registry (npmjs.com).

Comment on lines +1 to +12
import React from 'react'
import { ContactFormGridWithDetails } from '@/components/contact/contact-form'

const page = () => {
return (
<div>
<ContactFormGridWithDetails />
</div>
)
}

export default page No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix formatting and consider adding page metadata.

The pipeline indicates a Prettier formatting issue that must be resolved.

Run Prettier to fix:

npx prettier --write app/contact/page.tsx

Additionally, consider adding page metadata for SEO:

 import React from 'react'
 import { ContactFormGridWithDetails } from '@/components/contact/contact-form'
+import { Metadata } from 'next'
+
+export const metadata: Metadata = {
+  title: 'Contact Us | SurfSense',
+  description: 'Get in touch with the SurfSense team',
+}

 const page = () => {
   return (
-    <div>
-        <ContactFormGridWithDetails />
-    </div>
+    <ContactFormGridWithDetails />
   )
 }

 export default page
🧰 Tools
🪛 GitHub Actions: Code Quality Checks

[error] 1-1: Prettier formatting check failed. The formatter would rewrite content in this file. Run the formatter (e.g., 'prettier --write') to fix code style issues.

🤖 Prompt for AI Agents
In surfsense_web/app/contact/page.tsx lines 1-12, Prettier formatting errors
must be fixed and page metadata should be added; run the formatter (npx prettier
--write app/contact/page.tsx) to correct whitespace/formatting, then export
Next.js page metadata (e.g., export const metadata = { title: 'Contact —
YourSite', description: 'Short description for SEO' }) above the component and
ensure the component is a default export; save and re-run lint/CI.

Comment on lines 3 to +4
import { format } from "date-fns";
import { AnimatePresence, motion, type Variants } from "framer-motion";
import { AnimatePresence, motion, type Variants } from "motion/react";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix import order to resolve pipeline failure.

The pipeline reports an import/export sorting issue. The Biome linter requires imports to be organized in a specific order.

Please run your code formatter/linter to fix the import order:

# Fix import order with Biome
npx biome check --apply .
🧰 Tools
🪛 GitHub Actions: Code Quality Checks

[error] 3-3: The imports and exports are not sorted. Organize imports (Biome) to fix order.

🤖 Prompt for AI Agents
In surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx around
lines 3 to 4, the import statements are not ordered according to the Biome
linter rules which causes the pipeline failure; run the Biome fixer and reorder
imports to match the project's import-sort convention (external packages,
grouped/aliased imports, then local imports), e.g. run `npx biome check --apply
.` to automatically fix the import order and then commit the formatted file.

"use client";

import { motion } from "framer-motion";
import { motion } from "motion/react";
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify the motion/react import path.

The import has been changed from framer-motion to motion/react. According to the retrieved learnings, framer-motion is the official animation library for React, and "motion" as a standalone package is not a widely-adopted separate package. This import path may be incorrect or may reference an unofficial fork.

Please verify this import path change by checking:

  1. Whether motion/react is a valid export from an installed package
  2. If this is part of an intentional migration to a different package

🏁 Script executed:

#!/bin/bash
# Verify the motion/react import and package installation

echo "=== Checking package.json for motion-related dependencies ==="
cat package.json | jq '.dependencies, .devDependencies' | grep -i motion

echo -e "\n=== Checking if motion is a separate package or export from framer-motion ==="
# Check package-lock or yarn.lock for motion package
if [ -f package-lock.json ]; then
  cat package-lock.json | jq '.packages | keys[]' | grep motion || echo "No motion package found in package-lock.json"
elif [ -f yarn.lock ]; then
  grep '"motion@' yarn.lock || echo "No motion package found in yarn.lock"
fi

echo -e "\n=== Searching for other files using motion/react ==="
rg -l "from ['\"]motion/react['\"]" --type ts --type tsx || echo "No other files found"

echo -e "\n=== Searching for files still using framer-motion ==="
rg -l "from ['\"]framer-motion['\"]" --type ts --type tsx | head -5 || echo "No files using framer-motion found"

Length of output: 371


🏁 Script executed:

#!/bin/bash
# Locate all package.json and lock files within 4 directory levels
echo "=== Found package.json files ==="
fd --max-depth 4 package.json -t f

echo -e "\n=== Found lock files ==="
fd --max-depth 4 package-lock.json -t f
fd --max-depth 4 yarn.lock -t f
fd --max-depth 4 pnpm-lock.yaml -t f

# For each package.json found, print the dependencies section
for f in $(fd --max-depth 4 package.json -t f); do
  echo -e "\n=== Dependencies in $f ==="
  jq '.dependencies, .devDependencies' "$f"
done

Length of output: 3875


🏁 Script executed:

#!/bin/bash
# Search for framer-motion imports in surfsense_web
rg -g '*.tsx' -n "from ['\"]framer-motion['\"]" surfsense_web || echo "No framer-motion imports found"
rg -g '*.ts'  -n "from ['\"]framer-motion['\"]" surfsense_web || echo "No framer-motion imports found"

echo "------"

# Search for motion/react imports in surfsense_web
rg -g '*.tsx' -n "from ['\"]motion/react['\"]" surfsense_web || echo "No motion/react imports found"
rg -g '*.ts'  -n "from ['\"]motion/react['\"]" surfsense_web || echo "No motion/react imports found"

Length of output: 5516


Unify animation imports: Replace the lone import { motion } from "framer-motion" in surfsense_web/components/pricing.tsx with import { motion } from "motion/react", and remove the framer-motion dependency if it’s no longer used.

🤖 Prompt for AI Agents
In surfsense_web/app/dashboard/searchspaces/page.tsx around line 3, the project
is standardizing on the "motion" package; update any remaining imports of motion
from "framer-motion" (specifically in surfsense_web/components/pricing.tsx) to
use import { motion } from "motion/react" instead, then run a project-wide
search to confirm no other files import from "framer-motion" and if none remain
remove the framer-motion dependency from package.json and lockfile and run
npm/yarn install to update dependencies.

Comment on lines +5 to +6
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle({ client, schema });
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add validation for DATABASE_URL to prevent runtime crashes.

The non-null assertion on process.env.DATABASE_URL! will cause a runtime error if the environment variable is missing. Since the contact form is marked as OPTIONAL in the .env.example, the database setup should gracefully handle missing configuration.

Apply this diff to add proper validation:

+if (!process.env.DATABASE_URL) {
+  throw new Error(
+    'DATABASE_URL is not defined. Please set it in your .env file or disable the contact form feature.'
+  );
+}
+
 const client = postgres(process.env.DATABASE_URL);
 export const db = drizzle({ client, schema });

Additionally, consider connection pool configuration for production:

-const client = postgres(process.env.DATABASE_URL);
+const client = postgres(process.env.DATABASE_URL, {
+  max: 10, // Maximum pool size
+  idle_timeout: 20, // Close idle connections after 20s
+  connect_timeout: 10, // Connection timeout in seconds
+});

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In surfsense_web/app/db/index.ts around lines 5-6, the code uses a non-null
assertion on process.env.DATABASE_URL which will throw at runtime if the
variable is missing; update the file to validate DATABASE_URL before creating
the client: check if process.env.DATABASE_URL is present and either (a) throw a
clear, descriptive error with guidance when the app requires a DB, or (b) export
a no-op/mock db or undefined so the rest of the app can run when the database is
optional; if creating a client, pass configuration options instead of the raw
string and include connection pool settings (max connections, idle timeout)
suitable for production, and ensure exported db is typed to reflect the possible
undefined/no-op state so callers handle it safely.

export const usersTable = pgTable("users", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull().unique(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove the unique constraint on contact emails

Line [6]: Marking email as .unique() means a second submission from the same address will violate the constraint and your /api/contact handler will fail with a duplicate key error instead of accepting the new message. Contact forms usually allow repeat outreach, so please drop the uniqueness constraint (or model submissions in a separate table keyed by their own id). Apply this diff to unblock repeat submissions:

-	email: varchar({ length: 255 }).notNull().unique(),
+	email: varchar({ length: 255 }).notNull(),
📝 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
email: varchar({ length: 255 }).notNull().unique(),
email: varchar({ length: 255 }).notNull(),
🤖 Prompt for AI Agents
In surfsense_web/app/db/schema.ts around line 6, the email column is marked
.unique() which prevents repeat submissions; remove the .unique() call from the
email field definition (or alternatively model contact submissions in their own
table with a separate primary id) so multiple messages from the same address are
allowed and duplicate-key errors are avoided by the /api/contact handler.

Comment on lines +78 to +92
<div className="flex justify-center mb-10">
<label className="relative inline-flex items-center cursor-pointer">
<Label>
<Switch
ref={switchRef as any}
checked={!isMonthly}
onCheckedChange={handleToggle}
className="relative"
/>
</Label>
</label>
<span className="ml-2 font-semibold">
Annual billing <span className="text-primary">(Save 20%)</span>
</span>
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix nested labels so the Switch is properly announced

<label> wrapping another <Label> (which renders a <label>) creates invalid nested labels, so the Radix Switch loses its accessible name and screen readers won’t announce the control correctly. Please unwrap the Switch and point the single label at it via htmlFor (or add an aria-label) so the billing toggle keeps valid semantics.

Apply this diff:

-			<div className="flex justify-center mb-10">
-				<label className="relative inline-flex items-center cursor-pointer">
-					<Label>
-						<Switch
-							ref={switchRef as any}
-							checked={!isMonthly}
-							onCheckedChange={handleToggle}
-							className="relative"
-						/>
-					</Label>
-				</label>
-				<span className="ml-2 font-semibold">
-					Annual billing <span className="text-primary">(Save 20%)</span>
-				</span>
-			</div>
+			<div className="flex items-center justify-center mb-10 gap-2">
+				<Switch
+					id="billing-cycle"
+					ref={switchRef as any}
+					checked={!isMonthly}
+					onCheckedChange={handleToggle}
+					className="relative"
+					aria-label="Toggle annual billing"
+				/>
+				<Label htmlFor="billing-cycle" className="font-semibold cursor-pointer">
+					Annual billing <span className="text-primary">(Save 20%)</span>
+				</Label>
+			</div>
📝 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
<div className="flex justify-center mb-10">
<label className="relative inline-flex items-center cursor-pointer">
<Label>
<Switch
ref={switchRef as any}
checked={!isMonthly}
onCheckedChange={handleToggle}
className="relative"
/>
</Label>
</label>
<span className="ml-2 font-semibold">
Annual billing <span className="text-primary">(Save 20%)</span>
</span>
</div>
<div className="flex items-center justify-center mb-10 gap-2">
<Switch
id="billing-cycle"
ref={switchRef as any}
checked={!isMonthly}
onCheckedChange={handleToggle}
className="relative"
aria-label="Toggle annual billing"
/>
<Label htmlFor="billing-cycle" className="font-semibold cursor-pointer">
Annual billing <span className="text-primary">(Save 20%)</span>
</Label>
</div>
🤖 Prompt for AI Agents
In surfsense_web/components/pricing.tsx around lines 78 to 92, there is a nested
<label> (a <label> wrapping the custom <Label> which itself renders a <label>),
which produces invalid HTML and breaks the Radix Switch accessibility; remove
the outer <label> wrapper so only a single label exists, give the Switch an id
and have the label reference it via htmlFor (or, alternatively, add a clear
aria-label to the Switch), and keep the existing ref, checked and
onCheckedChange props unchanged so the visual/behavioral logic is preserved.

useSpring,
useTransform,
} from "framer-motion";
} from "motion/react";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the motion/react package is properly configured.

The import path has been updated from framer-motion to motion/react. Ensure this package is installed and that all exported APIs (MotionStyle, motion, SpringOptions, etc.) are available at this new path.

Run the following script to verify the package configuration:


🏁 Script executed:

#!/bin/bash
# Check if motion package exists and verify the export path
if [ -f "package.json" ]; then
  echo "=== Checking package.json for motion-related dependencies ==="
  cat package.json | jq '.dependencies, .devDependencies' | grep -E "(motion|framer)"
fi

echo -e "\n=== Checking if motion/react resolves correctly ==="
node -e "try { require.resolve('motion/react'); console.log('✓ motion/react resolves successfully'); } catch(e) { console.log('✗ motion/react does not resolve:', e.message); }"

echo -e "\n=== Searching for other files still using framer-motion ==="
rg -l "from ['\"]framer-motion['\"]" --type ts --type tsx 2>/dev/null || echo "No files found using framer-motion"

Length of output: 258


Fix broken motion/react import

The import motion/react fails to resolve (module not found) and there’s no motion or framer-motion dependency in package.json. Revert to framer-motion or add/adjust the motion package so that motion/react resolves correctly.

🤖 Prompt for AI Agents
In surfsense_web/components/ui/tilt.tsx around line 11 the import from
"motion/react" fails to resolve; replace the import with the equivalent from
"framer-motion" (e.g. import { motion } from "framer-motion") or alternatively
add the correct "motion" package to package.json and install it so
"motion/react" is resolvable; after changing the import, run npm/yarn install
and update any TypeScript types/import paths accordingly.

Comment on lines +1 to +11
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
out: './drizzle',
schema: './app/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

❓ Verification inconclusive

Add validation for DATABASE_URL to prevent CLI command failures.

Similar to the issue in app/db/index.ts, the non-null assertion on process.env.DATABASE_URL! will cause drizzle-kit commands (generate, push, migrate, studio) to crash if the environment variable is missing.

Apply this diff to add proper validation:

 import 'dotenv/config';
 import { defineConfig } from 'drizzle-kit';

+if (!process.env.DATABASE_URL) {
+  throw new Error(
+    'DATABASE_URL is not defined. Please set it in your .env file to use Drizzle Kit commands.'
+  );
+}
+
 export default defineConfig({
   out: './drizzle',
   schema: './app/db/schema.ts',
   dialect: 'postgresql',
   dbCredentials: {
-    url: process.env.DATABASE_URL!,
+    url: process.env.DATABASE_URL,
   },
 });

This will provide a clear error message when developers try to run database commands without configuring DATABASE_URL.


Add a runtime check for DATABASE_URL in drizzle.config.ts
In surfsense_web/drizzle.config.ts, throw a clear error if process.env.DATABASE_URL is missing before calling defineConfig.

 import 'dotenv/config';
 import { defineConfig } from 'drizzle-kit';

+if (!process.env.DATABASE_URL) {
+  throw new Error(
+    'DATABASE_URL is not defined. Please set it in your .env file to use Drizzle Kit commands.'
+  );
+}

 export default defineConfig({
   out: './drizzle',
   schema: './app/db/schema.ts',
   dialect: 'postgresql',
   dbCredentials: {
-    url: process.env.DATABASE_URL!,
+    url: process.env.DATABASE_URL,
   },
 });
📝 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
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './app/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
if (!process.env.DATABASE_URL) {
throw new Error(
'DATABASE_URL is not defined. Please set it in your .env file to use Drizzle Kit commands.'
);
}
export default defineConfig({
out: './drizzle',
schema: './app/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL,
},
});
🤖 Prompt for AI Agents
In surfsense_web/drizzle.config.ts around lines 1 to 11, add a runtime check
that reads process.env.DATABASE_URL into a const and if it's falsy throw a clear
Error (e.g. "Missing DATABASE_URL environment variable") before calling
defineConfig; then pass that const as the dbCredentials.url value to ensure
defineConfig always receives a defined string.

"drizzle-orm": "^0.44.5",
"emblor": "^1.4.8",
"framer-motion": "^12.23.9",
"framer-motion": "^12.23.22",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the "motion" package - likely incorrect dependency.

The learnings indicate that "motion" is not a standalone package but an export from framer-motion. The PR summary mentions migrating to motion/react imports, which suggests using import paths like import { motion } from "motion/react" from the framer-motion package itself, not a separate "motion" dependency.

Having both framer-motion and motion as separate dependencies at the same version is likely incorrect and may cause module resolution issues.

Based on learnings.

Run the following script to verify the "motion" package identity and check actual import usage:

Also applies to: 63-63


🏁 Script executed:

#!/bin/bash
# Description: Verify the motion package and check import patterns across the codebase

echo "=== Checking npm registry for standalone 'motion' package ==="
npm view motion versions --json 2>&1 | head -20

echo -e "\n=== Checking import patterns in codebase ==="
rg -n --type=tsx --type=jsx --type=ts --type=js 'from ["\x27](motion|framer-motion)' -A 1 | head -40

echo -e "\n=== Checking package.json in node_modules/motion if it exists ==="
if [ -d "node_modules/motion" ]; then
  cat node_modules/motion/package.json | jq '.name, .version, .description' 2>/dev/null || echo "motion package exists but package.json unreadable"
else
  echo "motion package not found in node_modules"
fi

Length of output: 459


Remove the standalone motion dependency
The npm registry has no motion package at version ^12.23.22 (latest is 0.9.14); use the motion/react entrypoint from your existing framer-motion dependency instead.

🤖 Prompt for AI Agents
In surfsense_web/package.json around line 57, remove the invalid standalone
"motion" dependency entry (version ^12.23.22) and rely on the existing
"framer-motion" package; then update any import statements in the codebase that
reference "motion" to import from the framer-motion entrypoint (e.g., import {
motion } from 'framer-motion' or from 'framer-motion/whatever/entrypoint' as
appropriate), run npm install to refresh lockfile, and ensure
package-lock.json/Yarn lock is committed.

@MODSetter MODSetter merged commit 73bfe13 into main Oct 1, 2025
5 of 10 checks passed
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: 5

♻️ Duplicate comments (1)
surfsense_web/components/pricing.tsx (1)

78-92: Nested labels remain unresolved.

The accessibility issue with nested <label> elements flagged in the previous review has not been addressed. Please apply the fix suggested in the existing review comment to ensure the Switch maintains proper accessible semantics.

🧹 Nitpick comments (2)
surfsense_web/components/pricing.tsx (2)

99-128: Animation and styling logic assumes exactly 3 plans.

The motion animation (lines 99-107) and className construction (lines 118-128) hardcode assumptions for a 3-plan layout using index === 0, index === 2, etc. If the plans array has a different length, the animations and styling will behave unexpectedly.

Consider either:

  1. Adding a prop validation or comment documenting the 3-plan requirement
  2. Making the animation logic more generic to handle variable plan counts

For example, you could determine edge plans dynamically:

const isEdgePlan = index === 0 || index === plans.length - 1;
const isLeftEdge = index === 0;
const isRightEdge = index === plans.length - 1;

81-82: Avoid as any type assertion.

The as any type assertion on the Switch ref bypasses type safety. Check if the Switch component properly exposes its ref type, or use a more specific assertion if needed.

If the Switch accepts RefObject<HTMLButtonElement>, try:

-ref={switchRef as any}
+ref={switchRef}

Or if it requires a callback ref:

ref={(node) => {
  if (node) (switchRef as React.MutableRefObject<HTMLButtonElement>).current = node;
}}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a15309 and 79429b0.

⛔ Files ignored due to path filters (1)
  • surfsense_web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • surfsense_web/components/pricing.tsx (1 hunks)
  • surfsense_web/package.json (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.rules/require_unique_id_props.mdc)

**/*.{jsx,tsx}: When mapping arrays to React elements in JSX/TSX, each rendered element must include a unique key prop
Keys used for React list items should be stable, predictable, and unique among siblings

Files:

  • surfsense_web/components/pricing.tsx
🧬 Code graph analysis (1)
surfsense_web/components/pricing.tsx (1)
surfsense_web/hooks/use-media-query.ts (1)
  • useMediaQuery (8-37)
🔇 Additional comments (6)
surfsense_web/components/pricing.tsx (5)

1-13: LGTM! Clean imports.

All imports are properly used throughout the component and align with the dependencies mentioned in the PR.


15-31: LGTM! Well-structured interfaces.

The interfaces are clear, complete, and appropriate for the component's requirements.


33-40: LGTM! Proper state and hook usage.

Component initialization is clean, with appropriate defaults and correct usage of useMediaQuery hook.


71-76: LGTM! Clean header markup.

The header structure is semantic and properly handles the multi-line description with whitespace-pre-line.


178-196: LGTM! CTA button and link properly implemented.

The Link component with buttonVariants and conditional styling based on isPopular is well-structured and follows Next.js best practices.

surfsense_web/package.json (1)

62-62: Fix invalid motion version pin.

motion 12.23.22 hasn’t been published yet, so installs will fail. The Motion changelog stops at 12.23.21 (Sep 24 2025), and the npm registry still serves 12.23.12 as the newest public tag. Drop this requirement back to the latest available patch before merging.

-		"motion": "^12.23.22",
+		"motion": "^12.23.21",

(motion.dev)

Likely an incorrect or invalid review comment.

Comment on lines +42 to +69
const handleToggle = (checked: boolean) => {
setIsMonthly(!checked);
if (checked && switchRef.current) {
const rect = switchRef.current.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;

confetti({
particleCount: 50,
spread: 60,
origin: {
x: x / window.innerWidth,
y: y / window.innerHeight,
},
colors: [
"hsl(var(--primary))",
"hsl(var(--accent))",
"hsl(var(--secondary))",
"hsl(var(--muted))",
],
ticks: 200,
gravity: 1.2,
decay: 0.94,
startVelocity: 30,
shapes: ["circle"],
});
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Confetti colors won't resolve CSS custom properties.

The confetti colors array uses hsl(var(--primary)) format, but canvas-confetti expects actual color values (e.g., "#3b82f6", "rgb(59, 130, 246)", "hsl(217, 91%, 60%)"). CSS custom properties aren't resolved at runtime by the confetti library, so these will be ignored or fall back to defaults.

To fix, either:

  1. Define the colors directly:
 colors: [
-  "hsl(var(--primary))",
-  "hsl(var(--accent))",
-  "hsl(var(--secondary))",
-  "hsl(var(--muted))",
+  "#3b82f6",
+  "#8b5cf6", 
+  "#ec4899",
+  "#f59e0b",
 ],
  1. Or read computed styles at runtime:
+const primaryColor = getComputedStyle(document.documentElement)
+  .getPropertyValue('--primary').trim();
+// Convert HSL values to full hsl() string
 colors: [
-  "hsl(var(--primary))",
+  `hsl(${primaryColor})`,
   // ... same for other colors
 ],
📝 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 handleToggle = (checked: boolean) => {
setIsMonthly(!checked);
if (checked && switchRef.current) {
const rect = switchRef.current.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
confetti({
particleCount: 50,
spread: 60,
origin: {
x: x / window.innerWidth,
y: y / window.innerHeight,
},
colors: [
"hsl(var(--primary))",
"hsl(var(--accent))",
"hsl(var(--secondary))",
"hsl(var(--muted))",
],
ticks: 200,
gravity: 1.2,
decay: 0.94,
startVelocity: 30,
shapes: ["circle"],
});
}
};
const handleToggle = (checked: boolean) => {
setIsMonthly(!checked);
if (checked && switchRef.current) {
const rect = switchRef.current.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
confetti({
particleCount: 50,
spread: 60,
origin: {
x: x / window.innerWidth,
y: y / window.innerHeight,
},
colors: [
"#3b82f6",
"#8b5cf6",
"#ec4899",
"#f59e0b",
],
ticks: 200,
gravity: 1.2,
decay: 0.94,
startVelocity: 30,
shapes: ["circle"],
});
}
};
🤖 Prompt for AI Agents
In surfsense_web/components/pricing.tsx around lines 42 to 69, the confetti
colors are passed as CSS custom property strings like "hsl(var(--primary))"
which canvas-confetti cannot resolve; change the implementation to compute
actual color values at runtime (or use hardcoded color strings). Use
getComputedStyle on the relevant element or document.documentElement to read
each --primary/--accent/--secondary/--muted value, convert/compose them into
valid CSS color strings (e.g., "hsl(...)" or "rgb(...)" or hex), trim and
provide sensible fallbacks if a variable is missing, then pass that array of
resolved color strings into confetti({ colors: [...] }). Ensure this resolution
happens inside handleToggle before calling confetti so the library receives real
color values.

Comment on lines +94 to +97
<div className="grid grid-cols-1 md:grid-cols-3 sm:2 gap-4">
{plans.map((plan, index) => (
<motion.div
key={index}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use stable keys instead of array index.

Using index as the React key violates the coding guideline requiring stable, unique keys. If the plans array is ever filtered, sorted, or reordered, React's reconciliation will break, causing incorrect animations and potential state bugs.

Apply this diff to use a stable identifier:

-{plans.map((plan, index) => (
+{plans.map((plan) => (
   <motion.div
-    key={index}
+    key={plan.name}

If plan names might not be unique, consider adding an id field to PricingPlan and using that instead.

As per coding guidelines.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In surfsense_web/components/pricing.tsx around lines 94 to 97, the component
uses the array index as the React key which is unstable; change the key to a
stable unique identifier such as plan.id (or plan.name if you guarantee
uniqueness), and if PricingPlan lacks an id add an id field to the type and
ensure each plan object includes a unique id before mapping so keys become
key={plan.id} (fallback to plan.name only if unique).

Comment on lines +154 to +156
willChange
className="font-variant-numeric: tabular-nums"
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Invalid className syntax breaks styling.

Line 155 uses CSS property syntax (font-variant-numeric: tabular-nums) as a className, which is invalid and won't apply any styling. Use the Tailwind utility class instead.

Apply this diff:

-className="font-variant-numeric: tabular-nums"
+className="tabular-nums"
📝 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
willChange
className="font-variant-numeric: tabular-nums"
/>
willChange
className="tabular-nums"
/>
🤖 Prompt for AI Agents
In surfsense_web/components/pricing.tsx around lines 154 to 156, the className
is using CSS property syntax ("font-variant-numeric: tabular-nums") which is
invalid; replace that value with the Tailwind utility class "tabular-nums"
(e.g., className="tabular-nums" or append "tabular-nums" to any existing class
string), removing the colon and property name so the tabular-nums utility is
applied correctly.

Comment on lines +169 to +176
<ul className="mt-5 gap-2 flex flex-col">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<Check className="h-4 w-4 text-primary mt-1 flex-shrink-0" />
<span className="text-left">{feature}</span>
</li>
))}
</ul>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use feature text as key instead of index.

The features map uses idx as the key, which violates the coding guideline. Since feature strings are likely unique within a plan, use the feature text itself as the key for better stability.

Apply this diff:

-{plan.features.map((feature, idx) => (
-  <li key={idx} className="flex items-start gap-2">
+{plan.features.map((feature) => (
+  <li key={feature} className="flex items-start gap-2">

If features might have duplicates, consider adding unique identifiers to the feature array in the data structure.

As per coding guidelines.

📝 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
<ul className="mt-5 gap-2 flex flex-col">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<Check className="h-4 w-4 text-primary mt-1 flex-shrink-0" />
<span className="text-left">{feature}</span>
</li>
))}
</ul>
<ul className="mt-5 gap-2 flex flex-col">
{plan.features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<Check className="h-4 w-4 text-primary mt-1 flex-shrink-0" />
<span className="text-left">{feature}</span>
</li>
))}
</ul>
🤖 Prompt for AI Agents
In surfsense_web/components/pricing.tsx around lines 169 to 176, the map over
plan.features uses the array index (idx) as the React key which is unstable;
change the key to use the feature string itself (key={feature}) to provide a
stable identifier, and if features can be duplicated, update the feature data to
include unique ids and use that id as the key instead (e.g., key={feature.id}).

"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"dotenv": "^17.2.3",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Correct the dotenv version.

dotenv 17.2.3 isn’t on npm—latest published is 17.2.2. This typo will break npm install; please pin to the released patch.

-		"dotenv": "^17.2.3",
+		"dotenv": "^17.2.2",

(socket.dev)

📝 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
"dotenv": "^17.2.3",
// In surfsense_web/package.json, update the dotenv dependency:
"dotenv": "^17.2.2",
🤖 Prompt for AI Agents
In surfsense_web/package.json around line 54, the dotenv dependency is
incorrectly set to "17.2.3" which does not exist on npm; change it to the
released patch "17.2.2" (pin exact version, not caret) in package.json and then
run npm install to update node_modules and regenerate the lockfile
(package-lock.json or yarn.lock) so the correct version is committed.

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.

1 participant