-
Notifications
You must be signed in to change notification settings - Fork 26
Feature project enhancement #84
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis update implements a major refactor and feature expansion for a flashcard application. Key changes include: introducing Razorpay payment integration and subscription management, replacing Stripe and Supabase with Prisma and Clerk-based authentication, modularizing flashcard and deck management, adding global search and study session features, improving UI and error handling, and centralizing type definitions. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI
participant API
participant FlashcardService
participant PaymentService
participant PrismaDB
participant ClerkAuth
participant Razorpay
User->>UI: Interacts (e.g., generate flashcards, start study, subscribe)
UI->>API: Sends request (e.g., /api/generate, /api/razorpay/create-order)
API->>ClerkAuth: Authenticate user
alt Authenticated
API->>FlashcardService: (e.g., generateFlashcards, createDeckWithFlashcards)
FlashcardService->>PrismaDB: Persist/read data
API->>PaymentService: createPaymentOrder (for subscription)
PaymentService->>Razorpay: Create order
Razorpay-->>PaymentService: Order details
PaymentService-->>API: Order info
API-->>UI: Response (e.g., flashcards, order info)
else Not Authenticated
API-->>UI: 401 Unauthorized
end
UI->>Razorpay: Open checkout (for payment)
Razorpay-->>UI: Payment result
UI->>API: /api/razorpay/verify-payment (with payment details)
API->>PaymentService: verifyPayment
PaymentService->>Razorpay: Verify signature
Razorpay-->>PaymentService: Verification result
PaymentService->>PrismaDB: Update user/payment status
API-->>UI: Verification result (subscription activated)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90+ minutes
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
next.config.jsOops! Something went wrong! :( ESLint: 9.31.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by src/app/api/decks/save/route.tsOops! Something went wrong! :( ESLint: 9.31.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by src/app/api/flashcards/[id]/route.tsOops! Something went wrong! :( ESLint: 9.31.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 34
♻️ Duplicate comments (2)
prisma/schema.prisma (1)
107-110:plan/billingCyclerepeat string smell identified aboveThe same enum recommendation applies to these columns to keep backend code and DB in sync.
prisma/migrations/20250725171641_complete_with_adding_razorpay/migration.sql (1)
113-118: Unique on nullablerazorpayPaymentIdrepeats the Prisma-level problemThe raw migration mirrors the schema concern; multiple pending payments will fail. Drop the unique or convert it to a partial index:
CREATE UNIQUE INDEX "Payment_razorpayPaymentId_key" ON "Payment" ("razorpayPaymentId") WHERE "razorpayPaymentId" IS NOT NULL;
🧹 Nitpick comments (40)
src/config/site.ts (1)
12-15: Empty external links may break footer / SEO components
github, andportfolioare now empty strings.
If downstream components build<a>tags blindly, they’ll render invalid hrefs (href=""). Either:
- Remove the keys completely when not configured, or
- Update UI to hide the links when value is falsy.
Please verify behaviour before shipping.
src/app/api/study/complete/route.ts (1)
31-31: Document duration unit assumption.The code assumes
durationis in milliseconds but this isn't documented, which could lead to incorrect conversions if callers pass different units.- duration: Math.floor(duration / 1000), // Convert to seconds + duration: Math.floor(duration / 1000), // Convert from milliseconds to secondssrc/lib/hooks/useLocalStorage.ts (1)
1-31: Consider adding storage event synchronization.Multiple instances of this hook with the same key won't stay synchronized when localStorage is updated from other tabs or components.
Add a storage event listener to synchronize across instances:
useEffect(() => { try { if (typeof window === 'undefined') return; const item = window.localStorage.getItem(key); if (item) { setStoredValue(JSON.parse(item)); } } catch (error) { console.warn(`Error reading localStorage key "${key}":`, error); } + + const handleStorageChange = (e: StorageEvent) => { + if (e.key === key && e.newValue !== null) { + try { + setStoredValue(JSON.parse(e.newValue)); + } catch (error) { + console.warn(`Error parsing storage event for key "${key}":`, error); + } + } + }; + + window.addEventListener('storage', handleStorageChange); + return () => window.removeEventListener('storage', handleStorageChange); }, [key]);src/app/api/decks/[id]/route.ts (1)
18-23: Consider adding cascade deletion documentation.While Prisma will likely handle cascade deletion of related flashcards through schema constraints, it would be helpful to document this behavior.
- // Delete deck and all associated flashcards + // Delete deck and all associated flashcards (cascaded by database constraints) await prisma.deck.delete({ where: { id: params.id, userId: userId // Ensure user owns the deck } });.env.example (3)
14-14: Remove unnecessary quotes from environment variable value.Environment variable values in .env files don't need quotes unless they contain spaces or special characters.
-GEMINI_API_KEY="" +GEMINI_API_KEY=
25-25: Remove quotes from DATABASE_URL.Database URLs typically don't require quotes in .env files.
-DATABASE_URL="postgresql://postgres:[email protected]:5432/postgres" +DATABASE_URL=postgresql://postgres:[email protected]:5432/postgres
27-30: Fix Razorpay configuration formatting and ordering.Multiple formatting issues: unnecessary quotes and incorrect key ordering for Next.js public variables.
# Razorpay Configuration +NEXT_PUBLIC_RAZORPAY_KEY_ID=your_razorpay_key_id -RAZORPAY_KEY_ID="your_razorpay_key_id" -RAZORPAY_KEY_SECRET="your_razorpay_key_secret" -NEXT_PUBLIC_RAZORPAY_KEY_ID="your_razorpay_key_id" +RAZORPAY_KEY_ID=your_razorpay_key_id +RAZORPAY_KEY_SECRET=your_razorpay_key_secret +src/components/decks/DeckCard.tsx (1)
27-31: Consider adding error handling for date formatting.The date formatting could potentially throw an error if
deck.createdAtis invalid. Consider adding error handling or validation.- Created: {new Date(deck.createdAt).toLocaleDateString()} + Created: {new Date(deck.createdAt).toLocaleDateString() || 'Invalid date'}src/lib/database.ts (1)
16-25: Consider adding connection cleanup and more robust error handling.The test connection function is useful, but consider these improvements:
export async function testDatabaseConnection() { try { await prisma.$connect() console.log('✅ Database connected successfully') return true } catch (error) { - console.error('❌ Database connection failed:', error) + console.error('❌ Database connection failed:', error instanceof Error ? error.message : 'Unknown error') return false + } finally { + await prisma.$disconnect() } }src/lib/hooks/useOptimisticMutation.ts (1)
44-44: Consider improving error type assertion.The error casting could be more robust to handle edge cases where the error might not be an Error instance.
- onError?.(error as Error, variables); + onError?.(error instanceof Error ? error : new Error('Unknown error'), variables);src/app/api/decks/save/route.ts (1)
37-38: Consider adding request validation for required fields.Add validation to ensure the request body exists and contains the expected structure before parsing:
+ if (!body) { + const error: ApiError = { error: 'Request body is required' }; + return NextResponse.json(error, { status: 400 }); + } + const validatedData = saveFlashcardsSchema.parse(body);src/app/api/study/record/route.ts (2)
55-72: Simple but effective spaced repetition algorithm.The algorithm provides a reasonable starting point for spaced repetition with different intervals based on performance and difficulty. Consider enhancing it with:
- More sophisticated algorithms like SM-2 or Anki's algorithm
- User-specific learning patterns
- Exponential backoff for repeated failures
For now, the current implementation is functional and appropriate for an MVP.
62-71: Consider adding validation for difficulty values.The switch statement should handle invalid difficulty values more explicitly:
switch (difficulty) { case 'EASY': return baseInterval * 7; // 7 days case 'MEDIUM': return baseInterval * 3; // 3 days case 'HARD': return baseInterval * 1; // 1 day default: - return baseInterval * 2; // 2 days + console.warn(`Unknown difficulty: ${difficulty}, defaulting to 2 days`); + return baseInterval * 2; // 2 days for unknown difficulty }src/app/layout.tsx (1)
20-20: Consider the timing of async conversion.While preparing the layout for future async operations is thoughtful, converting to async without immediate need could be considered premature. However, the commented examples provide good guidance for when this becomes necessary.
Also applies to: 25-27
src/utils/get-razorpay.ts (1)
1-5: Consider using proper Razorpay types.The global Window interface extension is necessary, but using
anyfor the Razorpay property reduces type safety. Consider installing@types/razorpayor defining a more specific interface if available.declare global { interface Window { - Razorpay: any; + Razorpay: typeof import('razorpay') | undefined; } }src/components/decks/DeckList.tsx (1)
18-49: Robust deletion handler with excellent error handling.The implementation includes all necessary safeguards:
- Race condition prevention
- Comprehensive error handling
- User feedback via toast notifications
- Proper state cleanup
Consider adding response status checking for more specific error messages:
if (!response.ok) { - throw new Error('Failed to delete deck'); + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.message || 'Failed to delete deck'); }src/components/ui/badge.tsx (1)
1-4: Consider adding "use client" directive.The component imports are clean, but consider whether this component needs the
"use client"directive for consistency with other UI components, especially if it will be used in interactive contexts.src/components/flashcards/FlashcardSaveDialog.tsx (1)
44-61: Consider adding request timeout and better error handlingThe fetch request lacks timeout handling and could benefit from more specific error handling.
Add timeout and improve error handling:
setIsLoading(true); try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout + const response = await fetch('/api/decks/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, + signal: controller.signal, body: JSON.stringify({ name: deckName.trim(), flashcards: flashcards.map(card => ({ question: card.question, answer: card.answer })) }), }); + clearTimeout(timeoutId); + if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to save flashcards'); }src/middleware.ts (1)
14-26: Excellent migration to Clerk v6 async pattern.The middleware correctly implements the new Clerk v6 approach by:
- Using async/await with
auth()- Manual authentication checks instead of
protect()- Preserving the original URL in redirect parameters for better UX
Consider adding error handling around the
auth()call for robustness:export default clerkMiddleware(async (auth, request) => { - const { userId } = await auth() + let userId; + try { + ({ userId } = await auth()); + } catch (error) { + console.error('Auth error in middleware:', error); + return NextResponse.redirect(new URL('/sign-in', request.url)); + }src/lib/analytics.ts (1)
2-10: Consider improving the singleton pattern.The singleton implementation works but could be more robust:
export class Analytics { private static instance: Analytics; + + private constructor() {} // Prevent external instantiation static getInstance(): Analytics { if (!Analytics.instance) { Analytics.instance = new Analytics(); } return Analytics.instance; }Alternatively, consider a simpler module-level singleton approach which is more idiomatic in modern JavaScript.
src/components/common/ErrorBoundary.tsx (1)
84-96: Solid HOC implementation with minor naming improvement opportunity.The higher-order component provides an easy way to wrap components with error boundaries. Consider improving the display name for better debugging:
export function withErrorBoundary<T extends object>( Component: React.ComponentType<T>, errorFallback?: ReactNode ) { - return function WrappedComponent(props: T) { + const WrappedComponent = function(props: T) { return ( <ErrorBoundary fallback={errorFallback}> <Component {...props} /> </ErrorBoundary> ); }; + + WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name})`; + return WrappedComponent; }src/components/flashcards/FlashcardViewer.tsx (2)
7-7: Remove unused importThe
Flashcardtype is imported but not used in this component. The interface uses inline type definition instead.-import type { Flashcard } from "@/types";
24-25: Optimize useCallback usage for handleFlipThe current implementation recreates the handler on every flip change, defeating the optimization purpose. Use the functional update pattern instead.
- const handleFlip = useCallback(() => setFlip(!flip), [flip]); + const handleFlip = useCallback(() => setFlip(prev => !prev), []);next.config.js (1)
48-50: Remove unnecessary alias spreadThe empty spread operation for
config.resolve.aliasdoesn't add any value.- config.resolve.alias = { - ...config.resolve.alias, - };src/app/api/search/route.ts (2)
40-44: Simplify difficulty filter logic.The conditional filter using an empty object works but could be cleaner.
- // Apply difficulty filter if specified - filters.difficulty.length > 0 ? { - difficulty: { in: filters.difficulty } - } : {}, + // Apply difficulty filter if specified + ...(filters.difficulty.length > 0 && { + difficulty: { in: filters.difficulty } + }),
113-119: Consider more specific error messages.The error handling could provide more context about the failure type.
} catch (error) { console.error('Search error:', error); + + if (error instanceof Error && error.message.includes('Clerk')) { + return NextResponse.json( + { error: 'Authentication error' }, + { status: 401 } + ); + } + return NextResponse.json( { error: 'Search failed' }, { status: 500 } ); }src/lib/services/flashcard.service.ts (2)
21-21: Consider API key security implications.The API key is included in the URL as a query parameter. While this follows Gemini's API pattern, be aware that URLs may be logged in various places (proxies, servers, etc.), potentially exposing the key.
66-81: Consider consolidating markdown cleanup logic.While the current implementation works, the multiple similar replacements could be consolidated for maintainability.
- content = content - .replace(/```json\s*/g, '') // Remove ```json markers - .replace(/```\s*/g, '') // Remove ``` markers - .replace(/``````json\s*/g, '') // Remove 6-backtick json markers - .replace(/``````\s*/g, '') // Remove 6-backtick variations with whitespace - .trim(); - - // Additional cleanup for any remaining markdown artifacts - if (content.startsWith('json\n')) { - content = content.substring(5); - } - if (content.startsWith('json ')) { - content = content.substring(5); - } + // Remove all markdown code block markers and json prefixes + content = content + .replace(/`{3,6}(?:json)?\s*/g, '') // Remove 3-6 backticks with optional json + .replace(/^json[\s\n]/i, '') // Remove json prefix + .trim();src/app/flashcards/[id]/page.tsx (1)
127-129: Consider fetching actual deck name from the data.The deck name is hardcoded to "Flashcard Set" but could be retrieved from the deck data for better UX.
- if (mappedFlashcards.length > 0) { - setDeckName(`Flashcard Set`); - } + if (mappedFlashcards.length > 0 && mappedFlashcards[0].deck) { + setDeckName(mappedFlashcards[0].deck.name || 'Flashcard Set'); + }src/components/study/StudyMode.tsx (1)
187-197: Consider tracking skipped cards in the backendSkipped cards are only tracked locally in stats but not recorded individually in the backend like answered cards. This might limit analytics capabilities.
Consider recording skipped cards to the backend for better learning analytics and to identify potentially problematic flashcards.
src/lib/services/payment.service.ts (2)
25-31: Consider making pricing configurableHardcoded pricing values reduce flexibility. Consider loading from configuration or database.
private getPricing() { + // TODO: Load from configuration or database return { monthly: { Pro: 500, Organizational: 1500 }, yearly: { Pro: 5000, Organizational: 15000 } }; }
59-75: Signature verification implemented correctly with room for improvementThe HMAC signature verification is correctly implemented. However, consider importing crypto at the top and adding env var validation.
Move the crypto import to the top of the file and add validation:
+import crypto from 'crypto'; async verifyPayment(paymentData: { razorpay_order_id: string; razorpay_payment_id: string; razorpay_signature: string; }): Promise<boolean> { - const crypto = require('crypto'); + if (!process.env.RAZORPAY_KEY_SECRET) { + throw new Error('Razorpay secret key not configured'); + } const body = paymentData.razorpay_order_id + '|' + paymentData.razorpay_payment_id; const expectedSignature = crypto - .createHmac('sha256', process.env.RAZORPAY_KEY_SECRET!) + .createHmac('sha256', process.env.RAZORPAY_KEY_SECRET) .update(body.toString()) .digest('hex');src/lib/services/user.service.ts (2)
6-8: Update outdated comment referencing SupabaseThe comment mentions "Supabase database" but the code uses Prisma. Update for accuracy.
/** - * Ensure user exists in Supabase database with REAL Clerk data (no dummy users) + * Ensure user exists in database with REAL Clerk data (no dummy users) */
185-210: Consider selective data loading for performanceThis method loads all related data including nested relations, which could impact performance for users with many decks and study sessions.
Consider adding options to selectively load relations:
-async getUserByClerkId(userId: string) { +async getUserByClerkId(userId: string, options?: { + includeDecks?: boolean; + includeFlashcards?: boolean; + includeStudySessions?: boolean; +}) { try { const user = await prisma.user.findUnique({ where: { clerkUserId: userId }, include: { - decks: { + decks: options?.includeDecks ? { include: { flashcards: true } - }, - flashcards: true, - studySessions: { + } : false, + flashcards: options?.includeFlashcards ?? false, + studySessions: options?.includeStudySessions ? { include: { records: true } - } + } : false } });src/app/pricing/page.tsx (1)
187-192: Populate Razorpay prefill data for better UXThe prefill object is empty but could be populated with user data from Clerk to save users from entering their details.
+// Get user data from Clerk +const user = await currentUser(); prefill: { - name: '', - email: '', - contact: '', + name: user ? `${user.firstName || ''} ${user.lastName || ''}`.trim() : '', + email: user?.primaryEmailAddress?.emailAddress || '', + contact: '', // Could be fetched from user metadata if stored },package.json (3)
10-12: Prisma script names LGTM but missingpostinstallhookThe helper scripts are useful, but remember that
prisma generatemust run after eachnpm i / pnpm ito keep the client in sync. A lightweight way is:"scripts": { + "postinstall": "prisma generate",This prevents runtime “Could not find … PrismaClient” errors on fresh installs.
31-32:@typescript-eslint/parsershould be a dev dependencyThe parser is only required at lint time; keeping it in
dependenciesbloats the production bundle size in serverless environments.- "@typescript-eslint/parser": "^8.38.0", + // move to devDependencies
44-46: Razorpay SDK pulls in crypto at build time – check edge runtime compatibilityIf you target the Vercel edge runtime (or any Web Worker-style environment) the
razorpayNode SDK will break because of nativecrypto/httpusage. You may need the lightweightrazorpay/dist/razorpay.esbrowser build or a server-only API route.Confirm your deployment targets before shipping.
src/types/index.ts (1)
139-140: Utility type already exists in TS 5.1
PartialByandRequiredBypatterns are now upstream.Optional<T, K>is fine but consider importing from'type-fest'instead of re-inventing unless tree-shaking is critical.prisma/migrations/20250725171641_complete_with_adding_razorpay/migration.sql (1)
131-142: Foreign keys useRESTRICT– considerON DELETE CASCADEfor user clean-upIf a user deletes their Clerk account you’ll have to orphan-clean decks, flashcards, payments manually. Cascading simplifies this and keeps referential integrity.
Adjust FK clauses before running in production.
📜 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 (71)
.env.example(2 hunks)next.config.js(1 hunks)next.config.mjs(0 hunks)package.json(2 hunks)pnpm-workspace.yaml(1 hunks)prisma/migrations/20241111182400_init/migration.sql(0 hunks)prisma/migrations/20241111182813_init/migration.sql(0 hunks)prisma/migrations/20241111183630_init/migration.sql(0 hunks)prisma/migrations/20250725171641_complete_with_adding_razorpay/migration.sql(5 hunks)prisma/schema.prisma(5 hunks)src/app/api/decks/[id]/route.ts(1 hunks)src/app/api/decks/route.ts(1 hunks)src/app/api/decks/save/route.ts(1 hunks)src/app/api/flashcards/[id]/route.ts(1 hunks)src/app/api/generate/route.ts(1 hunks)src/app/api/razorpay/create-order/route.ts(1 hunks)src/app/api/razorpay/verify-payment/route.ts(1 hunks)src/app/api/search/route.ts(1 hunks)src/app/api/study/complete/route.ts(1 hunks)src/app/api/study/record/route.ts(1 hunks)src/app/api/study/start/route.ts(1 hunks)src/app/flashcards/[id]/page.tsx(3 hunks)src/app/flashcards/page.tsx(4 hunks)src/app/layout.tsx(2 hunks)src/app/pricing/page.tsx(3 hunks)src/app/result/page.tsx(3 hunks)src/components/FAQ.tsx(1 hunks)src/components/ThemeProvider.tsx(1 hunks)src/components/common/ErrorBoundary.tsx(1 hunks)src/components/common/LoadingSpinner.tsx(1 hunks)src/components/common/OptimisticButton.tsx(1 hunks)src/components/core/Navigation.tsx(1 hunks)src/components/core/flash-card.tsx(1 hunks)src/components/decks/DeckCard.tsx(1 hunks)src/components/decks/DeckList.tsx(1 hunks)src/components/flashcards/FlashcardGenerator.tsx(1 hunks)src/components/flashcards/FlashcardSaveDialog.tsx(1 hunks)src/components/flashcards/FlashcardViewer.tsx(1 hunks)src/components/search/GlobalSearch.tsx(1 hunks)src/components/study/StudyMode.tsx(1 hunks)src/components/ui/badge.tsx(1 hunks)src/components/ui/button.tsx(1 hunks)src/components/ui/dialog.tsx(1 hunks)src/components/ui/dropdown-menu.tsx(4 hunks)src/components/ui/input.tsx(1 hunks)src/components/ui/progress.tsx(1 hunks)src/components/ui/skeleton-cards.tsx(1 hunks)src/components/ui/skeleton.tsx(1 hunks)src/config/site.ts(1 hunks)src/hooks/useDebounce.ts(1 hunks)src/lib/analytics.ts(1 hunks)src/lib/auth.ts(1 hunks)src/lib/database.ts(1 hunks)src/lib/db.ts(0 hunks)src/lib/flashcards.ts(1 hunks)src/lib/hooks/useLocalStorage.ts(1 hunks)src/lib/hooks/useOptimisticMutation.ts(1 hunks)src/lib/prisma.ts(0 hunks)src/lib/services/flashcard.service.ts(1 hunks)src/lib/services/payment.service.ts(1 hunks)src/lib/services/user.service.ts(1 hunks)src/lib/supabase.ts(0 hunks)src/lib/utils/lazy-imports.ts(1 hunks)src/middleware.ts(1 hunks)src/types/index.d.ts(0 hunks)src/types/index.ts(1 hunks)src/types/prisma.d.ts(0 hunks)src/types/supabase.ts(0 hunks)src/utils/get-razorpay.ts(1 hunks)src/utils/get-stripe.ts(0 hunks)tsconfig.json(2 hunks)
💤 Files with no reviewable changes (11)
- next.config.mjs
- src/types/prisma.d.ts
- src/utils/get-stripe.ts
- src/lib/db.ts
- src/lib/prisma.ts
- prisma/migrations/20241111183630_init/migration.sql
- prisma/migrations/20241111182813_init/migration.sql
- prisma/migrations/20241111182400_init/migration.sql
- src/types/index.d.ts
- src/types/supabase.ts
- src/lib/supabase.ts
🧰 Additional context used
🧬 Code Graph Analysis (27)
src/app/api/decks/route.ts (2)
src/app/api/flashcards/[id]/route.ts (1)
GET(5-37)src/lib/services/flashcard.service.ts (1)
flashcardService(301-301)
src/app/api/study/complete/route.ts (3)
src/app/api/study/record/route.ts (1)
POST(5-52)src/app/api/study/start/route.ts (1)
POST(6-50)src/lib/database.ts (1)
prisma(7-9)
src/app/api/decks/[id]/route.ts (1)
src/lib/database.ts (1)
prisma(7-9)
src/components/decks/DeckCard.tsx (3)
src/types/index.ts (1)
DeckCardProps(118-121)src/components/ui/card.tsx (2)
Card(79-79)CardContent(79-79)src/components/ui/button.tsx (1)
Button(56-56)
src/components/core/Navigation.tsx (1)
src/components/search/GlobalSearch.tsx (1)
GlobalSearch(39-308)
src/app/api/study/record/route.ts (3)
src/app/api/study/complete/route.ts (1)
POST(5-43)src/app/api/study/start/route.ts (1)
POST(6-50)src/lib/database.ts (1)
prisma(7-9)
src/components/ui/input.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/components/ui/skeleton.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/lib/hooks/useOptimisticMutation.ts (1)
src/components/ui/use-toast.ts (2)
useToast(194-194)toast(194-194)
src/components/common/ErrorBoundary.tsx (3)
src/lib/analytics.ts (1)
error(33-39)src/components/ui/card.tsx (4)
Card(79-79)CardHeader(79-79)CardTitle(79-79)CardContent(79-79)src/components/ui/button.tsx (1)
Button(56-56)
src/app/flashcards/page.tsx (1)
src/components/decks/DeckList.tsx (1)
DeckList(14-72)
src/app/api/study/start/route.ts (4)
src/app/api/study/complete/route.ts (1)
POST(5-43)src/app/api/study/record/route.ts (1)
POST(5-52)src/lib/services/user.service.ts (1)
userService(212-212)src/lib/database.ts (1)
prisma(7-9)
src/components/common/LoadingSpinner.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/app/api/flashcards/[id]/route.ts (2)
src/app/api/decks/route.ts (1)
GET(5-28)src/lib/database.ts (1)
prisma(7-9)
next.config.js (1)
src/middleware.ts (1)
config(28-33)
src/components/ui/progress.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/app/layout.tsx (2)
src/lib/utils.ts (1)
cn(4-6)src/components/ui/toaster.tsx (1)
Toaster(13-35)
src/components/flashcards/FlashcardSaveDialog.tsx (6)
src/types/index.ts (1)
FlashcardInput(52-55)src/components/ui/use-toast.ts (2)
useToast(194-194)toast(194-194)src/components/ui/dialog.tsx (5)
Dialog(112-112)DialogContent(117-117)DialogHeader(118-118)DialogTitle(120-120)DialogFooter(119-119)src/components/ui/label.tsx (1)
Label(26-26)src/components/ui/input.tsx (1)
Input(22-22)src/components/ui/button.tsx (1)
Button(56-56)
src/lib/services/payment.service.ts (1)
src/types/index.ts (3)
SubscriptionPlan(90-90)BillingCycle(91-91)RazorpayOrderResponse(98-103)
src/components/ui/badge.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/app/api/razorpay/create-order/route.ts (1)
src/lib/database.ts (1)
prisma(7-9)
src/app/flashcards/[id]/page.tsx (6)
src/types/index.ts (1)
Flashcard(27-27)src/components/ui/button.tsx (1)
Button(56-56)src/components/ui/skeleton-cards.tsx (1)
FlashcardSkeleton(4-42)src/components/study/StudyMode.tsx (1)
StudyMode(30-346)src/components/ui/card.tsx (4)
Card(79-79)CardHeader(79-79)CardTitle(79-79)CardContent(79-79)src/components/ui/badge.tsx (1)
Badge(36-36)
src/lib/services/flashcard.service.ts (4)
src/types/index.ts (4)
CreateFlashcardSetRequest(73-76)CreateFlashcardSetResponse(78-83)DeckWithFlashcards(47-49)Flashcard(27-27)src/lib/services/user.service.ts (1)
userService(212-212)src/lib/database.ts (1)
prisma(7-9)src/components/core/flash-card.tsx (1)
Flashcard(16-80)
src/components/study/StudyMode.tsx (6)
src/components/ui/use-toast.ts (2)
useToast(194-194)toast(194-194)src/components/ui/skeleton-cards.tsx (1)
StudyModeSkeleton(67-108)src/components/ui/progress.tsx (1)
Progress(28-28)src/components/ui/card.tsx (2)
Card(79-79)CardContent(79-79)src/components/ui/badge.tsx (1)
Badge(36-36)src/components/ui/button.tsx (1)
Button(56-56)
src/types/index.ts (1)
src/components/core/flash-card.tsx (1)
Flashcard(16-80)
src/components/flashcards/FlashcardGenerator.tsx (5)
src/components/ui/use-toast.ts (2)
useToast(194-194)toast(194-194)src/components/ui/form.tsx (5)
Form(171-171)FormField(177-177)FormItem(172-172)FormControl(174-174)FormMessage(176-176)src/components/ui/label.tsx (1)
Label(26-26)src/components/ui/textarea.tsx (1)
Textarea(24-24)src/components/ui/button.tsx (1)
Button(56-56)
src/lib/services/user.service.ts (1)
src/lib/database.ts (1)
prisma(7-9)
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 14-14: [QuoteCharacter] The value has quote characters (', ")
[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")
[warning] 28-28: [QuoteCharacter] The value has quote characters (', ")
[warning] 29-29: [QuoteCharacter] The value has quote characters (', ")
[warning] 30-30: [EndingBlankLine] No blank line at the end of the file
[warning] 30-30: [QuoteCharacter] The value has quote characters (', ")
[warning] 30-30: [UnorderedKey] The NEXT_PUBLIC_RAZORPAY_KEY_ID key should go before the RAZORPAY_KEY_ID key
🪛 Biome (2.1.2)
src/lib/database.ts
[error] 7-7: Shouldn't redeclare 'prisma'. Consider to delete it or rename it.
'prisma' is defined here:
(lint/suspicious/noRedeclare)
src/components/search/GlobalSearch.tsx
[error] 258-258: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
[error] 264-264: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (148)
src/config/site.ts (1)
16-16: Support email cleared – double-check user-facing flows
mailSupportis now empty. Any “Contact support” links or mailto buttons will fail.
If a support channel is still expected, populate a valid address or gate the UI behind a presence check.src/components/FAQ.tsx (1)
3-3: Good TypeScript typing improvement!Adding the explicit
JSXtype import enhances type safety for JSX elements and aligns with modern TypeScript best practices.src/hooks/useDebounce.ts (1)
1-17: Well-implemented debounce hook!This generic hook follows React best practices with proper dependency arrays, cleanup functions, and TypeScript generics. It will be valuable for optimizing search functionality and other rapid input scenarios.
src/components/ui/skeleton.tsx (1)
1-15: Solid skeleton loading component implementation!The component follows UI primitive best practices with proper TypeScript typing, flexible props handling, and appropriate default styling for loading states.
src/components/ui/input.tsx (2)
5-5: Good typing simplification!Using
React.ComponentProps<"input">is more direct and follows modern React TypeScript patterns compared to custom interfaces.
11-11: Improved responsive design and file input styling!The font size changes (
text-basewithmd:text-sm) provide better mobile UX, andfile:text-foregroundensures consistent theming for file inputs.src/lib/flashcards.ts (1)
1-1: Database import and Prisma export verifiedThe import change to
@/lib/databaseis correct, andsrc/lib/database.tsproperly exports theprismaclient.
- Confirmed
export const prismainsrc/lib/database.tswith global caching as expected.No further action required.
src/components/ThemeProvider.tsx (1)
5-6: LGTM! Correct import path update for next-themes upgrade.The import path change from the internal
"next-themes/dist/types"to the public"next-themes"API correctly aligns with the next-themes upgrade to v0.4.6. This follows best practices by using the official public API instead of internal implementation details.src/lib/auth.ts (2)
5-5: LGTM! Correct async auth() call for Clerk v6.The addition of
awaitto theauth()call is necessary for Clerk v6 compatibility, which now returns a Promise. This change is consistent with the authentication updates mentioned across the API routes.
2-2: Database import path and Prisma client export verified – ready to merge
- Confirmed
src/lib/database.tsexists and exportsprismaas a singleton.LGTM!
src/components/ui/dialog.tsx (1)
24-24: LGTM! Minor whitespace cleanup.Removed extraneous space in the className string for cleaner code. This formatting improvement doesn't affect functionality.
src/components/ui/button.tsx (1)
8-8: LGTM! Excellent button styling enhancements.The additions improve button functionality and consistency:
gap-2: Provides proper spacing between button content[&_svg]:pointer-events-none: Prevents SVG click interference[&_svg]:size-4 [&_svg]:shrink-0: Ensures consistent icon sizing and prevents flex shrinkageThese changes align with modern UI component best practices for buttons containing icons.
src/components/core/Navigation.tsx (1)
1-6: Incomplete component structure - clarify if this is documentation or a component.This file appears to contain a code snippet or example rather than a complete React component. If this is meant to be the actual Navigation component, it's missing:
- Component declaration/function
- Export statement
- Proper React component structure
If this is intended as the complete Navigation component, please apply this structure:
+import React from 'react'; import GlobalSearch from '@/components/search/GlobalSearch'; -// In your navigation component, add: -<div className="flex-1 max-w-xl mx-4"> - <GlobalSearch /> -</div> +export default function Navigation() { + return ( + <nav className="flex items-center justify-between p-4"> + {/* Other navigation elements */} + <div className="flex-1 max-w-xl mx-4"> + <GlobalSearch /> + </div> + </nav> + ); +}If this is documentation showing how to integrate GlobalSearch, consider moving it to a README or documentation file.
src/app/api/decks/route.ts (4)
1-3: Clean import structure.The imports are well-organized and follow the established patterns from the service layer refactor.
5-8: Correct Clerk v6 authentication pattern.The
await auth()usage is properly implemented and consistent with Clerk v6 requirements as noted in the comment.
10-15: Proper authentication handling.The unauthorized check and response format are consistent with the established API patterns across the codebase.
17-28: Well-structured service integration and error handling.The implementation correctly uses the service layer and provides comprehensive error handling with appropriate logging and response codes.
tsconfig.json (4)
3-7: Improved readability with multi-line formatting.The formatting change makes the library configuration more readable and maintainable.
25-28: Consistent formatting improvement.The multi-line format for the path alias is more consistent with the rest of the configuration.
29-29: Appropriate ES target for modern features.ES2017 provides good balance of modern JavaScript features (async/await, object spread) while maintaining broad compatibility.
31-39: Consistent multi-line formatting.The formatting changes improve readability and maintainability of the TypeScript configuration.
src/app/api/study/start/route.ts (5)
1-4: Well-organized imports following established patterns.The imports correctly use the new database module and service layer, consistent with the architectural refactor.
6-15: Consistent authentication implementation.The auth pattern matches the established Clerk v6 requirements and provides appropriate error responses.
26-27: Good practice ensuring user existence.The
ensureUserExistscall properly maintains data consistency between Clerk and the database.
29-36: Database operation structure is correct.The study session creation follows proper patterns, though the missing
deckIdfield was noted in the previous comment.
38-50: Proper response formatting and error handling.The success response provides necessary data (sessionId) and error handling follows established patterns with appropriate logging and status codes.
src/app/flashcards/page.tsx (4)
8-11: Good modularization and type safety.The DeckList component import and Deck type usage improve code organization and type safety. The SRP comment demonstrates good architectural awareness.
41-43: Proper state management callback.The
handleDeckDeletedcallback correctly uses functional state updates to maintain state consistency after deck deletion.
56-58: Minor formatting improvement.The multi-line format improves readability of the sign-in prompt text.
73-74: Excellent component composition and separation of concerns.The refactor to use
DeckListwith proper props and callback delegation improves code organization and reusability.src/app/api/decks/[id]/route.ts (1)
5-25: Solid implementation with proper security measures.The DELETE handler correctly implements:
- Async params handling for Next.js App Router
- Clerk v6 async authentication
- User ownership verification in the database query
- Proper error responses
src/app/api/flashcards/[id]/route.ts (1)
5-28: Excellent implementation following established patterns.The GET handler demonstrates:
- Correct async params handling for Next.js App Router
- Proper Clerk v6 async authentication
- User ownership verification through the userId filter
- Logical ordering by creation date for UI consistency
- Clean response structure
src/components/decks/DeckCard.tsx (4)
1-8: Clean imports and type safety implementation.The imports are well-organized and properly typed. Good use of type imports for better tree-shaking and clear separation of concerns.
10-16: Proper event handling for nested interactive elements.The delete handler correctly prevents the Link navigation when the delete button is clicked, avoiding conflicting interactions. The null-safe check for
onDeleteis appropriate.
30-30: Excellent null-safe access pattern for optional array.The use of optional chaining with fallback (
deck.flashcards?.length || 0) properly handles cases where the flashcards array might not be loaded or undefined.
34-44: Well-implemented conditional delete functionality with accessibility.The conditional rendering of the delete button with proper accessibility attributes (
aria-label) and visual feedback (opacity transition on hover) demonstrates good UX practices.src/lib/database.ts (2)
3-13: Standard Next.js Prisma singleton pattern implemented correctly.This is the recommended approach for Prisma client initialization in Next.js applications to prevent multiple instances during development hot reloading. The static analysis warning about redeclaration is a false positive - this pattern is explicitly documented in Prisma's Next.js guide.
7-9: Good logging configuration for development and debugging.The logging configuration for queries, errors, and warnings will be helpful for debugging database operations, especially during development.
src/lib/hooks/useOptimisticMutation.ts (3)
4-10: Well-designed interface with proper generic constraints.The interface design is clean and flexible, allowing for type-safe mutations with optional callback functions. The generic types
<T, R>properly represent input and return types.
22-49: Excellent implementation of optimistic update pattern.The hook correctly implements the optimistic update pattern:
- Applies optimistic update immediately
- Executes the mutation
- Handles success/error scenarios appropriately
- Reverts optimistic changes on error
- Provides user feedback via toast notifications
The error handling is robust, including type checking and proper error propagation.
49-49: Verify dependency array completeness.The dependency array includes all referenced variables. This is correct for preventing stale closures while avoiding unnecessary re-renders.
src/app/api/decks/save/route.ts (4)
11-17: Comprehensive input validation with Zod schema.The validation schema properly constrains input data with appropriate limits and requirements. The
satisfiesconstraint ensures type compatibility with the interface.
21-27: Proper authentication implementation with Clerk v6.The async
auth()call is correctly implemented for Clerk v6, and the unauthorized response properly returns structured error data matching theApiErrorinterface.
29-35: Good separation of concerns using service layer.The use of
flashcardService.createDeckWithFlashcards()properly abstracts the business logic away from the API route, making the code more maintainable and testable.
40-49: Excellent error handling with detailed validation feedback.The Zod error handling provides detailed field-level validation errors, which will be helpful for client-side form validation feedback. The error structure matches the
ApiErrorinterface consistently.src/app/layout.tsx (4)
6-7: Good fix on the ThemeProvider import path.The correction from
"../components/ThemeProvider"to a proper relative path resolves a potential import issue.
11-11: LGTM - Toaster import added correctly.The import follows the established pattern and is necessary for the toast notification system being integrated.
17-17: Typo fixed in metadata description.Good catch fixing "generatro" to "generator" in the application description.
32-32: Good cleanup removing trailing space.The className string cleanup improves code quality and the
cnutility properly handles class merging.src/utils/get-razorpay.ts (3)
7-27: Well-implemented script loading utility.The implementation includes all necessary safeguards:
- Early return if script already loaded
- Proper error handling with onload/onerror
- Duplicate prevention by checking existing script tags
- Clean Promise-based API
21-25: Excellent duplicate prevention logic.The check for existing script tags prevents unnecessary duplicate loads and potential conflicts. The attribute selector is precise and the immediate resolution is the correct behavior.
29-30: Good export pattern with clear documentation.Providing both named and default exports gives consumers flexibility in how they import the utility. The comment clearly explains the intention.
src/components/decks/DeckList.tsx (4)
1-12: Well-structured component with clear responsibility.The imports are clean, the interface is properly typed, and the single responsibility principle is explicitly documented. Good foundation for the component.
14-16: Good component setup with proper state management.The props destructuring is clean, and tracking the deletion state prevents concurrent deletion attempts. Toast integration is appropriate for user feedback.
51-59: Excellent empty state design.The empty state provides a user-friendly message that encourages action, with appropriate centered styling and muted text color for visual hierarchy.
61-72: Clean list rendering implementation.The deck mapping is properly implemented with correct key usage and prop passing. The spacing utility class provides good visual separation between cards.
src/components/ui/progress.tsx (4)
1-7: Clean imports with appropriate dependencies.The use of Radix UI provides accessibility out of the box, and the utility imports are all necessary for the component implementation.
8-11: Excellent TypeScript typing with proper ref forwarding.The forwardRef implementation correctly types the component with Radix UI primitives, ensuring full prop compatibility and proper ref handling.
12-19: Well-designed root container styling.The default styles provide a solid foundation with proper overflow handling and rounded corners. The class merging allows for customization while maintaining good defaults.
20-24: Correct progress calculation with smooth animations.The transform calculation properly handles the progress visualization, and the fallback to 0 for undefined values prevents errors. The transition provides good UX.
src/components/ui/badge.tsx (3)
6-24: Comprehensive variant system with excellent accessibility.The badge variants are well-designed with:
- Proper focus states for accessibility
- Consistent hover interactions
- Good color contrast following design system patterns
- Sensible default variant
26-28: Well-typed interface combining DOM and variant props.The interface properly extends HTML div attributes while including variant props, providing full type safety for consumers.
30-36: Clean component implementation with flexible exports.The Badge component properly handles prop spreading and class merging. Exporting both the component and variants provides flexibility for consumers who might need direct access to the variant classes.
src/app/api/generate/route.ts (6)
2-4: LGTM: Good modularization with proper importsThe imports properly separate concerns with Clerk authentication, service layer, and TypeScript types. This aligns well with the architectural improvements mentioned in the PR.
6-6: LGTM: Proper function signature with typed RequestThe function signature correctly uses the typed
Requestparameter instead ofany, improving type safety.
8-13: LGTM: Proper async authentication implementationThe authentication implementation correctly uses
awaitwith Clerk'sauth()function and provides a proper JSON error response for unauthorized access.
15-20: LGTM: Comprehensive input validationThe input validation properly checks for empty/whitespace-only text and returns appropriate error responses with consistent error structure.
22-26: LGTM: Clean service delegation with proper typingThe delegation to
flashcardService.generateFlashcardsfollows single responsibility principle and the response is properly typed withFlashcardGenerationResponse.
30-33: LGTM: Improved error handling with proper typingThe error handling provides detailed error messages while maintaining consistent API response structure with the
ApiErrortype.src/components/common/OptimisticButton.tsx (3)
8-17: LGTM: Well-designed component interfaceThe props interface is comprehensive and follows React best practices with proper TypeScript typing. The optional props have sensible defaults.
19-31: LGTM: Clean component setup with proper state managementThe component properly destructures props with defaults and uses appropriate state typing for the button states.
48-67: LGTM: Excellent UI feedback implementationThe button rendering logic provides clear visual feedback for all states with appropriate icons, colors, and disabled states. The conditional rendering and styling are well-implemented.
src/components/common/LoadingSpinner.tsx (3)
4-8: LGTM: Clean and flexible component interfaceThe props interface provides good flexibility with size variants, optional text, and custom className support.
10-31: LGTM: Well-implemented LoadingSpinner componentThe component properly implements size variants, conditional text rendering, and uses appropriate CSS classes for animations and styling. The use of the
cnutility for class merging is correct.
34-40: LGTM: Useful PageLoader componentThe
PageLoadercomponent provides a convenient wrapper for full-page loading states with sensible defaults and proper centering.src/components/ui/dropdown-menu.tsx (5)
30-30: LGTM: Improved spacing and SVG handlingThe addition of
gap-2provides better spacing between items, and[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0ensures consistent SVG behavior and sizing.
37-37: LGTM: Consistent icon positioningThe ChevronRight icon uses
ml-autofor proper right alignment in the submenu trigger.
50-50: LGTM: Enhanced animation and scroll behaviorThe addition of
origin-[--radix-dropdown-menu-content-transform-origin]and overflow handling improves the user experience with proper animation origins and scroll behavior.
68-68: LGTM: Consistent scroll and animation improvementsThe
max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hiddenand animation origin improvements provide better responsive behavior.
86-86: LGTM: Improved item styling consistencyThe styling improvements with
gap-2and SVG handling provide consistent spacing and icon behavior across dropdown menu items.src/components/flashcards/FlashcardSaveDialog.tsx (7)
17-22: LGTM: Well-defined component interfaceThe props interface clearly defines the required dependencies and callback functions for the dialog component.
24-32: LGTM: Proper component setup and hooks usageThe component correctly initializes state and uses the toast hook for user feedback.
34-42: LGTM: Good input validation with user feedbackThe validation properly checks for empty/whitespace-only deck names and provides clear error messaging via toast.
63-71: LGTM: Good success handling with proper cleanupThe success handling provides user feedback, resets state, and properly calls the callback functions.
72-82: LGTM: Comprehensive error handlingThe error handling provides detailed error messages and ensures loading state is properly reset in the finally block.
84-87: LGTM: Proper dialog cleanup on closeThe
handleClosefunction correctly resets form state when the dialog is closed.
89-131: LGTM: Well-structured dialog UI with proper accessibilityThe dialog implementation uses proper semantic HTML with labels, appropriate button states, and good user experience patterns. The form layout and button styling are consistent with the design system.
src/middleware.ts (2)
1-12: LGTM! Clean migration to Clerk v6 with proper route configuration.The import of
NextResponseand the route matcher configuration are correctly structured for the new async middleware pattern. The public routes are appropriately defined to exclude authentication requirements for sign-in/up flows, pricing, and public content.
28-34: Standard and correct middleware configuration.The matcher configuration properly excludes static assets while including API routes, which is essential for authentication enforcement across the application.
src/lib/analytics.ts (3)
12-20: Well-structured foundation for analytics tracking.The method correctly handles SSR by checking for
windowand provides a clean interface for event tracking. The console logging is appropriate for development, and the structure supports easy integration with production analytics services.
22-39: Excellent helper methods with good abstractions.The specialized methods provide clean interfaces for common analytics scenarios:
page()standardizes page view trackingtiming()enables performance monitoringerror()captures comprehensive error context including stack tracesThe consistent delegation to
track()maintains a unified event structure.
42-42: Good practice exporting a ready-to-use singleton instance.This provides a clean API for consumers and follows common patterns from popular libraries.
src/app/api/razorpay/create-order/route.ts (5)
32-86: Excellent authentication and validation implementation.The validation logic is comprehensive and secure:
- Proper Clerk v6 async authentication
- Robust plan and billing cycle validation against predefined constants
- Clear, informative error messages
- Safe amount lookup with fallback handling
The extensive logging will be valuable for debugging payment issues in production.
89-122: Excellent defensive programming with fallback strategies.The user management logic demonstrates good architectural thinking:
- Upsert pattern ensures user exists before payment processing
- Fallback logic handles potential schema evolution gracefully
- Continues processing even if user operations fail, which is appropriate since the user might already exist
- Clear logging distinguishes between warnings and errors
This resilient approach will handle database schema changes and edge cases well.
123-144: Solid Razorpay integration following best practices.The order creation is well-structured:
- Proper error handling around external API calls
- Comprehensive order metadata in notes for tracking
- Unique receipt generation with timestamp and user ID
- Appropriate error response for payment configuration issues
146-164: Appropriate error handling prioritizing payment flow.The payment record creation correctly treats database logging as non-critical:
- Uses warnings instead of errors to allow payment processing to continue
- Recognizes that the Razorpay order remains valid even if local record creation fails
- Good separation of concerns between payment processing and internal tracking
This approach ensures payment flows aren't broken by database issues.
166-193: Well-structured response and comprehensive error handling.The final response handling demonstrates good API design:
- Includes all necessary data for frontend Razorpay integration
- Comprehensive success logging aids debugging
- Generic error responses protect internal implementation details
- Appropriate HTTP status codes for different error scenarios
The extensive logging throughout the entire endpoint will be valuable for production troubleshooting.
src/components/common/ErrorBoundary.tsx (3)
8-31: Excellent React error boundary implementation.The component correctly implements the error boundary pattern:
- Proper TypeScript typing for props and state
getDerivedStateFromErrorfor clean state updatescomponentDidCatchfor side effects and logging- Optional
onErrorcallback enables integration with external error reportingFollows React best practices and official documentation patterns.
33-35: Good UX with retry functionality.The reset method provides users a way to recover from transient errors without requiring a full page refresh, improving the overall user experience.
37-81: Well-designed fallback UI with excellent UX considerations.The error boundary UI demonstrates thoughtful design:
- Respects custom fallback props for flexibility
- Consistent with the app's design system using Card components
- Development-only error details aid debugging without exposing sensitive info in production
- Multiple recovery options (retry vs. full refresh) give users control
- Appropriate visual hierarchy with icons and clear messaging
The accessibility and user experience are well-considered.
src/lib/utils/lazy-imports.ts (2)
5-21: All lazy-loaded component paths verifiedThe script confirms that all referenced files exist:
- src/components/core/flash-card.tsx
- src/components/core/generate.tsx
- src/components/FAQ.tsx
- src/app/pricing/page.tsx
No changes required.
23-32: ✔️ Confirmed dynamic import path exists; ready to merge– Verified that
src/utils/get-razorpay.tsis present and correctly imported.
– No further changes required.src/components/flashcards/FlashcardViewer.tsx (1)
39-54: Well-implemented keyboard navigationThe keyboard event handling is properly implemented with correct cleanup and dependencies. Good use of preventDefault for space key to avoid page scrolling.
src/app/api/razorpay/verify-payment/route.ts (1)
6-44: Solid authentication and validation implementationGood implementation with proper authentication checks, comprehensive field validation, and secure logging practices (truncating sensitive data).
next.config.js (2)
3-18: Proper Next.js 15 configuration migrationGood job updating the configuration for Next.js 15. The migration of
serverExternalPackagesandturbopackfrom experimental is correct, and the SVG handling setup is appropriate.
19-27: Good image optimization configurationExcellent choice supporting modern image formats (WebP and AVIF) for better performance and allowing Unsplash images.
src/components/ui/skeleton-cards.tsx (1)
1-108: Well-structured skeleton componentsExcellent implementation of skeleton loading states. The components accurately mirror their corresponding UI components with appropriate spacing and layout. Good use of the Skeleton primitive for consistent loading animations.
src/components/search/GlobalSearch.tsx (2)
39-94: Well-implemented search state managementGood implementation with debounced search, proper effect dependencies, and localStorage for recent searches. The useCallback optimization for performSearch is correctly implemented.
156-202: Clean filter implementationThe filter dropdown is well-implemented with proper state management using functional updates. Good UX with clear labels and icons.
src/components/flashcards/FlashcardGenerator.tsx (4)
1-25: Well-structured component setup with clear responsibility.The component follows good practices with typed props interface and clear single responsibility principle documentation.
26-44: Proper form validation and state management.Good implementation using zod schema validation with appropriate constraints and react-hook-form integration.
75-107: Clean and accessible form implementation.The component properly uses form UI components with good accessibility practices (labels, IDs) and comprehensive loading state management.
48-51: No action required:/api/generateexpects raw text
Confirmed that the API route usesawait req.text()to parse the body, so sendingbody: values.textis correct.src/app/api/search/route.ts (1)
70-98: Well-implemented deck search with useful metadata.Good implementation that includes flashcard counts and handles missing descriptions gracefully.
src/lib/services/flashcard.service.ts (5)
1-10: Clean service architecture with proper imports.Good separation of concerns with typed imports and clear documentation of additions.
35-62: Robust API integration with proper error handling.Good implementation with comprehensive response validation and error checking.
83-147: Excellent error handling and data validation.The implementation includes comprehensive JSON parsing with fallback strategies, thorough validation of flashcard structure, and user-friendly error messages for different failure scenarios.
149-235: Well-implemented deck operations with proper user validation.Excellent implementation with atomic deck/flashcard creation, comprehensive error handling that distinguishes authentication issues, and proper user existence validation.
237-302: Consistent implementation across all service methods.The remaining methods follow the established pattern with proper user validation, error handling, and logging. The singleton export is appropriate for this stateless service.
src/app/flashcards/[id]/page.tsx (3)
1-42: Proper hooks usage and comprehensive state management.Good implementation with all hooks at the top level (no conditional calls) and safe parameter extraction handling various formats.
44-106: Excellent implementation of navigation handlers and keyboard support.Proper use of useCallback with correct dependencies and comprehensive keyboard navigation with proper cleanup.
216-392: Excellent responsive UI with comprehensive interaction support.The implementation provides great UX with:
- Multiple navigation methods (buttons, keyboard, swipe)
- Responsive design with mobile-first considerations
- Clear mode switching UI
- Proper empty state handling
src/app/result/page.tsx (3)
17-50: Proper implementation of Suspense-compatible component structure.Excellent separation of the search params logic into ResultContent component, making it compatible with Suspense boundaries. Comprehensive error handling for all scenarios.
51-145: Excellent payment result UI with clear user guidance.The implementation provides:
- Clear visual feedback with appropriate icons and colors
- Helpful action buttons for each scenario
- Session ID display for support purposes
- Fully responsive design
147-187: Well-implemented loading states with Suspense.Great implementation of loading skeleton that matches the actual UI structure, providing smooth loading experience with React Suspense.
src/components/study/StudyMode.tsx (6)
1-22: Well-structured imports with proper optimization hooksGood use of
useCallbackfor performance optimization and comprehensive imports for UI components and icons.
23-29: Clean props interface definitionThe props interface is well-structured with appropriate types for the study mode functionality.
82-107: Excellent error handling and user feedback in session initializationGood implementation with proper useCallback usage, comprehensive error handling, and user-friendly toast notifications that allow continuing even if session initialization fails.
117-161: Robust answer handling with proper state managementWell-implemented answer submission with appropriate guards, error handling, and state updates. Good use of loading states to prevent double submissions.
198-291: Excellent UI implementation with comprehensive state handlingGreat attention to detail with loading states, empty states, responsive design, and accessibility. The proper apostrophe escaping and platform-specific text (tap vs click) show good UX consideration.
292-347: Well-designed answer interface with excellent mobile UXGreat implementation with color-coded difficulty buttons, responsive text, and helpful mobile gesture hints. The disabled state during submission prevents race conditions.
src/components/core/flash-card.tsx (4)
1-20: Clean component architecture with proper error handlingExcellent use of component composition, error boundaries, and suspense for a robust user experience.
16-28: Smart use of local storage for draft persistenceGreat implementation using localStorage to persist drafts, preventing data loss if users navigate away. The cleanup after successful save is properly handled.
29-69: Excellent component composition with proper loading and error statesGreat use of React patterns including ErrorBoundary, Suspense, and conditional rendering. The authentication check for save functionality is properly implemented.
70-81: Clean navigation implementationSimple and effective navigation to saved flashcards with consistent styling.
src/lib/services/payment.service.ts (2)
76-88: Good use of provider pattern with clear stub implementationThe stub Stripe provider properly implements the interface and clearly indicates it's not yet implemented. This follows SOLID principles well.
89-119: Excellent implementation of dependency injection and factory patternGreat use of SOLID principles with dependency injection and factory pattern. This makes the code easily extensible for new payment providers.
src/lib/services/user.service.ts (3)
55-82: Excellent error handling for unique constraint violationsGreat defensive programming with comprehensive error handling. The approach to update existing users when email conflicts occur is well thought out.
84-144: Well-implemented user synchronization with ClerkExcellent use of upsert pattern for idempotent synchronization. Good validation and comprehensive logging for debugging.
145-181: Clean utility methods with proper fallback handlingGood implementation of utility methods with appropriate error handling and fallback values. The display name logic handles edge cases well.
src/app/pricing/page.tsx (6)
1-41: Well-structured TypeScript interfaces and importsExcellent type definitions for pricing features and comprehensive imports for the payment flow implementation.
42-50: Clean state management setupGood initialization of all necessary hooks and state for the pricing page functionality.
110-187: Excellent payment flow implementation with comprehensive user feedbackGreat implementation with toast notifications at every step of the payment process. The delayed redirects ensure users see success messages.
225-371: Well-structured pricing data with clear feature differentiationExcellent organization of pricing tiers with clear feature lists and appropriate visual differentiation. The data structure is maintainable and easy to update.
372-464: Excellent UI implementation with clear visual hierarchyGreat implementation of the billing toggle and pricing cards. The popular plan highlighting and yearly savings calculations are well done.
465-581: Comprehensive feature display with excellent UX patternsGreat implementation with clear feature indicators, helpful tooltips, and trust-building elements. The loading states and CTAs are well-designed.
package.json (2)
6-6: Turbopack is still experimental – confirm local & CI parityRunning
next dev --turbopacklocally but falling back to the webpack-based compiler in production (next build) can yield hard-to-diagnose discrepancies.
Verify that:
- Every imported file type is supported by Turbopack (e.g. MDX, SVG as ReactComponent, PostCSS).
- Source-map support and env-expansion behave the same in both compilers.
If parity cannot be guaranteed, consider gating Turbopack behind an env flag until Next 15 makes it the default.
58-60: Duplicate type packages risk “multiple React versions” warnings
@types/react/@types/react-domare already transitively required byreact@19. Keeping explicit versions here (and again underpnpm.overrides) easily drifts out of sync and re-introduces the “Hooks can only be called inside the body of a function component” runtime error.Consider removing them from the top-level
devDependenciesand relying on React’s bundled types (or a single override if strictly necessary).
| onlyBuiltDependencies: | ||
| - '@clerk/shared' | ||
| - '@prisma/client' | ||
| - '@prisma/engines' | ||
| - prisma |
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.
💡 Verification agent
🧩 Analysis chain
Verify the onlyBuiltDependencies configuration purpose and package selection.
The onlyBuiltDependencies configuration restricts which dependencies get built in the workspace. Please verify:
- Whether this configuration is intended for performance optimization or compatibility reasons
- If these four packages (@clerk/shared, @prisma/client, @prisma/engines, prisma) are the complete set needed for your use case
- Whether other dependencies in the project might also need to be built
🏁 Script executed:
#!/bin/bash
# Verify pnpm workspace configuration and check for other potential dependencies that might need building
echo "Checking package.json for Prisma and Clerk related dependencies..."
cat package.json | jq '.dependencies, .devDependencies' | grep -E "(clerk|prisma)" || echo "No additional Clerk/Prisma deps found"
echo "Checking if there are other native dependencies that might need onlyBuiltDependencies..."
cat package.json | jq '.dependencies, .devDependencies' | grep -E "(node-gyp|native|canvas|sharp|sqlite)" || echo "No obvious native deps found"Length of output: 738
Update onlyBuiltDependencies to include all Clerk packages
Based on your package.json, the workspace config currently only lists @clerk/shared, but you’re also using:
@clerk/clerk-react@clerk/nextjs@clerk/themes
Without adding these, PNPM won’t build them in your workspace, which can lead to unexpected resolutions or missing code at runtime. Please update pnpm-workspace.yaml accordingly:
onlyBuiltDependencies:
- - '@clerk/shared'
+ - '@clerk/shared'
+ - '@clerk/clerk-react'
+ - '@clerk/nextjs'
+ - '@clerk/themes'
- '@prisma/client'
- '@prisma/engines'
- prismaNo other native or Prisma/Clerk packages were found in dependencies/devDependencies.
📝 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.
| onlyBuiltDependencies: | |
| - '@clerk/shared' | |
| - '@prisma/client' | |
| - '@prisma/engines' | |
| - prisma | |
| onlyBuiltDependencies: | |
| - '@clerk/shared' | |
| - '@clerk/clerk-react' | |
| - '@clerk/nextjs' | |
| - '@clerk/themes' | |
| - '@prisma/client' | |
| - '@prisma/engines' | |
| - prisma |
🤖 Prompt for AI Agents
In pnpm-workspace.yaml at lines 1 to 5, the onlyBuiltDependencies list includes
only '@clerk/shared' from Clerk packages but is missing '@clerk/clerk-react',
'@clerk/nextjs', and '@clerk/themes'. Update the onlyBuiltDependencies array to
add these three Clerk packages to ensure PNPM builds all necessary Clerk
packages in the workspace and prevents runtime issues.
| // ✅ NEW: Subscription & Payment fields | ||
| subscriptionPlan String? @default("free") // "free", "basic", "pro" | ||
| subscriptionCycle String? // "monthly", "yearly" | ||
| subscriptionStatus String? @default("inactive") // "active", "inactive", "cancelled", "expired" | ||
| subscriptionId String? // Razorpay subscription ID (if using subscriptions) | ||
| paymentId String? // Last successful payment ID | ||
| subscriptionEndsAt DateTime? // When current subscription expires | ||
| subscriptionStartedAt DateTime? // When subscription was activated | ||
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.
Free-text subscription fields invite data drift – use enums
subscriptionPlan, subscriptionCycle, and subscriptionStatus are declared as String?. Typos (‘mothly’, ‘montly’) will silently succeed and break billing logic. Define dedicated enums instead:
enum Plan { FREE BASIC PRO }
enum Cycle { MONTHLY YEARLY }
enum Status { ACTIVE INACTIVE CANCELLED EXPIRED }
subscriptionPlan Plan @default(FREE)
subscriptionCycle Cycle
subscriptionStatus Status @default(INACTIVE)🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 18 to 26, the subscriptionPlan,
subscriptionCycle, and subscriptionStatus fields are defined as optional
strings, which risks data drift due to typos. Replace these string fields with
enums by defining Plan, Cycle, and Status enums with the specified values, then
update the fields to use these enums with appropriate default values as shown in
the comment.
| flashcards Flashcard[] | ||
| decks Deck[] | ||
| studySessions StudySession[] | ||
| // ✅ NEW: Payment relation | ||
| payments Payment[] | ||
| } | ||
|
|
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
Relations reference clerkUserId but id remains primary – reconsider key strategy
Every child table now foreign-keys to User.clerkUserId. That’s fine, but:
- You still keep an unused surrogate PK
id. - Prisma cannot enforce ON UPDATE CASCADE for changes coming from Clerk because
clerkUserIdis not the primary key.
Simplify by making clerkUserId the PK (or use a composite [id, clerkUserId]) and drop the redundant string id if it’s never queried.
🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 30 to 37, the User model keeps an unused
surrogate primary key `id` while relations reference `clerkUserId`, which is not
the primary key. To fix this, make `clerkUserId` the primary key of the User
model or create a composite primary key combining `id` and `clerkUserId`. Remove
the redundant `id` field if it is never queried to simplify the schema and
enable Prisma to enforce ON UPDATE CASCADE properly.
| userId String // References User.clerkUserId | ||
| razorpayOrderId String @unique | ||
| razorpayPaymentId String? @unique // Null until payment is completed | ||
| razorpaySignature String? // Payment signature for verification | ||
| amount Int // Amount in paise (₹1 = 100 paise) | ||
| currency String @default("INR") | ||
| plan String // "basic", "pro" | ||
| billingCycle String // "monthly", "yearly" |
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.
Unique on nullable razorpayPaymentId can block multiple pending rows
PostgreSQL treats each NULL as distinct, but Prisma translates @unique to a regular unique constraint, meaning only one row with NULL is allowed. Until a payment is captured this column is NULL, so second attempts will violate the constraint.
Use @@index([razorpayPaymentId], map: "idx_payment_id_not_null") @db.NullsNotDistinct (Postgres 15+) or drop the uniqueness until after verification.
🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 100 to 107, the razorpayPaymentId field is
marked as @unique but is nullable, which causes issues because Prisma enforces a
unique constraint that does not allow multiple NULLs. To fix this, remove the
@unique attribute from razorpayPaymentId and instead add a conditional index
using @@index([razorpayPaymentId], map: "idx_payment_id_not_null") with the
@db.NullsNotDistinct attribute if using PostgreSQL 15 or later, or alternatively
drop the uniqueness constraint until the payment is verified.
| const flashcards = await prisma.flashcard.findMany({ | ||
| where: { | ||
| userId: userId, | ||
| deckId: params.id | ||
| }, | ||
| orderBy: { | ||
| createdAt: 'asc' | ||
| } | ||
| }); |
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.
💡 Verification agent
🧩 Analysis chain
Consider adding deck ownership verification.
While the current query filters by userId for flashcards, it doesn't verify that the user owns the deck itself. This could expose flashcards if deck IDs are predictable.
Verify if deck ownership should be validated:
🏁 Script executed:
#!/bin/bash
# Check if there are other API routes that validate deck ownership
rg -A 10 -B 5 "deckId.*params\.id" --type tsLength of output: 958
Ensure deck exists and is owned by the user before fetching flashcards
Add an explicit check at the top of your GET handler to verify the deck’s existence and ownership, returning a 404 if the deck doesn’t belong to the authenticated user. While filtering flashcards by userId prevents exposing another user’s cards, this makes the API’s behavior clear and distinguishes “no flashcards yet” from “deck not found.”
• File: src/app/api/flashcards/[id]/route.ts (before the prisma.flashcard.findMany call)
• Insert:
const deck = await prisma.deck.findUnique({
where: { id: params.id }
});
if (!deck || deck.userId !== userId) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}🤖 Prompt for AI Agents
In src/app/api/flashcards/[id]/route.ts around lines 18 to 26, before calling
prisma.flashcard.findMany, add a check to verify that the deck with id params.id
exists and is owned by the authenticated user (userId). Use
prisma.deck.findUnique to fetch the deck by id, then if the deck is null or its
userId does not match the authenticated userId, return a 404 response with an
error message. This ensures the API clearly distinguishes between a non-existent
or unauthorized deck and an empty flashcard list.
| constructor() { | ||
| const Razorpay = require('razorpay'); | ||
| this.razorpay = new Razorpay({ | ||
| key_id: process.env.RAZORPAY_KEY_ID!, | ||
| key_secret: process.env.RAZORPAY_KEY_SECRET!, | ||
| }); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add environment variable validation to prevent runtime errors
The constructor uses non-null assertions on environment variables without validation, which could cause runtime errors if they're not set.
constructor() {
const Razorpay = require('razorpay');
+
+ if (!process.env.RAZORPAY_KEY_ID || !process.env.RAZORPAY_KEY_SECRET) {
+ throw new Error('Razorpay environment variables are not configured');
+ }
+
this.razorpay = new Razorpay({
- key_id: process.env.RAZORPAY_KEY_ID!,
- key_secret: process.env.RAZORPAY_KEY_SECRET!,
+ key_id: process.env.RAZORPAY_KEY_ID,
+ key_secret: process.env.RAZORPAY_KEY_SECRET,
});
}📝 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.
| constructor() { | |
| const Razorpay = require('razorpay'); | |
| this.razorpay = new Razorpay({ | |
| key_id: process.env.RAZORPAY_KEY_ID!, | |
| key_secret: process.env.RAZORPAY_KEY_SECRET!, | |
| }); | |
| } | |
| constructor() { | |
| const Razorpay = require('razorpay'); | |
| if (!process.env.RAZORPAY_KEY_ID || !process.env.RAZORPAY_KEY_SECRET) { | |
| throw new Error('Razorpay environment variables are not configured'); | |
| } | |
| this.razorpay = new Razorpay({ | |
| key_id: process.env.RAZORPAY_KEY_ID, | |
| key_secret: process.env.RAZORPAY_KEY_SECRET, | |
| }); | |
| } |
🤖 Prompt for AI Agents
In src/lib/services/payment.service.ts around lines 17 to 24, the constructor
uses non-null assertions on environment variables without validating their
presence, risking runtime errors. Add explicit checks to verify that
process.env.RAZORPAY_KEY_ID and process.env.RAZORPAY_KEY_SECRET are defined
before using them. If either is missing, throw a clear error to prevent the
service from initializing with invalid configuration.
| const order = await this.razorpay.orders.create({ | ||
| amount: amount, | ||
| currency: 'INR', | ||
| receipt: `receipt_${Date.now()}`, | ||
| notes: { userId, plan, billingCycle: cycle } | ||
| }); | ||
|
|
||
| return { | ||
| orderId: order.id, | ||
| amount: order.amount, | ||
| currency: order.currency, | ||
| keyId: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID! | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for Razorpay API calls
The order creation doesn't handle potential Razorpay API errors, which could cause unhandled exceptions.
-const order = await this.razorpay.orders.create({
- amount: amount,
- currency: 'INR',
- receipt: `receipt_${Date.now()}`,
- notes: { userId, plan, billingCycle: cycle }
-});
+try {
+ const order = await this.razorpay.orders.create({
+ amount: amount,
+ currency: 'INR',
+ receipt: `receipt_${Date.now()}`,
+ notes: { userId, plan, billingCycle: cycle }
+ });
+
+ if (!process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID) {
+ throw new Error('Public Razorpay key not configured');
+ }
return {
orderId: order.id,
amount: order.amount,
currency: order.currency,
- keyId: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID!
+ keyId: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID
};
+} catch (error) {
+ console.error('Failed to create Razorpay order:', error);
+ throw new Error('Payment order creation failed');
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const order = await this.razorpay.orders.create({ | |
| amount: amount, | |
| currency: 'INR', | |
| receipt: `receipt_${Date.now()}`, | |
| notes: { userId, plan, billingCycle: cycle } | |
| }); | |
| return { | |
| orderId: order.id, | |
| amount: order.amount, | |
| currency: order.currency, | |
| keyId: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID! | |
| }; | |
| } | |
| try { | |
| const order = await this.razorpay.orders.create({ | |
| amount: amount, | |
| currency: 'INR', | |
| receipt: `receipt_${Date.now()}`, | |
| notes: { userId, plan, billingCycle: cycle } | |
| }); | |
| if (!process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID) { | |
| throw new Error('Public Razorpay key not configured'); | |
| } | |
| return { | |
| orderId: order.id, | |
| amount: order.amount, | |
| currency: order.currency, | |
| keyId: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID | |
| }; | |
| } catch (error) { | |
| console.error('Failed to create Razorpay order:', error); | |
| throw new Error('Payment order creation failed'); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/lib/services/payment.service.ts around lines 44 to 57, the Razorpay order
creation call lacks error handling, risking unhandled exceptions if the API call
fails. Wrap the order creation code in a try-catch block, catch any errors
thrown by the Razorpay API, and handle them appropriately by logging the error
and either rethrowing it or returning a meaningful error response.
| // Simple lazy loading for dynamic imports without complex fallbacks | ||
| export const createSimpleLazy = (importPath: string) => { | ||
| return lazy(() => import(importPath)); | ||
| }; |
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.
💡 Verification agent
🧩 Analysis chain
Consider removing or restricting this dynamic import helper.
The createSimpleLazy function has several potential issues:
- String-based import paths bypass TypeScript checking
- Can cause runtime errors with invalid paths
- May interfere with bundler static analysis and tree shaking
- Security risk if importPath comes from user input
Consider either:
- Removing this function if not actively used
- Restricting it to predefined paths with validation
- Using explicit lazy imports instead of dynamic string paths
🏁 Script executed:
#!/bin/bash
# Check if this function is actually used anywhere
echo "Searching for usage of createSimpleLazy..."
rg -n "createSimpleLazy" --type tsx --type ts --type jsx --type jsLength of output: 189
Let's re-run the search without file-type restrictions to locate any usages:
🏁 Script executed:
#!/bin/bash
echo "Searching for usage of createSimpleLazy in entire repo..."
rg -n "createSimpleLazy"Length of output: 240
Remove unused createSimpleLazy helper
We ran a repository-wide search for createSimpleLazy and only its own definition was found (no invocations). Since this utility:
- Bypasses TypeScript path validation
- Can introduce runtime import errors
- Hinders bundler optimizations and tree-shaking
- May pose a security risk if misused
and isn’t used anywhere, please remove it (or replace it with explicit, type-safe lazy imports).
• File to update: src/lib/utils/lazy-imports.ts (lines 34–37)
🤖 Prompt for AI Agents
In src/lib/utils/lazy-imports.ts between lines 34 and 37, remove the entire
createSimpleLazy function since it is unused and can cause issues with type
safety, runtime errors, bundler optimizations, and security. Delete these lines
to clean up the code and avoid potential problems.
| export interface DeckWithFlashcards extends PrismaDeck { | ||
| flashcards: PrismaFlashcard[]; | ||
| } | ||
|
|
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.
Name collision: two “Deck” types with different shapes
You re-export PrismaDeck as DeckWithFlashcards and declare a custom interface Deck. Downstream imports (import { Deck }) will pick the interface, not the Prisma model, potentially causing subtle type mismatches.
Either:
export { Deck as PrismaDeck } from '@prisma/client'; // keep original
export type DeckSummary = { …minimal fields… };or rename the custom one (DeckSummary) to avoid shadowing.
🤖 Prompt for AI Agents
In src/types/index.ts around lines 47 to 50, there is a naming collision between
the custom interface Deck and the PrismaDeck type, causing downstream imports of
Deck to refer to the interface instead of the Prisma model. To fix this, rename
the custom interface Deck to a distinct name like DeckSummary or another
descriptive name that avoids shadowing the PrismaDeck type, and update all
references accordingly to prevent type mismatches.
| export type SubscriptionPlan = 'Free' | 'Pro' | 'Organizational'; | ||
| export type BillingCycle = 'monthly' | 'yearly'; | ||
|
|
||
| export interface RazorpayOrderRequest { | ||
| plan: SubscriptionPlan; | ||
| billingCycle: BillingCycle; | ||
| } | ||
|
|
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
Enum/string mismatch – plan casing diverges from DB
'Free' | 'Pro' | 'Organizational' does not line up with lowercase 'free' | 'basic' | 'pro' used in the database & runtime. Normalise both sides, e.g.:
-export type SubscriptionPlan = 'Free' | 'Pro' | 'Organizational';
+export type SubscriptionPlan = 'free' | 'basic' | 'pro';🤖 Prompt for AI Agents
In src/types/index.ts around lines 90 to 97, the SubscriptionPlan type uses
capitalized strings ('Free', 'Pro', 'Organizational') that do not match the
lowercase strings ('free', 'basic', 'pro') used in the database and runtime.
Update the SubscriptionPlan type to use lowercase strings matching the database
values, and ensure any related code consuming this type is adjusted accordingly
for consistency.
Add Razorpay payments, study mode, search, and Prisma migration
this resolves #2 #3
#NEW
Summary by CodeRabbit
New Features
Enhancements
Bug Fixes
Chores
Documentation
Style
This release delivers a robust flashcard and subscription experience with improved performance, reliability, and user interaction.