Skip to content

Conversation

@HarshJS30
Copy link

@HarshJS30 HarshJS30 commented Nov 16, 2025

Summary

Fixes #155

This PR implements a complete, production-ready newsletters experience for Pro users on OpenSox.

The goal was to build something clean, scalable, and long-term friendly rather than a one-off implementation.


🎯 Features Implemented

1. Premium-Only Access

  • Non-Pro users: See a dedicated landing page with focused CTA encouraging upgrade
  • Pro users: Full access to newsletters listing, filtering, and article pages
  • Clean access control with graceful fallback

2. Scalable Architecture

Key Design Decision: Metadata/content separation for performance at scale

How it works:

  • Listing page: Only loads lightweight metadata from src/data/newsletters.ts
  • Article page: Full markdown content loaded on-demand from src/content/newsletters/*.md
  • Result: No unnecessary parsing/rendering work

Why this matters:

As newsletters grow to 50+, the listing page stays fast because we're not bundling all markdown content into the client.

src/data/newsletters.ts          → Fast metadata for listings
src/content/newsletters/*.md     → Full content loaded on-demand

This architecture prevents performance degradation as content scales.

3. Newsletter Listing Page

  • ✅ Sorted latest-first by date
  • ✅ Search functionality (by title)
  • ✅ Month filter
  • ✅ Year filter
  • ✅ Combined filtering works smoothly
  • ✅ Responsive layout for all breakpoints
  • ✅ Polished UI with subtle animations

4. Markdown-Based Content System

Tech stack:

  • gray-matter - YAML frontmatter parsing
  • remark - Markdown to HTML conversion
  • Tailwind Typography - Beautiful prose styling

Supported formatting:

  • Headings (h1-h6)
  • Bold text
  • Links
  • Images
  • Paragraphs
  • Lists

Developer benefit: Writing newsletters is as simple as creating a .md file. No UI changes needed for new content.

5. Article Page (Slug-Based Routing)

  • Clean, distraction-free reading experience
  • Cover image with date badge
  • Rendered markdown with Tailwind Typography
  • Mobile-optimized reading interface
  • Proper meta tags for SEO

6. GSAP Page Transitions

  • Smooth animations between newsletter views
  • Premium, intentional navigation feel
  • Non-intrusive, performance-optimized

📁 File Structure

src/
├── app/
│   ├── (main)/newsletters/
│   │   ├── page.tsx                    # Listing page with search/filters
│   │   ├── [slug]/page.tsx             # Individual article view
│   │   └── pagetransition.tsx          # GSAP transition wrapper
│   ├── content/newsletters/            # Markdown articles (sample data)
│   │   ├── getting-started-with-nextjs.md
│   │   ├── mastering-react-hooks.md
│   │   └── understanding-typescript.md
│   └── docs/
│       └── how-to-add-newsletter.md    # Team documentation
├── components/
│   ├── dashboard/
│   │   └── Sidebar.tsx                 # Updated with Newsletters link
│   └── non-pro-news/
│       └── News.tsx                    # Non-Pro landing component
├── data/
│   └── newsletters.ts                  # Metadata array (title, date, slug, etc.)
└── lib/
    └── newslettercontent.ts            # Content loader utility

📝 Content Management Workflow

Adding a new newsletter takes ~2 minutes:

  1. Create new .md file in src/content/newsletters/
  2. Add metadata entry to src/data/newsletters.ts
  3. Commit and deploy

No database. No CMS. No admin panel.

Full step-by-step guide included in: src/app/docs/how-to-add-newsletter.md


🎬 Demo

rec.mp4

📸 Screenshots

Non-Pro Landing Page

Non-Pro users see compelling CTA to upgrade

📦 Sample Data

Three example newsletters included for testing:

Content files:

src/content/newsletters/
├── getting-started-with-nextjs.md
├── mastering-react-hooks.md
└── understanding-typescript.md

Metadata:

src/data/newsletters.ts

All sample newsletters include:

  • Properly formatted markdown
  • Cover images
  • Metadata (title, date, author, excerpt)

💭 Design Decisions

Why Markdown instead of a CMS?

  • ✅ Zero infrastructure overhead
  • ✅ Version controlled content
  • ✅ Fast implementation
  • ✅ Easy migration path if CMS needed later
  • ✅ Developer-friendly workflow

Why separate metadata from content?

  • Performance: Listing page doesn't parse markdown
  • Flexibility: Can add metadata fields without touching content
  • Scalability: Handles 100+ newsletters without slowdown

Future Enhancements (Out of Scope)

Potential additions for later iterations:

  • Category/tag system for newsletters
  • Email subscription integration
  • Reading time estimates
  • Social sharing buttons


Closing Note

I've focused on building something genuinely scalable rather than just meeting the minimum requirements. The architecture supports growth without performance degradation, the markdown-based system makes content management trivial, and the code is clean and maintainable.

I've really enjoyed working on this feature, and if things work out, I'd love the opportunity to contribute more and work with you in the future.

Looking forward to your feedback!

Summary by CodeRabbit

  • New Features

    • Newsletters section: listing with search, month/year filters, result counts, and individual article pages rendering Markdown.
    • Animated page-transition component for newsletter navigation (smooth reveal/cover animations).
    • Promotional hero/CTA and Sidebar entry for Newsletters.
  • Refactor

    • Sidebar and dashboard layout updated for animated overlay behavior and improved responsive interactions.
  • Documentation

    • Added three newsletter articles and a how-to guide for adding newsletters.
  • Chores

    • Added Markdown rendering, animation and typography tooling; updated theme color tokens.

@vercel
Copy link

vercel bot commented Nov 16, 2025

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

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 16, 2025

Walkthrough

Adds a newsletters feature: listing and detail pages with markdown content, a markdown-to-HTML loader, page transition animation that intercepts internal links, sample newsletter data and UI, docs, Tailwind typography plugin and markdown/runtime deps, and sidebar/layout animation/overlay changes.

Changes

Cohort / File(s) Change Summary
Dependencies & Config
apps/web/package.json, apps/web/tailwind.config.ts
Added runtime/dev deps for markdown processing and animations (gray-matter, remark, remark-html, gsap) and enabled @tailwindcss/typography; adjusted theme color tokens.
Pages — Listing & Detail
apps/web/src/app/(main)/newsletters/page.tsx, apps/web/src/app/(main)/newsletters/[slug]/page.tsx
New client newsletters listing with search/month/year filters and paid gating; new async server detail page that resolves slug, loads HTML via getNewsletterContent, and returns 404 when missing.
Page Transition
apps/web/src/app/(main)/newsletters/pagetransition.tsx
New client PageTransition component that renders block overlay, runs reveal/cover animations, intercepts internal anchor clicks to animate then navigate, prevents concurrent navigations, and recreates blocks on pathname change.
Content Loader
apps/web/src/lib/newslettercontent.ts
New exported async getNewsletterContent(slug: string) that validates slug, reads markdown file, parses front matter with gray-matter, converts MD→HTML via remark+remark-html, and returns { htmlContent, meta }.
Data
apps/web/src/data/newsletters.ts
New Post interface and exported posts array with three sample newsletter entries and image imports.
Content Files
apps/web/src/app/content/newsletters/*
Added three markdown newsletters: getting-started-with-nextjs.md, mastering-react-hooks.md, understanding-typescript.md.
Docs
apps/web/src/app/docs/how-to-add-newsletter.md
New documentation describing how to add newsletter markdown and metadata, slug rules, assets, and the processing flow.
UI Components
apps/web/src/components/non-pro-news/News.tsx
New hero/CTA component for non-pro users linking to /pricing.
Navigation / Sidebar & Layout
apps/web/src/components/dashboard/Sidebar.tsx, apps/web/src/components/sidebar/SidebarItem.tsx, apps/web/src/app/(main)/dashboard/layout.tsx
Added "Newsletters" route; Sidebar signature changed to accept overlay?: boolean; replaced collapse icons; integrated framer-motion/AnimatePresence for animations and overlay behavior; adjusted hover/group styles and responsive layout to render mobile overlay via AnimatePresence.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant Browser
    participant PageTransition
    participant Router
    participant Server

    User->>Browser: click internal newsletter link
    Browser->>PageTransition: anchor click intercepted
    PageTransition->>PageTransition: run cover() animation (cover → left→right)
    PageTransition->>Router: router.push(url)
    Router->>Server: request /newsletters/[slug]
    Server->>Server: getNewsletterContent(slug) -> read file, gray-matter, remark→HTML
    Server-->>Router: htmlContent + meta (or 404)
    Router->>PageTransition: render new page
    PageTransition->>PageTransition: run reveal() animation (reveal → right→left)
    PageTransition-->>Browser: display rendered newsletter
Loading
sequenceDiagram
    autonumber
    participant User
    participant NewslettersPage
    participant Posts as PostsData
    participant Filters as FilterState

    User->>NewslettersPage: load /newsletters
    NewslettersPage->>Posts: derive months & years (memoized)
    User->>Filters: set search / month / year
    Filters->>NewslettersPage: update filter state
    NewslettersPage->>Posts: filter & sort posts (date desc)
    NewslettersPage-->>User: render filtered list
    User->>NewslettersPage: click "Read more"
    NewslettersPage->>PageTransition: start cover animation -> navigate to detail
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to:
    • pagetransition.tsx: event listener lifecycle, anchor interception, navigation race prevention, animation timing.
    • newslettercontent.ts: slug sanitization, error handling around file I/O, remark plugin configuration.
    • newsletters/page.tsx: client/server boundaries, memoization for months/years, filter correctness and paid gating.
    • Sidebar.tsx / layout.tsx: API change (overlay?: boolean), AnimatePresence interactions, responsive overlay behavior and accessibility.

Possibly related PRs

Poem

🐰
i hopped through markdown meadows, nibbling slugs with delight,
blocks dance and pages shimmer as transitions take flight,
months and filters scatter seeds, headings glow in light,
a cheerful rabbit cheers — newsletters stitched just right. 🥕

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 title 'Feat: Newsletters' clearly and concisely summarizes the main change: adding a newsletters feature for Pro users.
Linked Issues check ✅ Passed All core requirements from #155 are met: organization by date (month/year filters), rich content support (text, links, images, headings, bold), content management via code (documented), latest-first listing, readable layout, and GSAP transitions enhance UX.
Out of Scope Changes check ✅ Passed All changes are in scope. Sidebar updates, color token adjustments, SidebarItem styling, and layout modifications directly support the newsletters feature and UI polish.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 453a84e and 83f8936.

📒 Files selected for processing (1)
  • apps/web/src/app/content/newsletters/understanding-typescript.md (1 hunks)

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

❤️ Share

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

@cla-assistant
Copy link

cla-assistant bot commented Nov 16, 2025

CLA assistant check
All committers have signed the CLA.

@cla-assistant
Copy link

cla-assistant bot commented Nov 16, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
apps/web/src/components/dashboard/Sidebar.tsx (2)

17-19: Remove unused icon imports.

HeartIcon and EnvelopeIcon are imported but never used in this file. Only NewspaperIcon is actually utilized for the Newsletters navigation entry.

Apply this diff to remove the unused imports:

   SparklesIcon,
   StarIcon,
-  HeartIcon,
-  EnvelopeIcon,
   NewspaperIcon,
   DocumentTextIcon,

122-122: Consider removing the comment marker.

The comment {/* Find projects entry */} appears to be a leftover development note and doesn't add meaningful context since the code below is self-explanatory with the routes mapping.

apps/web/src/components/non-pro-news/News.tsx (1)

1-1: Prefer the existing custom Terminal icon for consistency.

The codebase already has a custom Terminal icon component at src/components/icons/icons.tsx. Importing from lucide-react creates an unnecessary duplication and potential inconsistency in icon styling across the application.

Apply this diff to use the custom icon:

-import { Terminal } from "lucide-react";
+import { Terminal } from "../icons/icons";
apps/web/src/app/content/newsletters/understanding-typescript.md (1)

514-514: Update the author placeholder.

The author field contains a placeholder [Your Name] that should be replaced with the actual author name before publishing.

apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1)

47-47: Minor: Hyphenate the compound adjective.

The phrase "file-system based router" should use a hyphen: "file-system-based router" when used as a compound adjective before a noun.

-Next.js uses a file-system based router. Simply create a file in the `app` directory:
+Next.js uses a file-system-based router. Simply create a file in the `app` directory:
apps/web/src/data/newsletters.ts (1)

12-12: Add space after colon for consistent formatting.

The coverimg property is missing a space after the colon, which is inconsistent with TypeScript formatting conventions.

Apply this diff:

-  coverimg:StaticImageData,
+  coverimg: StaticImageData,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 617cdb2 and 2791174.

⛔ Files ignored due to path filters (3)
  • apps/web/src/assets/images/gsoc.png is excluded by !**/*.png
  • apps/web/src/assets/images/opensox.jpg is excluded by !**/*.jpg
  • apps/web/src/assets/images/photu.jpg is excluded by !**/*.jpg
📒 Files selected for processing (13)
  • apps/web/package.json (2 hunks)
  • apps/web/src/app/(main)/newsletters/[slug]/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/pagetransition.tsx (1 hunks)
  • apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1 hunks)
  • apps/web/src/app/content/newsletters/mastering-react-hooks.md (1 hunks)
  • apps/web/src/app/content/newsletters/understanding-typescript.md (1 hunks)
  • apps/web/src/app/docs/how-to-add-newsletter.md (1 hunks)
  • apps/web/src/components/dashboard/Sidebar.tsx (3 hunks)
  • apps/web/src/components/non-pro-news/News.tsx (1 hunks)
  • apps/web/src/data/newsletters.ts (1 hunks)
  • apps/web/src/lib/newslettercontent.ts (1 hunks)
  • apps/web/tailwind.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/src/components/non-pro-news/News.tsx (1)
apps/web/src/components/icons/icons.tsx (1)
  • Terminal (1-5)
apps/web/src/app/(main)/newsletters/page.tsx (7)
apps/web/src/store/useShowSidebar.ts (1)
  • useShowSidebar (10-16)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-77)
apps/web/src/components/dashboard/Sidebar.tsx (1)
  • Sidebar (57-170)
apps/web/src/components/non-pro-news/News.tsx (1)
  • News (5-42)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-100)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx (3)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/lib/newslettercontent.ts (1)
  • getNewsletterContent (8-20)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-100)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx

[warning] 71-71: 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)/newsletters/[slug]/page.tsx

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

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

(lint/security/noDangerouslySetInnerHtml)

apps/web/package.json

[error] 48-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: unexpected character <

(parse)


[error] 49-49: String values must be double quoted.

(parse)


[error] 49-49: String values must be double quoted.

(parse)


[error] 50-50: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 50-50: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 50-50: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 50-51: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 50-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 51-51: unexpected character =

(parse)


[error] 52-52: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 52-52: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 52-52: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 52-53: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 52-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)


[error] 53-53: unexpected character >

(parse)

🪛 LanguageTool
apps/web/src/app/content/newsletters/getting-started-with-nextjs.md

[grammar] ~47-~47: Use a hyphen to join words.
Context: ... and Routing Next.js uses a file-system based router. Simply create a file in th...

(QB_NEW_EN_HYPHEN)

🔇 Additional comments (9)
apps/web/src/app/content/newsletters/mastering-react-hooks.md (2)

1-50: Verify front-matter/metadata requirements.

The markdown file lacks front-matter (YAML/gray-matter metadata block). Per the PR objectives, metadata and content are separated—metadata lives in src/data/newsletters.ts while content is in markdown files. Verify that this architecture means front-matter is not required here, or if the markdown files should include metadata such as title, date, author, and description for the content processing pipeline.


1-797: Content quality and technical accuracy are solid.

The newsletter provides comprehensive, well-structured React Hooks documentation with accurate code examples (TypeScript/JavaScript). Sections cover rules, essential hooks, performance hooks, advanced hooks, custom hooks, patterns, best practices, common mistakes, testing, and resources. Examples are syntactically correct and demonstrate best practices. Structure is logical and suitable for educational content.

apps/web/src/app/docs/how-to-add-newsletter.md (1)

1-110: LGTM!

The documentation is clear, comprehensive, and accurately describes the newsletter creation workflow. The step-by-step guide aligns well with the implementation in src/data/newsletters.ts and the content loading utilities.

apps/web/src/components/non-pro-news/News.tsx (1)

5-42: LGTM!

The component structure is clean with proper responsive design using Tailwind classes. The gradient effects, layout hierarchy, and CTA button integration are well implemented.

apps/web/src/app/content/newsletters/understanding-typescript.md (1)

1-513: LGTM!

The TypeScript tutorial is comprehensive, well-structured, and provides clear examples covering fundamental to advanced concepts. The content quality is excellent for a newsletter article.

apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1)

1-187: LGTM!

The Next.js tutorial provides clear, practical guidance with good code examples. The content structure flows well from basics to advanced topics and deployment.

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

125-127: LGTM!

The addition of the @tailwindcss/typography plugin is correctly implemented and will provide prose styling utilities needed for rendering the markdown newsletter content.

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

1-45: LGTM!

The Post interface and posts array are well-structured with proper TypeScript typing. The slug values correctly match the markdown filenames, and the data model aligns well with the content loading pipeline documented in how-to-add-newsletter.md.

apps/web/package.json (1)

30-31: Dependencies verified as secure and stable.

All newly added dependencies are using their latest stable versions with no known vulnerabilities:

  • gray-matter ^4.0.3: No CVEs
  • gsap ^3.13.0: Secure (prototype pollution vulnerability only affects < 3.6.0)
  • remark 15.0.1: No CVEs
  • remark-html 16.0.1: Secure (critical vulnerabilities only affect ≤14.0.0 and <13.0.2)

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

🧹 Nitpick comments (3)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx (3)

31-43: Specify explicit positioning for fixed back button.

The back button uses fixed positioning without explicit top or left values, which defaults to the element's static position. This could lead to inconsistent placement across browsers or viewport sizes.

Apply this diff to add explicit positioning:

 <div className="
   fixed 
+  top-4 left-4
   flex items-center 
   bg-[#9455f4] 
   rounded-2xl 
   text-[15px] 
   px-3 py-1.5
 ">

46-56: Improve image alt text for accessibility.

The cover image uses a generic alt text "img" which provides no meaningful information for screen readers or when images fail to load.

Apply this diff to use descriptive alt text:

 <Image
   src={post.coverimg}
   width={650}
-  alt="img"
+  alt={`Cover image for ${post.heading}`}
   className="
     mx-auto 
     mt-10 mb-16 
     max-w-full 
     lg:w-[650px]
   "
 />

77-80: Consider sanitizing HTML content for defense-in-depth security.

DOMPurify is already available in your dependencies. While the markdown content originates from repository-controlled files and is processed by remark (a trusted library), adding explicit HTML sanitization provides an additional security layer against potential supply-chain attacks or markdown processor bugs.

Update the component to sanitize before rendering:

import DOMPurify from 'dompurify';

// In the component:
const sanitizedContent = DOMPurify.sanitize(htmlContent);

// ...
<div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />

This is an optional best practice; your current implementation is secure given the trusted content source.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2791174 and 0dcbd6a.

📒 Files selected for processing (5)
  • apps/web/package.json (3 hunks)
  • apps/web/src/app/(main)/newsletters/[slug]/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/pagetransition.tsx (1 hunks)
  • apps/web/src/app/content/newsletters/mastering-react-hooks.md (1 hunks)
  • apps/web/src/app/content/newsletters/understanding-typescript.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/app/content/newsletters/mastering-react-hooks.md
  • apps/web/src/app/(main)/newsletters/pagetransition.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx (3)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/lib/newslettercontent.ts (1)
  • getNewsletterContent (8-20)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-118)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx

[warning] 78-78: 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)/newsletters/[slug]/page.tsx

[error] 79-79: 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 (5)
apps/web/package.json (2)

49-50: ✅ Git merge conflict resolved.

The previous critical issue with unresolved merge conflict markers has been properly resolved. Both @types/dompurify and @tailwindcss/typography are now correctly present in devDependencies without conflict markers.


30-31: Verification confirms package security status—but verify gray-matter usage patterns.

Security findings:

  • gsap@3.13.0, remark@15.0.1, remark-html@16.0.1, and @tailwindcss/typography@0.5.19 have no known vulnerabilities.
  • gray-matter@4.0.3 itself has no direct CVE, but known advisories describe unsafe usage patterns (CVE-2022-25863 / CVE-2023-22491) involving JavaScript deserialization of untrusted frontmatter.

Action required: Confirm that the newsletter implementation processes frontmatter from trusted sources only (e.g., author-controlled markdown files). If processing untrusted user-submitted content, disable JavaScript execution in gray-matter's parser.

apps/web/src/app/content/newsletters/understanding-typescript.md (1)

1-514: Well-structured TypeScript tutorial with comprehensive coverage.

The newsletter content is well-organized, covers essential TypeScript concepts progressively, and includes practical code examples. The structure flows logically from basics to advanced topics, and the inclusion of React integration and external resources adds value.

apps/web/src/app/(main)/newsletters/[slug]/page.tsx (2)

12-17: Excellent! Past issue resolved.

The missing slug validation has been properly implemented as suggested in the previous review. The code now checks if the post exists and returns a 404 via notFound() before attempting to access post properties or load the markdown file.


9-86: Clean implementation of newsletter detail page.

The component structure is well-organized with proper async/await handling, responsive design considerations, and good reading experience optimizations (70% width constraint on large screens with prose styling). The integration with PageTransition provides smooth navigation.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/dashboard/Sidebar.tsx (1)

83-95: Fix Tailwind class typo on mobile header background.

The class name bg-ox-sideba at line 83 of apps/web/src/components/dashboard/Sidebar.tsx is a misspelling of the defined Tailwind token bg-ox-sidebar (confirmed in tailwind.config.ts). The mobile sidebar header will not render with the intended background color.

-      <div className="flex justify-between items-center h-16 px-4 border-b border-ox-header xl:hidden bg-ox-sideba">
+      <div className="flex justify-between items-center h-16 px-4 border-b border-ox-header xl:hidden bg-ox-sidebar">
♻️ Duplicate comments (1)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)

71-99: Still hijacking modifier/middle‑clicks on links (breaks open‑in‑new‑tab behavior).

The guards for hashes, external URLs, and target="_blank" look good, but this handler still intercepts Cmd/Ctrl/Shift/Alt clicks and non‑left mouse buttons on internal links. That prevents users from opening newsletter links in a new tab or background window, which is what the earlier review comment was warning about.

Consider tightening the handler to only intercept plain left‑clicks on same‑origin internal links, e.g.:

-    const handleClick = (e: Event) => {
-      const target = e.currentTarget as HTMLAnchorElement
-      const href = target.getAttribute("href")
-      
-      // don't hijack external links, new tabs, or modified clicks
-      if (!href || href.startsWith("#")) {
-        return
-      }
-      
-      // don't hijack external URLs
-      if (href.startsWith("http") || href.startsWith("mailto") || href.startsWith("tel")) {
-        return
-      }
-      
-      // don't hijack links that open in new tab
-      if (target.target === "_blank") {
-        return
-      }
-
-      // only hijack internal navigation
-      if (href !== pathname) {
-        e.preventDefault()
-        cover(href)
-      }
-    }
+    const handleClick = (e: MouseEvent) => {
+      const target = e.currentTarget as HTMLAnchorElement
+      const href = target.getAttribute("href") ?? ""
+
+      // let browser handle same‑page anchors, external URLs, new‑tab or modified clicks
+      if (
+        !href ||
+        href.startsWith("#") ||
+        target.target === "_blank" ||
+        target.rel?.includes("external") ||
+        /^(https?:|mailto:|tel:)/.test(href) ||
+        e.button !== 0 ||
+        e.metaKey ||
+        e.ctrlKey ||
+        e.shiftKey ||
+        e.altKey
+      ) {
+        return
+      }
+
+      if (href !== pathname) {
+        e.preventDefault()
+        cover(href)
+      }
+    }

This keeps the transition animation for normal internal navigation but restores standard browser behaviors for everything else.

🧹 Nitpick comments (5)
apps/web/src/data/newsletters.ts (1)

7-14: Minor formatting: add space after colon in interface.

Line 12 is missing a space after the colon: coverimg:StaticImageData, should be coverimg: StaticImageData, for consistency with other properties.

Apply this diff:

 export interface Post {
   date: string;
   heading: string;
   description: string;
   image: StaticImageData;
-  coverimg:StaticImageData,
+  coverimg: StaticImageData,
   slug: string;           
 }
apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1)

31-41: Optional: specify language for fenced code block.

The directory tree structure at lines 31-41 uses a fenced code block without a language specifier. While this works, adding a language hint (e.g., text or plaintext) improves accessibility and potential syntax highlighting.

Based on static analysis hints.

Apply this diff:

-```
+```text
 my-next-app/
 ├── app/
 │   ├── layout.tsx
apps/web/src/app/(main)/newsletters/[slug]/page.tsx (1)

78-80: Consider using a React markdown renderer to avoid dangerouslySetInnerHTML.

While the current implementation works for controlled content (team-only markdown files), using dangerouslySetInnerHTML bypasses React's XSS protection. Consider using a React markdown component like react-markdown or sanitizing with DOMPurify for defense-in-depth.

Since the content source is controlled (committed markdown files, not user input), the immediate risk is low.

Based on static analysis hints.

Example with react-markdown:

import ReactMarkdown from 'react-markdown'

// Replace dangerouslySetInnerHTML with:
<div className="prose prose-invert max-w-none">
  <ReactMarkdown>{content}</ReactMarkdown>
</div>

Or sanitize with DOMPurify:

import DOMPurify from 'dompurify'

// In component:
const sanitizedHtml = DOMPurify.sanitize(htmlContent)
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
apps/web/src/app/(main)/newsletters/page.tsx (2)

221-243: Avoid nesting a <button> inside Next.js <Link>; style the anchor directly instead.

Wrapping a <button> inside the anchor that Link renders is invalid HTML and can cause inconsistent focus/interaction behavior. You can get the same visual result by styling the <a> element from Link as a button:

-                        <Link href={`/newsletters/${post.slug}`}>
-                          <button className="bg-[#9455f4] text-[15px] py-[5px] px-[10px] rounded-[20px] relative top-[30px] font-medium w-[130px] cursor-pointer transition-all duration-300 ease-in-out hover:bg-[#7c3dd8] max-[1024px]:static max-[1024px]:mt-[10px]">
-                            Read more...
-                          </button>
-                        </Link>
+                        <Link
+                          href={`/newsletters/${post.slug}`}
+                          className="inline-block bg-[#9455f4] text-[15px] py-[5px] px-[10px] rounded-[20px] relative top-[30px] font-medium w-[130px] text-center cursor-pointer transition-all duration-300 ease-in-out hover:bg-[#7c3dd8] max-[1024px]:static max-[1024px]:mt-[10px]"
+                        >
+                          Read more...
+                        </Link>

221-232: Consider giving Image meaningful dimensions instead of width={0}/height={0}.

Using width={0} and height={0} with CSS sizing works but is a bit of a hack and may trip Next Image warnings. Since posts images are static imports, you can pass their intrinsic dimensions or switch to fill:

-                      <Image
-                        src={post.image}
-                        alt={post.heading}
-                        width={0}
-                        height={0}
-                        className="object-cover shrink-0 w-[220px] h-[380px] border border-white ..."
-                      />
+                      <Image
+                        src={post.image}
+                        alt={post.heading}
+                        width={220}
+                        height={380}
+                        className="object-cover shrink-0 w-[220px] h-[380px] border border-white ..."
+                      />

(or adjust the numbers to match the actual aspect ratio).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2509fcd and 6c90a26.

⛔ Files ignored due to path filters (3)
  • apps/web/src/assets/images/gsoc.png is excluded by !**/*.png
  • apps/web/src/assets/images/opensox.jpg is excluded by !**/*.jpg
  • apps/web/src/assets/images/photu.jpg is excluded by !**/*.jpg
📒 Files selected for processing (15)
  • apps/web/package.json (3 hunks)
  • apps/web/src/app/(main)/dashboard/layout.tsx (2 hunks)
  • apps/web/src/app/(main)/newsletters/[slug]/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/pagetransition.tsx (1 hunks)
  • apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1 hunks)
  • apps/web/src/app/content/newsletters/mastering-react-hooks.md (1 hunks)
  • apps/web/src/app/content/newsletters/understanding-typescript.md (1 hunks)
  • apps/web/src/app/docs/how-to-add-newsletter.md (1 hunks)
  • apps/web/src/components/dashboard/Sidebar.tsx (9 hunks)
  • apps/web/src/components/non-pro-news/News.tsx (1 hunks)
  • apps/web/src/components/sidebar/SidebarItem.tsx (1 hunks)
  • apps/web/src/data/newsletters.ts (1 hunks)
  • apps/web/src/lib/newslettercontent.ts (1 hunks)
  • apps/web/tailwind.config.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/src/app/content/newsletters/understanding-typescript.md
  • apps/web/src/components/non-pro-news/News.tsx
  • apps/web/tailwind.config.ts
  • apps/web/src/app/docs/how-to-add-newsletter.md
🧰 Additional context used
🧬 Code graph analysis (4)
apps/web/src/app/(main)/newsletters/page.tsx (7)
apps/web/src/store/useShowSidebar.ts (1)
  • useShowSidebar (10-16)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-77)
apps/web/src/components/dashboard/Sidebar.tsx (1)
  • Sidebar (52-166)
apps/web/src/components/non-pro-news/News.tsx (1)
  • News (5-42)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-118)
apps/web/src/app/(main)/dashboard/layout.tsx (1)
apps/web/src/components/dashboard/Sidebar.tsx (1)
  • Sidebar (52-166)
apps/web/src/components/dashboard/Sidebar.tsx (3)
apps/web/src/store/useShowSidebar.ts (1)
  • useShowSidebar (10-16)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/components/dashboard/ProfilePic.tsx (1)
  • ProfilePic (11-59)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx (3)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/lib/newslettercontent.ts (1)
  • getNewsletterContent (8-20)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-118)
🪛 ast-grep (0.39.9)
apps/web/src/app/(main)/newsletters/[slug]/page.tsx

[warning] 78-78: 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)/newsletters/page.tsx

[error] 44-44: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 61-61: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

apps/web/src/app/(main)/newsletters/[slug]/page.tsx

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

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

(lint/security/noDangerouslySetInnerHtml)

🪛 LanguageTool
apps/web/src/app/content/newsletters/getting-started-with-nextjs.md

[grammar] ~47-~47: Use a hyphen to join words.
Context: ... and Routing Next.js uses a file-system based router. Simply create a file in th...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
apps/web/src/app/content/newsletters/getting-started-with-nextjs.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

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

30-31: LGTM! Dependency additions support the newsletter feature.

The merge conflict flagged in the previous review has been resolved. The added dependencies are appropriate:

  • gray-matter and remark/remark-html enable markdown parsing for newsletter content
  • gsap powers page transitions
  • @tailwindcss/typography provides prose styling for rendered articles

Also applies to: 41-42, 50-50

apps/web/src/app/content/newsletters/mastering-react-hooks.md (1)

1-796: LGTM! Comprehensive React Hooks guide with resolved placeholder.

The placeholder author attribution flagged in the previous review has been corrected. The content is thorough, well-structured, and includes practical code examples covering essential and advanced hooks, custom hooks, patterns, and best practices.

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

18-18: LGTM! Improved hover interactions with group-based coordination.

The updated hover styling provides a more cohesive user experience. The group class enables coordinated hover effects across the icon and text, with smooth transition-colors for visual polish.

Also applies to: 22-25, 28-28

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

16-45: LGTM! Newsletter metadata structure is well-defined.

The posts array provides appropriate metadata for the newsletter feature, with slugs correctly matching the markdown content files.

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

21-30: LGTM! Mobile overlay pattern enables different animation behaviors.

The dual-Sidebar approach (desktop always-mounted + mobile conditionally-mounted with overlay) enables different animation behaviors for each viewport. The AnimatePresence wrapper properly handles exit animations for the mobile overlay.

apps/web/src/app/content/newsletters/getting-started-with-nextjs.md (1)

1-187: LGTM! Comprehensive Next.js introduction for newsletters.

The content is well-structured, covers essential Next.js concepts, and includes practical examples. The minor formatting suggestions above are optional improvements.

apps/web/src/app/(main)/newsletters/[slug]/page.tsx (2)

12-17: LGTM! 404 handling correctly implemented.

The previous review's concern about missing slug validation has been addressed. The notFound() guard prevents errors when accessing non-existent newsletters.


21-85: LGTM! Newsletter article page with proper routing and styling.

The implementation correctly fetches newsletter content, handles 404s, and renders articles with appropriate styling and page transitions. The layout is responsive and follows the design system.

@HarshJS30 HarshJS30 closed this Nov 16, 2025
@HarshJS30 HarshJS30 reopened this Nov 16, 2025
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: 4

🧹 Nitpick comments (3)
apps/web/src/app/(main)/newsletters/page.tsx (3)

72-83: Consider enhancing the loading state with a skeleton or spinner.

The current loading state displays plain text, which works but could provide better user feedback with a skeleton loader or spinner component.

Example enhancement:

         <div className="flex-1 flex items-center justify-center">
-          <div className="text-white/60">Loading...</div>
+          <div className="flex flex-col items-center gap-3">
+            <div className="w-8 h-8 border-2 border-white/20 border-t-white/60 rounded-full animate-spin" />
+            <div className="text-white/60 text-sm">Loading newsletters...</div>
+          </div>
         </div>

176-220: Add labels and ARIA attributes for accessibility.

The search input and filter controls lack labels, making them inaccessible to screen reader users. Interactive controls need accessible names for WCAG compliance.

Apply these improvements:

                   <div className="relative flex-1">
+                    <label htmlFor="newsletter-search" className="sr-only">Search newsletters</label>
                     <input
+                      id="newsletter-search"
                       type="text"
                       placeholder="Search newsletters..."
                       value={searchQuery}
                       onChange={(e) => setSearchQuery(e.target.value)}
                       className="w-full py-[14px] pr-[45px] pl-5 text-base border border-white/20 rounded-xl bg-white/10 backdrop-blur-[10px] text-white placeholder:text-white/60"
                     />
 
                     {searchQuery && (
                       <button 
+                        aria-label="Clear search"
                         onClick={() => setSearchQuery("")}
                         className="absolute right-[15px] top-1/2 -translate-y-1/2 bg-white/20 w-7 h-7 rounded-full text-white flex items-center justify-center hover:bg-white/30 transition-colors"
                       >
                         ✕
                       </button>
                     )}
                   </div>
 
                   {/* Month Filter */}
+                  <label htmlFor="month-filter" className="sr-only">Filter by month</label>
                   <select
+                    id="month-filter"
                     value={selectedMonth}
                     onChange={(e) => setSelectedMonth(e.target.value)}
                     className="py-[14px] px-5 text-base border border-white/20 rounded-xl bg-white/10 backdrop-blur-[10px] text-white cursor-pointer max-[768px]:w-full"
                   >
                     <option value="" className="bg-[#0f0f0f]">All Months</option>
                     {months.map((month) => (
                       <option key={month} value={month} className="bg-[#0f0f0f]">
                         {month}
                       </option>
                     ))}
                   </select>
 
                   {/* Year Filter */}
+                  <label htmlFor="year-filter" className="sr-only">Filter by year</label>
                   <select
+                    id="year-filter"
                     value={selectedYear}
                     onChange={(e) => setSelectedYear(e.target.value)}
                     className="py-[14px] px-5 text-base border border-white/20 rounded-xl bg-white/10 backdrop-blur-[10px] text-white cursor-pointer max-[768px]:w-full"
                   >

Add aria-label to sidebar toggle buttons at lines 95-100 and 120-122:

           <button 
+            aria-label="Open sidebar"
             onClick={() => setShowSidebar(true)}
           <IconWrapper onClick={() => setShowSidebar(true)}>

For the IconWrapper, you may need to add aria-label support to the IconWrapper component or replace it with a button element that accepts aria-label.


138-167: Consider simplifying the complex relative positioning.

The current implementation uses multiple relative positioning offsets to create the staggered "NEWSLETTER" text layout. While functional, this approach is fragile and difficult to maintain across breakpoints.

Consider using CSS Grid or Flexbox with gaps for a more maintainable approach, or extracting this into a dedicated component with clearer positioning logic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c90a26 and b0384bb.

📒 Files selected for processing (2)
  • apps/web/src/app/(main)/newsletters/page.tsx (1 hunks)
  • apps/web/src/lib/newslettercontent.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/lib/newslettercontent.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/(main)/newsletters/page.tsx (7)
apps/web/src/store/useShowSidebar.ts (1)
  • useShowSidebar (10-16)
apps/web/src/hooks/useSubscription.ts (1)
  • useSubscription (11-77)
apps/web/src/data/newsletters.ts (1)
  • posts (16-45)
apps/web/src/components/dashboard/Sidebar.tsx (1)
  • Sidebar (52-166)
apps/web/src/components/non-pro-news/News.tsx (1)
  • News (5-42)
apps/web/src/components/ui/IconWrapper.tsx (1)
  • IconWrapper (10-22)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)
  • PageTransition (7-118)
🔇 Additional comments (1)
apps/web/src/app/(main)/newsletters/page.tsx (1)

15-20: LGTM! Hook placement follows Rules of Hooks.

All hooks are correctly placed at the top of the component before any conditional returns, which resolves the previous critical issue.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/src/app/content/newsletters/understanding-typescript.md (1)

448-466: Clarify that these redefine built-in utility types.

The examples redefine Readonly<T> and create Optional<T> (equivalent to built-in Partial<T>), which could confuse readers into thinking they need to define these types themselves.

Add a note above the code block:

 ### Mapped Types
+
+> **Note**: These examples demonstrate how mapped types work by recreating built-in utility types. In practice, use TypeScript's built-in `Readonly<T>` and `Partial<T>` instead of redefining them.
 
 ```typescript
🧹 Nitpick comments (1)
apps/web/src/app/(main)/newsletters/pagetransition.tsx (1)

67-106: Excellent improvements to click handler guards!

The click handler now properly prevents hijacking of external links, new-tab navigation, and modified clicks, addressing all concerns from the previous review. The guard clauses (lines 77-95) correctly filter out:

  • Hash/fragment links
  • External protocols (http/https/mailto/tel)
  • target="_blank" and rel="external" anchors
  • Modified clicks (cmd/ctrl/shift, non-left button)

Optional refinement: Line 82 could use a more precise regex pattern:

-  if (href.startsWith("http") || href.startsWith("mailto") || href.startsWith("tel")) {
+  if (/^(https?:|mailto:|tel:)/.test(href)) {
     return
   }

This ensures protocol matching is exact (e.g., won't accidentally match "httpsomething"), though the practical risk is minimal.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0384bb and 453a84e.

📒 Files selected for processing (4)
  • apps/web/src/app/(main)/newsletters/page.tsx (1 hunks)
  • apps/web/src/app/(main)/newsletters/pagetransition.tsx (1 hunks)
  • apps/web/src/app/content/newsletters/understanding-typescript.md (1 hunks)
  • apps/web/src/lib/newslettercontent.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/(main)/newsletters/page.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/content/newsletters/understanding-typescript.md (1)
apps/web/src/app/(main)/sheet/[moduleId]/page.tsx (1)
  • ModuleDocPage (59-243)
🪛 markdownlint-cli2 (0.18.1)
apps/web/src/app/content/newsletters/understanding-typescript.md

446-446: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (1)
apps/web/src/lib/newslettercontent.ts (1)

8-45: LGTM! All previous concerns have been addressed.

The function correctly implements:

  • Path traversal prevention (line 11)
  • Async file I/O (line 24)
  • Comprehensive error handling (lines 23-27, 40-44)
  • Structured return with safe defaults for missing metadata

@HarshJS30
Copy link
Author

hey there @apsinghdev , all code rabbit issues are resolved , please review at your convenience thanks :p

@HarshJS30 HarshJS30 closed this Nov 16, 2025
@railway-app railway-app bot temporarily deployed to opensox (insightful-communication / prod) November 17, 2025 12:41 Inactive
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.

[bounty-to-hire]: create ui for newsletter

1 participant