Skip to content

Conversation

@huamanraj
Copy link
Contributor

@huamanraj huamanraj commented Dec 11, 2025

UI examples

image image image

Summary by CodeRabbit

  • New Features

    • Testimonials gallery and image testimonials added.
    • Testimonial submission flow for paid subscribers with avatar and social link support.
    • Dashboard shows "Submit Testimonial" for eligible users.
  • Improvements

    • Avatar URL validation and trusted-host checks for uploads.
    • Slack join flow opens in a new tab and UI refined.
    • Subscription verification now retries before redirecting.
  • Chores

    • Dependency updates and Redis configuration samples added.

✏️ Tip: You can customize this high-level summary in your review settings.


Note

Implements an end‑to‑end testimonials feature and supporting infra across API, Web, and Shared packages.

  • API: Adds Prisma Testimonial model/migration and testimonial tRPC router (getAll, getMyTestimonial, submit) gated to paid users; introduces strict validateAvatarUrl (HTTPS, host allowlist, HEAD check, size/content-type); exports RazorpayOrderSuccess; .env.example adds REDIS_URL.
  • Web: New public /testimonials gallery (mixes backend text with static image items) and Pro‑only /testimonials/submit form (zod/react-hook-form); Pro dashboard shows "Submit Testimonial" CTA; Slack join now opens in new tab; checkout verifies subscription with retries; landing layout includes Navbar; added remote image hosts; pricing banner date updated.
  • Shared: Adds Redis client/cache helpers (ioredis) and re-exports types.

Minor refactors/dep updates and small copy/UI tweaks included.

Written by Cursor Bugbot for commit e3f4f38. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Dec 11, 2025

@huamanraj is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a testimonials feature: database model and migration, backend TRPC routes with avatar validation and paid-user gating, frontend gallery and guarded submission UI, Redis utilities and config, subscription/refetch logic and related dependency/config updates, plus minor UI and comment cleanups.

Changes

Cohort / File(s) Summary
Database: Testimonial model & migration
apps/api/prisma/schema.prisma, apps/api/prisma/migrations/20251218121459_add_testimonials/migration.sql
New Testimonial model (unique userId, content, name, avatar, optional socialLink, timestamps) and DB migration with FK (ON DELETE CASCADE) and unique index.
Backend: TRPC router & avatar validation
apps/api/src/routers/testimonial.ts, apps/api/src/utils/avatar-validator.ts, apps/api/src/routers/_app.ts
New testimonialRouter with getAll, getMyTestimonial, submit (protected, paid-only). Server-side avatar URL validation (HTTPS, allowlist, HEAD check, size/type checks). App router exposes testimonial route.
API config & env
apps/api/.env.example, apps/api/tsconfig.json, apps/api/package.json
Added REDIS_URL to env example; minor tsconfig formatting; package.json EOF formatting change.
Redis infra & types
packages/shared/types/redis.ts, packages/shared/types/index.ts, packages/shared/package.json
New Redis utility (singleton client, redisCache get/set/del/delPattern with graceful degradation), ioredis dependency added, redis types re-exported.
Frontend: Testimonials pages & data
apps/web/src/app/(main)/testimonials/page.tsx, apps/web/src/app/(main)/testimonials/submit/page.tsx, apps/web/src/data/testimonials.ts
New gallery page (fetch TRPC text testimonials, interleave local image testimonials). New guarded submit page using React Hook Form + Zod, subscription gating, avatar preview, validation and TRPC mutation. Local image testimonial dataset added.
Frontend: Pro dashboard integration
apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
Adds TRPC getMyTestimonial query (paid-only) and UI: Show "Submit Testimonial" button when none exists; Slack join opens in new tab.
Subscription & checkout flows
apps/web/src/hooks/useSubscription.ts, apps/web/src/components/checkout/CheckoutWrapper.tsx, apps/web/src/components/checkout/checkout-confirmation.tsx, apps/api/src/services/payment.service.ts
useSubscription now exposes refetch. CheckoutWrapper adds retry loop (3×, 2s) before redirecting. Checkout confirmation adds access-token-based join flow, better error handling, and button layout changes. RazorpayOrderSuccess interface exported.
Web dependencies & image hosts
apps/web/package.json, apps/web/next.config.js
Added react-hook-form, @hookform/resolvers, zod; bumped @trpc/* versions. Added remote image hosts (i.pravatar.cc, picsum.photos, standardcoldpressedoil.com).
Layout, pricing, and minor cleanups
apps/web/src/app/(main)/(landing)/layout.tsx, apps/web/src/app/(main)/(landing)/pricing/page.tsx, apps/web/src/app/(main)/dashboard/oss-programs/*, apps/web/src/components/oss-programs/ProgramCard.tsx, apps/web/src/data/oss-programs/index.ts
Navbar added to landing layout; pricing discount text updated; removed/adjusted comments and minor formatting elsewhere.
Submodule pointer
apps/web/src/content/newsletters-premium
Submodule commit reference updated.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Browser as Client (Browser)
    participant SubmitPage as Next.js Submit Page
    participant TRPC as tRPC Router
    participant AuthSvc as Auth / Session
    participant PaymentSvc as Subscription Service
    participant AvatarVal as Avatar Validator
    participant PrismaDB as Prisma Database

    Browser->>SubmitPage: GET /testimonials/submit
    SubmitPage->>AuthSvc: resolve session
    alt not authenticated
        SubmitPage-->>Browser: render sign-in prompt
    else authenticated
        SubmitPage->>TRPC: getMyTestimonial()
        TRPC->>PaymentSvc: checkSubscriptionStatus(user)
        PaymentSvc-->>TRPC: isPaid
        alt not paid
            TRPC-->>SubmitPage: FORBIDDEN
            SubmitPage-->>Browser: render upgrade notice
        else paid
            TRPC-->>SubmitPage: testimonial|null
            SubmitPage-->>Browser: render form (if null)
            Browser->>SubmitPage: POST form (name, content, avatar, socialLink)
            SubmitPage->>TRPC: submit mutation
            TRPC->>AvatarVal: validateAvatarUrl(avatar)
            AvatarVal-->>TRPC: validation result
            alt invalid avatar
                TRPC-->>SubmitPage: TRPCError BAD_REQUEST
                SubmitPage-->>Browser: show error
            else avatar valid
                TRPC->>PrismaDB: create Testimonial record
                PrismaDB-->>TRPC: created record
                TRPC-->>SubmitPage: success
                SubmitPage-->>Browser: redirect /testimonials
            end
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • auto onboarding #131 — Subscription/status APIs and payment-service changes; overlaps with subscription gating and payment checks used by testimonial routes.
  • UI enforce design consistency #193 — Dashboard UI changes around Join Slack and button handling; touches same dashboard file where testimonial button was added.
  • [chore] extend offer #229 — Pricing page text updates; modifies the same pricing page discount copy changed here.

Poem

🐰 I hopped through code with hoop and twirl,

Validated avatars and added a swirl.
From form to DB, with paid-only gate,
Testimonials bloom — oh how first-rate! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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] pro users testimonials page with redis caching' clearly references the main feature additions: testimonials page for pro users and Redis caching infrastructure. It accurately reflects the primary changes in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings

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.

@cla-assistant
Copy link

cla-assistant bot commented Dec 11, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
5 out of 6 committers have signed the CLA.

✅ apsinghdev
✅ huamanraj
✅ praveenzsp
✅ SGNayak12
✅ mizurex
❌ Lucifer-0612
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

@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: 8

🧹 Nitpick comments (17)
apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1)

113-127: Verify the new button follows design system and accessibility guidelines.

The addition of the "Submit Testimonial" button follows the existing pattern and uses design system colors (bg-brand-purple, hover:bg-brand-purple-light). However, consider the following:

  1. Consistency: The "Join Slack" button has a disabled state, but the "Submit Testimonial" button does not. Should it also be disabled while joining or have its own loading state?

  2. Accessibility: Consider adding aria-label attributes for screen readers, especially since the buttons are in a flex container.

As per coding guidelines, the use of Tailwind design token classes is correct.

Optional enhancement:

             <div className="flex flex-wrap gap-4 justify-center">
               <button
                 onClick={handleJoinSlack}
                 disabled={isJoining}
+                aria-label="Join our Slack community"
                 className="px-4 py-2 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-medium rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
               >
                 {isJoining ? "Joining..." : "Join Slack"}
               </button>
               <button
                 onClick={() => router.push("/testimonials/submit")}
+                aria-label="Submit your testimonial"
                 className="px-4 py-2 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-medium rounded-lg transition-colors duration-200 text-sm"
               >
                 Submit Testimonial
               </button>
             </div>
apps/web/src/data/testimonials.ts (2)

1-1: Remove unnecessary comment.

Per coding guidelines, code should be self-documenting. The type and constant names clearly indicate this is image testimonials data.

-// Image testimonials data
 export type ImageTestimonial = {

9-9: Use UPPER_SNAKE_CASE for constants.

Per coding guidelines, constants should use UPPER_SNAKE_CASE.

-export const imageTestimonials: ImageTestimonial[] = [
+export const IMAGE_TESTIMONIALS: ImageTestimonial[] = [

Note: Update the import in apps/web/src/app/(main)/testimonials/page.tsx accordingly.

apps/api/prisma/schema.prisma (1)

48-57: Consider adding an index on createdAt for query performance.

The getAll endpoint orders testimonials by createdAt desc. As the testimonials table grows, an index would optimize this query.

   user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+  @@index([createdAt(sort: Desc)])
 }
apps/web/src/app/(main)/testimonials/page.tsx (3)

37-37: Replace hardcoded hex values with design tokens.

Per coding guidelines, never use hardcoded hex values directly in components. Use semantic color classes from the design token system instead.

-      <div className="mb-4 break-inside-avoid rounded-xl border border-[#252525] bg-neutral-900/50 overflow-hidden hover:border-neutral-700 transition-colors">
+      <div className="mb-4 break-inside-avoid rounded-xl border border-neutral-800 bg-neutral-900/50 overflow-hidden hover:border-neutral-700 transition-colors">

Apply similar changes to other occurrences on lines 52, 124, 127, 146, 166.

Also applies to: 52-52


54-61: Add sizes attribute to Image with fill prop.

When using fill prop, providing a sizes attribute helps Next.js optimize image loading and prevents layout shift warnings.

         <div className="relative w-10 h-10 rounded-full overflow-hidden border border-white/10">
           <Image
             src={item.user.avatar}
             alt={item.user.name}
             fill
+            sizes="40px"
             className="object-cover"
           />
         </div>

34-34: Use const with arrow functions for component definitions.

Per coding guidelines, prefer const with arrow functions instead of function declarations, and define a type for const functions.

-const TestimonialCard = ({ item }: { item: Testimonial }) => {
+type TestimonialCardProps = {
+  item: Testimonial;
+};
+
+const TestimonialCard: React.FC<TestimonialCardProps> = ({ item }) => {
-const TestimonialsPage = () => {
+const TestimonialsPage: React.FC = () => {

Also applies to: 78-78

apps/api/src/routers/testimonial.ts (2)

17-17: Add context to log statements for debugging.

Per coding guidelines, log errors with context (userId, endpoint, timestamp) for debugging. Consider using structured logging.

-            console.log("Testimonials served from cache");
+            console.log("[testimonials:getAll] cache HIT");
-        console.log("Testimonials fetched from database and cached");
+        console.log("[testimonials:getAll] fetched from database and cached");
-        console.log("Testimonials cache invalidated after submission");
+        console.log("[testimonials:submit] cache invalidated", { userId });

Also applies to: 20-20, 31-31, 92-92


15-15: Use a specific type instead of any[] for cached data.

Define a proper type for the cached testimonials to maintain type safety throughout the codebase.

+type CachedTestimonial = {
+    id: string;
+    userId: string;
+    content: string;
+    name: string;
+    avatar: string;
+    createdAt: Date;
+    updatedAt: Date;
+};
+
 export const testimonialRouter = router({
     getAll: publicProcedure.query(async ({ ctx }: any) => {
         // try to get from cache first
-        const cached = await redisCache.get<any[]>(TESTIMONIALS_CACHE_KEY);
+        const cached = await redisCache.get<CachedTestimonial[]>(TESTIMONIALS_CACHE_KEY);

Alternatively, import the generated Prisma type for Testimonial if available.

packages/shared/types/redis.ts (4)

3-8: Variable shadowing is intentional but can be clarified.

The static analysis tool flags line 8 as a redeclaration of redis. This pattern is intentional—global.redis persists across HMR in development, while the module-level redis holds the reference for the current process. Consider renaming the module-level variable to redisInstance to make the distinction clearer and silence the linter.

-let redis: Redis | undefined;
+let redisInstance: Redis | undefined;

Then update references at lines 44, 47, 48, 56, 62 accordingly.


45-59: Production Redis config is inconsistent with development.

The development configuration includes lazyConnect: true, but the production configuration omits it. This inconsistency may cause different connection behaviors between environments.

             if (!redis) {
                 redis = new Redis(redisUrl, {
                     maxRetriesPerRequest: 3,
                     retryStrategy(times) {
                         const delay = Math.min(times * 50, 2000);
                         return delay;
                     },
+                    lazyConnect: true,
                 });

93-93: Avoid any type for the value parameter.

Per coding guidelines, avoid using any. Use a generic or unknown type instead to maintain type safety.

-    async set(key: string, value: any, ttl?: number): Promise<boolean> {
+    async set<T>(key: string, value: T, ttl?: number): Promise<boolean> {

137-141: KEYS command can block Redis in production.

The KEYS command scans the entire keyspace and blocks the server, which is problematic with large datasets. Consider using SCAN for production-safe iteration.

     async delPattern(pattern: string): Promise<boolean> {
         const client = getRedisClient();
         if (!client) return false;

         try {
-            const keys = await client.keys(pattern);
-            if (keys.length > 0) {
-                await client.del(...keys);
-            }
+            let cursor = "0";
+            do {
+                const [nextCursor, keys] = await client.scan(cursor, "MATCH", pattern, "COUNT", 100);
+                cursor = nextCursor;
+                if (keys.length > 0) {
+                    await client.del(...keys);
+                }
+            } while (cursor !== "0");
             return true;
         } catch (error) {
apps/web/src/app/(main)/testimonials/submit/page.tsx (4)

59-61: Replace alert() with a toast notification or inline error display.

Using alert() blocks the UI and provides poor UX. Consider using a toast component for non-blocking error feedback.

     onError: (error: any) => {
-      alert("Error submitting testimonial: " + error.message);
+      // use a toast notification library or inline error state
+      // e.g., toast.error("Error submitting testimonial: " + error.message);
     },

117-123: Use design tokens instead of hardcoded hex values.

Per coding guidelines, never use hardcoded hex values like #101010 and #252525 directly in components. Reference colors from the design token system using Tailwind classes.

Replace hardcoded colors with the appropriate semantic Tailwind classes from your design system. For example, if #101010 maps to a background token, use bg-background or the equivalent defined in design-tokens.ts.

Also applies to: 129-129, 166-166, 201-201


98-108: Replace alert() with consistent error handling.

Line 100 uses alert() for missing avatar validation. For consistency and better UX, use the same error notification pattern as other errors in this component.


238-241: Image onError handler bypasses Next.js optimization.

Directly setting src on the image element bypasses Next.js Image optimization. Consider using a state variable to handle fallback images properly.

// add state for avatar error
const [avatarError, setAvatarError] = React.useState(false);

// in the Image component:
<Image
  src={avatarError ? `https://i.pravatar.cc/150?u=${session?.user?.email}` : displayAvatar}
  alt="Profile Picture"
  fill
  className="object-cover"
  onError={() => setAvatarError(true)}
/>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 050e439 and f17d0d2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • apps/api/.env.example (1 hunks)
  • apps/api/package.json (1 hunks)
  • apps/api/prisma/schema.prisma (1 hunks)
  • apps/api/src/routers/_app.ts (2 hunks)
  • apps/api/src/routers/testimonial.ts (1 hunks)
  • apps/api/tsconfig.json (1 hunks)
  • apps/web/next.config.js (1 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1 hunks)
  • apps/web/src/app/(main)/testimonials/page.tsx (1 hunks)
  • apps/web/src/app/(main)/testimonials/submit/page.tsx (1 hunks)
  • apps/web/src/data/testimonials.ts (1 hunks)
  • packages/shared/package.json (1 hunks)
  • packages/shared/types/index.ts (1 hunks)
  • packages/shared/types/redis.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/data/testimonials.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
  • packages/shared/types/redis.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/index.ts
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/next.config.js
  • apps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/data/testimonials.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/data/testimonials.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
  • packages/shared/types/redis.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/index.ts
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/data/testimonials.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
  • packages/shared/types/redis.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/index.ts
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/next.config.js
  • apps/web/src/app/(main)/testimonials/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/data/testimonials.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
  • packages/shared/types/redis.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/index.ts
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/data/testimonials.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/data/testimonials.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
  • packages/shared/types/redis.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/index.ts
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/next.config.js
  • apps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/data/testimonials.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/_app.ts
🧠 Learnings (15)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/api/package.json
  • apps/api/src/routers/_app.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Document all required environment variables in `.env.example`

Applied to files:

  • apps/api/.env.example
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Provide alt text for images

Applied to files:

  • apps/web/src/data/testimonials.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Provide alt text for images

Applied to files:

  • apps/web/src/data/testimonials.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers

Applied to files:

  • apps/api/src/routers/_app.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Export types from shared package for consistency across apps

Applied to files:

  • packages/shared/types/index.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Remove unused imports

Applied to files:

  • packages/shared/types/index.ts
  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Optimize images using next/image

Applied to files:

  • apps/web/next.config.js
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{logging,middleware,utils}/*.{js,ts} : Use structured logging (JSON format) for easier parsing and analysis

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{config,environment,setup}/*.{js,ts} : Use type-safe environment variable access with proper defaults

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Organize imports: react → third-party → local components → utils → types

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use UPPER_SNAKE_CASE for constants

Applied to files:

  • apps/api/tsconfig.json
🧬 Code graph analysis (2)
apps/api/src/routers/testimonial.ts (3)
apps/api/src/trpc.ts (3)
  • router (38-38)
  • publicProcedure (39-39)
  • protectedProcedure (40-40)
packages/shared/types/redis.ts (1)
  • redisCache (72-148)
apps/api/src/services/user.service.ts (1)
  • userService (5-89)
apps/api/src/routers/_app.ts (3)
apps/api/src/routers/testimonial.ts (1)
  • testimonialRouter (12-96)
apps/web/src/app/(main)/(landing)/pricing/page.tsx (4)
  • testimonial (623-635)
  • testimonials (496-640)
  • testimonial (610-619)
  • testimonial (594-606)
apps/web/src/components/landing-sections/testimonials.tsx (1)
  • div (45-82)
🪛 Biome (2.1.2)
packages/shared/types/redis.ts

[error] 8-8: Shouldn't redeclare 'redis'. Consider to delete it or rename it.

'redis' is defined here:

(lint/suspicious/noRedeclare)

🪛 dotenv-linter (4.0.0)
apps/api/.env.example

[warning] 34-34: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


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

(EndingBlankLine)

🔇 Additional comments (9)
apps/api/package.json (1)

42-42: No functional changes detected.

This is a formatting-only change with no impact on dependencies or behavior.

apps/api/tsconfig.json (1)

38-38: No functional changes detected.

This is a formatting-only change with no impact on TypeScript configuration.

packages/shared/package.json (1)

11-15: LGTM!

The added dependencies are appropriate for Redis support:

  • ioredis is the standard Redis client for Node.js
  • @types/node provides necessary type definitions for Node.js APIs

These align with the new Redis utilities introduced in packages/shared/types/redis.ts.

packages/shared/types/index.ts (1)

2-3: LGTM!

The addition of the Redis export is well-organized and follows the established pattern. The semicolon addition ensures consistency across export statements.

Based on learnings, this properly exports types from the shared package for consistency across apps.

apps/web/package.json (1)

13-47: Dependencies are compatible with the project.

@hookform/resolvers 5.2.2, react-hook-form 7.68.0, and zod 4.1.9 are fully compatible with each other and with Next.js 15.5.3. These versions are appropriate for form validation in the testimonials feature.

apps/api/src/routers/_app.ts (1)

7-7: LGTM!

The testimonial router is correctly imported and registered following the existing pattern for other routers.

Also applies to: 25-25

apps/web/src/app/(main)/testimonials/submit/page.tsx (3)

1-19: Import organization looks good.

Imports are correctly organized: React → third-party libraries → local components → utils/hooks. This follows the coding guidelines.


23-34: Form validation with zod is well implemented.

The schema validation with character limits and the integration with react-hook-form using zodResolver is clean and follows best practices.

Also applies to: 64-76


84-96: Effect handles form population correctly.

The useEffect properly resets form values when existing testimonial data or session changes, with correct dependencies in the array.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (1)
apps/api/src/utils/avatar-validator.ts (1)

164-171: Consider adding error logging for debugging.

The function silently returns false on all errors, which is appropriate for Zod refinement. However, logging failed validations would aid debugging without exposing details to the client.

As per coding guidelines for apps/api/src/**/*.{js,ts}, consider adding structured logging:

 export async function avatarUrlRefinement(url: string): Promise<boolean> {
     try {
         await validateAvatarUrl(url);
         return true;
     } catch (error) {
+        // log error with context for debugging (don't expose to client)
+        console.error('Avatar URL validation failed in refinement', { 
+            url: url.substring(0, 50), // truncate for privacy
+            error: error instanceof TRPCError ? error.message : 'Unknown error'
+        });
         return false;
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f17d0d2 and c25bf86.

📒 Files selected for processing (4)
  • apps/api/src/routers/testimonial.ts (1 hunks)
  • apps/api/src/trpc.ts (2 hunks)
  • apps/api/src/utils/avatar-validator.ts (1 hunks)
  • apps/web/src/app/(main)/testimonials/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/api/src/routers/testimonial.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/src/{lib,utils}/**

📄 CodeRabbit inference engine (.cursorrules)

Use kebab-case or camelCase for utility file names

Files:

  • apps/api/src/utils/avatar-validator.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/{middleware,logging,utils}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Include correlation IDs in logs for request tracing across services

Files:

  • apps/api/src/utils/avatar-validator.ts
apps/api/src/**/{logging,middleware,utils}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Use structured logging (JSON format) for easier parsing and analysis

Files:

  • apps/api/src/utils/avatar-validator.ts
🧠 Learnings (3)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/api/src/trpc.ts
🔇 Additional comments (3)
apps/api/src/utils/avatar-validator.ts (2)

1-16: LGTM!

The imports and allowlist configuration are appropriate. The trusted hosts cover major OAuth providers and CDN services.


18-32: LGTM!

The validation limits and private IP ranges are comprehensive, covering IPv4/IPv6 private networks, localhost, and link-local addresses.

apps/api/src/trpc.ts (1)

5-8: LGTM!

The ProtectedContext type accurately represents the context shape after authentication, improving type clarity for protected procedures.

Copy link
Contributor

@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 (2)
apps/api/src/utils/avatar-validator.ts (1)

102-109: SSRF protection applied correctly.

The addition of redirect: "error" at line 105 properly addresses the SSRF vulnerability flagged in the previous review. This prevents attackers from bypassing private IP checks via HTTP redirects.

apps/api/src/trpc.ts (1)

5-44: Type safety improvements look good.

The removal of as any from the protectedProcedure export (line 44) properly addresses the previous review concern. The new approach using ProtectedContext type and typed middleware is a significant improvement. The remaining cast at line 32 is acceptable since it's narrowing the context type after runtime validation confirms the user exists.

Based on learnings and coding guidelines, this avoids the any type and uses proper TypeScript types as required.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c25bf86 and a5722c4.

📒 Files selected for processing (2)
  • apps/api/src/trpc.ts (3 hunks)
  • apps/api/src/utils/avatar-validator.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/src/{lib,utils}/**

📄 CodeRabbit inference engine (.cursorrules)

Use kebab-case or camelCase for utility file names

Files:

  • apps/api/src/utils/avatar-validator.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/trpc.ts
apps/api/src/**/{middleware,logging,utils}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Include correlation IDs in logs for request tracing across services

Files:

  • apps/api/src/utils/avatar-validator.ts
apps/api/src/**/{logging,middleware,utils}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Use structured logging (JSON format) for easier parsing and analysis

Files:

  • apps/api/src/utils/avatar-validator.ts
🧠 Learnings (8)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{database,models,services}/*.{js,ts} : Always use parameterized queries or ORM methods to prevent SQL injection

Applied to files:

  • apps/api/src/trpc.ts
🧬 Code graph analysis (2)
apps/api/src/utils/avatar-validator.ts (1)
apps/api/test-ddos.js (1)
  • response (28-28)
apps/api/src/trpc.ts (1)
apps/api/src/context.ts (1)
  • Context (26-26)

@apsinghdev
Copy link
Owner

i've tested this branch and found some suggestions:

pls take a look

  1. rename "testimonial" to "what you think about me?" in the form
  2. testimonials shouldn't be updated once submitted.
  3. also provide an option to add the social
    • just one link - either linkedin, x or whatever)
    • validation is required (only support - twitter, linkedin, insta and yt)
    • it should be optional tho
    • we can show it like this
Screenshot 2025-12-17 at 12 51 01 PM
  1. i tried making a payment but after the successful payment, it didn't redirected me to /checkout page. pls check.

@huamanraj
Copy link
Contributor Author

huamanraj commented Dec 17, 2025

@apsinghdev fixed it!

optional social link and icon based on link input

image

button invisible after testimonial get submitted

image

also payment issue fixed

Copy link
Contributor

@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: 1

♻️ Duplicate comments (3)
apps/api/src/routers/testimonial.ts (2)

14-14: Remove any type annotations; let tRPC infer types.

Per coding guidelines, avoid any type. tRPC should correctly infer context types from procedure definitions.

-    getAll: publicProcedure.query(async ({ ctx }: any) => {
+    getAll: publicProcedure.query(async ({ ctx }) => {
-    getMyTestimonial: protectedProcedure.query(async ({ ctx }: any) => {
+    getMyTestimonial: protectedProcedure.query(async ({ ctx }) => {
-        .mutation(async ({ ctx, input }: any) => {
+        .mutation(async ({ ctx, input }) => {

Also applies to: 37-37, 81-81


43-48: Misleading error message for read operation.

The error message says "submit testimonials" but this is getMyTestimonial (a read operation).

         if (!isPaidUser) {
             throw new TRPCError({
                 code: "FORBIDDEN",
-                message: "Only premium users can submit testimonials",
+                message: "Only premium users can view their testimonials",
             });
         }
apps/web/src/app/(main)/testimonials/submit/page.tsx (1)

72-74: Remove unnecessary any type casts on trpc.

The tRPC client is properly typed with AppRouter. These casts bypass type checking and violate coding guidelines.

-  const { data, isLoading: isDataLoading } = (
-    trpc as any
-  ).testimonial.getMyTestimonial.useQuery(undefined, {
+  const { data, isLoading: isDataLoading } = trpc.testimonial.getMyTestimonial.useQuery(undefined, {
-  const submitMutation = (trpc as any).testimonial.submit.useMutation({
+  const submitMutation = trpc.testimonial.submit.useMutation({

Also applies to: 89-89

🧹 Nitpick comments (8)
apps/web/src/app/(main)/testimonials/page.tsx (3)

6-6: Consider using project's custom icon components.

The project has custom Twitter and Youtube icons in apps/web/src/components/icons/icons.tsx. Consider using these for consistency across the codebase, unless lucide-react icons are preferred project-wide.


28-32: Remove duplicate type definition.

ImageTestimonial is already defined and exported from apps/web/src/data/testimonials.ts. Import it instead of redefining.

+import { imageTestimonials, ImageTestimonial } from "@/data/testimonials";
-import { imageTestimonials } from "@/data/testimonials";

-type ImageTestimonial = TestimonialBase & {
-  type: "image";
-  imageUrl: string;
-  alt: string;
-};

61-61: Replace hardcoded hex values with design tokens.

Per coding guidelines, avoid hardcoded hex values like #252525 and #101010. Use semantic color classes from the design token system (e.g., bg-surface-primary, border-border-primary).

Example locations to update:

  • Line 61: border-[#252525] → use border token
  • Line 80: border-[#252525] → use border token
  • Line 170: bg-[#101010] → use background token

Also applies to: 80-80, 170-170

apps/api/src/routers/testimonial.ts (2)

18-21: Consider using structured logging instead of console.log.

For production observability, consider using a structured logger with context (e.g., request ID, timestamp) instead of raw console.log. Per coding guidelines: "Log errors with context (userId, endpoint, timestamp) for debugging."

Also applies to: 32-32


91-93: Remove extra blank lines.

             if (!isPaidUser) {
                 throw new TRPCError({
                     code: "FORBIDDEN",
                     message: "Only premium users can submit testimonials",
                 });
             }
-
-

             // Check if testimonial already exists - prevent updates
apps/web/src/app/(main)/testimonials/submit/page.tsx (3)

96-98: Type the error parameter properly.

Avoid any for the error parameter. Use tRPC's error type for proper type safety.

+import { TRPCClientError } from "@trpc/client";

-    onError: (error: any) => {
+    onError: (error: TRPCClientError<any>) => {
       setError(error.message || "Error submitting testimonial");
     },

289-292: Consider using a local fallback image.

The fallback uses an external service (pravatar.cc). Consider using a local placeholder image for better reliability and privacy.


155-155: Replace hardcoded hex values with design tokens.

Similar to the testimonials list page, replace hardcoded colors like bg-[#101010] and border-[#252525] with semantic design token classes.

Also applies to: 167-167, 253-253

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37b6cbd and baf013a.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma (1 hunks)
  • apps/api/src/routers/testimonial.ts (1 hunks)
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (3 hunks)
  • apps/web/src/app/(main)/testimonials/page.tsx (1 hunks)
  • apps/web/src/app/(main)/testimonials/submit/page.tsx (1 hunks)
  • apps/web/src/components/checkout/CheckoutWrapper.tsx (1 hunks)
  • apps/web/src/hooks/useSubscription.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/prisma/schema.prisma
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Use PascalCase for component file names (e.g., UserProfile.tsx)

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/routers/testimonial.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/routers/testimonial.ts
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
🧠 Learnings (11)
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{services,repositories}/*.{js,ts} : Avoid N+1 queries; use eager loading or batch queries when fetching related data

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.{js,ts} : Always await async operations; never forget to handle promise rejections

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/api/src/routers/testimonial.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
🧬 Code graph analysis (4)
apps/api/src/routers/testimonial.ts (4)
apps/api/src/trpc.ts (3)
  • router (42-42)
  • publicProcedure (43-43)
  • protectedProcedure (44-44)
packages/shared/types/redis.ts (1)
  • redisCache (72-148)
apps/api/src/services/user.service.ts (1)
  • userService (5-89)
apps/api/src/utils/avatar-validator.ts (1)
  • validateAvatarUrl (46-159)
apps/web/src/app/(main)/testimonials/page.tsx (4)
apps/web/src/data/testimonials.ts (2)
  • ImageTestimonial (2-7)
  • imageTestimonials (9-40)
apps/web/src/components/icons/icons.tsx (2)
  • Twitter (175-177)
  • Youtube (200-202)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/skeleton.tsx (1)
  • Skeleton (15-15)
apps/web/src/app/(main)/testimonials/submit/page.tsx (7)
apps/api/src/trpc.ts (1)
  • router (42-42)
apps/web/src/lib/auth/config.ts (1)
  • session (43-49)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-79)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/label.tsx (1)
  • Label (26-26)
apps/web/src/components/ui/input.tsx (1)
  • Input (22-22)
apps/web/src/components/ui/textarea.tsx (1)
  • Textarea (22-22)
apps/web/src/components/checkout/CheckoutWrapper.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-79)
🔇 Additional comments (12)
apps/web/src/app/(main)/testimonials/page.tsx (4)

37-56: LGTM!

The getSocialIcon helper correctly parses URLs and handles errors gracefully with a null fallback.


58-115: Well-structured component with proper type narrowing.

Good use of discriminated union for type narrowing and proper accessibility attributes on the social link.


123-167: LGTM!

The interleaving logic is correct and useMemo appropriately memoizes the computation with the correct dependency.


220-236: LGTM! Empty state logic is now correct.

The conditions are properly mutually exclusive - the grid renders only when allTestimonials.length > 0, and the empty state only when length === 0.

apps/api/src/routers/testimonial.ts (2)

59-80: LGTM! Comprehensive input validation.

Good use of zod with detailed constraints. The avatar URL is properly validated via validateAvatarUrl which implements SSRF protections.


94-104: LGTM! Testimonial immutability properly enforced.

The check for existing testimonial with clear error message correctly implements the PR requirement that testimonials cannot be edited once submitted.

apps/web/src/hooks/useSubscription.ts (1)

28-28: LGTM! Exposing refetch is appropriate.

The addition of refetch to the hook's return value is a clean change that enables consumers (like CheckoutWrapper) to manually trigger subscription status refresh.

Also applies to: 77-77

apps/web/src/components/checkout/CheckoutWrapper.tsx (2)

15-29: LGTM! Retry logic is well-implemented.

Good use of cleanup to clear timeout, defensive optional chaining on refetch, and reasonable retry parameters (3 attempts, 2s intervals).


32-40: LGTM! Good loading state UX.

The dynamic message ("Loading..." vs "Verifying payment...") provides appropriate feedback during different phases.

apps/web/src/app/(main)/testimonials/submit/page.tsx (3)

45-62: LGTM! Client-side validation mirrors server schema.

Good practice to have consistent validation on both client and server. The schema correctly validates name length, content bounds, and social link platforms.


133-146: LGTM! Submit handler with proper validation.

Good check for avatar existence before submission with clear error message.


164-199: Well-structured access control states.

Good UX with clear messaging for unauthenticated, non-premium, and already-submitted states. Each includes appropriate CTAs.

Also applies to: 201-236, 254-273

Copy link
Contributor

@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: 0

Caution

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

⚠️ Outside diff range comments (5)
apps/api/src/services/payment.service.ts (5)

58-68: Add context to error logging.

The error log on line 65 lacks context such as planId and timestamp. This makes debugging production issues difficult.

As per coding guidelines, errors should be logged with context (userId, endpoint, timestamp) for debugging.

Apply this diff to improve error logging:

     } catch (error) {
-      console.error("Error fetching plan:", error);
+      console.error("error fetching plan:", {
+        planId,
+        error: error instanceof Error ? error.message : error,
+        timestamp: new Date().toISOString(),
+      });
       throw new Error("Failed to fetch plan");

73-105: Add timeout for external API call and avoid any type.

This method has several issues:

  1. The Razorpay API call on line 77 has no timeout, which can cause hanging requests if Razorpay is slow or unresponsive.
  2. Line 85 uses any type instead of unknown.
  3. Missing logging for successful order creation, which is a critical operation.

As per coding guidelines, implement timeouts for external API calls and avoid any type.

Consider:

  1. Wrapping the Razorpay call with a timeout mechanism or configuring timeout at the client level.
  2. Changing error: any to error: unknown and using type guards.
  3. Adding structured logging for successful order creation.
-    } catch (error: any) {
+    } catch (error: unknown) {
-      if (error.error) {
+      if (error && typeof error === 'object' && 'error' in error) {
         return {
-          error: error.error,
+          error: (error as any).error,
         } as RazorpayError;
       }
 
       // Handle unexpected errors
       return {
         error: {
           code: "INTERNAL_ERROR",
-          description: error.message || "An unexpected error occurred",
+          description: error instanceof Error ? error.message : "an unexpected error occurred",

110-138: Add input validation for security-critical signature verification.

This security-critical method lacks input validation. Empty or invalid inputs could lead to incorrect verification results. Additionally, successful signature verifications should be logged for audit trails.

As per coding guidelines, log all critical operations (auth attempts, payment processing, data mutations).

Apply this diff:

   verifyPaymentSignature(
     orderId: string,
     paymentId: string,
     signature: string
   ): boolean {
     try {
+      // validate inputs
+      if (!orderId || !paymentId || !signature) {
+        console.error("signature verification failed: missing required parameters");
+        return false;
+      }
+
       const keySecret = process.env.RAZORPAY_KEY_SECRET;
       if (!keySecret) {
         throw new Error("RAZORPAY_KEY_SECRET not configured");
       }
 
       // Create the expected signature
       const generatedSignatureHex = crypto
         .createHmac("sha256", keySecret)
         .update(`${orderId}|${paymentId}`)
         .digest("hex");
 
       const a = Buffer.from(signature, "hex");
       const b = Buffer.from(generatedSignatureHex, "hex");
 
       if (a.length !== b.length) return false;
 
       // Compare signatures securely
-      return crypto.timingSafeEqual(a, b);
+      const isValid = crypto.timingSafeEqual(a, b);
+      if (isValid) {
+        console.log("payment signature verified successfully", {
+          orderId,
+          timestamp: new Date().toISOString(),
+        });
+      }
+      return isValid;
     } catch (error) {
-      console.error("Signature verification error:", error);
+      console.error("signature verification error:", {
+        error: error instanceof Error ? error.message : error,
+        timestamp: new Date().toISOString(),
+      });
       return false;

143-174: Replace any return type and add logging for critical operation.

This method has several issues:

  1. Return type is any on line 146 instead of a specific Prisma type.
  2. Error logging lacks context (userId, paymentId).
  3. Missing logging for successful payment creation, which is a critical operation.

As per coding guidelines, avoid any type and log all critical operations.

Apply this diff:

   async createPaymentRecord(
     userId: string,
     paymentData: PaymentData
-  ): Promise<any> {
+  ): Promise<import('@prisma/client').Payment> {
     try {
       // Check if payment already exists (idempotency)
       const existingPayment = await prisma.payment.findUnique({
         where: { razorpayPaymentId: paymentData.razorpayPaymentId },
       });
 
       if (existingPayment) {
+        console.log("payment record already exists (idempotent)", {
+          userId,
+          paymentId: existingPayment.id,
+          timestamp: new Date().toISOString(),
+        });
         return existingPayment;
       }
 
       // Create new payment record
       const payment = await prisma.payment.create({
         data: {
           userId,
           razorpayPaymentId: paymentData.razorpayPaymentId,
           razorpayOrderId: paymentData.razorpayOrderId,
           amount: paymentData.amount, // Amount in paise (smallest currency unit)
           currency: paymentData.currency,
           status: PAYMENT_STATUS.CAPTURED,
         },
       });
 
+      console.log("payment record created successfully", {
+        userId,
+        paymentId: payment.id,
+        amount: payment.amount,
+        timestamp: new Date().toISOString(),
+      });
+
       return payment;
     } catch (error) {
-      console.error("Error creating payment record:", error);
+      console.error("error creating payment record:", {
+        userId,
+        razorpayPaymentId: paymentData.razorpayPaymentId,
+        error: error instanceof Error ? error.message : error,
+        timestamp: new Date().toISOString(),
+      });
       throw new Error("Failed to create payment record");

179-256: Wrap subscription creation and payment update in a transaction.

This method has a critical data consistency issue: the subscription creation (line 234) and payment update (line 246) are two separate database operations without a transaction wrapper. If the payment update fails after subscription creation, the database will be in an inconsistent state with an orphaned subscription.

Additional issues:

  1. Return type is any (line 183) instead of a specific Prisma type.
  2. Error logging lacks context (userId, planId, paymentId).
  3. Missing logging for successful subscription creation (critical operation).

As per coding guidelines, wrap database transactions in try-catch blocks with proper rollback on failure.

Apply this diff:

   async createSubscription(
     userId: string,
     planId: string,
     paymentId: string
-  ): Promise<any> {
+  ): Promise<import('@prisma/client').Subscription> {
     try {
       // Get plan details
       const plan = await prisma.plan.findUnique({
         where: { id: planId },
       });
 
       if (!plan) {
         throw new Error("Plan not found");
       }
 
       // Calculate end date - Currently only yearly plan is supported
       const startDate = new Date();
       const endDate = new Date(startDate);
 
       // Set subscription for 1 year (yearly plan)
       endDate.setFullYear(endDate.getFullYear() + 1);
 
       // Future plan intervals (commented out for now):
       // switch (plan.interval.toLowerCase()) {
       //   case "monthly":
       //     endDate.setMonth(endDate.getMonth() + 1);
       //     break;
       //   case "quarterly":
       //     endDate.setMonth(endDate.getMonth() + 3);
       //     break;
       //   case "yearly":
       //   case "annual":
       //     endDate.setFullYear(endDate.getFullYear() + 1);
       //     break;
       //   default:
       //     endDate.setFullYear(endDate.getFullYear() + 1);
       // }
 
       // Check if user already has an active subscription for this payment
       const existingSubscription = await prisma.subscription.findFirst({
         where: {
           userId,
           payments: {
             some: {
               id: paymentId,
             },
           },
         },
       });
 
       if (existingSubscription) {
+        console.log("subscription already exists for payment (idempotent)", {
+          userId,
+          subscriptionId: existingSubscription.id,
+          timestamp: new Date().toISOString(),
+        });
         return existingSubscription;
       }
 
-      // Create subscription
-      const subscription = await prisma.subscription.create({
-        data: {
-          userId,
-          planId,
-          status: SUBSCRIPTION_STATUS.ACTIVE,
-          startDate,
-          endDate,
-          autoRenew: true,
-        },
-      });
-
-      // Link payment to subscription
-      await prisma.payment.update({
-        where: { id: paymentId },
-        data: { subscriptionId: subscription.id },
+      // use transaction to ensure data consistency
+      const subscription = await prisma.$transaction(async (tx) => {
+        // create subscription
+        const newSubscription = await tx.subscription.create({
+          data: {
+            userId,
+            planId,
+            status: SUBSCRIPTION_STATUS.ACTIVE,
+            startDate,
+            endDate,
+            autoRenew: true,
+          },
+        });
+
+        // link payment to subscription
+        await tx.payment.update({
+          where: { id: paymentId },
+          data: { subscriptionId: newSubscription.id },
+        });
+
+        return newSubscription;
       });
 
+      console.log("subscription created successfully", {
+        userId,
+        subscriptionId: subscription.id,
+        planId,
+        timestamp: new Date().toISOString(),
+      });
+
       return subscription;
     } catch (error) {
-      console.error("Error creating subscription:", error);
+      console.error("error creating subscription:", {
+        userId,
+        planId,
+        paymentId,
+        error: error instanceof Error ? error.message : error,
+        timestamp: new Date().toISOString(),
+      });
       throw new Error("Failed to create subscription");
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between baf013a and 0aea504.

📒 Files selected for processing (2)
  • apps/api/src/services/payment.service.ts (1 hunks)
  • apps/web/src/app/(main)/(landing)/pitch/page.tsx (19 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/app/(main)/(landing)/pitch/page.tsx
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/api/src/services/payment.service.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/api/src/services/payment.service.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/api/src/services/payment.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/api/src/services/payment.service.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{services,models,database}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Encrypt sensitive data (passwords, tokens, API keys) before storing in database

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{database,services}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Handle database connection failures gracefully with retry logic

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{database,models,services}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Always use parameterized queries or ORM methods to prevent SQL injection

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{services,database}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Wrap database transactions in try-catch blocks with proper rollback on failure

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{services,repositories}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid N+1 queries; use eager loading or batch queries when fetching related data

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{services,models}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Validate data against schema before database operations

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{database,clients,services}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Use connection pooling for database and external service clients

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{services,clients,handlers}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Implement timeouts for external API calls to prevent hanging requests

Files:

  • apps/api/src/services/payment.service.ts
apps/api/src/**/{middleware,services,routes,controllers}/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Log all critical operations (auth attempts, payment processing, data mutations)

Files:

  • apps/api/src/services/payment.service.ts
🔇 Additional comments (1)
apps/api/src/services/payment.service.ts (1)

18-31: LGTM!

The interface export enables external modules to import and use this type, which aligns with the broader API surface expansion in this PR.

@apsinghdev
Copy link
Owner

apsinghdev commented Dec 18, 2025

@huamanraj everything looks fine but i don't know why am not getting to the testimonials page on clicking the button. also, getting some errors in the console.

ref

Screen.Recording.2025-12-18.at.11.05.39.PM.mov
  • also, please a add a home button on /testimonial page to go to /dashboard/home
  • one more suggestion: pls don't cache the testimonials as i think we won't need caching for it as it's only 1 review per pro user. keep the redis functionality so that we don't have to rewrite it in project search.

Copy link
Contributor

@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: 1

♻️ Duplicate comments (4)
apps/api/src/routers/testimonial.ts (4)

8-8: Avoid any type; use proper typing for context.

This issue was already flagged in a previous review. Per coding guidelines, avoid any type. TRPC should infer the context type correctly from the procedure definition.

As per coding guidelines and learnings retrieved from long-term memory.


19-19: Avoid any type; use proper typing for context.

This issue was already flagged in a previous review. Per coding guidelines, avoid any type. TRPC should infer the context type correctly from the procedure definition.

As per coding guidelines and learnings retrieved from long-term memory.


25-30: Misleading error message in read operation.

This issue was already flagged in a previous review. The error message says "submit testimonials" but this is the getMyTestimonial endpoint (read operation). The message should reflect access/viewing restrictions instead.


63-63: Avoid any type; use proper typing for context.

This issue was already flagged in a previous review. Per coding guidelines, avoid any type. TRPC should infer the context type correctly from the procedure and input definitions.

As per coding guidelines and learnings retrieved from long-term memory.

🧹 Nitpick comments (1)
apps/api/src/routers/testimonial.ts (1)

74-75: Remove unnecessary blank lines.

Two consecutive empty lines here serve no purpose and reduce code readability.

🔎 Proposed fix
             }
-

-
             // Check if testimonial already exists - prevent updates
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aea504 and cd72787.

📒 Files selected for processing (4)
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/user.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/api/src/routers/user.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/api/src/routers/testimonial.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/api/src/routers/testimonial.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/api/src/routers/testimonial.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/api/src/routers/testimonial.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/api/src/routers/testimonial.ts
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/routers/testimonial.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/routers/testimonial.ts
🧠 Learnings (10)
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{services,repositories}/*.{js,ts} : Avoid N+1 queries; use eager loading or batch queries when fetching related data

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.{js,ts} : Always await async operations; never forget to handle promise rejections

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/api/src/routers/testimonial.ts
🧬 Code graph analysis (1)
apps/api/src/routers/testimonial.ts (3)
apps/api/src/trpc.ts (3)
  • router (42-42)
  • publicProcedure (43-43)
  • protectedProcedure (44-44)
apps/api/src/services/user.service.ts (1)
  • userService (5-89)
apps/api/src/utils/avatar-validator.ts (1)
  • validateAvatarUrl (46-159)
🔇 Additional comments (1)
apps/api/src/routers/testimonial.ts (1)

88-89: Good implementation of avatar URL validation.

The addition of validateAvatarUrl properly addresses the server-side security validation concerns raised in previous reviews. The validation function (shown in relevant code snippets) implements HTTPS-only, IP blocking, domain allowlist, HEAD request validation, and content-type/size checks.

@apsinghdev apsinghdev force-pushed the feat/testimonials-page branch from c2b9246 to cfa2360 Compare December 29, 2025 15:28
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Copy link
Contributor

@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

Caution

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

⚠️ Outside diff range comments (2)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)

82-85: Avoid hardcoded hex values; use design token classes instead.

Line 84 uses text-[#A970FF] which violates the coding guidelines. As per guidelines, always reference colors from the design token system using Tailwind classes.

Proposed fix
          <p className="text-lg lg:text-xl text-white/90 leading-relaxed font-light max-w-3xl mx-auto">
            If you have any doubts, feel free to ping us here:{" "}
-           <span className="text-[#A970FF]">hi@opensox.ai</span>
+           <span className="text-brand-purple">hi@opensox.ai</span>
          </p>
apps/api/src/trpc.ts (1)

44-44: Replace :any type annotation with proper typing.

The :any type annotation on protectedProcedure violates the coding guideline to avoid any type. A previous review flagged a similar issue with as any cast (marked as addressed in commit a5722c4), but the current code still uses :any as a type annotation, which similarly removes type safety.

Based on learnings and coding guidelines for apps/api/src/**/*.ts: Avoid any type; use unknown for truly dynamic data and narrow with type guards.

🔎 Recommended fix

Infer the proper type from the middleware result:

-export const protectedProcedure:any = t.procedure.use(isAuthed);
+export const protectedProcedure = t.procedure.use(isAuthed);

TypeScript should infer the correct type automatically. If portability issues arise, investigate the root cause rather than suppressing types with any.

Alternatively, if explicit typing is required:

import type { inferProcedureBuilderResolverOptions } from '@trpc/server';

type AuthedProcedure = ReturnType<typeof t.procedure.use>;

export const protectedProcedure: AuthedProcedure = t.procedure.use(isAuthed);
♻️ Duplicate comments (3)
apps/api/.env.example (1)

33-35: Address the formatting issues flagged in the previous review.

The REDIS_URL environment variable addition is correct, but the formatting issues (extra blank line at line 34 and missing EOF newline) were already flagged in the previous review and remain unresolved.

apps/api/src/trpc.ts (1)

7-7: Use lowercase for comments.

The comment "Type for context" starts with an uppercase letter, violating the coding guideline requiring lowercase comments. This was flagged in a previous review but not addressed.

-// Type for context after authentication middleware
+// type for context after authentication middleware

As per coding guidelines for **/*.{js,jsx,ts,tsx,py,java,go,rb,php}.

apps/web/src/app/(main)/testimonials/submit/page.tsx (1)

73-81: Remove unnecessary any type cast on trpc.

The (trpc as any) casts bypass TypeScript's type checking and violate coding guidelines. The tRPC client is properly typed with AppRouter which includes the testimonialRouter.

🔎 Proposed fix
-  const { data, isLoading: isDataLoading } = (
-    trpc as any
-  ).testimonial.getMyTestimonial.useQuery(undefined, {
+  const { data, isLoading: isDataLoading } = trpc.testimonial.getMyTestimonial.useQuery(undefined, {
     enabled: !!isPaidUser,
     retry: false,
     refetchOnWindowFocus: true,
     refetchOnMount: true,
     staleTime: 0,
   });

Also applies to line 88.

🧹 Nitpick comments (9)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)

45-48: Potential runtime error if error response is not JSON.

If the server returns a non-JSON error response (e.g., HTML 500 page), response.json() will throw, and the catch block will display a generic message instead of the actual error.

Proposed fix
      if (!response.ok) {
-       const errorData = await response.json();
-       setError(errorData.error || "Failed to join community");
+       try {
+         const errorData = await response.json();
+         setError(errorData.error || "Failed to join community");
+       } catch {
+         setError("Failed to join community");
+       }
        return;
      }
apps/web/src/app/(main)/(landing)/layout.tsx (1)

4-4: Consider extracting props type for consistency.

While inline type definitions are valid, extracting to a separate type interface improves consistency with the coding guideline to "Define a type when defining const functions" and "Use PascalCase for types and interfaces with descriptive names".

🔎 Optional type extraction
+type LayoutProps = {
+  children: React.ReactNode
+}
+
-const Layout = ({ children }: { children: React.ReactNode }) => {
+const Layout = ({ children }: LayoutProps) => {
apps/web/src/hooks/useSubscription.ts (1)

29-29: Avoid as any for better type safety.

The type cast to any bypasses TypeScript's type checking and removes IDE autocomplete support. Consider properly typing the tRPC client or using a more specific type assertion to maintain type safety throughout the hook.

As per coding guidelines, prefer proper TypeScript types and avoid any.

💡 Alternative approach

If the tRPC types are incomplete, consider either:

  1. Updating the tRPC client generation to include proper types for user.subscriptionStatus
  2. Creating a specific type interface for the query result instead of casting to any
-  } = (trpc.user as any).subscriptionStatus.useQuery(undefined, {
+  } = trpc.user.subscriptionStatus.useQuery(undefined, {
apps/web/src/app/(main)/testimonials/page.tsx (3)

28-32: Remove duplicate type definition.

ImageTestimonial is already exported from apps/web/src/data/testimonials.ts (lines 1-6). Import it instead of redefining to maintain DRY principles.

🔎 Proposed fix
 import { trpc } from "@/lib/trpc";
-import { imageTestimonials } from "@/data/testimonials";
+import { imageTestimonials, ImageTestimonial } from "@/data/testimonials";
 import { Skeleton } from "@/components/ui/skeleton";

 type TestimonialBase = {
   id: string;
   type: "text" | "image";
 };

 type TextTestimonial = TestimonialBase & {
   type: "text";
   content: string;
   user: {
     name: string;
     username?: string;
     avatar: string;
     socialLink?: string;
   };
 };

-type ImageTestimonial = TestimonialBase & {
-  type: "image";
-  imageUrl: string;
-  alt: string;
-};
-
 type Testimonial = TextTestimonial | ImageTestimonial;

61-61: Consider using design tokens for border colors.

Hardcoded hex values like #252525 appear throughout. Per coding guidelines, prefer referencing colors from the design token system using Tailwind classes (e.g., border-border or semantic color classes). This applies to multiple occurrences in this file.


83-89: Add error handling for avatar images.

External avatar URLs may fail to load. Consider adding an onError handler with a fallback, similar to the submit page implementation (line 332-334).

🔎 Proposed fix
         <div className="relative w-10 h-10 rounded-full overflow-hidden border border-white/10">
           <Image
             src={item.user.avatar}
             alt={item.user.name}
             fill
             className="object-cover"
+            onError={(e) => {
+              (e.target as HTMLImageElement).src =
+                `https://i.pravatar.cc/150?u=${item.user.name}`;
+            }}
           />
         </div>
apps/web/src/app/(main)/testimonials/submit/page.tsx (2)

92-94: Avoid any type in error handler.

Per coding guidelines, avoid any. Use unknown and narrow, or import TRPCClientErrorLike for proper typing.

🔎 Proposed fix
-    onError: (error: any) => {
+    onError: (error: { message?: string }) => {
       setError(error.message || "Error submitting testimonial");
     },

383-386: Remove or restore commented-out character count.

The content field's character count is commented out while the name field displays it. Either remove this dead code or restore it for consistent UX.

🔎 Proposed fix (restore)
-                    {/* <p className="text-xs text-neutral-500 ml-auto">
-                      {contentValue?.length || 0}/1500
-                    </p> */}
+                    <p className="text-xs text-neutral-500 ml-auto">
+                      {contentValue?.length || 0}/1500
+                    </p>
apps/web/package.json (1)

21-23: Align tRPC versions across packages.

@trpc/server in apps/web is at ^11.5.1, while @trpc/client and @trpc/react-query are at ^11.7.2. Additionally, apps/api has @trpc/server at ^11.7.2. While tRPC v11.x maintains backward compatibility across minor and patch versions, keeping all tRPC packages aligned to the same version prevents potential issues and ensures consistent behavior across the monorepo. Update apps/web's @trpc/server to ^11.7.2 to match the client packages and align with apps/api.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2b9246 and cfa2360.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • apps/api/.env.example
  • apps/api/package.json
  • apps/api/prisma/migrations/20251218121459_add_testimonials/migration.sql
  • apps/api/prisma/schema.prisma
  • apps/api/src/routers/_app.ts
  • apps/api/src/routers/testimonial.ts
  • apps/api/src/routers/user.ts
  • apps/api/src/services/payment.service.ts
  • apps/api/src/trpc.ts
  • apps/api/src/utils/avatar-validator.ts
  • apps/api/tsconfig.json
  • apps/web/next.config.js
  • apps/web/package.json
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/web/src/app/(main)/dashboard/oss-programs/[slug]/page.tsx
  • apps/web/src/app/(main)/dashboard/oss-programs/page.tsx
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/components/oss-programs/ProgramCard.tsx
  • apps/web/src/data/oss-programs/index.ts
  • apps/web/src/data/testimonials.ts
  • apps/web/src/hooks/useSubscription.ts
  • packages/shared/package.json
  • packages/shared/types/index.ts
  • packages/shared/types/redis.ts
💤 Files with no reviewable changes (4)
  • apps/web/src/app/(main)/dashboard/oss-programs/[slug]/page.tsx
  • apps/web/src/app/(main)/dashboard/oss-programs/page.tsx
  • apps/web/src/data/oss-programs/index.ts
  • apps/web/src/components/oss-programs/ProgramCard.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • apps/api/src/routers/user.ts
  • apps/api/src/routers/testimonial.ts
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
  • apps/web/src/data/testimonials.ts
  • apps/api/package.json
  • apps/web/next.config.js
  • apps/api/src/utils/avatar-validator.ts
  • apps/api/src/routers/_app.ts
  • apps/api/prisma/schema.prisma
  • apps/api/src/services/payment.service.ts
  • packages/shared/types/index.ts
  • packages/shared/package.json
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/redis.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/redis.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/redis.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/redis.ts
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • packages/shared/types/redis.ts
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/hooks/useSubscription.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
apps/api/src/**/*.{js,ts}

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)

Files:

  • apps/api/src/trpc.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)

Avoid any type; use unknown for truly dynamic data and narrow with type guards

Files:

  • apps/api/src/trpc.ts
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Use PascalCase for component file names (e.g., UserProfile.tsx)

Files:

  • apps/web/src/hooks/useSubscription.ts
🧠 Learnings (28)
📚 Learning: 2025-12-17T12:39:29.991Z
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 262
File: apps/web/src/components/checkout/CheckoutWrapper.tsx:5-5
Timestamp: 2025-12-17T12:39:29.991Z
Learning: In Next.js apps, avoid using redirect() from next/navigation inside Client Components during render. It should not be used in event handlers like onClick or onSubmit; for those cases, use useRouter().push() or router.replace() instead. This guidance applies to TSX files (React components) across the codebase; implement in client components to handle conditional redirects during render, and use router-based navigation in handlers.

Applied to files:

  • apps/web/src/app/(main)/testimonials/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always use lowercase when writing comments

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:38.559Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursor/rules/general_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:38.559Z
Learning: Applies to **/*.{js,jsx,ts,tsx,py,java,go,rb,php} : Always use lowercase when writing comments

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use comments to explain 'why', not 'what'

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/api/src/trpc.ts
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:38.559Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursor/rules/general_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:38.559Z
Learning: Applies to **/*.{js,jsx,ts,tsx,py,java,go,rb,php} : Use comments to explain 'why', not 'what'

Applied to files:

  • apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Remove unused imports

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Use absolute imports from `@/` prefix when available

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Maintain proper heading hierarchy in page components

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Maintain proper heading hierarchy

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Always follow the design system defined in `apps/web/src/lib/design-tokens.ts` and `apps/web/tailwind.config.ts`

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Organize imports: react → third-party → local components → utils → types

Applied to files:

  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unused imports

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{logging,middleware,utils}/*.{js,ts} : Use structured logging (JSON format) for easier parsing and analysis

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:38.559Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursor/rules/general_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:38.559Z
Learning: Applies to **/*.{js,jsx,ts,tsx,py,java,go,rb,php} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{config,environment,setup}/*.{js,ts} : Use type-safe environment variable access with proper defaults

Applied to files:

  • apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Document all required environment variables in `.env.example`

Applied to files:

  • apps/api/.env.example
🧬 Code graph analysis (3)
apps/web/src/app/(main)/testimonials/page.tsx (4)
apps/web/src/data/testimonials.ts (2)
  • ImageTestimonial (2-7)
  • imageTestimonials (9-16)
apps/web/src/components/icons/icons.tsx (2)
  • Twitter (175-177)
  • Youtube (200-202)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/skeleton.tsx (1)
  • Skeleton (15-15)
apps/web/package.json (1)
apps/web/src/providers/trpc-provider.tsx (1)
  • TRPCProvider (11-53)
apps/web/src/app/(main)/testimonials/submit/page.tsx (8)
apps/api/src/trpc.ts (1)
  • router (42-42)
apps/web/src/lib/auth/config.ts (1)
  • session (48-59)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-79)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/skeleton.tsx (1)
  • Skeleton (15-15)
apps/web/src/components/ui/label.tsx (1)
  • Label (26-26)
apps/web/src/components/ui/input.tsx (1)
  • Input (22-22)
apps/web/src/components/ui/textarea.tsx (1)
  • Textarea (22-22)
🪛 Biome (2.1.2)
packages/shared/types/redis.ts

[error] 8-8: Shouldn't redeclare 'redis'. Consider to delete it or rename it.

'redis' is defined here:

(lint/suspicious/noRedeclare)

🪛 dotenv-linter (4.0.0)
apps/api/.env.example

[warning] 34-34: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


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

(EndingBlankLine)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (10)
apps/web/src/app/(main)/(landing)/pricing/page.tsx (1)

499-499: LGTM!

The promotional deadline text update is clear and appropriate.

apps/web/src/components/checkout/checkout-confirmation.tsx (1)

91-102: LGTM! Good use of semantic color classes and router-based navigation.

The implementation correctly uses design token classes (bg-brand-purple, text-text-primary, bg-surface-elevated, etc.) and follows the learning about using router.push() in event handlers instead of redirect().

Minor note: coding guidelines recommend rounded-[16px] for buttons and duration-100/duration-300/duration-600 for transitions, but the current values are acceptable.

apps/web/src/hooks/useSubscription.ts (1)

28-28: LGTM! Refetch capability properly exposed.

The addition of refetch to the query destructuring and return value correctly exposes manual refetch functionality for subscription status, enabling consumers to trigger fresh fetches when needed (e.g., after payment flow).

Also applies to: 77-77

apps/api/tsconfig.json (1)

36-36: LGTM!

Formatting cleanup looks good.

packages/shared/types/redis.ts (1)

14-67: Verify the lazyConnect configuration inconsistency between environments.

In development (line 33), the Redis client is configured with lazyConnect: true, but this option is missing in production (line 48). Additionally, the production client only has an error handler but lacks the connect handler present in development.

This asymmetry may be intentional, but it could also cause unexpected behavior differences between environments. If lazy connection is desired for both, the configuration should be consistent. Otherwise, document why production requires immediate connection.

apps/api/prisma/migrations/20251218121459_add_testimonials/migration.sql (1)

1-19: LGTM! Migration schema aligns with requirements.

The schema correctly enforces one testimonial per user via the unique constraint on userId. The foreign key with CASCADE delete ensures cleanup when users are removed. The nullable socialLink supports the optional social link requirement from the PR discussion.

apps/web/src/app/(main)/testimonials/page.tsx (2)

117-167: LGTM! Well-structured data combination logic.

The useMemo correctly memoizes the testimonial combination, and the interleaving algorithm provides good visual distribution. Dependency array is correct.


219-235: Previous review feedback addressed.

The empty state and grid rendering conditions are now mutually exclusive - good fix.

apps/web/src/app/(main)/testimonials/submit/page.tsx (2)

288-294: Home navigation button added as requested.

This addresses the reviewer's request to add a home button that navigates to /dashboard/home.


296-440: Well-structured form with proper state handling.

The implementation correctly handles:

  • Immutable testimonials (prevents editing after submission)
  • Optional social link with platform validation
  • Character limits with real-time feedback
  • Proper error states and loading indicators

@vercel
Copy link

vercel bot commented Dec 30, 2025

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

Project Deployment Review Updated (UTC)
opensox-website Ready Ready Preview, Comment Dec 30, 2025 6:01pm

Copy link
Contributor

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)

72-73: Use semantic design tokens for success state colors.

The hardcoded green color classes (border-green-400/30, bg-green-500/20, text-green-400) should be replaced with semantic color tokens from the design system that describe purpose rather than appearance.

As per coding guidelines, "Use semantic color names that describe purpose, not appearance."

🔎 Suggested approach

Consider adding semantic tokens to the design system for success states (e.g., bg-success, text-success, border-success) or use existing semantic tokens if available. If green is the standard success color in your design system, define it as a semantic token rather than using raw color values.

♻️ Duplicate comments (3)
apps/web/src/app/(main)/testimonials/submit/page.tsx (3)

53-56: Fix validation message mismatch.

The schema enforces a 1500 character maximum, but the error message incorrectly states "1000 characters". This will confuse users when they exceed the actual limit.

🔎 Proposed fix
   content: z
     .string()
     .min(10, "Testimonial must be at least 10 characters")
-    .max(1500, "Testimonial must be at most 1000 characters"),
+    .max(1500, "Testimonial must be at most 1500 characters"),

75-83: Remove unnecessary type cast.

The (trpc as any) cast is unnecessary and bypasses TypeScript's type checking. The tRPC client is properly typed with AppRouter and should correctly infer the testimonial router types.

🔎 Proposed fix
-  const { data, isLoading: isDataLoading } = (
-    trpc as any
-  ).testimonial.getMyTestimonial.useQuery(undefined, {
+  const { data, isLoading: isDataLoading } = trpc.testimonial.getMyTestimonial.useQuery(undefined, {
     enabled: !!isPaidUser,
     retry: false,
     refetchOnWindowFocus: true,

Based on learnings, proper TypeScript types should be used throughout.


90-97: Remove type casts and avoid any type.

Two issues here:

  1. The (trpc as any) cast bypasses type safety (same issue as above)
  2. The error parameter uses any type, which violates coding guidelines
🔎 Proposed fix
-  const submitMutation = (trpc as any).testimonial.submit.useMutation({
+  const submitMutation = trpc.testimonial.submit.useMutation({
     onSuccess: async () => {
       router.push("/testimonials");
     },
-    onError: (error: any) => {
+    onError: (error: { message?: string }) => {
       setError(error.message || "Error submitting testimonial");
     },
   });

Based on learnings, avoid any type and use proper TypeScript types.

🧹 Nitpick comments (3)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)

99-111: Add ARIA attributes for better accessibility.

The Join button should include aria-busy and aria-label attributes to improve screen reader announcements during the loading state.

🔎 Suggested enhancement
                <button
                  onClick={handleJoinCommunity}
                  disabled={isJoining}
+                 aria-busy={isJoining}
+                 aria-label={isJoining ? "Joining community" : "Join community"}
                  className="px-8 py-3 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-semibold rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
                >
                  {isJoining ? "Joining..." : "Join"}
                </button>
apps/web/src/app/(main)/testimonials/submit/page.tsx (2)

1-19: Reorganize imports per coding guidelines.

The imports should follow the pattern: React → third-party → local components → utils → types. Currently, they are interspersed.

🔎 Suggested import organization
 "use client";

 import React, { useEffect, useState } from "react";
+import Image from "next/image";
+import { useRouter } from "next/navigation";
+import { useSession } from "next-auth/react";
 import { useForm } from "react-hook-form";
 import { zodResolver } from "@hookform/resolvers/zod";
 import * as z from "zod";
+import { Loader2, Link as LinkIcon, ArrowLeft } from "lucide-react";
+
 import { trpc } from "@/lib/trpc";
 import { useSubscription } from "@/hooks/useSubscription";
-import { useRouter } from "next/navigation";
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
 import { Textarea } from "@/components/ui/textarea";
 import { Label } from "@/components/ui/label";
 import { Skeleton } from "@/components/ui/skeleton";
-import Image from "next/image";
 import Navbar from "@/components/landing-sections/navbar";
 import Footer from "@/components/landing-sections/footer";
-import { Loader2, Link as LinkIcon, ArrowLeft } from "lucide-react";
-import { useSession } from "next-auth/react";

Based on coding guidelines.


385-387: Remove commented code.

The commented-out character count display should be removed if it's no longer needed. Keeping commented code reduces maintainability.

🔎 Proposed cleanup
                   <div className="flex justify-between items-center">
                     {errors.content && (
                       <p className="text-red-500 text-xs">
                         {errors.content.message}
                       </p>
                     )}
-                    {/* <p className="text-xs text-neutral-500 ml-auto">
-                      {contentValue?.length || 0}/1500
-                    </p> */}
                   </div>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfa2360 and 345fc68.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • apps/web/src/app/(main)/(landing)/layout.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/content/newsletters-premium
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/content/newsletters-premium
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/(main)/(landing)/layout.tsx
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
🧠 Learnings (10)
📚 Learning: 2025-12-17T12:39:29.991Z
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 262
File: apps/web/src/components/checkout/CheckoutWrapper.tsx:5-5
Timestamp: 2025-12-17T12:39:29.991Z
Learning: In Next.js apps, avoid using redirect() from next/navigation inside Client Components during render. It should not be used in event handlers like onClick or onSubmit; for those cases, use useRouter().push() or router.replace() instead. This guidance applies to TSX files (React components) across the codebase; implement in client components to handle conditional redirects during render, and use router-based navigation in handlers.

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
🧬 Code graph analysis (1)
apps/web/src/components/checkout/checkout-confirmation.tsx (2)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/web/src/lib/auth/config.ts (1)
  • session (48-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (3)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)

22-65: Race condition protection is properly implemented.

The isJoining state guard (line 23), disabled button state (line 101), and finally block (lines 62-64) correctly prevent multiple concurrent API calls. This addresses the concern raised in the previous review.

apps/web/src/app/(main)/testimonials/submit/page.tsx (2)

31-43: LGTM! Social link validation correctly implemented.

The validation logic properly checks for exact hostname matches or valid subdomains using endsWith("." + platform), preventing false positives like "faketwitter.com". This aligns with backend validation expectations.


290-296: LGTM! Home navigation properly implemented.

The back button to /dashboard/home addresses the reviewer's request for a home button on the testimonials page. The implementation correctly uses router.push() in an onClick handler, which aligns with Next.js best practices for client-side navigation.

Based on learnings, router.push() is the correct approach for navigation in client component event handlers.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (3)
apps/web/src/app/(main)/testimonials/submit/page.tsx (3)

53-56: Validation message mismatch already flagged.

This issue has already been identified in previous review comments. The schema enforces .max(1500) but the error message states "at most 1000 characters".


75-83: The (trpc as any) cast has already been flagged.

This unnecessary type cast bypasses TypeScript's type checking and was already identified in previous review comments. Remove the cast to maintain type safety.


90-97: The (trpc as any) cast has already been flagged.

This unnecessary type cast was already identified in previous review comments. Additionally, the error parameter on line 94 is typed as any, which also violates type safety guidelines.

🧹 Nitpick comments (9)
apps/web/src/components/checkout/checkout-confirmation.tsx (3)

3-8: Reorder imports per coding guidelines.

The import order should follow: React → third-party → local components → utils → types. Currently, the local util import (@/lib/utils) appears before some third-party imports.

As per coding guidelines, organize imports in the prescribed sequence.

🔎 Proposed reordering
 import React, { useState } from "react";
 import { Check } from "lucide-react";
-import { cn } from "@/lib/utils";
 import { useSession } from "next-auth/react";
 import { useRouter } from "next/navigation";
 import type { Session } from "next-auth";
+import { cn } from "@/lib/utils";

70-70: Use lowercase for comments.

Comments on lines 70, 77, and 95 begin with capital letters, which violates the coding guideline requiring lowercase comments.

As per coding guidelines, "Always use lowercase when writing comments."

🔎 Suggested fixes
-        {/* Success Icon */}
+        {/* success icon */}
-        {/* Main Message */}
+        {/* main message */}
-          {/* Action Buttons - Only shown when logged in */}
+          {/* action buttons - only shown when logged in */}

Also applies to: 77-77, 95-95


98-112: Consider aligning button styling with design guidelines.

The buttons use rounded-lg and duration-200, which work correctly but differ slightly from the design system standards.

As per coding guidelines, buttons should use rounded-[16px] for border radius and standard animation durations (duration-100, duration-300, or duration-600).

🔎 Suggested alignment
                 <button
                   onClick={handleJoinCommunity}
                   disabled={isJoining}
-                  className="px-8 py-3 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-semibold rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
+                  className="px-8 py-3 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-semibold rounded-[16px] transition-colors duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
                 >
                   {isJoining ? "Joining..." : "Join"}
                 </button>
                 <button
                   onClick={() => router.push("/dashboard/home")}
-                  className="px-8 py-3 bg-surface-elevated hover:bg-surface-hover text-text-primary font-semibold rounded-lg transition-colors duration-200 border border-border"
+                  className="px-8 py-3 bg-surface-elevated hover:bg-surface-hover text-text-primary font-semibold rounded-[16px] transition-colors duration-300 border border-border"
                 >
                   Home
                 </button>
apps/web/src/components/checkout/CheckoutWrapper.tsx (2)

3-6: Reorder imports per coding guidelines.

Imports should follow this order: React → third-party libraries → local components → utils → types. Currently, the next/navigation import appears after the local hook import.

🔎 Suggested import order
 import React, { useEffect, useState } from "react";
+import { redirect } from "next/navigation";
 import { useSubscription } from "@/hooks/useSubscription";
-import { redirect } from "next/navigation";
 import CheckoutConfirmation from "./checkout-confirmation";

Based on coding guidelines for apps/web/src/**/*.{tsx,ts,jsx,js}: "Organize imports in order: React → third-party → local components → utils → types".


8-8: Consider using const arrow function per coding guidelines.

The guidelines recommend using const arrow functions instead of function declarations for consistency across the codebase.

🔎 Suggested function style
-export default function CheckoutWrapper() {
+const CheckoutWrapper = (): JSX.Element => {
   const { isPaidUser, isLoading, refetch } = useSubscription();
   const [retryCount, setRetryCount] = useState(0);
+  // ... rest of component
+};
+
+export default CheckoutWrapper;

Based on coding guidelines for apps/web/src/**/*.{tsx,ts,jsx,js}: "Use const with arrow functions instead of function declarations" and "Define a type when defining const functions".

apps/web/src/app/(main)/testimonials/submit/page.tsx (4)

3-19: Reorder imports to follow coding guidelines.

The import order does not follow the guideline: React → third-party → local components → utils → types. Currently, next/navigation (line 9), lucide-react (line 18), and next-auth/react (line 19) are placed after local imports. Group all third-party imports together before local imports.

🔎 Suggested import order
 "use client";

 import React, { useEffect, useState } from "react";
 import { useForm } from "react-hook-form";
 import { zodResolver } from "@hookform/resolvers/zod";
 import * as z from "zod";
+import { useRouter } from "next/navigation";
+import { Loader2, Link as LinkIcon, ArrowLeft } from "lucide-react";
+import { useSession } from "next-auth/react";
+import Image from "next/image";
 import { trpc } from "@/lib/trpc";
 import { useSubscription } from "@/hooks/useSubscription";
-import { useRouter } from "next/navigation";
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
 import { Textarea } from "@/components/ui/textarea";
 import { Label } from "@/components/ui/label";
 import { Skeleton } from "@/components/ui/skeleton";
-import Image from "next/image";
 import Navbar from "@/components/landing-sections/navbar";
 import Footer from "@/components/landing-sections/footer";
-import { Loader2, Link as LinkIcon, ArrowLeft } from "lucide-react";
-import { useSession } from "next-auth/react";

As per coding guidelines, imports should be organized with all third-party libraries grouped before local imports.


31-43: Use const arrow function for consistency.

The validateSocialLink function uses a function declaration. Per coding guidelines, prefer const arrow functions over function declarations for consistency.

🔎 Suggested refactor
-const validateSocialLink = (url: string) => {
+const validateSocialLink = (url: string): boolean => {
   if (!url) return true;
   try {
     const parsedUrl = new URL(url);
     return SUPPORTED_PLATFORMS.some(
       (platform) =>
         parsedUrl.hostname === platform ||
         parsedUrl.hostname.endsWith("." + platform)
     );
   } catch {
     return false;
   }
 };

As per coding guidelines, use const with arrow functions instead of function declarations.


69-69: Use const arrow function for component definition.

The component uses a function declaration. Per coding guidelines, prefer const arrow functions over function declarations.

🔎 Suggested refactor
-export default function SubmitTestimonialPage() {
+const SubmitTestimonialPage = () => {
   // ... component body
-}
+};
+
+export default SubmitTestimonialPage;

As per coding guidelines, use const with arrow functions instead of function declarations.


82-82: Consider a reasonable staleTime to reduce unnecessary refetches.

Setting staleTime: 0 combined with refetchOnWindowFocus: true and refetchOnMount: true may cause excessive server requests. Unless real-time freshness is critical for testimonial submission status, consider a reasonable staleTime (e.g., 30 seconds) to balance data freshness with performance.

🔎 Suggested configuration
   const { data, isLoading: isDataLoading } = (
     trpc as any
   ).testimonial.getMyTestimonial.useQuery(undefined, {
     enabled: !!isPaidUser,
     retry: false,
     refetchOnWindowFocus: true,
     refetchOnMount: true,
-    staleTime: 0, // Always fetch fresh data
+    staleTime: 30000, // consider data fresh for 30 seconds
   });
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 345fc68 and e3f4f38.

📒 Files selected for processing (3)
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
  • apps/web/src/components/checkout/checkout-confirmation.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Use PascalCase for component file names (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
🧠 Learnings (25)
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : NEVER use hardcoded hex values (e.g., `#5519f7`) directly in components; ALWAYS reference colors from the design token system using Tailwind classes

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use semantic color names that describe purpose, not appearance

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Always follow the design system defined in `apps/web/src/lib/design-tokens.ts` and `apps/web/tailwind.config.ts`

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Always follow the design system defined in `apps/web/src/lib/design-tokens.ts` and `apps/web/tailwind.config.ts`

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Use 'class:' instead of the ternary operator in class tags whenever possible

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-12-17T12:39:29.991Z
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 262
File: apps/web/src/components/checkout/CheckoutWrapper.tsx:5-5
Timestamp: 2025-12-17T12:39:29.991Z
Learning: In Next.js apps, avoid using redirect() from next/navigation inside Client Components during render. It should not be used in event handlers like onClick or onSubmit; for those cases, use useRouter().push() or router.replace() instead. This guidance applies to TSX files (React components) across the codebase; implement in client components to handle conditional redirects during render, and use router-based navigation in handlers.

Applied to files:

  • apps/web/src/components/checkout/checkout-confirmation.tsx
  • apps/web/src/app/(main)/testimonials/submit/page.tsx
  • apps/web/src/components/checkout/CheckoutWrapper.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use appropriate border radius: small elements `rounded-lg`, medium `rounded-xl`, large `rounded-2xl`, buttons `rounded-[16px]`

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile `p-4` (1rem) and desktop `p-[60px]`

Applied to files:

  • apps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Extract reusable logic into custom hooks

Applied to files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Extract reusable logic into custom hooks

Applied to files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Ensure keyboard navigation works in interactive components

Applied to files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Avoid unnecessary re-renders

Applied to files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*[Cc]omponent.{tsx,ts} : Keep components focused and single-responsibility

Applied to files:

  • apps/web/src/components/checkout/CheckoutWrapper.tsx
🧬 Code graph analysis (3)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)
apps/web/src/lib/auth/config.ts (1)
  • session (48-59)
apps/web/src/app/(main)/testimonials/submit/page.tsx (8)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/web/src/lib/auth/config.ts (1)
  • session (48-59)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-79)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/skeleton.tsx (1)
  • Skeleton (15-15)
apps/web/src/components/ui/label.tsx (1)
  • Label (26-26)
apps/web/src/components/ui/input.tsx (1)
  • Input (22-22)
apps/web/src/components/ui/textarea.tsx (1)
  • Textarea (22-22)
apps/web/src/components/checkout/CheckoutWrapper.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (7)
apps/web/src/components/checkout/checkout-confirmation.tsx (3)

22-65: LGTM! Concurrent API call protection and error handling are solid.

The handleJoinCommunity function now correctly prevents concurrent API calls with the isJoining guard (line 23) and ensures state cleanup via the finally block (lines 62-64). This addresses the previous review concern about multiple simultaneous requests.

Additional improvements:

  • Opening Slack invite in a new tab (line 58) with proper security attributes
  • Proper error handling with early returns and user feedback
  • Access token validation before making the request

92-92: LGTM! Hardcoded color replaced with design token.

The email text now uses the semantic design token text-brand-purple-light instead of the hardcoded hex value, correctly following the coding guidelines.


95-115: LGTM! Two-button layout improves UX.

The addition of a dedicated "Home" button alongside "Join" provides users with a clear navigation path after checkout. The router-based navigation (router.push) is the correct approach for client component navigation, and the responsive flex layout handles mobile/desktop views appropriately.

apps/web/src/components/checkout/CheckoutWrapper.tsx (3)

9-21: Excellent retry mechanism implementation!

The retry logic is well-structured:

  • Caps retries at 3 attempts to prevent infinite loops
  • Uses appropriate 2-second delay between attempts
  • Properly cleans up the timeout to prevent memory leaks
  • Defensive optional chaining on refetch

This addresses the payment verification race condition mentioned in the PR objectives.


23-31: Good UX improvement with retry feedback.

The loading state correctly covers both initial load and retry attempts, and the conditional text ("Verifying payment..." after the first retry) provides helpful feedback to users during the payment verification process.


33-37: Race condition successfully fixed!

The direct conditional redirect approach correctly addresses the race condition flagged in previous reviews. By calling redirect() directly during render (rather than using a separate shouldRedirect state set in useEffect), the code ensures that <CheckoutConfirmation /> is only rendered when isPaidUser is true.

The logic is sound:

  • Non-paid users after 3 retries: redirect to /pricing
  • Paid users: see the checkout confirmation
  • No intermediate state where non-paid users can see premium content

Per learnings: using redirect() during render in Client Components is valid (it should not be used in event handlers).

apps/web/src/app/(main)/testimonials/submit/page.tsx (1)

334-337: No action needed — i.pravatar.cc is already configured in Next.js.

The external image domain is properly included in next.config.js under remotePatterns with protocol "https" and hostname "i.pravatar.cc", so the fallback will work without runtime errors.

onSuccess: async () => {
router.push("/testimonials");
},
onError: (error: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace any type with proper error typing.

The error parameter is typed as any, which bypasses TypeScript's type checking. Use a more specific type or unknown and narrow with type guards.

🔎 Suggested fix
-    onError: (error: any) => {
-      setError(error.message || "Error submitting testimonial");
+    onError: (error: unknown) => {
+      const message = error instanceof Error ? error.message : "Error submitting testimonial";
+      setError(message);
     },

Based on learnings, avoid any type; use unknown for truly dynamic data and narrow with type guards.

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

🤖 Prompt for AI Agents
In apps/web/src/app/(main)/testimonials/submit/page.tsx around line 94, the
onError callback types its parameter as `any`; change the parameter to `unknown`
and add a narrow/type-guard before using it. Create a small type-guard (e.g.,
isError that checks for an object with a string `message` or use `instanceof
Error`) and use that guard inside onError to extract a safe message or fallback
string, then pass that safe value to any logger/state updater; remove all uses
of `any` for this error handling path.

Comment on lines +385 to +387
{/* <p className="text-xs text-neutral-500 ml-auto">
{contentValue?.length || 0}/1500
</p> */}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove commented out code.

The character counter code is commented out. Per coding guidelines, remove commented out code rather than leaving it in the codebase. Either uncomment it to restore the functionality or delete it entirely.

🔎 Suggested action

If the character counter is not needed, remove these lines:

                   <div className="flex justify-between items-center">
                     {errors.content && (
                       <p className="text-red-500 text-xs">
                         {errors.content.message}
                       </p>
                     )}
-                    {/* <p className="text-xs text-neutral-500 ml-auto">
-                      {contentValue?.length || 0}/1500
-                    </p> */}
                   </div>

If it should be displayed, uncomment it:

                   <div className="flex justify-between items-center">
                     {errors.content && (
                       <p className="text-red-500 text-xs">
                         {errors.content.message}
                       </p>
                     )}
-                    {/* <p className="text-xs text-neutral-500 ml-auto">
-                      {contentValue?.length || 0}/1500
-                    </p> */}
+                    <p className="text-xs text-neutral-500 ml-auto">
+                      {contentValue?.length || 0}/1500
+                    </p>
                   </div>

As per coding guidelines, avoid leaving commented-out code in the codebase.

📝 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
{/* <p className="text-xs text-neutral-500 ml-auto">
{contentValue?.length || 0}/1500
</p> */}
<div className="flex justify-between items-center">
{errors.content && (
<p className="text-red-500 text-xs">
{errors.content.message}
</p>
)}
</div>
🤖 Prompt for AI Agents
In apps/web/src/app/(main)/testimonials/submit/page.tsx around lines 385 to 387,
there is a commented-out character counter snippet; remove the commented code or
restore it. If the counter is not needed, delete those three commented lines
entirely; if it should be visible, uncomment the JSX and ensure it uses the
existing contentValue state (e.g., {contentValue?.length || 0}/1500) and proper
styling so the component renders correctly.

{
protocol: "https",
hostname: "standardcoldpressedoil.com",
},
Copy link

Choose a reason for hiding this comment

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

Avatar hosts mismatch causes images to fail to render

The ALLOWED_IMAGE_HOSTS in avatar-validator.ts permits avatars from graph.facebook.com, pbs.twimg.com, cdn.discordapp.com, i.imgur.com, res.cloudinary.com, ik.imagekit.io, images.unsplash.com, and ui-avatars.com, but these hosts are missing from next.config.js remotePatterns. Users can successfully submit testimonials with avatars from these hosts, but the Next.js Image component on the testimonials page will throw an error because the hostnames are not configured. This affects users with OAuth avatars from Facebook, Twitter, or Discord, and anyone using other allowed image hosts.

Additional Locations (1)

Fix in Cursor Fix in Web

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.

4 participants