Skip to content

Conversation

@huamanraj
Copy link
Collaborator

@huamanraj huamanraj commented Dec 4, 2025

fix #230

2025-12-06_12-59-05.mp4
2025-12-06_13-00-27.mp4

Summary by CodeRabbit

  • New Features

    • Full sponsorship flow: public sponsor landing, payment/subscription flow, image upload, submission form, sponsor listing/cards, dashboard spotlight.
  • Bug Fixes / Reliability

    • Secure, signature-verified webhook handling and reduced request body limits for safer payment processing.
  • Dependencies

    • Added Cloudinary and client-side form/validation libraries to support uploads and form validation.
  • Refactor

    • Payment–user relation relaxed to support optional associations.
  • Chores

    • Build scripts and image hosting configuration updated.

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

@vercel
Copy link

vercel bot commented Dec 4, 2025

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

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

Adds a Sponsor system: new Prisma Sponsor model and User relation, Cloudinary image uploads and upload endpoint, Razorpay order/subscription/payment flows with webhook handling, a TRPC sponsor router, and frontend sponsor pages/components for purchase, asset submission, and display.

Changes

Cohort / File(s) Summary
Database Schema
apps/api/prisma/schema.prisma
Added Sponsor model; made Payment.userId optional and Payment.user relation nullable; added sponsors Sponsor[] relation on User.
Backend: Entrypoint & Uploads
apps/api/src/index.ts
Added Cloudinary setup, multer import, reduced body size limits, new POST /upload/sponsor-image endpoint, and delegated /webhook/razorpay to handleRazorpayWebhook.
Backend: Webhooks
apps/api/src/webhooks.ts
New handleRazorpayWebhook handler verifying Razorpay signatures, dispatching events (payment.captured, subscription.charged, subscription status changes), and updating payments/sponsors.
Backend: Sponsor Router
apps/api/src/routers/sponsor.ts, apps/api/src/routers/_app.ts
New sponsorRouter with public procedures: uploadImage, createSubscription, verifyPayment, submitAssets, getActiveSponsors; _app.ts exposes sponsor: sponsorRouter.
Backend: Dependency
apps/api/package.json
Added cloudinary dependency.
Frontend: Sponsor Pages & Layout
apps/web/src/app/(main)/sponsor/page.tsx, apps/web/src/app/(main)/sponsor/submit/page.tsx, apps/web/src/app/(main)/sponsor/layout.tsx
New sponsor landing page with Razorpay flow, submission page that mounts SponsorForm via paymentId query, and sponsor layout component.
Frontend: Sponsor Components
apps/web/src/components/sponsor/SponsorCard.tsx, apps/web/src/components/sponsor/SponsorForm.tsx
Added SponsorCard and SponsorForm (react-hook-form + zod); form supports client image upload (base64 → TRPC → Cloudinary) and submits assets to backend.
Frontend: Landing & Dashboard Integration
apps/web/src/components/landing-sections/SponsorSection.tsx, apps/web/src/app/(main)/(landing)/page.tsx, apps/web/src/components/dashboard/DashboardSponsorList.tsx, apps/web/src/components/dashboard/DashboardContainer.tsx
Inserted SponsorSection on landing page; added DashboardSponsorList and injected sponsor block into DashboardContainer.
Frontend: Config & UI tweaks
apps/web/package.json, apps/web/next.config.js, apps/web/src/components/ui/custom-button.tsx, apps/web/src/components/ui/accordion.tsx, apps/web/src/components/landing-sections/Hero.tsx
Added react-hook-form, @hookform/resolvers, zod deps and build:with-premium script; allowed Cloudinary remote images; PrimaryButton supports disabled; minor formatting/ui tweaks.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Browser)
    participant Frontend as Frontend (Next.js)
    participant Backend as Backend (TRPC/Express)
    participant Razorpay as Razorpay
    participant Cloudinary as Cloudinary
    participant DB as Database (Prisma)

    rect rgb(200,220,255)
    note over User,Frontend: Purchase flow
    User->>Frontend: Click "Become a Sponsor"
    Frontend->>Backend: trpc.createSubscription()
    Backend->>Razorpay: Create order/subscription
    Razorpay-->>Backend: Order/Subscription details
    Backend-->>Frontend: orderId, amount, currency, key
    Frontend->>Razorpay: Open checkout & complete payment
    Razorpay->>Backend: Webhook (payment.captured)
    Backend->>DB: Upsert Payment
    Backend->>DB: Create Sponsor (plan_status=pending_submission)
    end

    rect rgb(220,255,220)
    note over User,Cloudinary: Asset submission
    Frontend->>User: Redirect to /sponsor/submit?paymentId=...
    User->>Frontend: Upload image + submit form
    Frontend->>Backend: trpc.uploadImage(base64)
    Backend->>Cloudinary: Upload image
    Cloudinary-->>Backend: Image URL
    Backend-->>Frontend: { url }
    Frontend->>Backend: trpc.submitAssets(..., imageUrl, paymentId)
    Backend->>DB: Update Sponsor (details + plan_status=active)
    Backend-->>Frontend: Success
    end

    rect rgb(255,240,200)
    note over Razorpay,Backend: Subscription lifecycle
    Razorpay->>Backend: Webhook (subscription.charged / cancelled / pending / failed)
    Backend->>DB: Update Sponsor.plan_status by razorpay_sub_id
    DB-->>Backend: Acknowledged
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing careful review:
    • apps/api/src/webhooks.ts — signature verification, idempotency, event→status mapping.
    • apps/api/src/routers/sponsor.ts — payment verification, upsert semantics, error handling with external services.
    • apps/api/prisma/schema.prisma — migration implications from making Payment.userId nullable and adding Sponsor model.
    • Frontend/backend contract surfaces (TRPC zod schemas and returned payload shapes).

Possibly related PRs

Poem

🐰 I hopped through code with a carrot grin,
Coins chimed true and logos tucked in,
Cloudinary caught each glossy art,
Razorpay hummed and played its part,
Sponsors now bloom on pages — what a win! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Feat/sponsor' is vague and uses generic phrasing that doesn't convey meaningful information about the changeset. Use a more descriptive title like 'Add sponsor system with Razorpay integration and asset uploads' to clearly summarize the primary changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements all primary objectives from issue #230: Sponsor model with required fields, Razorpay webhook handlers, Cloudinary image uploads, frontend sponsor pages, submission forms, and active sponsor display on landing and dashboard.
Out of Scope Changes check ✅ Passed All changes directly support the sponsor feature: database schema, API endpoints, webhook handlers, frontend components, and configuration updates are all within scope of issue #230.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cla-assistant
Copy link

cla-assistant bot commented Dec 4, 2025

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

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (29)
apps/web/src/app/(main)/sponsor/layout.tsx-1-2 (1)

1-2: Fix import order to follow coding guidelines.

Imports should be ordered: React → third-party → local components → utils → types.

As per coding guidelines.

Apply this diff to fix the import order:

-import Navbar from "@/components/landing-sections/navbar";
 import React from "react";
+import Navbar from "@/components/landing-sections/navbar";
apps/api/prisma/schema.prisma-106-116 (1)

106-116: Fix naming convention inconsistency.

The Sponsor model uses snake_case for field names (company_name, image_url, razorpay_payment_id, etc.) while all other models in the schema use camelCase (firstName, createdAt, razorpayPaymentId, etc.). Maintain consistent naming conventions across the schema.

Apply this diff:

 model Sponsor {
   id                String   @id @default(cuid())
-  company_name      String
+  companyName       String
   description       String
   website           String
-  image_url         String
-  razorpay_payment_id String?
-  razorpay_sub_id   String?
-  plan_status       String   // active, cancelled, pending_payment, pending_submission, failed
-  created_at        DateTime @default(now())
+  imageUrl          String
+  razorpayPaymentId String?
+  razorpaySubId     String?
+  planStatus        String   // active, cancelled, pending_payment, pending_submission, failed
+  createdAt         DateTime @default(now())
 }
apps/web/src/components/dashboard/DashboardSponsorList.tsx-26-26 (1)

26-26: Replace hardcoded color with design token.

Use semantic color names from the design token system instead of hardcoded values like text-white.

As per coding guidelines.

Apply this diff:

-        <h2 className="text-xl font-semibold text-white">Featured Sponsors</h2>
+        <h2 className="text-xl font-semibold text-text-primary">Featured Sponsors</h2>
apps/web/src/components/dashboard/DashboardSponsorReminder.tsx-18-37 (1)

18-37: Replace hardcoded colors with design system tokens.

Multiple hardcoded color values (yellow-500, yellow-400, neutral-400, text-white, text-black) violate the design system guidelines. Use semantic color names from the design token system for warnings and alerts.

As per coding guidelines.

Consider using semantic warning/alert colors from the design system. For example:

-    <div className="w-full mb-6 bg-yellow-500/10 border border-yellow-500/20 rounded-xl p-4 flex items-center justify-between">
+    <div className="w-full mb-6 bg-warning-surface border border-warning-border rounded-xl p-4 flex items-center justify-between">
       <div className="flex items-center gap-3">
-        <div className="p-2 bg-yellow-500/20 rounded-lg">
-          <AlertTriangle className="w-5 h-5 text-yellow-500" />
+        <div className="p-2 bg-warning-surface-hover rounded-lg">
+          <AlertTriangle className="w-5 h-5 text-warning-text" />
         </div>
         <div>
-          <h3 className="text-white font-medium">Complete your sponsorship</h3>
-          <p className="text-neutral-400 text-sm">
+          <h3 className="text-text-primary font-medium">Complete your sponsorship</h3>
+          <p className="text-text-secondary text-sm">
             You have a pending sponsorship payment. Upload your assets to go
             live.
           </p>
         </div>
       </div>
       <Link
         href={`/sponsor/submit?paymentId=${pendingSponsorship.paymentId}`}
-        className="px-4 py-2 bg-yellow-500 text-black font-medium rounded-lg text-sm hover:bg-yellow-400 transition-colors"
+        className="px-4 py-2 bg-warning-accent text-warning-accent-text font-medium rounded-lg text-sm hover:bg-warning-accent-hover transition-colors"
       >
         Complete Setup
       </Link>

Note: Verify the exact warning/alert color tokens available in your design system at apps/web/src/lib/design-tokens.ts.

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

apps/web/src/components/dashboard/DashboardSponsorList.tsx-29-29 (1)

29-29: Replace hardcoded green colors with design system colors.

Use semantic color names from the design token system instead of hardcoded values like text-green-400 and text-green-300. Consider using text-brand-purple or another semantic color from the design system.

As per coding guidelines.

Apply this diff:

-          className="text-sm text-green-400 hover:text-green-300"
+          className="text-sm text-brand-purple hover:text-brand-purple/80"
apps/web/src/components/dashboard/DashboardSponsorReminder.tsx-9-11 (1)

9-11: Remove any type assertion for type safety.

The type assertion (as any) bypasses TypeScript's type checking. The TRPC query should be properly typed.

As per coding guidelines.

Apply this diff:

-  const { data: pendingSponsorship, isLoading } = (
-    trpc.sponsor.getPendingSponsorship as any
-  ).useQuery();
+  const { data: pendingSponsorship, isLoading } =
+    trpc.sponsor.getPendingSponsorship.useQuery();
apps/web/src/components/dashboard/DashboardSponsorList.tsx-42-53 (1)

42-53: Replace hardcoded neutral colors with design system tokens.

Multiple hardcoded color values (border-neutral-800, bg-neutral-900/20, bg-neutral-800, text-neutral-500, text-neutral-400) violate the design system guidelines. Use semantic color names from the design token system.

As per coding guidelines.

Apply this diff:

           <Link href="/sponsor" className="group block h-full">
-            <div className="aspect-[16/10] w-full h-full rounded-2xl border border-dashed border-neutral-800 bg-neutral-900/20 hover:bg-neutral-900/40 hover:border-neutral-700 transition-all duration-300 flex flex-col items-center justify-center gap-3 p-6">
-              <div className="w-12 h-12 rounded-full bg-neutral-800 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
-                <span className="text-xl text-neutral-500">+</span>
+            <div className="aspect-[16/10] w-full h-full rounded-2xl border border-dashed border-dash-border bg-surface-secondary/20 hover:bg-surface-secondary/40 hover:border-dash-border/70 transition-all duration-300 flex flex-col items-center justify-center gap-3 p-6">
+              <div className="w-12 h-12 rounded-full bg-surface-secondary flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
+                <span className="text-xl text-text-muted">+</span>
               </div>
               <div className="text-center">
-                <h3 className="text-neutral-400 font-medium text-sm group-hover:text-white transition-colors">
+                <h3 className="text-text-muted font-medium text-sm group-hover:text-text-primary transition-colors">
                   Your Ad Here
                 </h3>
               </div>
apps/api/src/routers/sponsor.ts-30-43 (1)

30-43: Persist order information to database.

The created Razorpay order is not saved to the database, which could make it difficult to track payment flow or debug issues.

Consider saving the order to a database table before returning it to the client:

const order = await paymentService.createOrder({...});

if ("error" in order) {
  throw new Error(order.error.description);
}

// Save order to DB
await prisma.order.create({
  data: {
    razorpay_order_id: order.id,
    user_id: user.id,
    amount: order.amount,
    currency: order.currency,
    status: 'created',
  },
});

return {...};
apps/web/src/components/sponsor/SponsorForm.tsx-55-82 (1)

55-82: Remove extensive TODO/implementation comments.

These comments indicate uncertainty and incomplete implementation, making the code harder to maintain.

Remove or consolidate the speculative comments. If the upload implementation is working as intended, clean up the commentary. If issues remain, create a GitHub issue to track them.

apps/web/src/app/(main)/sponsor/page.tsx-28-29 (1)

28-29: Replace alert() with proper toast notifications.

Using alert() provides poor user experience and blocks the UI.

Consider using a toast notification library or custom toast component for better UX.

     onFailure: (error) => {
       console.error("Payment failed:", error);
-      alert("Payment failed: " + error.message);
+      // TODO: Show toast notification instead
       setLoading(false);
     },
     onError: (error: any) => {
       console.error("Subscription creation failed:", error);
-      alert("Failed to initiate payment. Please try again.");
+      // TODO: Show toast notification instead
       setLoading(false);
     },

Also applies to: 59-60

apps/api/src/webhooks.ts-66-94 (1)

66-94: Replace any types with proper TypeScript interfaces.

Using any for payload types bypasses type safety.

As per coding guidelines, avoid any type; use proper interfaces for Razorpay webhook payloads.

Define interfaces:

interface RazorpayPaymentEntity {
  id: string;
  amount: number;
  currency: string;
  description?: string;
  // add other fields as needed
}

interface RazorpaySubscriptionEntity {
  id: string;
  status: string;
  // add other fields as needed
}

interface RazorpayWebhookPayload {
  payment?: { entity: RazorpayPaymentEntity };
  subscription?: { entity: RazorpaySubscriptionEntity };
}

async function handleSubscriptionCharged(payload: RazorpayWebhookPayload) {
  // implementation
}
apps/api/src/routers/sponsor.ts-16-16 (1)

16-16: Replace any types with proper TypeScript types.

Multiple instances of any type bypass TypeScript's type safety.

As per coding guidelines, avoid any type. Define proper interfaces for ctx and input parameters.

Example for createSubscription:

interface CreateSubscriptionInput {
  planId: string;
}

interface ProtectedContext {
  user: {
    id: string;
    email: string;
    // other user fields
  };
}

.mutation(async ({ ctx, input }: { ctx: ProtectedContext, input: CreateSubscriptionInput }) => {

Apply similar typing for all procedures.

Also applies to: 40-40, 58-58, 65-65, 111-111

apps/web/src/components/sponsor/SponsorForm.tsx-109-112 (1)

109-112: Replace alert() with proper form validation feedback.

Using alert() provides poor UX.

Use form error state instead:

  const onSubmit = (values: z.infer<typeof formSchema>) => {
     if (!imageUrl) {
-      alert("Please upload an image");
+      form.setError("root", {
+        type: "manual",
+        message: "Please upload a company logo",
+      });
       return;
     }

Then display the error in the JSX:

{form.formState.errors.root && (
  <p className="text-xs text-red-500 mt-1">
    {form.formState.errors.root.message}
  </p>
)}
apps/web/src/components/landing-sections/SponsorSection.tsx-22-22 (1)

22-22: Replace hardcoded hex values with design system tokens.

The code uses hardcoded hex values #252525 and #0A0A0A directly, which violates the design system guidelines.

As per coding guidelines, always use Tailwind classes from the design token system instead of hardcoded hex values. Replace with semantic color names like border-neutral-800 and bg-surface-primary.

Apply this diff:

-    <section className="w-full py-20 px-4 md:px-8 border-b border-[#252525] bg-[#0A0A0A]">
+    <section className="w-full py-20 px-4 md:px-8 border-b border-neutral-800 bg-neutral-950">
apps/web/src/components/sponsor/SponsorForm.tsx-101-102 (1)

101-102: Provide user feedback for upload errors.

Upload errors are only logged to console without user notification.

Show user-friendly error messages:

     } catch (error) {
       console.error("Upload failed", error);
+      form.setError("root", {
+        type: "manual", 
+        message: "Failed to upload image. Please try again.",
+      });
     } finally {
apps/web/src/components/sponsor/SponsorForm.tsx-52-52 (1)

52-52: Use environment variable for upload preset.

Hardcoding the upload preset is a security risk and lacks flexibility.

-    formData.append("upload_preset", "opensox_sponsor"); // Make sure this preset exists or use signed upload
+    const uploadPreset = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET;
+    if (!uploadPreset) {
+      throw new Error("Cloudinary upload preset not configured");
+    }
+    formData.append("upload_preset", uploadPreset);

Note: The preset is already used on line 85, so remove the hardcoded one on line 52.

apps/web/src/app/(main)/sponsor/page.tsx-76-348 (1)

76-348: Multiple hardcoded hex values violate design system.

The component contains numerous hardcoded hex colors throughout (e.g., #0A0A0A, #252525, #555).

As per coding guidelines, all colors should use Tailwind classes from the design token system. Review lines 121, 165, 171, 207, 220, 233, 246, 296 and replace all hex values with appropriate design tokens like bg-neutral-950, border-neutral-800, etc.

apps/api/src/routers/sponsor.ts-65-108 (1)

65-108: Use database transaction for atomic operations.

The submitAssets procedure performs multiple database operations (read, upsert) without transaction guarantees, risking data inconsistency.

Wrap the database operations in a transaction:

.mutation(async ({ ctx, input }: { ctx: any, input: any }) => {
  return await prisma.$transaction(async (tx) => {
    // Verify payment
    const payment = await tx.payment.findUnique({
      where: { razorpayPaymentId: input.razorpayPaymentId },
    });

    if (!payment || payment.status !== "captured") {
      throw new Error("Valid payment not found");
    }

    if (payment.userId !== ctx.user.id) {
      throw new Error("Unauthorized");
    }

    // Upsert sponsor
    const existingSponsor = await tx.sponsor.findFirst({
      where: { razorpay_payment_id: input.razorpayPaymentId },
    });

    if (existingSponsor) {
      return await tx.sponsor.update({...});
    } else {
      return await tx.sponsor.create({...});
    }
  });
}),
apps/api/src/webhooks.ts-60-62 (1)

60-62: Add structured error logging with context.

The error logging lacks context like endpoint, timestamp, and event type.

As per coding guidelines for API code, log errors with context (userId, endpoint, timestamp) for debugging.

Apply this diff:

     } catch (error: any) {
-        console.error("Webhook error:", error);
+        console.error("Webhook processing failed:", {
+            error: error.message,
+            endpoint: "/api/razorpay/webhook",
+            timestamp: new Date().toISOString(),
+            eventType: req.body?.event
+        });
         return res.status(500).json({ error: "Internal server error" });
     }
apps/web/src/app/(main)/sponsor/submit/page.tsx-15-18 (1)

15-18: Remove commented-out code.

The redirect logic is commented out. Either implement it or remove it to keep the code clean.

As per coding guidelines, remove unused code.

Apply this diff:

  useEffect(() => {
    setMounted(true);
-    if (!paymentId) {
-      // If no payment ID, redirect back to sponsor page
-      // router.push("/sponsor");
-    }
  }, [paymentId, router]);
apps/api/src/routers/sponsor.ts-111-138 (1)

111-138: Optimize database queries to avoid N+1 problem.

The nested loop performs a database query for each payment, which is inefficient.

Refactor to fetch all relevant data in a single query:

getPendingSponsorship: protectedProcedure.query(async ({ ctx }: { ctx: any }) => {
  const pendingPayment = await prisma.payment.findFirst({
    where: {
      userId: ctx.user.id,
      status: "captured",
      subscriptionId: { not: null },
      sponsor: null, // assumes relation exists
    },
    orderBy: { createdAt: "desc" },
    include: {
      sponsor: true,
    },
  });

  if (pendingPayment && !pendingPayment.sponsor) {
    return {
      paymentId: pendingPayment.razorpayPaymentId,
      amount: pendingPayment.amount,
      date: pendingPayment.createdAt,
    };
  }

  return null;
}),

Note: This assumes a relation between Payment and Sponsor models.

apps/web/src/components/landing-sections/SponsorSection.tsx-44-58 (1)

44-58: Add aria-label to the sponsor Link for accessibility.

The Link element lacks an aria-label, which is important for screen readers.

As per coding guidelines, include proper aria labels for accessibility.

Apply this diff:

-          {(!hasSponsors || sponsors.length < 3) && (
-            <Link href="/sponsor" className="group block h-full">
+          {(!hasSponsors || sponsors.length < 3) && (
+            <Link 
+              href="/sponsor" 
+              className="group block h-full"
+              aria-label="Become a sponsor of OpenSox"
+            >

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

apps/web/src/app/(main)/sponsor/page.tsx-52-52 (1)

52-52: Replace hardcoded hex value with design system token.

As per coding guidelines, never use hardcoded hex values directly.

         theme: {
-          color: "#4dd0a4",
+          color: "rgb(77, 208, 164)", // reference from design tokens
         },

Note: Razorpay requires hex/rgb format, so you may need to extract the actual color value from your design tokens.

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

apps/web/src/app/(main)/(landing)/page.tsx-17-19 (1)

17-19: Replace hardcoded hex values with design system tokens.

The code uses hardcoded hex values #101010 and #252525, which violates the design system guidelines.

As per coding guidelines, always use Tailwind classes from the design token system.

Apply this diff:

-      <main className="min-h-screen w-full bg-[#101010] text-white font-sans overflow-hidden relative">
+      <main className="min-h-screen w-full bg-neutral-950 text-white font-sans overflow-hidden relative">
       <Navbar />
-      <div className="min-h-screen w-full max-w-[2000px] mx-auto border-x border-[#252525] overflow-hidden">
+      <div className="min-h-screen w-full max-w-[2000px] mx-auto border-x border-neutral-800 overflow-hidden">
apps/web/src/components/sponsor/SponsorForm.tsx-29-29 (1)

29-29: Remove unsafe as any type cast.

As per coding guidelines, avoid any type.

-  const submitAssetsMutation = (trpc.sponsor.submitAssets as any).useMutation({
+  const submitAssetsMutation = trpc.sponsor.submitAssets.useMutation({
apps/web/src/app/(main)/sponsor/submit/page.tsx-32-32 (1)

32-32: Replace hardcoded hex value with design system token.

The code uses #4dd0a4 directly, which violates the design system guidelines.

As per coding guidelines, use Tailwind classes from the design token system.

Apply this diff:

-          className="bg-[#4dd0a4] text-black font-bold py-2 px-6 rounded-lg"
+          className="bg-brand-green text-black font-bold py-2 px-6 rounded-lg"
apps/web/src/components/sponsor/SponsorForm.tsx-209-210 (1)

209-210: Replace hardcoded hex values with design system tokens.

As per coding guidelines, use Tailwind classes from the design token system.

-          className="w-full bg-[#4dd0a4] text-black font-bold py-3 rounded-xl hover:bg-[#3db08a] transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
+          className="w-full bg-brand-green text-black font-bold py-3 rounded-xl hover:bg-brand-green-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
apps/web/src/components/sponsor/SponsorForm.tsx-162-166 (1)

162-166: Add aria-label to input fields.

Form inputs lack proper accessibility labels.

As per coding guidelines, include proper aria labels for accessibility. While you have visual <label> elements, ensure they're properly associated:

           <input
+            id="companyName"
             {...form.register("companyName")}
             className="w-full bg-neutral-800 border border-neutral-700 rounded-lg px-4 py-2 text-white focus:ring-2 focus:ring-green-500 outline-none"
             placeholder="Acme Corp"
+            aria-describedby="companyName-error"
           />

And update the label:

-          <label className="block text-sm font-medium text-neutral-400 mb-2">
+          <label htmlFor="companyName" className="block text-sm font-medium text-neutral-400 mb-2">
             Company Name
           </label>

Apply similar changes to other input fields.

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

apps/web/src/app/(main)/sponsor/page.tsx-37-39 (1)

37-39: Remove unsafe as any type cast.

Using as any bypasses TypeScript's type safety and should be avoided.

As per coding guidelines, avoid any type. Use proper TypeScript types.

Apply this diff:

-  const createSubscriptionMutation = (
-    trpc.sponsor.createSubscription as any
-  ).useMutation({
+  const createSubscriptionMutation = trpc.sponsor.createSubscription.useMutation({
🟡 Minor comments (1)
apps/web/src/app/(main)/sponsor/page.tsx-28-29 (1)

28-29: Avoid exposing error details to users.

Displaying raw error messages in alerts can expose sensitive implementation details.

Consider showing a generic user-friendly message instead of error.message.

-      alert("Payment failed: " + error.message);
+      // Show generic message: "Payment failed. Please try again."

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

🧹 Nitpick comments (15)
apps/web/src/components/ui/custom-button.tsx (2)

3-5: Reorder imports to follow the project conventions.

The imports should be organized in the order: React → third-party → local components → utils → types. Currently, the local utils import appears before the third-party import.

Apply this diff to fix the import order:

 "use client";

-import { cn } from "@/lib/utils";
 import { motion } from "framer-motion";
 import type React from "react";
+import { cn } from "@/lib/utils";

As per coding guidelines, imports should follow the order: React → third-party → local components → utils → types.


7-19: Define a separate type for component props.

The props should be defined as a separate type or interface rather than inline with the destructuring. This improves maintainability and follows the project's TypeScript conventions.

Apply this diff to extract the props type:

+type PrimaryButtonProps = {
+  children: React.ReactNode;
+  animate?: boolean;
+  classname?: string;
+  onClick?: () => void;
+  disabled?: boolean;
+};
+
-const PrimaryButton = ({
-  children,
-  animate = true,
-  classname,
-  onClick,
-  disabled,
-}: {
-  children: React.ReactNode;
-  animate?: boolean;
-  classname?: string;
-  onClick?: () => void;
-  disabled?: boolean;
-}) => {
+const PrimaryButton = ({
+  children,
+  animate = true,
+  classname,
+  onClick,
+  disabled,
+}: PrimaryButtonProps) => {

As per coding guidelines, const functions should have defined types.

apps/web/src/components/ui/accordion.tsx (1)

23-41: align header text color with design tokens instead of text-white

accordionprimitive.header now uses className="flex text-white". design rules prefer semantic, design-token-based color classes (for example text-text-primary or other tokenized variants) instead of raw tailwind palette names like text-white. please consider switching this to the appropriate semantic text token (or reverting to the project’s token you were using before, if that was intentional) so the accordion trigger header stays aligned with the design system.

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

114-114: Consider using an enum for type-safe plan status.

The planStatus field uses a String type with a comment listing possible values. Consider defining a SponsorStatus enum similar to PaymentStatus and SubscriptionStatus for better type safety and validation at the database level.

Add an enum definition:

+enum SponsorStatus {
+  active
+  cancelled
+  pending_payment
+  pending_submission
+  failed
+}
+
 model Sponsor {
   id                String   @id @default(cuid())
   companyName       String
   description       String
   website           String
   imageUrl          String
   razorpayPaymentId String?
   razorpaySubId     String?
-  planStatus        String   // active, cancelled, pending_payment, pending_submission, failed
+  planStatus        SponsorStatus
   createdAt         DateTime @default(now())
 }

112-113: Consider adding indexes for frequently queried fields.

If razorpayPaymentId and razorpaySubId are frequently used in queries (as suggested by the sponsor router code), consider adding indexes to improve query performance.

 model Sponsor {
   id                String   @id @default(cuid())
   companyName       String
   description       String
   website           String
   imageUrl          String
   razorpayPaymentId String?  @unique
   razorpaySubId     String?
   planStatus        SponsorStatus
   createdAt         DateTime @default(now())
+
+  @@index([razorpayPaymentId])
+  @@index([razorpaySubId])
 }
apps/web/src/components/landing-sections/SponsorSection.tsx (3)

8-10: Define return type for the component function.

The component is defined as an arrow function without an explicit return type.

As per coding guidelines, define a type when defining const functions for better type safety.

Apply this diff:

-const SponsorSection = () => {
+const SponsorSection: React.FC = () => {

12-14: Add loading skeleton instead of returning null.

Returning null during loading creates a layout shift and poor UX. Consider showing a skeleton placeholder.

  if (isLoading) {
-    return null; // Or a skeleton
+    return (
+      <section className="w-full py-20 px-4 md:px-8 border-b border-neutral-800 bg-neutral-950">
+        <div className="max-w-7xl mx-auto">
+          <div className="text-center mb-12">
+            <div className="h-10 bg-neutral-800 rounded w-48 mx-auto mb-4 animate-pulse" />
+            <div className="h-6 bg-neutral-800 rounded w-64 mx-auto animate-pulse" />
+          </div>
+          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+            {[1, 2, 3].map((i) => (
+              <div key={i} className="aspect-[16/10] bg-neutral-800 rounded-2xl animate-pulse" />
+            ))}
+          </div>
+        </div>
+      </section>
+    );
  }

9-10: Add error handling for the TRPC query.

The query lacks error handling, which could result in undefined behavior if the request fails.

  const { data: sponsors, isLoading } =
    trpc.sponsor.getActiveSponsors.useQuery();
+
+  // handle error state
+  if (!isLoading && !sponsors) {
+    return null;
+  }
apps/web/src/app/(main)/sponsor/submit/page.tsx (1)

7-7: Define return type for the component function.

As per coding guidelines, define a type when defining const functions.

-const SponsorSubmitPage = () => {
+const SponsorSubmitPage: React.FC = () => {
apps/web/src/app/(main)/(landing)/page.tsx (1)

15-15: Define return type for the component function.

As per coding guidelines, define a type when defining const functions.

-const Landing = () => {
+const Landing: React.FC = () => {
apps/web/src/app/(main)/sponsor/page.tsx (1)

18-18: Define return type for the component function.

As per coding guidelines, define a type when defining const functions.

-const SponsorPage = () => {
+const SponsorPage: React.FC = () => {
apps/web/src/components/sponsor/SponsorCard.tsx (1)

19-24: Consider adding aria-label for better accessibility.

While the Link has proper rel attributes, an explicit aria-label would improve screen reader experience.

As per coding guidelines, include proper aria labels for accessibility.

     <Link
       href={sponsor.website}
       target="_blank"
       rel="noopener noreferrer"
       className="group block"
+      aria-label={`Visit ${sponsor.company_name} website`}
     >
apps/api/src/webhooks.ts (2)

86-93: Validate database operation success.

The updateMany operation doesn't check if any records were actually updated, which could indicate a data inconsistency.

-    await prisma.sponsor.updateMany({
+    const result = await prisma.sponsor.updateMany({
         where: {
             razorpay_sub_id: subId,
         },
         data: {
             plan_status: "active",
         },
     });
+    
+    if (result.count === 0) {
+        console.warn(`No sponsor found for subscription ${subId}`);
+    }

77-84: Clarify or remove uncertain implementation comments.

The lengthy comments express uncertainty about the data flow and implementation approach.

These comments suggest the implementation logic may need review. Either clarify the flow or remove speculative comments to improve code readability.

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

26-28: Add better error message for missing plan.

The generic error doesn't help with debugging.

             if (!plan) {
-                throw new Error("Plan not found");
+                throw new Error(`Plan with ID '${input.planId}' not found`);
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cdc72d and d7c85f9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • apps/api/prisma/schema.prisma (1 hunks)
  • apps/api/src/index.ts (1 hunks)
  • apps/api/src/routers/_app.ts (1 hunks)
  • apps/api/src/routers/sponsor.ts (1 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
  • apps/web/package.json (3 hunks)
  • apps/web/src/app/(main)/(landing)/page.tsx (1 hunks)
  • apps/web/src/app/(main)/sponsor/layout.tsx (1 hunks)
  • apps/web/src/app/(main)/sponsor/page.tsx (1 hunks)
  • apps/web/src/app/(main)/sponsor/submit/page.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardContainer.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx (1 hunks)
  • apps/web/src/components/landing-sections/SponsorSection.tsx (1 hunks)
  • apps/web/src/components/sponsor/SponsorCard.tsx (1 hunks)
  • apps/web/src/components/sponsor/SponsorForm.tsx (1 hunks)
  • apps/web/src/components/ui/accordion.tsx (4 hunks)
  • apps/web/src/components/ui/custom-button.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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/dashboard/DashboardSponsorReminder.tsx
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/api/src/index.ts
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/api/src/routers/_app.ts
  • apps/web/src/app/(main)/(landing)/page.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/web/src/app/(main)/(landing)/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/api/src/index.ts
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/api/src/routers/_app.ts
  • apps/web/src/app/(main)/(landing)/page.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/api/src/index.ts
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/api/src/routers/_app.ts
  • apps/web/src/app/(main)/(landing)/page.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.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/dashboard/DashboardSponsorReminder.tsx
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/api/src/index.ts
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/api/src/routers/_app.ts
  • apps/web/src/app/(main)/(landing)/page.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/web/src/app/(main)/(landing)/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/dashboard/DashboardSponsorReminder.tsx
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/api/src/index.ts
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/api/src/routers/_app.ts
  • apps/web/src/app/(main)/(landing)/page.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

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

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
apps/web/src/components/**/*.{tsx,ts}

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

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

Files:

  • apps/web/src/components/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.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/dashboard/DashboardSponsorReminder.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
  • apps/web/src/app/(main)/(landing)/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/webhooks.ts
  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.ts
  • apps/api/src/routers/_app.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.ts
  • apps/api/src/routers/_app.ts
apps/api/src/{index,main,app,server}.{js,ts}

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

Validate required environment variables on startup; fail fast if missing

Files:

  • apps/api/src/index.ts
🧠 Learnings (26)
📚 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/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/sponsor/SponsorForm.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/components/ui/accordion.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} : Provide alt text for images

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.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/**/*.{tsx,ts} : Optimize images using next/image

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.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/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/app/(main)/(landing)/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} : Prefer functional components with TypeScript

Applied to files:

  • apps/web/src/components/landing-sections/SponsorSection.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/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/(landing)/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} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/app/(main)/sponsor/layout.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/app/(main)/(landing)/page.tsx
  • apps/web/package.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)/sponsor/layout.tsx
  • apps/web/src/components/ui/accordion.tsx
  • apps/web/src/app/(main)/(landing)/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/app/(main)/sponsor/layout.tsx
  • apps/web/package.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)/sponsor/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} : 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

Applied to files:

  • apps/web/src/components/ui/custom-button.tsx
  • apps/web/src/components/ui/accordion.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/ui/accordion.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} : Remove unused imports

Applied to files:

  • apps/web/src/components/ui/accordion.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/ui/accordion.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} : Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)

Applied to files:

  • apps/web/src/components/ui/accordion.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/components/ui/accordion.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/components/ui/accordion.tsx
  • apps/web/src/app/(main)/(landing)/page.tsx
  • apps/web/package.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/**/{middleware,services,routes,controllers}/*.{js,ts} : Log all critical operations (auth attempts, payment processing, data mutations)

Applied to files:

  • apps/api/src/index.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,services}/*.{js,ts} : Handle database connection failures gracefully with retry logic

Applied to files:

  • apps/api/src/index.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} : Implement rate limiting on all public endpoints to prevent abuse

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: 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: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/app/(main)/(landing)/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/package.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} : Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags

Applied to files:

  • apps/web/package.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/**/*.{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/package.json
🧬 Code graph analysis (12)
apps/web/src/components/dashboard/DashboardSponsorReminder.tsx (1)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/api/src/webhooks.ts (1)
apps/api/src/services/payment.service.ts (2)
  • verifyPaymentSignature (110-138)
  • RazorpayError (33-43)
apps/web/src/app/(main)/sponsor/submit/page.tsx (1)
apps/web/src/components/sponsor/SponsorForm.tsx (1)
  • SponsorForm (22-220)
apps/web/src/components/landing-sections/SponsorSection.tsx (2)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/sponsor/SponsorCard.tsx (1)
  • SponsorCard (17-45)
apps/web/src/components/sponsor/SponsorForm.tsx (2)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/ui/custom-button.tsx (1)
apps/web/src/lib/utils.ts (1)
  • cn (4-6)
apps/api/src/index.ts (1)
apps/api/src/webhooks.ts (1)
  • handleRazorpayWebhook (7-64)
apps/web/src/components/dashboard/DashboardContainer.tsx (1)
apps/web/src/components/dashboard/ProjectsContainer.tsx (1)
  • ProjectsContainer (54-179)
apps/web/src/app/(main)/sponsor/page.tsx (2)
apps/web/src/hooks/useRazorpay.ts (1)
  • useRazorpay (18-80)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/dashboard/DashboardSponsorList.tsx (2)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/sponsor/SponsorCard.tsx (1)
  • SponsorCard (17-45)
apps/api/src/routers/_app.ts (2)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/api/src/routers/sponsor.ts (1)
  • sponsorRouter (8-151)
apps/web/src/app/(main)/(landing)/page.tsx (2)
apps/web/src/components/landing-sections/how-it-works.tsx (1)
  • HowItWorks (33-66)
apps/web/src/components/faq/FaqSection.tsx (1)
  • FaqSection (10-62)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (12)
apps/web/src/components/sponsor/SponsorForm.tsx (3)

48-76: consider surfacing upload errors in‑ui instead of using alerts

handleImageUpload currently reports validation and upload failures via alert(...), which is blocking and not aligned with typical app‑level ux.

it would be nicer to:

  • track an uploadError state and render it near the upload area, or
  • use your existing toast/notification system if one exists.

for example:

-  const [imageUrl, setImageUrl] = useState<string | null>(null);
+  const [imageUrl, setImageUrl] = useState<string | null>(null);
+  const [uploadError, setUploadError] = useState<string | null>(null);

   const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
     const file = e.target.files?.[0];
     if (!file) return;

-    // validate file size (e.g. 5MB)
+    // validate file size (e.g. 5mb)
     if (file.size > 5 * 1024 * 1024) {
-      alert("File size too large. Max 5MB allowed.");
+      setUploadError("file size too large. max 5mb allowed.");
       return;
     }

     setUploading(true);
+    setUploadError(null);

     try {
       const base64 = await new Promise<string>((resolve, reject) => {
         const reader = new FileReader();
@@
       const result = await uploadImageMutation.mutateAsync({ file: base64 });
       setImageUrl(result.url);
     } catch (error) {
-      console.error("Upload failed", error);
-      alert("Failed to upload image. Please try again.");
+      console.error("upload failed", error);
+      setUploadError("failed to upload image. please try again.");
     } finally {
       setUploading(false);
     }
   };

and then render uploadError under the upload area if present.


94-206: tweak comments to follow lowercase / “why not what” guideline

layout comments like {/* Logo Upload Section */}, {/* Upload Area */}, {/* Preview Area */} describe what the code does and use capitalized phrases. guidelines ask for lowercase comments and comments explaining why rather than what.

either:

  • remove these comments (the jsx structure is already self‑explanatory), or
  • reword them briefly to explain intent in lowercase if there’s a non‑obvious reason for the structure.

for example:

-        {/* Logo Upload Section */}
+        {/* logo upload, kept separate from details for better visual focus */}
@@
-            {/* Upload Area */}
+            {/* upload area */}
@@
-            {/* Preview Area */}
+            {/* preview area */}

292-327: optional: add subtle inline validation hint for required logo

you already disable the submit button when !imageUrl and show a small note; this is good. if you want to make it more consistent with other field errors, you could style this message similarly to the zod error text (but with neutral color), or surface a formal validation error when submitting without a logo.

this is non‑blocking; the current behavior is functionally correct.

apps/api/src/webhooks.ts (5)

64-67: Avoid any type; use unknown and narrow with type guards.

Per coding guidelines, avoid any type. Use unknown for error handling and narrow appropriately.

-    } catch (error: any) {
+    } catch (error: unknown) {
         console.error("Webhook error:", error);

71-77: Define a TypeScript interface for the payload structure.

Using any for payload bypasses type safety. Define an interface for the expected Razorpay payment webhook payload to catch access errors at compile time and improve maintainability. Based on learnings, strict TypeScript interfaces should be defined for request/response bodies.

interface RazorpayPaymentPayload {
  payment: {
    entity: {
      id: string;
      order_id: string;
      amount: number;
      currency: string;
      contact?: string;
      email?: string;
      notes?: { name?: string };
    };
  };
}

106-109: Add context to error log for debugging.

Per coding guidelines, log errors with context. Include paymentId to aid debugging when payment detail fetches fail.

         } catch (error) {
-            console.error("failed to fetch payment details from razorpay:", error);
+            console.error("failed to fetch payment details from razorpay:", { paymentId, error });
             // continue without contact details if fetch fails
         }

199-209: Remove dead code and clarify default status logic.

Lines 199-201 contain an empty conditional block that does nothing. Additionally, the default newStatus = "active" is never used since all handled event types overwrite it. Consider removing the dead code and using explicit status mapping.

-    // If we don't have subscription entity directly (e.g. payment.failed), try to get from payment
-    if (!subId && payment && payment.description) {
-        // sometimes description contains sub id or we check notes
-    }
-
     if (!subId) return;

-    let newStatus = "active";
-    if (eventType === "subscription.pending") newStatus = "pending_payment";
-    if (eventType === "subscription.halted") newStatus = "unpaid"; // or halted
-    if (eventType === "subscription.cancelled") newStatus = "cancelled";
-    if (eventType === "payment.failed") newStatus = "failed";
+    const statusMap: Record<string, string> = {
+        "subscription.pending": "pending_payment",
+        "subscription.halted": "unpaid",
+        "subscription.cancelled": "cancelled",
+        "payment.failed": "failed",
+    };
+    const newStatus = statusMap[eventType];
+    if (!newStatus) return;

162-190: Apply consistent typing to subscription webhook handlers.

Similar to handlePaymentCaptured, this function should use typed interfaces for the payload parameter instead of any. Define interfaces for subscription and payment entities.

interface RazorpaySubscriptionPayload {
  payment: { entity: { id: string } };
  subscription: { entity: { id: string } };
}

async function handleSubscriptionCharged(payload: RazorpaySubscriptionPayload) {
  // ...
}
apps/api/prisma/schema.prisma (2)

34-46: user–sponsor relation looks correct but consider delete behavior

the sponsors Sponsor[] relation correctly mirrors Sponsor.user? @relation(fields: [userId], references: [id]), so prisma will wire this as expected. however, if you ever delete users, current Sponsor.user has no onDelete strategy, so deleting a user with sponsors will likely fail due to fk constraints.

consider explicitly choosing a strategy (e.g. cascade or set null) to make user deletion behavior predictable:

model Sponsor {
  // ...
  user  User? @relation(fields: [userId], references: [id], onDelete: SetNull)
}

106-122: consider stronger typing and constraints on sponsor status and payment linkage

the Sponsor model covers all required fields, but a few improvements would make it safer:

  • plan_status is a raw String with a comment for allowed values. using a prisma enum (e.g. enum SponsorPlanStatus { active cancelled pending_payment pending_submission failed }) would prevent invalid states at both db and typescript levels.
  • razorpay_payment_id is conceptually one‑to‑one with a sponsor, but there is no @unique on it. adding a unique constraint (or at least an index) would protect against accidental duplicates and help lookups.
  • field naming mixes snake_case here (razorpay_payment_id, image_url, created_at) with camelCase elsewhere (e.g. Payment.razorpayPaymentId, createdAt). for long‑term maintainability, consider standardizing on one convention.

none of these are blockers, but tightening them now will reduce bugs in the sponsor lifecycle and simplify querying.

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

1-21: double‑check sponsor currency and environment assumptions

defining SPONSOR_MONTHLY_AMOUNT = 50000 and SPONSOR_CURRENCY = "USD" is clear, but note that:

  • the Payment model’s currency default is "INR" and its comment mentions paise; here we’re using cents and "USD".
  • whether razorpay supports usd on your account is configuration‑dependent.
  • createSubscription and the manual Payment insert both rely on these constants, so if you ever change the sponsor price/currency, you must update them in one place only.

also, cloudinary.config is called with empty strings if env vars are missing; in that case uploads will just fail at runtime. consider failing fast (e.g. throwing during startup) if required env vars like CLOUDINARY_CLOUD_NAME or RAZORPAY_KEY_ID are not set so misconfiguration isn’t only discovered on first request.


38-65: order creation looks fine, but handle razorpay key presence explicitly

using paymentService.createOrder with a sponsor‑specific receipt and notes: { type: "sponsor" } is a good separation from other order types.

two small things to tighten:

  • process.env.RAZORPAY_KEY_ID can be undefined, which would be returned to clients as key: undefined and cause confusing frontend failures. better to detect this and throw a server error:
-            return {
-                orderId: order.id,
-                amount: order.amount,
-                currency: order.currency,
-                key: process.env.RAZORPAY_KEY_ID,
-            };
+            const keyId = process.env.RAZORPAY_KEY_ID;
+            if (!keyId) {
+                throw new TRPCError({
+                    code: "INTERNAL_SERVER_ERROR",
+                    message: "razorpay key id is not configured on the server",
+                });
+            }
+
+            return {
+                orderId: order.id,
+                amount: order.amount,
+                currency: order.currency,
+                key: keyId,
+            };
  • if paymentService.createOrder can throw (network / auth errors), consider wrapping it with try/catch and converting to a TRPCError instead of a bare Error, for consistent error codes.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7c85f9 and 654d372.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • apps/api/package.json (1 hunks)
  • apps/api/prisma/clear_sponsors.sql (1 hunks)
  • apps/api/prisma/schema.prisma (4 hunks)
  • apps/api/src/index.ts (2 hunks)
  • apps/api/src/routers/sponsor.ts (1 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
  • apps/web/next.config.js (1 hunks)
  • apps/web/src/app/(main)/sponsor/page.tsx (1 hunks)
  • apps/web/src/app/(main)/sponsor/submit/page.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardContainer.tsx (1 hunks)
  • apps/web/src/components/sponsor/SponsorForm.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/app/(main)/sponsor/page.tsx
  • apps/web/src/components/dashboard/DashboardContainer.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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/index.ts
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/webhooks.ts
  • apps/web/next.config.js
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/index.ts
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/index.ts
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/webhooks.ts
  • apps/web/next.config.js
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/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/api/src/index.ts
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/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/api/src/index.ts
  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/webhooks.ts
  • apps/web/next.config.js
  • apps/api/src/routers/sponsor.ts
  • apps/web/src/app/(main)/sponsor/submit/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/index.ts
  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.ts
apps/api/src/{index,main,app,server}.{js,ts}

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

Validate required environment variables on startup; fail fast if missing

Files:

  • apps/api/src/index.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

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

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
apps/web/src/components/**/*.{tsx,ts}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
🧠 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,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies

Applied to files:

  • apps/api/src/index.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} : Implement rate limiting on all public endpoints to prevent abuse

Applied to files:

  • apps/api/src/index.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/index.ts
  • apps/api/package.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/**/{middleware,services,routes,controllers}/*.{js,ts} : Log all critical operations (auth attempts, payment processing, data mutations)

Applied to files:

  • apps/api/src/index.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,services}/*.{js,ts} : Handle database connection failures gracefully with retry logic

Applied to files:

  • apps/api/src/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/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/components/sponsor/SponsorForm.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/sponsor/SponsorForm.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/**/*.{tsx,ts} : Optimize images using next/image

Applied to files:

  • apps/web/next.config.js
🧬 Code graph analysis (4)
apps/api/src/index.ts (1)
apps/api/src/webhooks.ts (1)
  • handleRazorpayWebhook (8-68)
apps/web/src/components/sponsor/SponsorForm.tsx (2)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/api/src/webhooks.ts (1)
apps/api/src/clients/razorpay.ts (1)
  • rz_instance (18-21)
apps/web/src/app/(main)/sponsor/submit/page.tsx (1)
apps/web/src/components/sponsor/SponsorForm.tsx (1)
  • SponsorForm (22-332)
🔇 Additional comments (6)
apps/web/src/app/(main)/sponsor/submit/page.tsx (1)

7-22: sponsor submit flow and payment id handling look consistent

early-return on missing paymentId plus the mounted guard keeps the render logic simple and avoids potential client/server search param mismatches. wiring of paymentId into the sponsor form via props is clear and matches the intended flow.

apps/web/src/components/sponsor/SponsorForm.tsx (1)

18-44: props and form schema wiring look solid

SponsorFormProps has a clear, descriptive razorpayPaymentId prop, and the inferred form type from the schema keeps the inputs and validation in sync. this matches the guidance about descriptive prop names and typed props. based on learnings, this is aligned with the preferred pattern.

apps/web/next.config.js (1)

17-20: LGTM!

The Cloudinary remote pattern is correctly configured, enabling Next.js image optimization for sponsor images stored in Cloudinary. This aligns with the sponsor feature requirements.

apps/api/package.json (1)

40-41: Verify Zod 4 compatibility and update validation schemas if needed.

Zod 4 introduces breaking changes from Zod 3 including unified error customization, new string validators, and modified default behaviors. Review existing validation schemas throughout the codebase (especially in routes, controllers, and middleware) to ensure they're compatible with Zod 4 syntax. The Cloudinary ^2.0.0 addition is appropriate for sponsor image storage functionality.

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

66-79: making payment.userId optional is reasonable, but ensure all call sites handle null

switching userId and user to optional on Payment matches the sponsor flow where payments can exist without an authenticated user, and onDelete: Cascade on user is consistent with the rest of the schema. the main risk is existing code assuming payment.userId / payment.user are always present.

please sanity‑check usages of these fields elsewhere (api and web) to avoid runtime errors or missing auth checks when userId is null.

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

22-37: Public image upload endpoint needs basic abuse protection and validation

uploadImage is fully public and accepts any string, directly forwarding it to cloudinary. This aligns with "no auth required", but also means:

  • Anyone can script uploads to your opensox/sponsors folder, potentially driving up storage/traffic costs.
  • There is no validation on content type, size, or dimensions, even though the issue mentions validating image ratio/file.

Consider at minimum:

- .input(z.object({ file: z.string() }))
+ .input(
+   z.object({
+     // require a data url and keep a reasonable size limit
+     file: z
+       .string()
+       .regex(/^data:image\/(png|jpe?g|webp);base64,/, "invalid image data url"),
+   })
+)
- .mutation(async ({ input }: { input: { file: string } }) => {
+ .mutation(async ({ input }) => {

And, if possible, add simple server-side checks on the decoded payload size and/or use a locked-down cloudinary upload preset for this folder.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/web/src/components/sponsor/SponsorCard.tsx (3)

7-15: Consider removing unused interface fields.

The id and description fields are defined in the interface but never referenced in the component. If these fields are not needed for this component, consider removing them to keep the interface minimal and focused.

Apply this diff if the fields are genuinely unused:

 interface SponsorCardProps {
   sponsor: {
-    id: string;
     company_name: string;
-    description: string;
     website: string;
     image_url: string;
   };
 }

If these fields are kept for consistency with the backend model or future use, consider adding a comment explaining this.


19-24: Add aria-label for better accessibility.

The link lacks an aria-label, which would help screen reader users understand the link's purpose. Consider adding a descriptive label.

Apply this diff:

     <Link
       href={sponsor.website}
       target="_blank"
       rel="noopener noreferrer"
+      aria-label={`Visit ${sponsor.company_name} website`}
       className="group block"
     >

Based on coding guidelines, proper aria labels should be included for accessibility.


35-35: Use semantic color naming instead of appearance-based colors.

The text-green-400 class uses appearance-based naming rather than semantic naming that describes purpose. Consider using a semantic color from your design token system.

Apply this diff:

-        <h3 className="text-white font-medium text-lg group-hover:text-green-400 transition-colors">
+        <h3 className="text-white font-medium text-lg group-hover:text-brand-success transition-colors">

(Replace text-brand-success with the appropriate semantic color class from your design token system as defined in apps/web/src/lib/design-tokens.ts)

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 654d372 and 3211bfd.

📒 Files selected for processing (3)
  • apps/web/src/components/landing-sections/Hero.tsx (1 hunks)
  • apps/web/src/components/landing-sections/SponsorSection.tsx (1 hunks)
  • apps/web/src/components/sponsor/SponsorCard.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/components/landing-sections/Hero.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/landing-sections/SponsorSection.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/sponsor/SponsorCard.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.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/sponsor/SponsorCard.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.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/sponsor/SponsorCard.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
apps/web/src/components/**/*.{tsx,ts}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorCard.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/sponsor/SponsorCard.tsx
🧠 Learnings (5)
📚 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/components/sponsor/SponsorCard.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.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} : Prefer functional components with TypeScript

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.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/sponsor/SponsorCard.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/**/*.{tsx,ts} : Optimize images using next/image

Applied to files:

  • apps/web/src/components/sponsor/SponsorCard.tsx
🔇 Additional comments (2)
apps/web/src/components/sponsor/SponsorCard.tsx (2)

1-6: LGTM: Imports are properly organized.

The imports follow the correct order (React → third-party) as specified in the coding guidelines.


27-32: LGTM: Image implementation follows best practices.

The Image component correctly uses Next.js optimization, includes proper alt text, and implements smooth hover transitions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
apps/web/src/components/dashboard/DashboardSponsorList.tsx (2)

3-9: clean up imports and make component typing explicit

the react import is not used and can be removed, and imports should follow the repo convention: react → third-party (next/link, lucide-react) → local (@/lib/trpc, @/components/...). also consider typing the component explicitly, e.g. const DashboardSponsorList: React.FC = () => { ... }, to align with the “define a type when defining const functions” guideline. as per coding guidelines.


13-19: improve loading state accessibility

the loading branch currently renders only the Loader2 icon, which is invisible to screen readers. wrapping this in a container with role="status" and an aria-label (and/or an sr-only text node) plus marking the icon aria-hidden will make the state accessible while keeping the visual the same. based on learnings, accessibility labels are expected in interactive or dynamic ui.

apps/web/src/components/landing-sections/SponsorSection.tsx (3)

3-8: align imports and component typing with conventions

react is imported but not used and can be dropped. also, Link (third-party) should come before local imports so the order is react → third-party → local. optionally, add an explicit component type (const SponsorSection: React.FC = () => { ... }) to follow the “define a type for const functions” rule. as per coding guidelines.


12-14: avoid null loading ui and remove inline todo-style comment

returning null while sponsors load means the section pops in only after data is ready, which can cause layout shifts. consider rendering a lightweight skeleton or placeholder grid instead, and drop the // Or a skeleton comment once you do so, since guidelines prefer avoiding todo-style inline comments. based on learnings, loading states and comments should be explicit and purposeful.


30-55: consider extracting a shared sponsor placeholder card

the “become a sponsor” placeholder card here is very similar to the dashboard’s “your logo here” placeholder. extracting a small SponsorPlaceholderCard component (with configurable label/size) would reduce duplication and keep future visual tweaks in one place. this is non-blocking but would improve maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3211bfd and ca0eca9.

📒 Files selected for processing (3)
  • apps/web/src/app/(main)/sponsor/page.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx (1 hunks)
  • apps/web/src/components/landing-sections/SponsorSection.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/(main)/sponsor/page.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/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.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/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.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/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

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

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
apps/web/src/components/**/*.{tsx,ts}

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

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

Files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.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/landing-sections/SponsorSection.tsx
  • apps/web/src/components/dashboard/DashboardSponsorList.tsx
🧠 Learnings (7)
📚 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/components/landing-sections/SponsorSection.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/components/landing-sections/SponsorSection.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/landing-sections/SponsorSection.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/landing-sections/SponsorSection.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/components/landing-sections/SponsorSection.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} : Prefer functional components with TypeScript

Applied to files:

  • apps/web/src/components/landing-sections/SponsorSection.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} : Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop

Applied to files:

  • apps/web/src/components/landing-sections/SponsorSection.tsx
🧬 Code graph analysis (2)
apps/web/src/components/landing-sections/SponsorSection.tsx (3)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/sponsor/SponsorCard.tsx (1)
  • SponsorCard (17-42)
apps/web/src/providers/trpc-provider.tsx (1)
  • TRPCProvider (11-53)
apps/web/src/components/dashboard/DashboardSponsorList.tsx (2)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/components/sponsor/SponsorCard.tsx (1)
  • SponsorCard (17-42)
🔇 Additional comments (2)
apps/web/src/components/dashboard/DashboardSponsorList.tsx (1)

9-52: sponsor list logic and placeholder behavior look good

query usage, slicing to three sponsors, and backfilling with “your logo here” placeholders keeps the grid stable and matches the described dashboard sponsor slot behavior. no blocking issues from my side here.

apps/web/src/components/landing-sections/SponsorSection.tsx (1)

8-57: landing sponsor section behavior matches the sponsor flow

fetching active sponsors, capping to three, and backfilling the grid with cta placeholders to /sponsor aligns with the described landing-page sponsor requirements. layout, spacing, and overall flow look good once the design-token color issue is addressed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/web/src/components/sponsor/SponsorForm.tsx (1)

136-176: replace hardcoded hex colors with design token classes

lines 138 and 159 use text-[#4dd0a4] which violates the design system rule against hardcoded hex values. use semantic color tokens instead.

                 {uploading ? (
                   <div className="flex flex-col items-center gap-3">
-                    <Loader2 className="w-8 h-8 text-[#4dd0a4] animate-spin" />
+                    <Loader2 className="w-8 h-8 text-primary animate-spin" />
                     <p className="text-sm text-neutral-400">Uploading...</p>
                   </div>
                     <div className="w-12 h-12 rounded-full bg-green-500/10 flex items-center justify-center">
                       <svg
-                        className="w-6 h-6 text-[#4dd0a4]"
+                        className="w-6 h-6 text-primary"
                         fill="none"

based on learnings, always reference colors from the design token system using tailwind classes.

🧹 Nitpick comments (10)
apps/web/src/app/(main)/sponsor/submit/page.tsx (2)

13-19: remove or enable the commented redirect logic

the redirect code on line 17 is commented out but the comment on line 16 suggests it should redirect. this creates confusion about the intended behavior. either enable the redirect or remove the dead code and comment.

   useEffect(() => {
     setMounted(true);
-    if (!paymentId) {
-      // If no payment ID, redirect back to sponsor page
-      // router.push("/sponsor");
-    }
-  }, [paymentId, router]);
+  }, []);

alternatively, if redirect is intended:

   useEffect(() => {
     setMounted(true);
-    if (!paymentId) {
-      // If no payment ID, redirect back to sponsor page
-      // router.push("/sponsor");
-    }
+    if (!paymentId) {
+      router.push("/sponsor");
+    }
   }, [paymentId, router]);

30-41: button styling looks good, minor radius adjustment suggested

the button correctly uses semantic color tokens (bg-primary, text-primary-foreground). per coding guidelines, buttons should use rounded-[16px] instead of rounded-lg.

           className="
             bg-primary text-primary-foreground
             font-bold py-2 px-6 rounded-lg
+            rounded-[16px]
             hover:bg-primary/90
apps/api/src/index.ts (1)

23-28: validate cloudinary credentials on startup

cloudinary is configured with empty string fallbacks. if credentials are missing, uploads will silently fail at runtime. per coding guidelines, validate required environment variables on startup.

+// validate cloudinary credentials
+const CLOUDINARY_CLOUD_NAME = process.env.CLOUDINARY_CLOUD_NAME;
+const CLOUDINARY_API_KEY = process.env.CLOUDINARY_API_KEY;
+const CLOUDINARY_API_SECRET = process.env.CLOUDINARY_API_SECRET;
+
+if (!CLOUDINARY_CLOUD_NAME || !CLOUDINARY_API_KEY || !CLOUDINARY_API_SECRET) {
+  console.warn("Cloudinary credentials not configured - sponsor image uploads will fail");
+}
+
 // Configure Cloudinary (kept local to this route)
 cloudinary.config({
-  cloud_name: process.env.CLOUDINARY_CLOUD_NAME || "",
-  api_key: process.env.CLOUDINARY_API_KEY || "",
-  api_secret: process.env.CLOUDINARY_API_SECRET || "",
+  cloud_name: CLOUDINARY_CLOUD_NAME || "",
+  api_key: CLOUDINARY_API_KEY || "",
+  api_secret: CLOUDINARY_API_SECRET || "",
 });
apps/api/src/webhooks.ts (3)

71-110: define proper types for webhook payloads

the payload: any parameter and nested access patterns lack type safety. consider defining interfaces for razorpay webhook payloads to catch errors at compile time.

interface RazorpayPaymentEntity {
  id: string;
  order_id: string;
  amount: number;
  currency: string;
  contact?: string;
  email?: string;
  notes?: { name?: string };
}

interface PaymentCapturedPayload {
  payment: { entity: RazorpayPaymentEntity };
}

async function handlePaymentCaptured(payload: PaymentCapturedPayload) {
  const payment = payload.payment.entity;
  // ...
}

162-190: consider logging when no sponsors are updated

updateMany silently succeeds even if no records match. for observability, consider logging when a subscription charge doesn't match any sponsor.

-    await prisma.sponsor.updateMany({
+    const result = await prisma.sponsor.updateMany({
         where: {
             razorpay_sub_id: subId,
         },
         data: {
             plan_status: "active",
         },
     });
+
+    if (result.count === 0) {
+        console.warn(`subscription.charged: no sponsor found for sub_id ${subId}`);
+    }

221-221: remove unnecessary comment

the trailing comment "No changes required for webhook handlers in this change set" appears to be a leftover from development and should be removed.

-// No changes required for webhook handlers in this change set.
apps/web/src/components/sponsor/SponsorForm.tsx (4)

49-77: consider using the dedicated upload endpoint instead of base64

the image upload converts files to base64, which increases payload size by ~33% and can be slow for larger files. the api already has a dedicated /upload/sponsor-image endpoint that handles multipart uploads more efficiently.

// alternative using fetch with FormData
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
  const file = e.target.files?.[0];
  if (!file) return;

  if (file.size > 5 * 1024 * 1024) {
    alert("File size too large. Max 5MB allowed.");
    return;
  }

  setUploading(true);
  try {
    const formData = new FormData();
    formData.append("file", file);
    
    const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/upload/sponsor-image`, {
      method: "POST",
      body: formData,
    });
    
    if (!response.ok) throw new Error("Upload failed");
    const result = await response.json();
    setImageUrl(result.url);
  } catch (error) {
    console.error("Upload failed", error);
    alert("Failed to upload image. Please try again.");
  } finally {
    setUploading(false);
  }
};

227-231: add aria-describedby for accessibility

inputs should reference their error messages for screen readers when validation fails.

             <input
               {...form.register("companyName")}
+              aria-describedby={form.formState.errors.companyName ? "companyName-error" : undefined}
+              aria-invalid={!!form.formState.errors.companyName}
               className="w-full bg-neutral-900 border border-neutral-800 rounded-xl px-4 py-3 text-white placeholder:text-neutral-600 focus:border-neutral-700 focus:ring-1 focus:ring-neutral-700 outline-none transition-colors"
               placeholder="Acme Corporation"
             />
             {form.formState.errors.companyName && (
-              <p className="text-xs text-red-400 flex items-center gap-1">
+              <p id="companyName-error" className="text-xs text-red-400 flex items-center gap-1" role="alert">

306-334: LGTM on semantic colors, minor radius suggestion

the submit button now correctly uses semantic color tokens (bg-primary, text-primary-foreground). per coding guidelines, buttons should use rounded-[16px] instead of rounded-lg.

             className="w-full sm:w-auto sm:min-w-[200px] bg-primary text-primary-foreground font-semibold py-3.5 px-8 rounded-lg hover:bg-primary/90 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 group"
+            className="w-full sm:w-auto sm:min-w-[200px] bg-primary text-primary-foreground font-semibold py-3.5 px-8 rounded-[16px] hover:bg-primary/90 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 group"

92-101: simplify form validity check

the manual validation checks duplicate the zod schema logic. since mode: "onChange" is set, formState.isValid already reflects the schema validation state.

-  // watch form values to check if all fields are completed
-  const watchedValues = form.watch();
-  const isFormValid =
-    watchedValues.companyName?.trim().length >= 2 &&
-    watchedValues.description?.trim().length >= 10 &&
-    watchedValues.website?.trim().length > 0 &&
-    form.formState.isValid &&
-    imageUrl &&
-    !uploading;
+  const isFormValid = form.formState.isValid && imageUrl && !uploading;
   const isSubmitting = submitAssetsMutation.isPending;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca0eca9 and 80435b7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • apps/api/src/index.ts (4 hunks)
  • apps/api/src/routers/sponsor.ts (1 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
  • apps/web/src/app/(main)/sponsor/submit/page.tsx (1 hunks)
  • apps/web/src/components/landing-sections/SponsorSection.tsx (1 hunks)
  • apps/web/src/components/sponsor/SponsorForm.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/landing-sections/SponsorSection.tsx
  • apps/api/src/routers/sponsor.ts
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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/sponsor/SponsorForm.tsx
  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.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/sponsor/SponsorForm.tsx
  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
  • apps/web/src/app/(main)/sponsor/submit/page.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.tsx
apps/web/src/components/**/*.{tsx,ts}

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

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

Files:

  • apps/web/src/components/sponsor/SponsorForm.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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/submit/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/index.ts
  • apps/api/src/webhooks.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/index.ts
  • apps/api/src/webhooks.ts
apps/api/src/{index,main,app,server}.{js,ts}

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

Validate required environment variables on startup; fail fast if missing

Files:

  • apps/api/src/index.ts
🧠 Learnings (23)
📚 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/components/sponsor/SponsorForm.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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/sponsor/SponsorForm.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} : 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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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/components/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/sponsor/SponsorForm.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/components/sponsor/SponsorForm.tsx
  • apps/web/src/app/(main)/sponsor/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 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

Applied to files:

  • apps/web/src/components/sponsor/SponsorForm.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} : Implement rate limiting on all public endpoints to prevent abuse

Applied to files:

  • apps/api/src/index.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/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} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/api/src/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:

  • apps/api/src/index.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,jsx,js} : Organize imports: react → third-party → local components → utils → types

Applied to files:

  • apps/api/src/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} : Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)

Applied to files:

  • apps/api/src/index.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/index.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/**/{middleware,services,routes,controllers}/*.{js,ts} : Log all critical operations (auth attempts, payment processing, data mutations)

Applied to files:

  • apps/api/src/index.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/index.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,services}/*.{js,ts} : Handle database connection failures gracefully with retry logic

Applied to files:

  • apps/api/src/index.ts
🧬 Code graph analysis (3)
apps/web/src/components/sponsor/SponsorForm.tsx (3)
apps/api/src/trpc.ts (1)
  • router (38-38)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/providers/trpc-provider.tsx (1)
  • TRPCProvider (11-53)
apps/api/src/index.ts (1)
apps/api/src/webhooks.ts (1)
  • handleRazorpayWebhook (8-68)
apps/api/src/webhooks.ts (1)
apps/api/src/clients/razorpay.ts (1)
  • rz_instance (18-21)
🔇 Additional comments (7)
apps/web/src/app/(main)/sponsor/submit/page.tsx (1)

46-59: LGTM!

the success state rendering is well-structured with responsive layout and proper prop passing to SponsorForm.

apps/api/src/index.ts (3)

112-126: LGTM!

the multer configuration has appropriate security measures: memory storage to avoid temp file issues, 10MB size limit, single file restriction, and strict MIME type validation for images only.


75-77: LGTM!

the global request size limits have been appropriately reduced to 5mb, addressing the previous concern about DoS risk from excessive limits.


222-223: LGTM!

the webhook handler is properly delegated to the external handleRazorpayWebhook function, with the import correctly placed at the top of the file.

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

8-68: LGTM on webhook signature validation

the signature validation uses proper HMAC-SHA256 with timing-safe comparison to prevent timing attacks. the event routing is well-structured with appropriate handlers for each event type.

apps/web/src/components/sponsor/SponsorForm.tsx (2)

1-11: LGTM!

imports are well-organized following the pattern: react → third-party (react-hook-form, zod, lucide-react) → local (trpc, router, Image).


12-20: LGTM!

the zod schema has appropriate validation rules and the SponsorFormProps interface is properly typed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
apps/api/src/index.ts (1)

128-167: protect sponsor upload endpoint and remove any from upload flow

this endpoint is currently unauthenticated, so anyone can upload images into your opensox/sponsors cloudinary folder, limited only by apiLimiter. given this is part of the paid sponsor flow, it should be behind your existing auth middleware (per backend rule to do auth via middleware, not in handlers) before upload.single("file").

the implementation also introduces Promise<any> and catch (err: any), which breaks the “avoid any” rule for apps/api/src/**/*.ts. you can keep things typed without pulling in cloudinary’s full types by defining a small local result shape and treating errors as unknown, e.g.:

+interface SponsorImageUploadResult {
+  secure_url: string;
+}
@@
-      const result = await new Promise<any>((resolve, reject) => {
+      const result = await new Promise<SponsorImageUploadResult>((resolve, reject) => {
         const stream = cloudinary.uploader.upload_stream({ folder }, (error, uploadResult) => {
           if (error) return reject(error);
-          resolve(uploadResult);
+          resolve(uploadResult as SponsorImageUploadResult);
         });
         stream.end(file.buffer);
       });
@@
-    } catch (err: any) {
-      const isLimit = err?.message?.toLowerCase()?.includes("file too large");
-      return res.status(isLimit ? 413 : 400).json({ error: err.message || "Upload failed" });
+    } catch (err: unknown) {
+      const message =
+        typeof err === "object" &&
+        err !== null &&
+        "message" in err &&
+        typeof (err as { message?: unknown }).message === "string"
+          ? (err as { message: string }).message
+          : "upload failed";
+      const isLimit = message.toLowerCase().includes("file too large");
+      return res.status(isLimit ? 413 : 400).json({ error: message });
     }

and then insert your existing auth middleware into the chain:

 app.post(
   "/upload/sponsor-image",
-  apiLimiter,
+  apiLimiter,
+  authMiddleware, // or whatever shared auth guard you use elsewhere
   (req, res, next) => {

lastly, please update the new comments in this block (// Dedicated upload endpoint..., // Stream upload to Cloudinary) to use lower-case to match the project convention.

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

192-219: complete or remove fallback subscription id extraction

the block

if (!subId && payment && payment.description) {
    // sometimes description contains sub id or we check notes
}

is still empty, so in cases like payment.failed where the subscription id must be derived from the payment, subId will remain null and the function returns early without updating any sponsor records. either implement the intended extraction (e.g., from payment.notes.subscription_id or a well-defined pattern in description) or remove the unused conditional to avoid dead code and clarify behavior. the existing comment here can also be made lower-case to match the repo style.

🧹 Nitpick comments (3)
apps/api/src/index.ts (1)

112-126: multer config is solid; consider route-level handling for filter/limit errors

the multer setup (memory storage, 10mb per-file limit, strict image mimetype allowlist) looks good for sponsor uploads. note that errors from fileFilter or size limits will currently bubble to the global error handler and return a 500; you may want a small error-handling middleware for this route to translate invalid type/too-large cases into 400/413 responses instead. also, adjust the new comments here to lower-case per repository style.

apps/api/src/webhooks.ts (2)

8-68: tighten webhook payload typing and basic validation

the main webhook handler currently treats the parsed event/payload as implicit any and uses catch (error: any). per backend rules for apps/api/src/**/*.ts, it would be better to define a minimal type for the razorpay webhook envelope (with event and payload fields) or to type body as unknown and narrow it with runtime checks before accessing event/payload. similarly, type the caught error as unknown and narrow when logging. this will make it harder for unexpected payload shapes to cause runtime errors and aligns with the “avoid any” guideline. also, the new inline comments here (// Get signature, // Verify webhook signature, // Parse the event) should be lower-case to match the project’s comment style.


221-221: remove stale file-level comment

// No changes required for webhook handlers in this change set. is misleading in a new file that defines the handlers; suggest removing it to avoid confusion.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80435b7 and caa83ee.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • apps/api/src/index.ts (4 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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/webhooks.ts
  • apps/api/src/index.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/index.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/index.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/webhooks.ts
  • apps/api/src/index.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/webhooks.ts
  • apps/api/src/index.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/webhooks.ts
  • apps/api/src/index.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/index.ts
apps/api/src/{index,main,app,server}.{js,ts}

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

Validate required environment variables on startup; fail fast if missing

Files:

  • apps/api/src/index.ts
🧠 Learnings (12)
📚 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} : Implement rate limiting on all public endpoints to prevent abuse

Applied to files:

  • apps/api/src/index.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/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} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/api/src/index.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,jsx,js} : Organize imports: react → third-party → local components → utils → types

Applied to files:

  • apps/api/src/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:

  • apps/api/src/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} : Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)

Applied to files:

  • apps/api/src/index.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/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/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/index.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/index.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/**/{middleware,services,routes,controllers}/*.{js,ts} : Log all critical operations (auth attempts, payment processing, data mutations)

Applied to files:

  • apps/api/src/index.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/index.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,services}/*.{js,ts} : Handle database connection failures gracefully with retry logic

Applied to files:

  • apps/api/src/index.ts
🧬 Code graph analysis (2)
apps/api/src/webhooks.ts (1)
apps/api/src/clients/razorpay.ts (1)
  • rz_instance (18-21)
apps/api/src/index.ts (1)
apps/api/src/webhooks.ts (1)
  • handleRazorpayWebhook (8-68)
🔇 Additional comments (3)
apps/api/src/index.ts (2)

75-77: body size limit change looks appropriate

reducing the global json/urlencoded limits to 5mb while keeping /webhook/razorpay on express.raw is a good balance between dos protection and usability; this aligns with the earlier feedback about overly large body limits.


224-225: webhook routing is wired correctly

registering app.post("/webhook/razorpay", handleRazorpayWebhook) here, together with the earlier express.raw middleware on the same path, is the right pattern for keeping the raw body available for signature verification while isolating this route from the global json parser.

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

162-190: subscription charged handler logic looks straightforward

updating sponsors matched by razorpay_sub_id to plan_status: "active" on subscription.charged is a clear mapping and aligns with the described sponsor lifecycle; as long as razorpay_sub_id is populated elsewhere in the flow, this handler is fine.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/api/src/webhooks.ts (1)

112-128: Replace check-then-create with upsert to avoid race condition on retries.

The current findUnique followed by conditional create can lead to duplicate-key errors when Razorpay retries webhook delivery and two concurrent requests race into the create call.

Apply this diff to make the operation idempotent:

-    // check if payment already exists
-    const existingPayment = await prisma.payment.findUnique({
-        where: { razorpayPaymentId: paymentId },
-    });
-
-    if (!existingPayment) {
-        // create payment record without user (for sponsors)
-        await prisma.payment.create({
-            data: {
-                razorpayPaymentId: paymentId,
-                razorpayOrderId: orderId,
-                amount: amount,
-                currency: currency,
-                status: "captured",
-            },
-        });
-    }
+    // upsert payment to handle retries idempotently
+    await prisma.payment.upsert({
+        where: { razorpayPaymentId: paymentId },
+        create: {
+            razorpayPaymentId: paymentId,
+            razorpayOrderId: orderId,
+            amount,
+            currency,
+            status: "captured",
+        },
+        update: {},
+    });
🧹 Nitpick comments (4)
apps/api/src/webhooks.ts (3)

40-43: Replace any type with proper webhook payload typing.

The payload variable is typed as any, which bypasses TypeScript's type safety. Consider defining proper types for Razorpay webhook payloads or using unknown with type guards.

As per coding guidelines, avoid any type; use unknown for truly dynamic data and narrow with type guards.

-        // Parse the event
-        const event = JSON.parse(body);
-        const eventType = event.event;
-        const payload = event.payload;
+        // parse the event
+        const event = JSON.parse(body) as { event: string; payload: unknown };
+        const eventType = event.event;
+        const payload = event.payload;

162-165: Remove unused payment variable.

The payment variable is extracted but never used in this function.

 async function handleSubscriptionCharged(payload: any) {
-    const payment = payload.payment.entity;
     const subscription = payload.subscription.entity;
 
-    // Update sponsor status to active if it matches a known subscription
-  
+    // update sponsor status to active if it matches a known subscription

70-71: Avoid any type for webhook payload parameters.

All three handler functions use any for their payload parameter. Define proper types for Razorpay webhook payloads or use unknown with type guards to maintain type safety.

As per coding guidelines, avoid any type; use unknown for truly dynamic data and narrow with type guards.

Example for handlePaymentCaptured:

-async function handlePaymentCaptured(payload: any) {
+type PaymentCapturedPayload = {
+    payment: {
+        entity: {
+            id: string;
+            order_id: string;
+            amount: number;
+            currency: string;
+            contact?: string | number;
+            email?: string;
+            notes?: { name?: string } | null;
+        };
+    };
+};
+
+async function handlePaymentCaptured(payload: unknown) {
+    const typedPayload = payload as PaymentCapturedPayload;
+    const payment = typedPayload.payment.entity;

Also applies to: 162-162, 181-181

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

221-222: Type payment fetch response instead of using any cast.

The as any cast bypasses type safety when accessing order_id.

As per coding guidelines, avoid any type.

-                // Fetch payment details from Razorpay to get the linked order_id
-                const rpPayment = await rz_instance.payments.fetch(input.razorpayPaymentId);
-                const linkedOrderId = (rpPayment as any)?.order_id;
+                // fetch payment details from razorpay to get the linked order_id
+                type RazorpayPaymentWithOrder = {
+                    order_id?: string;
+                };
+                const rpPayment = (await rz_instance.payments.fetch(
+                    input.razorpayPaymentId
+                )) as RazorpayPaymentWithOrder;
+                const linkedOrderId = rpPayment.order_id;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caa83ee and da6089b.

📒 Files selected for processing (2)
  • apps/api/src/routers/sponsor.ts (1 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
🧰 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/webhooks.ts
  • apps/api/src/routers/sponsor.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

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

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.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/webhooks.ts
  • apps/api/src/routers/sponsor.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/webhooks.ts
  • apps/api/src/routers/sponsor.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/webhooks.ts
  • apps/api/src/routers/sponsor.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/webhooks.ts
  • apps/api/src/routers/sponsor.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 239
File: apps/api/src/index.ts:139-166
Timestamp: 2025-12-06T08:42:03.292Z
Learning: In the sponsor feature (apps/api/src), sponsor-related upload endpoints like /upload/sponsor-image do not require user authentication because sponsors are paying customers who may not be registered OpenSox users. The uploaded assets are validated through payment verification in subsequent steps.
📚 Learning: 2025-12-06T08:42:03.292Z
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 239
File: apps/api/src/index.ts:139-166
Timestamp: 2025-12-06T08:42:03.292Z
Learning: In the sponsor feature (apps/api/src), sponsor-related upload endpoints like /upload/sponsor-image do not require user authentication because sponsors are paying customers who may not be registered OpenSox users. The uploaded assets are validated through payment verification in subsequent steps.

Applied to files:

  • apps/api/src/routers/sponsor.ts
🧬 Code graph analysis (1)
apps/api/src/webhooks.ts (1)
apps/api/src/clients/razorpay.ts (1)
  • rz_instance (18-21)
🔇 Additional comments (7)
apps/api/src/webhooks.ts (1)

188-232: Subscription ID extraction logic is now complete.

The fallback logic for extracting subscription IDs from payment data has been properly implemented with pattern matching and multiple fallback strategies. This addresses the incomplete implementation flagged in previous reviews.

apps/api/src/routers/sponsor.ts (6)

18-20: LGTM!

Constants are properly defined in UPPER_SNAKE_CASE, making the sponsor pricing configuration clear and centralized.


38-65: LGTM!

Order creation logic is correct, with proper notes.type = "sponsor" metadata for validation in subsequent steps. The receipt ID generation ensures uniqueness.


92-104: Critical security validation now in place.

The order validation (amount, currency, and notes.type === "sponsor") prevents unauthorized activation of sponsor slots using unrelated payments. This addresses the critical issue flagged in previous reviews.


107-122: Race condition resolved with upsert.

Replacing findUnique + create with upsert makes payment creation idempotent and prevents duplicate-key errors on concurrent webhook retries. This addresses the issue flagged in previous reviews.


220-242: Order validation prevents unauthorized sponsor activation.

The addition of order validation (amount, currency, notes.type) and enforcement of the pending_submission state prevents attackers from using arbitrary payments to activate sponsor slots. This addresses the critical authorization gap flagged in previous reviews.


282-300: PII exposure resolved with selective field projection.

The endpoint now uses select to return only public-facing fields (id, company_name, image_url, website, plan_status, created_at), removing the privacy issue with exposed contact details. This addresses the major issue flagged in previous reviews.

Note: The description field is currently omitted. If sponsor descriptions are meant to be displayed publicly on the landing page, consider adding it to the select clause.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/api/src/index.ts (1)

127-182: Replace Promise<any> with proper Cloudinary response typing.

The upload endpoint is well-structured with appropriate validation, rate limiting, and multipart enforcement. However, line 164 uses Promise<any> which violates the coding guideline to avoid any types.

Apply this diff to add proper typing:

+      // cloudinary upload response type
+      interface CloudinaryUploadResult {
+        secure_url: string;
+        public_id: string;
+        bytes: number;
+        format: string;
+      }
+
       // Stream upload to Cloudinary
       const folder = "opensox/sponsors";
-      const result = await new Promise<any>((resolve, reject) => {
+      const result = await new Promise<CloudinaryUploadResult>((resolve, reject) => {
         const stream = cloudinary.uploader.upload_stream({ folder }, (error, uploadResult) => {
           if (error) return reject(error);
-          resolve(uploadResult);
+          resolve(uploadResult as CloudinaryUploadResult);
         });
         stream.end(file.buffer);
       });

Based on coding guidelines: Avoid any type; use proper TypeScript types.

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

45-57: Use TRPCError for consistency with other procedures.

The uploadImage procedure throws a generic Error on line 55, while other procedures in this router consistently use TRPCError. This creates inconsistent error handling across the API.

Apply this diff:

         } catch (error) {
             console.error("cloudinary upload error:", error);
-            throw new Error("image upload failed");
+            throw new TRPCError({
+                code: "INTERNAL_SERVER_ERROR",
+                message: "image upload failed",
+                cause: error,
+            });
         }

This matches the error handling pattern used in other procedures (e.g., lines 208-212, 294-299).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da6089b and e89c77c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • apps/api/src/index.ts (4 hunks)
  • apps/api/src/routers/sponsor.ts (1 hunks)
  • apps/api/src/webhooks.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/webhooks.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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/index.ts
  • apps/api/src/routers/sponsor.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

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

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.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/index.ts
  • apps/api/src/routers/sponsor.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/index.ts
  • apps/api/src/routers/sponsor.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/index.ts
  • apps/api/src/routers/sponsor.ts
apps/api/src/**/*.ts

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

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

Files:

  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.ts
apps/api/src/{index,main,app,server}.{js,ts}

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

Validate required environment variables on startup; fail fast if missing

Files:

  • apps/api/src/index.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 239
File: apps/api/src/index.ts:139-166
Timestamp: 2025-12-06T08:42:03.292Z
Learning: In the sponsor feature (apps/api/src), sponsor-related upload endpoints like /upload/sponsor-image do not require user authentication because sponsors are paying customers who may not be registered OpenSox users. The uploaded assets are validated through payment verification in subsequent steps.
📚 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} : Implement rate limiting on all public endpoints to prevent abuse

Applied to files:

  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.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/**/{middleware,services,routes,controllers}/*.{js,ts} : Log all critical operations (auth attempts, payment processing, data mutations)

Applied to files:

  • apps/api/src/index.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/index.ts
  • apps/api/src/routers/sponsor.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} : Organize imports in order: React → third-party → local components → utils → types

Applied to files:

  • apps/api/src/index.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,jsx,js} : Organize imports: react → third-party → local components → utils → types

Applied to files:

  • apps/api/src/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:

  • apps/api/src/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} : Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)

Applied to files:

  • apps/api/src/index.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/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/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type

Applied to files:

  • apps/api/src/index.ts
📚 Learning: 2025-12-06T08:42:03.292Z
Learnt from: huamanraj
Repo: apsinghdev/opensox PR: 239
File: apps/api/src/index.ts:139-166
Timestamp: 2025-12-06T08:42:03.292Z
Learning: In the sponsor feature (apps/api/src), sponsor-related upload endpoints like /upload/sponsor-image do not require user authentication because sponsors are paying customers who may not be registered OpenSox users. The uploaded assets are validated through payment verification in subsequent steps.

Applied to files:

  • apps/api/src/index.ts
  • apps/api/src/routers/sponsor.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/{index,main,app,server}.{js,ts} : Validate required environment variables on startup; fail fast if missing

Applied to files:

  • apps/api/src/index.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/**/{config,environment,setup}/*.{js,ts} : Use type-safe environment variable access with proper defaults

Applied to files:

  • apps/api/src/index.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} : Never log sensitive information (passwords, tokens, credit card numbers)

Applied to files:

  • apps/api/src/index.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/index.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/index.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,services}/*.{js,ts} : Handle database connection failures gracefully with retry logic

Applied to files:

  • apps/api/src/index.ts
🧬 Code graph analysis (1)
apps/api/src/routers/sponsor.ts (3)
apps/api/src/trpc.ts (2)
  • router (38-38)
  • publicProcedure (39-39)
apps/api/src/services/payment.service.ts (1)
  • paymentService (54-257)
apps/api/src/clients/razorpay.ts (1)
  • rz_instance (18-21)
🔇 Additional comments (10)
apps/api/src/index.ts (5)

17-19: LGTM!

The new imports are properly organized at the top of the file and follow the coding guidelines for import organization.


23-36: LGTM!

The early environment variable validation follows the coding guideline to validate required env vars on startup and fail fast if missing. The implementation correctly checks for both missing and empty values.


90-92: LGTM!

The global request size limits have been appropriately reduced to 5mb to prevent DoS attacks, while the dedicated upload endpoint uses multer with its own stricter limits. This addresses the previous security concern.


239-240: LGTM!

The webhook handler is properly delegated to the centralized handleRazorpayWebhook function, and the import is correctly placed at the top of the file. This addresses the previous review concern about inline imports.


38-43: Remove duplicate Cloudinary configuration or consolidate to single initialization.

The original review comment raises a valid architectural concern: Cloudinary's SDK should be configured once at application startup, not in multiple modules. Web search confirms this aligns with Cloudinary best practices—initializing the SDK in one place creates a single source of truth and prevents fragmented configuration.

However, I cannot access the repository to verify that apps/api/src/routers/sponsor.ts contains the claimed duplicate configuration with empty string fallbacks. To complete this review, confirm whether sponsor.ts has a duplicate cloudinary.config() call and whether it can be safely removed without breaking functionality.

apps/api/src/routers/sponsor.ts (5)

18-20: LGTM!

The constants follow the coding guideline to use UPPER_SNAKE_CASE and are properly defined for the sponsor subscription pricing.


60-86: LGTM!

The createSubscription procedure correctly creates a Razorpay order with the fixed sponsor amount, currency, and includes the type: "sponsor" note that will be used for order validation in subsequent steps.


89-214: LGTM!

The verifyPayment procedure correctly implements critical security validations:

  • Signature verification (lines 100-104)
  • Order invariant validation for amount, currency, and sponsor type (lines 114-125)
  • Payment upsert to prevent race conditions (lines 128-143)
  • Proper TypeScript typing instead of any (lines 146-154)
  • Creates sponsor record with pending_submission status to enforce proper flow

This addresses all the critical security concerns from previous reviews.


252-279: LGTM!

The submitAssets procedure correctly implements critical security validations:

  • Fetches and validates the linked order's amount, currency, and sponsor type (lines 252-263)
  • Enforces the proper flow by checking for pending_submission state (lines 266-279)

This prevents unauthorized sponsor activation via payment replay attacks, addressing the critical security concern from previous reviews.


304-321: LGTM!

The getActiveSponsors procedure correctly returns only public-facing fields, excluding contact PII (email, phone). This addresses the privacy concern from previous reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] create sponsor for opensox

6 participants