-
Notifications
You must be signed in to change notification settings - Fork 136
[feat] add sheet and revamp dashboard #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas needing extra attention:
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
completedStepsJSONB 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
completedStepsfield uses the flexibleJson?type. While the API layer validates this asstring[](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
.htmlor.mdfiles 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 removinganytype annotation.The
ctxparameter is typed asany, which bypasses TypeScript's type checking. SinceprotectedProcedureshould provide proper typing for authenticated contexts, the explicitanyannotation 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 removinganytype annotation.Similar to
getCompletedSteps, theanytype 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 tighteningisExternaltypingThe 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
isExternalis explicitly boolean so its intent is clearer and TypeScript doesn’t widen it tostring | 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 explicitlyThe Pro CTA correctly sends paid users to
/dashboard/pro/dashboardand everyone else to/pricing, and the expanded vs collapsed rendering looks good. One UX edge case: whileuseSubscriptionis still loading,isPaidUserwill be falsy, so a paid user who clicks very quickly could be routed to/pricinginstead of the Pro dashboard.If you want to avoid that, you could expose
isLoadingfromuseSubscription()here and either:
- Disable the Pro entry while loading, or
- Only treat users as unpaid when
isPaidUser === falseand notundefined.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
📒 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
planNameprop 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.comis 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, andox-headercolor 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
planNameprop 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
useSubscriptionmanages 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
useEffecthook 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
badgeprop 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.webpand/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
updateCompletedStepsendpoint 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
SheetModuleinterface is well-structured with appropriate types and helpful inline comments. The optionalcomingSoonfield 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=tsapps/web/src/data/sheet/module-24.ts (1)
3-11: Module definition is consistent; please confirm the video URL
module24matches theSheetModuleshape and thecomingSoonflag will be respected by the module page. The only thing to double‑check is thevideoUrl(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 goodUsing
useSessionto sourcesession?.user?.imageand wiring it intoProfilePic, plus turning the title into aLinkto/, 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 consistentThe added
/dashboard/sheetentry, updated “OSS Projects” label, and mobile/desktop headers usingbg-ox-sidebarandborder-ox-headerall align well with the new dashboard look, and the use ofusePathnamefor 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 solidThe
ProfileMenumakes good use ofuseSessionfor name/email/image, reusesProfilePic, and the outside‑click handler tied to.profile-menu-containeris a clean way to close the dropdown. The account navigation and logout actions are straightforward and correctly wired throughuseRouterandsignOut.apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)
10-22: Module resolution, “coming soon” gating, and actions behave as expectedUsing
useParamsto derivemoduleId, looking upcurrentModulefromsheetModules, and callingnotFound()when missing is a clean flow. The conditionalcomingSoonbranch 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.
handleDownloadPDFandhandleShareare 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 anycast bypasses TypeScript's type checking. The TRPC client is properly configured withcreateTRPCReact<AppRouter>()and imports the complete router type, sotrpc.usershould 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
apps/web/src/data/sheet/module-19.ts (1)
3-11: Resolve duplicate module name and placeholdervideoUrl.
module19uses the same name asmodule-11(“Live fix/implement the issue - 3”) and was previously called out as duplicatingmodule-28as 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,
videoUrlis currently empty whilecomingSoonis 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
isPaidUserboolean. While this works for the current Free/Pro model, consider using thesubscriptionobject fromuseSubscription()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
subscriptionobject 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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
dompurifyand@types/dompurifymatches 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/documentguards 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
isComingSoonis false. WhencomingSoon: 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 bothvideoUrl: ""andcomingSoon: 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 emptyvideoUrlis never accessed. The broken player concern is mitigated by the existingisComingSoonguard. When the module transitions tocomingSoon: false, ensurevideoUrlis 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 ofapps/web/src/app/(main)/sheet/[moduleId]/page.tsxand returns early with a "Coming Soon" message, preventing any module content (including video) from rendering. ThevideoUrlfield 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
videoUrlcombined withcomingSoon: truecorrectly 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: truecorrectly 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: truefor modules under development.apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (2)
11-37: Solid sanitization implementation with appropriate fallbacks.The
sanitizeHTMLSyncfunction 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
handleDownloadPDFfunction 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ 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/noDangerouslySetInnerHtmlrule. 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
sanitizeHTMLSyncfunction returns unsanitized HTML ifDOMPurifyInstanceis 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:
- Rendering a loading skeleton until
isDOMPurifyLoadedis true, or- 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
📒 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
useEffecthook properly loads DOMPurify on mount, and theuseMemodependency onisDOMPurifyLoadedensures the content is re-sanitized after DOMPurify loads. The reactive dependency chain correctly handles the async loading pattern.</review_comment_end>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 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
📒 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
isLoadingfrom 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/home→apps/web/src/app/(main)/dashboard/home/page.tsx✓/pricing→apps/web/src/app/(main)/(landing)/pricing/page.tsx✓
fixes: #120
Summary by CodeRabbit
New Features
Updates