-
Notifications
You must be signed in to change notification settings - Fork 186
[Feat] pro users testimonials page with redis caching #262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@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. |
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:
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?
Accessibility: Consider adding
aria-labelattributes 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.tsxaccordingly.apps/api/prisma/schema.prisma (1)
48-57: Consider adding an index oncreatedAtfor query performance.The
getAllendpoint orders testimonials bycreatedAt 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: Addsizesattribute to Image withfillprop.When using
fillprop, providing asizesattribute 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
constwith 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 ofany[]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
Testimonialif 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.redispersists across HMR in development, while the module-levelredisholds the reference for the current process. Consider renaming the module-level variable toredisInstanceto 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: Avoidanytype for thevalueparameter.Per coding guidelines, avoid using
any. Use a generic orunknowntype 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:KEYScommand can block Redis in production.The
KEYScommand scans the entire keyspace and blocks the server, which is problematic with large datasets. Consider usingSCANfor 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: Replacealert()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
#101010and#252525directly 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
#101010maps to a background token, usebg-backgroundor the equivalent defined indesign-tokens.ts.Also applies to: 129-129, 166-166, 201-201
98-108: Replacealert()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: ImageonErrorhandler bypasses Next.js optimization.Directly setting
srcon 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/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 inapps/web/src/lib/design-tokens.tsandapps/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
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/data/testimonials.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/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, avoidany
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 insrc/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.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/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.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/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.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/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 insrc/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.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/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.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/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.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/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.tsapps/api/src/routers/_app.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/routers/testimonial.tsapps/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.jsonapps/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.tsapps/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:
ioredisis the standard Redis client for Node.js@types/nodeprovides necessary type definitions for Node.js APIsThese 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
zodResolveris 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/api/src/utils/avatar-validator.ts (1)
164-171: Consider adding error logging for debugging.The function silently returns
falseon 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
📒 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.tsapps/api/src/trpc.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
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 insrc/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/api/src/trpc.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/utils/avatar-validator.tsapps/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
ProtectedContexttype accurately represents the context shape after authentication, improving type clarity for protected procedures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 anyfrom theprotectedProcedureexport (line 44) properly addresses the previous review concern. The new approach usingProtectedContexttype 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
anytype and uses proper TypeScript types as required.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.tsapps/api/src/trpc.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
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 insrc/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/api/src/trpc.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/utils/avatar-validator.tsapps/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 fixed it! optional social link and icon based on link input
button invisible after testimonial get submitted
also payment issue fixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
apps/api/src/routers/testimonial.ts (2)
14-14: Removeanytype annotations; let tRPC infer types.Per coding guidelines, avoid
anytype. 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 unnecessaryanytype 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.
ImageTestimonialis already defined and exported fromapps/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
#252525and#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 tokenAlso 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 updatesapps/web/src/app/(main)/testimonials/submit/page.tsx (3)
96-98: Type the error parameter properly.Avoid
anyfor 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]andborder-[#252525]with semantic design token classes.Also applies to: 167-167, 253-253
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 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.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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 inapps/web/src/lib/design-tokens.tsandapps/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
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
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 insrc/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.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsapps/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.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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 insrc/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.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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
anytype; useunknownfor 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 inapps/web/src/lib/design-tokens.tsandapps/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 usinganytype
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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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)
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
getSocialIconhelper 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
useMemoappropriately 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 whenlength === 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
validateAvatarUrlwhich 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
refetchto 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
planIdand 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 avoidanytype.This method has several issues:
- The Razorpay API call on line 77 has no timeout, which can cause hanging requests if Razorpay is slow or unresponsive.
- Line 85 uses
anytype instead ofunknown.- Missing logging for successful order creation, which is a critical operation.
As per coding guidelines, implement timeouts for external API calls and avoid
anytype.Consider:
- Wrapping the Razorpay call with a timeout mechanism or configuring timeout at the client level.
- Changing
error: anytoerror: unknownand using type guards.- 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: Replaceanyreturn type and add logging for critical operation.This method has several issues:
- Return type is
anyon line 146 instead of a specific Prisma type.- Error logging lacks context (userId, paymentId).
- Missing logging for successful payment creation, which is a critical operation.
As per coding guidelines, avoid
anytype 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:
- Return type is
any(line 183) instead of a specific Prisma type.- Error logging lacks context (userId, planId, paymentId).
- 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
📒 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, avoidany
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 insrc/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
anytype; useunknownfor 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.
|
@huamanraj everything looks fine but i don't know why am not getting to the ref Screen.Recording.2025-12-18.at.11.05.39.PM.mov
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
apps/api/src/routers/testimonial.ts (4)
8-8: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. TRPC should infer the context type correctly from the procedure definition.As per coding guidelines and learnings retrieved from long-term memory.
19-19: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. 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
getMyTestimonialendpoint (read operation). The message should reflect access/viewing restrictions instead.
63-63: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. 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
📒 Files selected for processing (4)
apps/api/src/routers/testimonial.tsapps/api/src/routers/user.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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, avoidany
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 insrc/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
anytype; useunknownfor 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
validateAvatarUrlproperly 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.
c2b9246 to
cfa2360
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:anytype annotation with proper typing.The
:anytype annotation onprotectedProcedureviolates the coding guideline to avoidanytype. A previous review flagged a similar issue withas anycast (marked as addressed in commit a5722c4), but the current code still uses:anyas a type annotation, which similarly removes type safety.Based on learnings and coding guidelines for
apps/api/src/**/*.ts: Avoidanytype; useunknownfor 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 middlewareAs 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 unnecessaryanytype cast on trpc.The
(trpc as any)casts bypass TypeScript's type checking and violate coding guidelines. The tRPC client is properly typed withAppRouterwhich includes thetestimonialRouter.🔎 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: Avoidas anyfor better type safety.The type cast to
anybypasses 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:
- Updating the tRPC client generation to include proper types for
user.subscriptionStatus- 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.
ImageTestimonialis already exported fromapps/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
#252525appear throughout. Per coding guidelines, prefer referencing colors from the design token system using Tailwind classes (e.g.,border-borderor 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
onErrorhandler 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: Avoidanytype in error handler.Per coding guidelines, avoid
any. Useunknownand narrow, or importTRPCClientErrorLikefor 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/serverin apps/web is at^11.5.1, while@trpc/clientand@trpc/react-queryare at^11.7.2. Additionally, apps/api has@trpc/serverat^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/serverto^11.7.2to match the client packages and align with apps/api.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
apps/api/.env.exampleapps/api/package.jsonapps/api/prisma/migrations/20251218121459_add_testimonials/migration.sqlapps/api/prisma/schema.prismaapps/api/src/routers/_app.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/user.tsapps/api/src/services/payment.service.tsapps/api/src/trpc.tsapps/api/src/utils/avatar-validator.tsapps/api/tsconfig.jsonapps/web/next.config.jsapps/web/package.jsonapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/app/(main)/dashboard/oss-programs/[slug]/page.tsxapps/web/src/app/(main)/dashboard/oss-programs/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/components/oss-programs/ProgramCard.tsxapps/web/src/data/oss-programs/index.tsapps/web/src/data/testimonials.tsapps/web/src/hooks/useSubscription.tspackages/shared/package.jsonpackages/shared/types/index.tspackages/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/api/src/trpc.tsapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/redis.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
apps/web/src/**/*.{ts,tsx}: Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/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
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/app/(main)/testimonials/page.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/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, avoidany
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 insrc/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/api/src/trpc.tsapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/api/src/trpc.tsapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/api/src/trpc.tsapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/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 insrc/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/api/src/trpc.tsapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/hooks/useSubscription.tsapps/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 inapps/web/src/lib/design-tokens.tsandapps/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 usinganytype
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
anytype; useunknownfor 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.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/app/(main)/(landing)/layout.tsxapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/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.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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)
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 usingrouter.push()in event handlers instead ofredirect().Minor note: coding guidelines recommend
rounded-[16px]for buttons andduration-100/duration-300/duration-600for transitions, but the current values are acceptable.apps/web/src/hooks/useSubscription.ts (1)
28-28: LGTM! Refetch capability properly exposed.The addition of
refetchto 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 withCASCADEdelete ensures cleanup when users are removed. The nullablesocialLinksupports 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
useMemocorrectly 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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 withAppRouterand should correctly infer thetestimonialrouter 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 avoidanytype.Two issues here:
- The
(trpc as any)cast bypasses type safety (same issue as above)- The error parameter uses
anytype, 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
anytype 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-busyandaria-labelattributes 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
apps/web/src/app/(main)/(landing)/layout.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/checkout-confirmation.tsxapps/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.tsxapps/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 inapps/web/src/lib/design-tokens.tsandapps/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
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/components/checkout/checkout-confirmation.tsxapps/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, avoidany
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 insrc/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.tsxapps/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.tsxapps/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.tsxapps/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 insrc/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.tsxapps/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.tsxapps/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 inapps/web/src/lib/design-tokens.tsandapps/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 usinganytype
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.tsxapps/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.tsxapps/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
isJoiningstate guard (line 23), disabled button state (line 101), andfinallyblock (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/homeaddresses the reviewer's request for a home button on the testimonials page. The implementation correctly usesrouter.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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
errorparameter on line 94 is typed asany, 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-lgandduration-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, orduration-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/navigationimport 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), andnext-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
validateSocialLinkfunction 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: 0combined withrefetchOnWindowFocus: trueandrefetchOnMount: truemay 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
📒 Files selected for processing (3)
apps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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 inapps/web/src/lib/design-tokens.tsandapps/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
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/components/checkout/checkout-confirmation.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
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 insrc/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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 insrc/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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 inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
Files:
apps/web/src/components/checkout/checkout-confirmation.tsxapps/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 usinganytype
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.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/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
handleJoinCommunityfunction now correctly prevents concurrent API calls with theisJoiningguard (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-lightinstead 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
refetchThis 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 separateshouldRedirectstate set inuseEffect), the code ensures that<CheckoutConfirmation />is only rendered whenisPaidUseristrue.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.ccis already configured in Next.js.The external image domain is properly included in
next.config.jsunderremotePatternswith protocol "https" and hostname "i.pravatar.cc", so the fallback will work without runtime errors.
| onSuccess: async () => { | ||
| router.push("/testimonials"); | ||
| }, | ||
| onError: (error: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
| {/* <p className="text-xs text-neutral-500 ml-auto"> | ||
| {contentValue?.length || 0}/1500 | ||
| </p> */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
| {/* <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", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.



UI examples
Summary by CodeRabbit
New Features
Improvements
Chores
✏️ 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.
Testimonialmodel/migration andtestimonialtRPC router (getAll,getMyTestimonial,submit) gated to paid users; introduces strictvalidateAvatarUrl(HTTPS, host allowlist, HEAD check, size/content-type); exportsRazorpayOrderSuccess;.env.exampleaddsREDIS_URL./testimonialsgallery (mixes backend text with static image items) and Pro‑only/testimonials/submitform (zod/react-hook-form); Pro dashboard shows "Submit Testimonial" CTA; Slack join now opens in new tab; checkout verifies subscription with retries; landing layout includesNavbar; added remote image hosts; pricing banner date updated.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.