Skip to content

Conversation

@apsinghdev
Copy link
Owner

@apsinghdev apsinghdev commented Nov 15, 2025

fixes: #120

Summary by CodeRabbit

  • New Features

    • Interactive OSS Sheet: per-module completion toggles, progress ring, PDF export, and share link.
    • Account Settings page and a Pro-only dashboard.
    • Module reader with download/share actions and improved profile image handling.
    • Small Pro badge component for sheet items.
  • Updates

    • Rebranded "Premium" → "Pro" across UI, pricing, and legal copy.
    • Sidebar and profile menu navigation refined; new Pro flows.
    • Many new learning modules added and wider remote avatar image support.

@vercel
Copy link

vercel bot commented Nov 15, 2025

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

Project Deployment Preview Comments Updated (UTC)
opensox-web Ready Ready Preview Comment Nov 15, 2025 5:53pm
opensox-website Ready Ready Preview Comment Nov 15, 2025 5:53pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 15, 2025

Walkthrough

Adds a sheet feature with 32 module data files and types, client pages for sheet listing and module docs (PDF/share), persistence for user completed steps (Prisma migration, schema, service + TRPC endpoints), UI updates (sidebar, profile, progress badge), branding text changes to "Pro", Tailwind tokens, and dompurify dependency.

Changes

Cohort / File(s) Summary
Database & Prisma
apps/api/prisma/migrations/20251114063002_add_completed_steps/migration.sql, apps/api/prisma/schema.prisma
Add completedSteps JSONB field to User (migration + Prisma schema optional Json? field).
API: services & routers
apps/api/src/services/user.service.ts, apps/api/src/routers/user.ts
Add getCompletedSteps and updateCompletedSteps service methods; add protected TRPC endpoints getCompletedSteps (query) and updateCompletedSteps (mutation with zod input).
Web: sheet data & types
apps/web/src/data/sheet/types.ts, apps/web/src/data/sheet/sheet.ts, apps/web/src/data/sheet/index.ts, apps/web/src/data/sheet/module-{0..31}.ts
New SheetModule type, aggregator sheetModules array, and 32 module files exporting module data (id, name, docContent HTML, videoUrl, optional comingSoon).
Web: sheet pages
apps/web/src/app/(main)/dashboard/sheet/page.tsx, apps/web/src/app/(main)/sheet/[moduleId]/page.tsx
New Sheet listing page with completion tracking, progress bar, PDF export, share; module doc page with DOMPurify sanitization, download/share actions.
Web: components (sheet & UI)
apps/web/src/components/sheet/ProgressBar.tsx, apps/web/src/components/sheet/OpensoxProBadge.tsx
Add ProgressBar component and OpensoxProBadge linking to pricing.
Web: dashboard & navigation
apps/web/src/app/(main)/dashboard/layout.tsx, apps/web/src/components/dashboard/Sidebar.tsx, apps/web/src/components/dashboard/DashboardHeader.tsx, apps/web/src/components/dashboard/ProfilePic.tsx, apps/web/src/components/sidebar/SidebarItem.tsx
Layout header link to home, sidebar additions (OSS Sheet route, Pro flow, profile dropdown), ProfilePic now accepts imageUrl prop with fallback/error handling, SidebarItem gains optional badge.
Web: new pages
apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx, apps/web/src/app/(main)/dashboard/account/page.tsx
Add Pro dashboard page (subscription-guarded) and Account settings page showing plan status and CTA to pricing.
Branding & copy updates
apps/web/src/app/(main)/(landing)/pricing/page.tsx, apps/web/src/app/(main)/legal/privacy/page.tsx, apps/web/src/app/(main)/legal/terms/page.tsx, apps/web/src/components/checkout/checkout-confirmation.tsx, apps/web/src/components/payment/PaymentFlow.tsx, apps/web/src/components/payment/RazorpayButton.tsx, apps/web/src/components/faq/faqData.ts
Replace "Premium" branding with "Pro" across pricing, legal, checkout, payment docs, and FAQ strings.
Removed component & layout cleanup
apps/web/src/components/landing-sections/SupportDropdown.tsx, apps/web/src/app/layout.tsx
Remove SupportDropdown component and its usage from root layout.
Config & deps
apps/web/next.config.js, apps/web/tailwind.config.ts, apps/web/package.json
Add lh3.googleusercontent.com to remotePatterns; add Tailwind tokens ox-sidebar, ox-content, ox-header; add dompurify runtime and @types/dompurify dev dependency.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SheetPage
    participant TRPC
    participant DB
    participant ModulePage
    Note over SheetPage,ModulePage: New sheet UI + module doc flows

    User->>SheetPage: GET /dashboard/sheet
    SheetPage->>TRPC: getCompletedSteps()
    TRPC->>DB: SELECT completedSteps FROM User
    DB-->>TRPC: completedSteps
    TRPC-->>SheetPage: completedSteps
    SheetPage->>SheetPage: render modules, ProgressBar

    User->>SheetPage: Toggle module checkbox
    SheetPage->>SheetPage: update local completedSteps
    SheetPage->>TRPC: updateCompletedSteps(updatedArray)
    TRPC->>DB: UPDATE User.completedSteps
    DB-->>TRPC: OK
    TRPC-->>SheetPage: success

    User->>ModulePage: Open /sheet/[moduleId]
    ModulePage->>ModulePage: lazy-load DOMPurify -> sanitize HTML
    User->>ModulePage: Download PDF / Share
    ModulePage->>Window: open printable HTML / copy URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas needing extra attention:

  • apps/web/src/app/(main)/dashboard/sheet/page.tsx — TRPC integration, state sync, mutation handling, PDF generation.
  • apps/web/src/app/(main)/sheet/[moduleId]/page.tsx — DOMPurify lazy load, sanitize fallback, use of dangerouslySetInnerHTML and print popup behavior.
  • apps/api/src/services/user.service.ts and migration — migration SQL vs Prisma schema alignment and handling of null/empty arrays.
  • apps/web/src/components/dashboard/Sidebar.tsx & ProfilePic.tsx — session handling, navigation, image fallback and onError behavior.
  • apps/web/src/data/sheet/sheet.ts — verify all module imports and ordering.

Possibly related PRs

"🐰
I hopped through modules, thirty-two in line,
Marked every step with a tiny sign,
Pro badges gleam and PDFs unfurl,
I nibble bugs and share the pearl,
Code, carrots, and a joyful twirl 🥕"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'add sheet and revamp dashboard' clearly summarizes the main changes: adding a new sheet feature and updating the dashboard UI.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #120: extensible sheet structure (32 modules), UI matching site vibe with dashboard revamp, optimized content with docs/videos/completion checks, downloadable PDF support, and Pro plan integration.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked issue objectives. Backend additions (completed steps tracking) directly support the completion check feature, branding changes (Premium→Pro) align with plan implementation, and all UI components serve the sheet/dashboard requirements.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/sheet

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

❤️ Share

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

Copy link
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: 26

♻️ Duplicate comments (6)
apps/web/src/data/sheet/module-22.ts (1)

10-10: Placeholder video URL (same as module-18).

This module also uses the Rick Roll placeholder video. See the comment on module-18 for details and a verification script to identify all placeholder URLs.

apps/web/src/data/sheet/module-30.ts (1)

10-10: Placeholder video URL (duplicate).

Same Rick Roll placeholder as modules 18 and 22. See module-18 review for the verification script.

apps/web/src/data/sheet/module-14.ts (1)

10-11: Replace placeholder video URL.

This module has the same placeholder video URL issue as module-21 (Rick Roll video). Consider using a proper placeholder or actual content URL.

See comment on module-21.ts for the same issue and suggested fix.

apps/web/src/data/sheet/module-12.ts (1)

3-12: Placeholder video URL detected.

Same placeholder video URL as flagged in module-15. Ensure all placeholder URLs are replaced with actual content before removing comingSoon: true.

apps/web/src/data/sheet/module-23.ts (1)

3-12: Placeholder video URL detected.

Same placeholder pattern as previous modules. This will be identified by the verification script suggested in the module-15 review.

apps/web/src/data/sheet/module-25.ts (1)

3-12: Placeholder video URL detected.

Same placeholder pattern as other modules under development.

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

455-455: Inconsistent brand name capitalization.

There are inconsistencies in how the brand name is capitalized within testimonials and throughout the page:

  • Line 455: "opensox Pro" (lowercase 'o')
  • Lines 465, 499: "OpenSox Pro" (capital 'O' and 'S')
  • Most other places: "Opensox Pro" (capital 'O', lowercase 's')

This inconsistency could confuse users about the official brand name. Standardize the capitalization to match your official branding (likely "Opensox Pro").

Also applies to: 465-465, 499-499

apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1)

32-35: Consider placeholder content tone.

The current message has an informal, personal tone ("hi investors, ajeetunc is on the way..."). While this may be intentional for early access or beta users, consider whether this matches your desired brand voice for production.

apps/api/prisma/migrations/20251114063002_add_completed_steps/migration.sql (1)

1-2: LGTM! Migration adds progress tracking support.

The completedSteps JSONB column appropriately supports flexible storage of user progress data for the "30 days open source series" feature.

Consider adding a GIN index if you plan to query or filter within the JSONB structure frequently:

CREATE INDEX idx_user_completed_steps ON "User" USING GIN ("completedSteps");

However, this can be deferred to a future migration based on actual query patterns and performance needs.

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

41-41: Consider documenting the expected JSON structure.

The completedSteps field uses the flexible Json? type. While the API layer validates this as string[] (array of module IDs), the schema itself doesn't enforce this structure. Consider adding a comment documenting the expected shape to help future maintainers understand the data contract.

Apply this diff to add documentation:

   lastLogin      DateTime       @updatedAt
+  /// Array of completed module IDs (e.g., ["module-0", "module-1"])
   completedSteps Json?
   accounts       Account[]
apps/web/src/data/sheet/module-8.ts (1)

8-8: Inconsistent capitalization of "OPEN SOURCE".

Line 8 uses "OPEN SOURCE" in all caps, while other modules (e.g., module-9, module-10) use normal casing. Maintain consistent terminology across all module documentation.

Apply this diff to fix the capitalization:

-      <p>Strategies and techniques for learning quickly while actively contributing to OPEN SOURCE projects.</p>
+      <p>Strategies and techniques for learning quickly while actively contributing to open source projects.</p>
apps/web/src/data/sheet/module-10.ts (1)

8-8: Inconsistent capitalization of "OPEN SOURCE".

Similar to module-8, this uses "OPEN SOURCE" in all caps. Standardize the terminology across all modules for consistency.

Apply this diff:

-      <p>Second live session on fixing and implementing an issue in an OPEN SOURCE project.</p>
+      <p>Second live session on fixing and implementing an issue in an open source project.</p>
apps/web/src/components/sheet/OpensoxProBadge.tsx (1)

17-22: Fragile conditional logic with string matching.

The conditional check className?.includes("px-1.5") on line 20 is brittle because it relies on substring matching. This could produce false positives if the className contains strings like "px-1.5rem" or other variants, and it couples the styling logic to specific class name patterns.

Consider one of these alternatives:

Option 1: Add a dedicated boolean prop

 interface OpensoxProBadgeProps {
   className?: string;
+  compact?: boolean;
 }

-export function OpensoxProBadge({ className }: OpensoxProBadgeProps) {
+export function OpensoxProBadge({ className, compact }: OpensoxProBadgeProps) {
   return (
     <Link href="/pricing">
       <div
         className={cn(
           "inline-flex items-center justify-center px-2.5 py-0.5 rounded-full border border-dashed border-ox-purple bg-ox-purple/20 cursor-pointer hover:bg-ox-purple/30 transition-colors",
           className
         )}
       >
         <span
           className={cn(
             "text-[10px] sm:text-xs font-bold text-[#c4a5f0]",
-            className?.includes("px-1.5") && "text-[8px]"
+            compact && "text-[8px]"
           )}
         >
           OX Pro
         </span>

Option 2: Use CSS data attributes

 interface OpensoxProBadgeProps {
   className?: string;
+  size?: "default" | "small";
 }

-export function OpensoxProBadge({ className }: OpensoxProBadgeProps) {
+export function OpensoxProBadge({ className, size = "default" }: OpensoxProBadgeProps) {
   return (
     <Link href="/pricing">
       <div
         className={cn(
           "inline-flex items-center justify-center px-2.5 py-0.5 rounded-full border border-dashed border-ox-purple bg-ox-purple/20 cursor-pointer hover:bg-ox-purple/30 transition-colors",
           className
         )}
       >
         <span
           className={cn(
             "text-[10px] sm:text-xs font-bold text-[#c4a5f0]",
-            className?.includes("px-1.5") && "text-[8px]"
+            size === "small" && "text-[8px]"
           )}
         >
apps/web/src/data/sheet/module-1.ts (1)

6-155: Consider extracting large HTML content to separate files.

The docContent spans 150 lines of inline HTML with embedded styles. While this works, consider these alternatives for better maintainability:

  • Store content as separate .html or .md files and import them
  • Use a CMS or content management system for easier editing
  • Extract common styles into reusable CSS classes

This is optional since the current pattern appears consistent across all modules.

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

17-21: Improve type safety by removing any type annotation.

The ctx parameter is typed as any, which bypasses TypeScript's type checking. Since protectedProcedure should provide proper typing for authenticated contexts, the explicit any annotation is unnecessary.

Apply this diff to improve type safety:

- getCompletedSteps: protectedProcedure.query(async ({ ctx }: any) => {
+ getCompletedSteps: protectedProcedure.query(async ({ ctx }) => {

23-37: Improve type safety by removing any type annotation.

Similar to getCompletedSteps, the any type annotation should be removed for better type safety.

Apply this diff:

  updateCompletedSteps: protectedProcedure
    .input(
      z.object({
        completedSteps: z.array(z.string()),
      })
    )
-   .mutation(async ({ ctx, input }: any) => {
+   .mutation(async ({ ctx, input }) => {
apps/web/src/components/sheet/ProgressBar.tsx (1)

35-56: Consider using Tailwind color tokens for consistency.

Lines 39 and 48 use hardcoded color values (rgb(38 38 38) and #9455f4) while the rest of the component uses Tailwind utilities. For better maintainability and theme consistency, consider defining these as Tailwind color tokens.

If custom colors are defined in your Tailwind config, refactor to use them:

           <circle
             cx="32"
             cy="32"
             r="28"
-            stroke="rgb(38 38 38)"
+            stroke="currentColor"
+            className="text-neutral-800"
             strokeWidth="6"
             fill="none"
           />
           {/* Progress circle */}
           <circle
             cx="32"
             cy="32"
             r="28"
-            stroke="#9455f4"
+            stroke="currentColor"
+            className="text-purple-500"
             strokeWidth="6"
             fill="none"
             strokeDasharray={circumference}
             strokeDashoffset={offset}
             strokeLinecap="round"
-            className="transition-all duration-500 ease-in-out"
+            className="transition-all duration-500 ease-in-out text-purple-500"
           />
apps/web/src/components/dashboard/ProfilePic.tsx (1)

7-55: Robust external/fallback logic; consider tightening isExternal typing

The component’s behavior (prop‑driven image, error fallback to local avatar, and border styling) looks solid and matches the header/sidebar usage. One small improvement would be to ensure isExternal is explicitly boolean so its intent is clearer and TypeScript doesn’t widen it to string | false:

-  const isExternal =
-    imageUrl &&
-    (imageUrl.startsWith("http://") || imageUrl.startsWith("https://"));
+  const isExternal = !!(
+    imageUrl &&
+    (imageUrl.startsWith("http://") || imageUrl.startsWith("https://"))
+  );
apps/web/src/components/dashboard/Sidebar.tsx (1)

53-66: Pro navigation logic works; consider handling the loading state explicitly

The Pro CTA correctly sends paid users to /dashboard/pro/dashboard and everyone else to /pricing, and the expanded vs collapsed rendering looks good. One UX edge case: while useSubscription is still loading, isPaidUser will be falsy, so a paid user who clicks very quickly could be routed to /pricing instead of the Pro dashboard.

If you want to avoid that, you could expose isLoading from useSubscription() here and either:

  • Disable the Pro entry while loading, or
  • Only treat users as unpaid when isPaidUser === false and not undefined.

Example sketch:

const { isPaidUser, isLoading } = useSubscription();

const proClickHandler = () => {
  if (isLoading) return; // or show a spinner/toast instead
  if (isPaidUser) router.push("/dashboard/pro/dashboard");
  else router.push("/pricing");
};

Also applies to: 132-154

apps/web/src/app/(main)/dashboard/sheet/page.tsx (4)

58-67: Consider adding authentication guard for better UX.

While the backend will reject unauthenticated requests, checking authentication upfront provides better user experience by preventing unnecessary UI updates and showing appropriate feedback.

 const handleCheckboxChange = (moduleId: string, checked: boolean) => {
+  if (!session?.user) {
+    // Could show a toast notification here
+    return;
+  }
   let newCompletedSteps: string[];
   if (checked) {

149-151: Add comment explaining the setTimeout delay.

The 250ms delay before printing is a common workaround to ensure the document is fully loaded before the print dialog opens, but this isn't immediately obvious.

     printWindow.document.write(htmlContent);
     printWindow.document.close();
     printWindow.focus();
+    // Wait for document to fully load before triggering print dialog
     setTimeout(() => {
       printWindow.print();
     }, 250);

154-163: Consider showing user feedback on clipboard failure.

The clipboard operation can fail due to browser permissions or security restrictions. While logging is helpful for debugging, users won't see console errors.

   try {
     await navigator.clipboard.writeText(url);
     setCopied(true);
     setTimeout(() => setCopied(false), 2000);
   } catch (clipboardErr) {
     console.error("Failed to copy:", clipboardErr);
+    // Could show a toast notification: "Failed to copy link"
   }

165-171: Consider handling unauthenticated state explicitly.

While the dashboard route likely has authentication middleware, explicitly handling the unauthenticated state improves code clarity and provides better error messages if the middleware configuration changes.

   if (status === "loading" || isLoadingSteps) {
     return (
       <div className="w-full p-6 flex items-center justify-center h-[80vh]">
         <p className="text-ox-gray">Loading...</p>
       </div>
     );
   }
+
+  if (!session?.user) {
+    return (
+      <div className="w-full p-6 flex items-center justify-center h-[80vh]">
+        <p className="text-ox-gray">Please sign in to view this page.</p>
+      </div>
+    );
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5445fd9 and 07582c8.

📒 Files selected for processing (61)
  • apps/api/prisma/migrations/20251114063002_add_completed_steps/migration.sql (1 hunks)
  • apps/api/prisma/schema.prisma (1 hunks)
  • apps/api/src/routers/user.ts (2 hunks)
  • apps/api/src/services/user.service.ts (1 hunks)
  • apps/web/next.config.js (1 hunks)
  • apps/web/src/app/(main)/(landing)/pricing/page.tsx (9 hunks)
  • apps/web/src/app/(main)/dashboard/account/page.tsx (1 hunks)
  • apps/web/src/app/(main)/dashboard/layout.tsx (2 hunks)
  • apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1 hunks)
  • apps/web/src/app/(main)/dashboard/sheet/page.tsx (1 hunks)
  • apps/web/src/app/(main)/legal/privacy/page.tsx (1 hunks)
  • apps/web/src/app/(main)/legal/terms/page.tsx (5 hunks)
  • apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1 hunks)
  • apps/web/src/app/layout.tsx (0 hunks)
  • apps/web/src/components/checkout/checkout-confirmation.tsx (1 hunks)
  • apps/web/src/components/dashboard/DashboardHeader.tsx (3 hunks)
  • apps/web/src/components/dashboard/ProfilePic.tsx (1 hunks)
  • apps/web/src/components/dashboard/Sidebar.tsx (6 hunks)
  • apps/web/src/components/faq/faqData.ts (1 hunks)
  • apps/web/src/components/landing-sections/SupportDropdown.tsx (0 hunks)
  • apps/web/src/components/payment/PaymentFlow.tsx (1 hunks)
  • apps/web/src/components/payment/RazorpayButton.tsx (1 hunks)
  • apps/web/src/components/sheet/OpensoxProBadge.tsx (1 hunks)
  • apps/web/src/components/sheet/ProgressBar.tsx (1 hunks)
  • apps/web/src/components/sidebar/SidebarItem.tsx (2 hunks)
  • apps/web/src/data/sheet/index.ts (1 hunks)
  • apps/web/src/data/sheet/module-0.ts (1 hunks)
  • apps/web/src/data/sheet/module-1.ts (1 hunks)
  • apps/web/src/data/sheet/module-10.ts (1 hunks)
  • apps/web/src/data/sheet/module-11.ts (1 hunks)
  • apps/web/src/data/sheet/module-12.ts (1 hunks)
  • apps/web/src/data/sheet/module-13.ts (1 hunks)
  • apps/web/src/data/sheet/module-14.ts (1 hunks)
  • apps/web/src/data/sheet/module-15.ts (1 hunks)
  • apps/web/src/data/sheet/module-16.ts (1 hunks)
  • apps/web/src/data/sheet/module-17.ts (1 hunks)
  • apps/web/src/data/sheet/module-18.ts (1 hunks)
  • apps/web/src/data/sheet/module-19.ts (1 hunks)
  • apps/web/src/data/sheet/module-2.ts (1 hunks)
  • apps/web/src/data/sheet/module-20.ts (1 hunks)
  • apps/web/src/data/sheet/module-21.ts (1 hunks)
  • apps/web/src/data/sheet/module-22.ts (1 hunks)
  • apps/web/src/data/sheet/module-23.ts (1 hunks)
  • apps/web/src/data/sheet/module-24.ts (1 hunks)
  • apps/web/src/data/sheet/module-25.ts (1 hunks)
  • apps/web/src/data/sheet/module-26.ts (1 hunks)
  • apps/web/src/data/sheet/module-27.ts (1 hunks)
  • apps/web/src/data/sheet/module-28.ts (1 hunks)
  • apps/web/src/data/sheet/module-29.ts (1 hunks)
  • apps/web/src/data/sheet/module-3.ts (1 hunks)
  • apps/web/src/data/sheet/module-30.ts (1 hunks)
  • apps/web/src/data/sheet/module-31.ts (1 hunks)
  • apps/web/src/data/sheet/module-4.ts (1 hunks)
  • apps/web/src/data/sheet/module-5.ts (1 hunks)
  • apps/web/src/data/sheet/module-6.ts (1 hunks)
  • apps/web/src/data/sheet/module-7.ts (1 hunks)
  • apps/web/src/data/sheet/module-8.ts (1 hunks)
  • apps/web/src/data/sheet/module-9.ts (1 hunks)
  • apps/web/src/data/sheet/sheet.ts (1 hunks)
  • apps/web/src/data/sheet/types.ts (1 hunks)
  • apps/web/tailwind.config.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/src/app/layout.tsx
  • apps/web/src/components/landing-sections/SupportDropdown.tsx
🧰 Additional context used
🧬 Code graph analysis (45)
apps/web/src/data/sheet/module-11.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-6.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-25.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/components/sheet/OpensoxProBadge.tsx (2)
apps/web/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/ui/badge.tsx (2)
  • Badge (30-34)
  • BadgeProps (26-28)
apps/web/src/data/sheet/module-15.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-4.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-13.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-12.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-29.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/dashboard/account/page.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-75)
apps/web/src/data/sheet/module-18.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/components/sheet/ProgressBar.tsx (1)
apps/web/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/data/sheet/module-30.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-26.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-3.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-17.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-1.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-19.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-24.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-31.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-28.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-10.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-9.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-20.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/api/src/routers/user.ts (2)
apps/api/src/trpc.ts (1)
  • protectedProcedure (40-40)
apps/api/src/services/user.service.ts (1)
  • userService (5-89)
apps/web/src/data/sheet/module-23.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (3)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-75)
apps/web/src/components/checkout/CheckoutWrapper.tsx (1)
  • CheckoutWrapper (8-33)
apps/web/src/app/(main)/dashboard/page.tsx (1)
  • Dashboard (3-9)
apps/web/src/data/sheet/module-0.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)
apps/web/src/data/sheet/sheet.ts (1)
  • sheetModules (36-69)
apps/web/src/data/sheet/module-16.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-21.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-2.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/components/sidebar/SidebarItem.tsx (1)
apps/web/src/components/ui/badge.tsx (2)
  • BadgeProps (26-28)
  • Badge (30-34)
apps/web/src/data/sheet/module-14.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/dashboard/layout.tsx (4)
apps/web/src/components/dashboard/Sidebar.tsx (1)
  • Sidebar (49-161)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/components/blogs/BlogHeader.tsx (1)
  • BlogHeader (5-24)
apps/web/src/components/landing-sections/Hero.tsx (1)
  • div (9-80)
apps/api/src/services/user.service.ts (1)
apps/api/src/prisma.ts (1)
  • ExtendedPrismaClient (121-121)
apps/web/src/data/sheet/sheet.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-7.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/dashboard/sheet/page.tsx (6)
apps/web/src/lib/auth/config.ts (1)
  • session (43-49)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/data/sheet/sheet.ts (1)
  • sheetModules (36-69)
apps/web/src/components/sheet/ProgressBar.tsx (1)
  • ProgressBar (11-64)
apps/web/src/components/icons/icons.tsx (1)
  • Youtube (200-202)
apps/web/src/components/sheet/OpensoxProBadge.tsx (1)
  • OpensoxProBadge (8-28)
apps/web/src/data/sheet/module-8.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-5.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/components/dashboard/Sidebar.tsx (5)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-75)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/components/sheet/OpensoxProBadge.tsx (1)
  • OpensoxProBadge (8-28)
apps/web/src/components/sidebar/SidebarItem.tsx (1)
  • SidebarItem (13-32)
apps/web/src/components/dashboard/ProfilePic.tsx (1)
  • ProfilePic (11-59)
apps/web/src/data/sheet/module-22.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/components/dashboard/DashboardHeader.tsx (2)
apps/web/src/lib/auth/config.ts (1)
  • session (43-49)
apps/web/src/components/dashboard/ProfilePic.tsx (1)
  • ProfilePic (11-59)
apps/web/src/data/sheet/module-27.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[warning] 162-162: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[error] 163-163: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (40)
apps/web/src/components/payment/RazorpayButton.tsx (1)

29-46: Documentation example aligns with global Pro branding update.

The JSDoc example correctly reflects the branding change from "Opensox Premium" to "Opensox Pro," consistent with the broader rebranding across the app in this PR.

apps/web/src/app/(main)/legal/privacy/page.tsx (1)

354-354: LGTM! Branding terminology updated consistently.

The update from "premium subscribers" to "Pro subscribers" aligns with the broader Pro branding initiative across the application.

apps/web/src/app/(main)/legal/terms/page.tsx (1)

62-62: LGTM! Pro branding consistently applied throughout Terms of Service.

All references to "Premium" have been properly updated to "Pro" across multiple sections (service descriptions, subscription terms, payment terms, and mentoring services). The changes are consistent with the application-wide rebranding effort.

Also applies to: 139-139, 142-142, 164-164, 173-173, 265-265

apps/web/src/app/(main)/(landing)/pricing/page.tsx (6)

64-64: LGTM: Branding updates in subscription messaging.

The updates from "Premium" to "Pro" are consistent with the rebranding effort.

Also applies to: 72-72


96-96: LGTM: Consistent rebranding in plan features.

The feature descriptions have been correctly updated to reflect the "Pro" branding.

Also applies to: 99-99, 103-106


205-205: LGTM: Section heading updated.

The section heading correctly reflects the new "Pro" branding.


380-380: LGTM: PaymentFlow component prop updated.

The planName prop correctly uses the new "Pro" branding, maintaining consistency in the checkout flow.


435-435: LGTM: Testimonial card badge updated.

The badge text in the testimonial card correctly reflects the "Pro" branding.


537-537: LGTM: Testimonials section heading updated.

The section heading correctly reflects the new "Pro" customer branding.

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

78-84: LGTM! Branding update is consistent.

The text changes from "premium" to "Pro" align with the broader rebranding effort across the application.

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

9-12: LGTM! Google profile images support added.

Adding lh3.googleusercontent.com is appropriate for supporting Google Sign-In profile images.

apps/web/tailwind.config.ts (1)

60-62: LGTM! New theme colors added for dashboard.

The new ox-sidebar, ox-content, and ox-header color tokens provide consistent theming for the dashboard UI components. The naming convention aligns with existing color tokens.

apps/web/src/components/payment/PaymentFlow.tsx (1)

32-40: LGTM! Consistent branding update.

The default planName prop and documentation have been updated from "Opensox Premium" to "Opensox Pro", maintaining consistency with the rebranding effort.

apps/web/src/app/(main)/dashboard/account/page.tsx (1)

1-44: LGTM! Simple and clean account settings page.

The account page correctly uses the subscription hook to display the user's plan status. The UI is straightforward with appropriate use of the new theme tokens.

Minor observation: The page doesn't show a loading state while the subscription status is being fetched, but since useSubscription manages its own loading state internally (per the provided snippet), this is acceptable as the page will render with the current state.

apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1)

11-15: LGTM! Correct subscription guard pattern.

The useEffect hook properly redirects non-paid users to the pricing page, preventing unauthorized access to Pro features.

apps/web/src/components/sidebar/SidebarItem.tsx (2)

10-10: LGTM! Badge support added to sidebar items.

The optional badge prop enables displaying additional UI elements (like Pro badges) next to item names, which aligns with the Pro feature additions in this PR.


23-28: LGTM! Badge rendering is correctly positioned.

The badge is appropriately rendered only when the sidebar is not collapsed and is positioned next to the item name using flexbox layout.

apps/web/src/data/sheet/module-16.ts (1)

3-12: Module structure looks good.

The module definition follows the expected structure and has a unique, descriptive name. The placeholder video URL and comingSoon flag will need to be updated with actual content before release.

apps/web/src/app/(main)/dashboard/layout.tsx (2)

8-8: Good addition of navigation link.

Adding the Link component for home navigation improves the mobile header UX and aligns with the navigation pattern used in other components like Sidebar and BlogHeader.

Also applies to: 28-30


18-18: Design token updates look consistent.

The new design tokens (bg-ox-content, border-ox-header) are applied consistently across the layout and align with the broader design system updates in this PR.

Also applies to: 23-24, 32-32

apps/web/src/data/sheet/module-10.ts (1)

3-12: Module structure is correct.

The module has a unique name and follows the expected structure. Note the placeholder video URL will need to be replaced with actual content.

apps/web/src/data/sheet/index.ts (1)

1-2: LGTM!

Clean barrel export pattern for the sheet module system. The type and data exports provide a well-structured public API.

apps/web/src/data/sheet/module-4.ts (1)

1-18: LGTM!

Good consistency between the embedded video link in docContent (line 7) and the videoUrl field (line 17). The module structure is clean and follows the established pattern.

apps/web/src/data/sheet/module-1.ts (1)

39-39: Image assets verified—no issues found.

Both referenced image files (/images/doc.webp and /images/dm.webp) exist in the public directory and will load correctly in the UI.

apps/web/src/data/sheet/module-0.ts (1)

6-111: All image assets verified and present in the public directory.

All five images referenced in the module have been confirmed to exist at the expected paths in apps/web/public/images/. No action required.

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

3-3: LGTM! Zod import added for input validation.

The import is correctly placed and used for validating the updateCompletedSteps endpoint input.

apps/web/src/data/sheet/sheet.ts (1)

1-70: LGTM! Clean module aggregation pattern.

The aggregator correctly imports all 32 modules (0-31) and exports them in a well-organized array. The structure makes it easy to add or modify modules in the future, aligning with the PR objective of making the sheet extensible.

apps/web/src/data/sheet/module-7.ts (1)

1-24: LGTM! Concise module with clear instructions.

The module appropriately directs users to follow along with the video for hands-on implementation. The structure is clean and consistent with other modules.

apps/web/src/data/sheet/module-6.ts (1)

1-194: LGTM! Comprehensive guide for issue selection.

The module provides detailed, practical guidance on finding and selecting appropriate issues for first-time contributors, with clear steps and realistic examples. The content quality is high and aligns well with the educational objectives of the sheet feature.

apps/web/src/data/sheet/module-3.ts (1)

1-154: LGTM! Educational content is well-organized and all external links are accessible.

The module provides comprehensive guidance on discovering open source projects with multiple practical methods and examples. Verification of the three external URLs (opensox.ai, algora.io, and github.com/trending) confirms they are all currently accessible and responding normally.

apps/web/src/data/sheet/types.ts (1)

1-7: LGTM! Clean interface definition.

The SheetModule interface is well-structured with appropriate types and helpful inline comments. The optional comingSoon field provides flexibility for marking modules under development.

apps/web/src/data/sheet/module-2.ts (1)

3-189: LGTM! Comprehensive educational content.

Module 2 demonstrates the expected quality for production modules with detailed educational content, proper formatting, and a valid video URL. The inline styling is consistent and the content is well-structured.

apps/web/src/components/sheet/ProgressBar.tsx (1)

11-14: LGTM! Correct progress calculation.

The percentage calculation properly handles division by zero, and the SVG math for the circular progress indicator is accurate.

apps/api/src/services/user.service.ts (1)

54-69: <!-- [scratchpad_start]
[task verification_2/2 continued]
[observations]

  • ExtendedPrismaClient imported from "../prisma.js"
  • Used in union with PrismaClient: ExtendedPrismaClient | PrismaClient
  • Line 58 and 79 both use (prisma as any) cast
  • Other methods like getUserCount (line 9) use prisma without cast
  • Both getCompletedSteps and updateCompletedSteps use the cast

[analysis]

  • The pattern suggests ExtendedPrismaClient may not be properly typed to include user model or completedSteps field
  • Union type might need type narrowing or proper interface definition
  • Need to see how ExtendedPrismaClient is defined in prisma.js

[pending]

  • View prisma.js to see ExtendedPrismaClient definition
  • Determine if cast is necessary due to type definition gap
  • Check if other methods also need the cast

[actions]

  • Read apps/api/src/prisma.ts or prisma.js
  • Analyze ExtendedPrismaClient type definition
#!/bin/bash
# Find and read the prisma.js/prisma.ts file
echo "=== Looking for prisma type definition file ==="
find apps/api/src -name "prisma.*" -type f

echo -e "\n=== Reading prisma.js or prisma.ts ==="
cat apps/api/src/prisma.ts 2>/dev/null || cat apps/api/src/prisma.js 2>/dev/null || echo "File not found"

echo -e "\n=== Checking if completedSteps is accessed in other places ==="
rg -n "completedSteps" apps/api/src --type=ts
apps/web/src/data/sheet/module-24.ts (1)

3-11: Module definition is consistent; please confirm the video URL

module24 matches the SheetModule shape and the comingSoon flag will be respected by the module page. The only thing to double‑check is the videoUrl (dQw4w9WgXcQ), which looks like a placeholder rather than a final playlist video.

apps/web/src/components/dashboard/DashboardHeader.tsx (1)

9-16: Session‑aware avatar and header link look good

Using useSession to source session?.user?.image and wiring it into ProfilePic, plus turning the title into a Link to /, is straightforward and correct. No issues from a logic or UX perspective.

Also applies to: 32-34, 46-46

apps/web/src/components/dashboard/Sidebar.tsx (2)

6-7: Sidebar routing and branding updates are consistent

The added /dashboard/sheet entry, updated “OSS Projects” label, and mobile/desktop headers using bg-ox-sidebar and border-ox-header all align well with the new dashboard look, and the use of usePathname for active state is consistent with the existing pattern.

Also applies to: 17-19, 26-42, 70-75, 77-111


163-255: Profile dropdown implementation looks solid

The ProfileMenu makes good use of useSession for name/email/image, reuses ProfilePic, and the outside‑click handler tied to .profile-menu-container is a clean way to close the dropdown. The account navigation and logout actions are straightforward and correctly wired through useRouter and signOut.

apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)

10-22: Module resolution, “coming soon” gating, and actions behave as expected

Using useParams to derive moduleId, looking up currentModule from sheetModules, and calling notFound() when missing is a clean flow. The conditional comingSoon branch gives a clear “Coming Soon” experience while still showing the module name and a “Back to Sheet” link, and the main branch combines Back, Download, and Share actions in a predictable layout.

handleDownloadPDF and handleShare are defensive (both guard against missing module and handle window/clipboard failures) and fulfill the “downloadable notes + shareable link” requirements without obvious runtime pitfalls.

Also applies to: 76-85, 87-117, 119-158

apps/web/src/app/(main)/dashboard/sheet/page.tsx (1)

37-42: Remove type assertion to preserve type safety.

The as any cast bypasses TypeScript's type checking. The TRPC client is properly configured with createTRPCReact<AppRouter>() and imports the complete router type, so trpc.user should be properly typed without the cast. This pattern appears in multiple files (useSubscription.ts, sheet/page.tsx), suggesting a systemic issue worth resolving at the source.

However, verify that removing the cast compiles without TypeScript errors. If errors occur, the underlying TRPC type definitions or router exports need investigation rather than suppressing the warning with as any.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
apps/web/src/data/sheet/module-19.ts (1)

3-11: Resolve duplicate module name and placeholder videoUrl.

module19 uses the same name as module-11 (“Live fix/implement the issue - 3”) and was previously called out as duplicating module-28 as well. That makes it hard to distinguish modules in the UI. Consider giving this a unique, more specific title (e.g., by topic or session tag) or updating the other module(s) so each name is unique.

Also, videoUrl is currently empty while comingSoon is true. As with other modules, ensure the UI doesn’t try to render a video for an empty URL and plan to replace this with the actual playlist link before making it live.

apps/web/src/data/sheet/module-17.ts (1)

5-5: Duplicate module name with module-9 (already flagged).

This concern was previously raised: module-17 and module-9 both use "Live fix/implement the issue - 1" as the name, causing potential user confusion.

apps/web/src/app/(main)/dashboard/sheet/page.tsx (1)

310-311: S.No displays 0-based indexing instead of 1-based (already flagged).

This was previously identified: the S.No column shows 0-based indexing (0, 1, 2...) while it should display 1-based serial numbers (1, 2, 3...) to match user expectations and the PDF export (line 173).

🧹 Nitpick comments (2)
apps/web/src/app/(main)/dashboard/account/page.tsx (2)

10-10: Plan derivation is simple but may need expansion.

The plan label is derived solely from the isPaidUser boolean. While this works for the current Free/Pro model, consider using the subscription object from useSubscription() if you plan to support multiple paid tiers in the future (e.g., Pro, Team, Enterprise).


27-51: Clean implementation of the plan card.

The plan display and CTA are well-implemented. The layout is minimal but functional for showing subscription status.

Optional enhancements to consider:

  • Display additional account details (e.g., email, subscription start date) using the subscription object from the hook.
  • Add an aria-label to the "Active" badge for screen readers: aria-label="Plan is active".
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07582c8 and 63a4519.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • apps/api/src/services/user.service.ts (1 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/src/app/(main)/dashboard/account/page.tsx (1 hunks)
  • apps/web/src/app/(main)/dashboard/sheet/page.tsx (1 hunks)
  • apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1 hunks)
  • apps/web/src/data/sheet/module-0.ts (1 hunks)
  • apps/web/src/data/sheet/module-10.ts (1 hunks)
  • apps/web/src/data/sheet/module-11.ts (1 hunks)
  • apps/web/src/data/sheet/module-12.ts (1 hunks)
  • apps/web/src/data/sheet/module-13.ts (1 hunks)
  • apps/web/src/data/sheet/module-14.ts (1 hunks)
  • apps/web/src/data/sheet/module-15.ts (1 hunks)
  • apps/web/src/data/sheet/module-16.ts (1 hunks)
  • apps/web/src/data/sheet/module-17.ts (1 hunks)
  • apps/web/src/data/sheet/module-18.ts (1 hunks)
  • apps/web/src/data/sheet/module-19.ts (1 hunks)
  • apps/web/src/data/sheet/module-20.ts (1 hunks)
  • apps/web/src/data/sheet/module-21.ts (1 hunks)
  • apps/web/src/data/sheet/module-22.ts (1 hunks)
  • apps/web/src/data/sheet/module-23.ts (1 hunks)
  • apps/web/src/data/sheet/module-24.ts (1 hunks)
  • apps/web/src/data/sheet/module-25.ts (1 hunks)
  • apps/web/src/data/sheet/module-26.ts (1 hunks)
  • apps/web/src/data/sheet/module-27.ts (1 hunks)
  • apps/web/src/data/sheet/module-28.ts (1 hunks)
  • apps/web/src/data/sheet/module-29.ts (1 hunks)
  • apps/web/src/data/sheet/module-30.ts (1 hunks)
  • apps/web/src/data/sheet/module-31.ts (1 hunks)
  • apps/web/src/data/sheet/module-8.ts (1 hunks)
  • apps/web/src/data/sheet/module-9.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • apps/web/src/data/sheet/module-27.ts
  • apps/web/src/data/sheet/module-16.ts
  • apps/web/src/data/sheet/module-12.ts
  • apps/web/src/data/sheet/module-9.ts
  • apps/web/src/data/sheet/module-21.ts
  • apps/web/src/data/sheet/module-29.ts
  • apps/web/src/data/sheet/module-18.ts
  • apps/web/src/data/sheet/module-13.ts
  • apps/api/src/services/user.service.ts
  • apps/web/src/data/sheet/module-14.ts
  • apps/web/src/data/sheet/module-24.ts
  • apps/web/src/data/sheet/module-10.ts
  • apps/web/src/data/sheet/module-23.ts
  • apps/web/src/data/sheet/module-30.ts
  • apps/web/src/data/sheet/module-28.ts
  • apps/web/src/data/sheet/module-0.ts
  • apps/web/src/data/sheet/module-20.ts
  • apps/web/src/data/sheet/module-8.ts
🧰 Additional context used
🧬 Code graph analysis (11)
apps/web/src/data/sheet/module-26.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/dashboard/account/page.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-75)
apps/web/src/data/sheet/module-11.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-31.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-25.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-15.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-17.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/data/sheet/module-19.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)
apps/web/src/data/sheet/sheet.ts (1)
  • sheetModules (36-69)
apps/web/src/app/(main)/dashboard/sheet/page.tsx (5)
apps/web/src/lib/trpc.ts (1)
  • trpc (4-4)
apps/web/src/data/sheet/sheet.ts (1)
  • sheetModules (36-69)
apps/web/src/components/sheet/ProgressBar.tsx (1)
  • ProgressBar (11-64)
apps/web/src/components/icons/icons.tsx (1)
  • Youtube (200-202)
apps/web/src/components/sheet/OpensoxProBadge.tsx (1)
  • OpensoxProBadge (8-28)
apps/web/src/data/sheet/module-22.ts (1)
apps/web/src/data/sheet/types.ts (1)
  • SheetModule (1-7)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[warning] 205-205: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[error] 206-206: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (11)
apps/web/package.json (1)

27-27: Add DOMPurify runtime + types — looks consistent.

Adding dompurify and @types/dompurify matches the new HTML sanitization usage in the sheet UI; nothing else to flag from this manifest change alone.

Please double‑check that DOMPurify is only used in browser/client components or behind window/document guards so it doesn’t break on the Next.js server.

Also applies to: 45-45

apps/web/src/data/sheet/module-22.ts (1)

3-11: Remove review comment — UI guards are already in place.

The dashboard page (line 349–365) conditionally renders the video link only when isComingSoon is false. When comingSoon: true, a disabled span is shown instead of a link. The module detail page (line 129) also gates access to coming-soon modules. Since module-22 has both videoUrl: "" and comingSoon: true, the empty URL is never exposed to users. The pattern is intentional and safe.

Likely an incorrect or invalid review comment.

apps/web/src/data/sheet/module-26.ts (1)

3-11: Empty videoUrl is properly guarded by UI — no action needed.

The sheet UI correctly handles this case. When comingSoon: true (line 302 of page.tsx), the conditional rendering (lines 349-365) displays a disabled YouTube icon instead of rendering a Link component, so the empty videoUrl is never accessed. The broken player concern is mitigated by the existing isComingSoon guard. When the module transitions to comingSoon: false, ensure videoUrl is populated with the actual YouTube link before release.

apps/web/src/data/sheet/module-11.ts (1)

3-11: Review comment is incorrect—the UI already guards against empty videoUrl.

The page component explicitly checks if (currentModule.comingSoon) at line 129 of apps/web/src/app/(main)/sheet/[moduleId]/page.tsx and returns early with a "Coming Soon" message, preventing any module content (including video) from rendering. The videoUrl field is not used anywhere in the page rendering logic, so no broken embed can occur. The codebase is already safe; no additional UI guards are needed.

Likely an incorrect or invalid review comment.

apps/web/src/data/sheet/module-15.ts (1)

10-11: Empty videoUrl with comingSoon flag is appropriate.

The previous concern about placeholder video URLs has been resolved. Using an empty string for videoUrl combined with comingSoon: true correctly indicates this module is not yet ready.

apps/web/src/data/sheet/module-31.ts (1)

10-11: Empty videoUrl with comingSoon flag is appropriate.

The placeholder video URL issue has been addressed. Using an empty string combined with comingSoon: true correctly signals this module is under development.

apps/web/src/data/sheet/module-25.ts (1)

3-12: Module structure looks good.

The module definition follows the established pattern with appropriate use of comingSoon: true for modules under development.

apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (2)

11-37: Solid sanitization implementation with appropriate fallbacks.

The sanitizeHTMLSync function correctly:

  • Returns unsanitized HTML during SSR (line 15-17) to avoid hydration mismatches
  • Lazy-loads DOMPurify only on the client (line 22)
  • Handles both default and named exports (line 24)
  • Provides graceful fallback if DOMPurify fails to load (lines 29-35)

This is a well-thought-out approach for client-side HTML sanitization.


51-112: PDF generation correctly uses sanitized content.

The handleDownloadPDF function properly sanitizes both the module name (line 57-59) and content (line 100) before writing to the print window, maintaining security throughout the document generation flow.

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

60-92: Excellent optimistic update implementation with proper error handling.

The mutation correctly implements:

  • onMutate: Cancels in-flight queries, snapshots previous state, and updates optimistically (lines 61-72)
  • onSuccess: Synchronizes with server response (lines 74-76)
  • onError: Rolls back to previous state on failure (lines 77-88)
  • onSettled: Invalidates cache to ensure consistency (lines 89-91)

This follows TRPC optimistic update best practices and resolves the previous concern about missing error handling.


38-52: Type assertions are documented and acceptable for TRPC union narrowing.

The explicit type intersections with inline documentation (lines 38-39) address TypeScript's difficulty narrowing TRPC procedure union types. While not ideal, this is a pragmatic workaround that:

  • Documents why the assertion is necessary
  • Maintains runtime type safety (procedures are correctly typed at runtime)
  • Provides explicit type signatures for IDE support

This resolves the previous concern about unsafe type assertions.

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

♻️ Duplicate comments (1)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)

235-236: Replace ESLint comment with Biome ignore directive (previously requested).

As noted in the previous review, the ESLint disable comment on line 235 does not suppress Biome's lint/security/noDangerouslySetInnerHtml rule. The static analysis tools are still flagging line 236.

Apply this diff:

-            // eslint-disable-next-line react/no-danger -- Safe: docContent is sanitized with DOMPurify before rendering
+            {/* biome-ignore lint/security/noDangerouslySetInnerHtml: docContent is sanitized with DOMPurify before rendering */}
             dangerouslySetInnerHTML={{ __html: sanitizedDocContent }}

</review_comment_end>

🧹 Nitpick comments (2)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (2)

38-57: Consider guarding against rendering until DOMPurify loads to eliminate XSS window.

The sanitizeHTMLSync function returns unsanitized HTML if DOMPurifyInstance is null (lines 47-51). While the current implementation relies on trusted, code-authored content from static modules (per past review discussion), returning unsanitized HTML creates a brief window where XSS could occur if the content source ever changes to include untrusted input.

For defense in depth, consider either:

  1. Rendering a loading skeleton until isDOMPurifyLoaded is true, or
  2. Throwing an error or returning an empty string if DOMPurify isn't ready yet

This would make the trust boundary more explicit and prevent accidental XSS if future refactoring changes the content source.

</review_comment_end>


139-141: Document the print delay rationale.

The 250ms delay before calling print() is a magic number. Adding a comment explaining this timing (likely waiting for the new window to render styles/content before triggering the print dialog) would improve maintainability.

+    // Wait for the new window to fully render before triggering print dialog
     setTimeout(() => {
       printWindow.print();
     }, 250);

</review_comment_end>

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63a4519 and 7b54236.

📒 Files selected for processing (1)
  • apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)
apps/web/src/data/sheet/sheet.ts (1)
  • sheetModules (36-69)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[warning] 235-235: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx

[error] 236-236: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (1)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)

68-79: LGTM: DOMPurify loading and memoization logic is sound.

The useEffect hook properly loads DOMPurify on mount, and the useMemo dependency on isDOMPurifyLoaded ensures the content is re-sanitized after DOMPurify loads. The reactive dependency chain correctly handles the async loading pattern.

</review_comment_end>

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

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

14-17: Consider enhancing the loading experience.

The loading state currently displays plain text. For a more polished UX, consider adding a spinner or skeleton loader.

Example with a simple spinner:

 {isLoading ? (
   <div className="flex items-center justify-center h-full">
-    <span className="text-zinc-400">Loading...</span>
+    <div className="flex items-center gap-2">
+      <div className="w-4 h-4 border-2 border-zinc-400 border-t-transparent rounded-full animate-spin" />
+      <span className="text-zinc-400">Loading...</span>
+    </div>
   </div>
 ) : (
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b54236 and 9c655f0.

📒 Files selected for processing (1)
  • apps/web/src/app/(main)/dashboard/account/page.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/(main)/dashboard/account/page.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-75)
🔇 Additional comments (2)
apps/web/src/app/(main)/dashboard/account/page.tsx (2)

8-8: Loading state properly implemented!

This addresses the previous review concern. The component now correctly handles the loading state by destructuring isLoading from the hook and conditionally rendering a loading placeholder. This prevents the UI from flashing "Free" before the subscription status is fetched.

Also applies to: 14-17


22-22: Routes are valid; no changes needed.

Both navigation links reference existing routes in the app structure:

  • /dashboard/homeapps/web/src/app/(main)/dashboard/home/page.tsx
  • /pricingapps/web/src/app/(main)/(landing)/pricing/page.tsx

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.

v2-dashboard: create 30 days open source series sheet

2 participants