Skip to content

Conversation

ginzahatemi
Copy link
Collaborator

@ginzahatemi ginzahatemi commented Sep 17, 2025

PR Description:

  • Renamed and refactored Prisma models: updated PostGroup and Post naming and relationships in schema.prisma.
  • Updated mock data container in PostGallery.tsx from mockPostsData to mockPostGroup for consistency with schema changes.
  • Adjusted component usage to reflect new mock data naming.
  • Prepared codebase for further alignment between frontend types and backend schema.

Summary by CodeRabbit

  • New Features

    • Introduced Post Groups to organize multiple posts under one title with group-level bullish/neutral/bearish summaries and total posts count; gallery/cards now display grouped summaries and aggregated sentiment/source info.
  • Refactor

    • Consolidated previous sub-items into posts within PostGroups; per-post content, sentiment, source, categories, subcategories and timestamps moved to the new Post items; APIs/props updated to accept postGroups.
  • Chores

    • Database migrations and seed data updated to the PostGroup → Post model and renamed total posts field.
  • Style

    • Tag pill spacing and vertical centering improved.

Copy link

coderabbitai bot commented Sep 17, 2025

Walkthrough

Moves per-post summaries and metadata into a new PostGroup model, replaces Subpost with Post (individual posts) linked to PostGroup, updates Prisma schema and migrations with backfill and NOT NULL enforcement, updates seed to create PostGroups with nested Posts, and adapts frontend components/types to PostGroup→Post.

Changes

Cohort / File(s) Summary of Changes
Prisma migrations
prisma/migrations/.../migration.sql, prisma/migrations/.../migration.sql
Adds public.PostGroup table; drops public.Subpost and its FK; alters public.Post to add postGroupId, content, createdAt, updatedAt, link, categories, subcategories, sentiment, source; backfills values and sets NOT NULLs; renames/drops totalSubpoststotalposts; adds FK Post.postGroupId → PostGroup.id.
Prisma schema
prisma/schema.prisma
Introduces PostGroup model (id, title, totalposts, summaries, posts: Post[]); replaces old Subpost model with Post representing individual posts that reference PostGroup via postGroupId; enums unchanged.
Seeding
prisma/seed.ts
Refactors seeding from Posts+Subposts to PostGroups with nested Posts: deletes/creates postGroup records with nested posts.createMany, renames counters/loop vars (MAX_POSTGROUP, MIN_POSTS, MAX_POSTS) and removes Subpost flows.
UI components
src/components/PostComponents/PostGallery.tsx
Replaces PostData shape with PostGroup/Post types and mockPostGroup; renames SinglePostCardPostCard, gallery prop postspostGroups; aggregates sentiments/sources/categories across group posts; updates layout, icons, and imports (Source from @prisma/client).
UI styling tweak
src/components/PostComponents/TagRenderer.tsx
Adjusts tag pill spacing and alignment: px-1 py-0.5flex items-center justify-center px-1 py-1 to center content and increase vertical padding.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Migr as migration.sql
  participant DB as Database
  Note over Migr,DB: Schema migration + data backfill to introduce PostGroup and promote Posts
  Migr->>DB: ALTER TABLE Post ADD COLUMN postGroupId, content, createdAt, updatedAt, sentiment, source, categories, subcategories, link
  Migr->>DB: BACKFILL new columns (content from title/defaults, timestamps, sentiment=NEUTRAL, source=REDDIT, arrays=[])
  Migr->>DB: CREATE TABLE PostGroup (id,title,totalposts,summaries...)
  Migr->>DB: UPDATE Post SET postGroupId = <generated> -- associate existing rows to groups
  Migr->>DB: ALTER TABLE Post ALTER COLUMN ... SET NOT NULL
  Migr->>DB: DROP TABLE Subpost
  Migr->>DB: ADD FK Post.postGroupId → PostGroup.id ON DELETE RESTRICT ON UPDATE CASCADE
  DB-->>Migr: Migration completed
Loading
sequenceDiagram
  autonumber
  participant Seed as prisma/seed.ts
  participant DB as Database
  Note over Seed,DB: Seed creates PostGroups with nested Posts
  Seed->>DB: deleteMany(PostGroup)
  loop per generated postGroup
    Seed->>DB: create(PostGroup { title, totalposts, bullishSummary..., posts: { createMany: [...] } })
  end
  DB-->>Seed: Created PostGroups + Posts
Loading
sequenceDiagram
  autonumber
  participant UI as PostsGallery
  participant API as Data (PostGroup[])
  participant Card as PostCard
  Note over UI,Card: Frontend consumes PostGroup and aggregates group-level info
  UI->>API: fetch postGroups
  loop for each postGroup
    UI->>Card: render postGroup (title, totalposts, summaries, posts[])
    Card->>Card: aggregate sentiments, sources, categories across posts[]
    Card-->>UI: rendered card
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • tasin2610

Poem

In burrows neat I sorted threads,
Groups of posts in comfy beds.
Summaries nest where posts belong,
A rabbit hums a tidy song.
Hop—data grouped, and all is strong! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately summarizes the primary work in this changeset: a Prisma schema refactor alongside corresponding updates to the PostGallery mock data. It is specific, tied to the main backend and frontend changes described in the PR, and readable for teammates scanning history.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/schema-and-postgallery

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

❤️ Share

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

Copy link

@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

Caution

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

⚠️ Outside diff range comments (1)
src/components/PostComponents/PostGallery.tsx (1)

9-9: Don’t import Prisma client into a client component; use a type-only import.

Importing @prisma/client at runtime in "use client" code bloats or breaks the bundle. Use a type-only import (or a local union type).

-import { Sentiment } from "@prisma/client";
+import type { Sentiment } from "@prisma/client";

Alternatively: type Sentiment = "BULLISH" | "NEUTRAL" | "BEARISH";

🧹 Nitpick comments (12)
prisma/schema.prisma (3)

51-59: Rename totalposts to idiomatic totalPosts without another migration using @Map.

Prisma field names are typically camelCase. Keep the DB column as-is and expose a camelCased field via @map for clarity and future DX.

 model PostGroup {
   id             String  @id @default(uuid())
   title          String
   posts          Post[]
-  totalposts  Int     @default(0)
+  totalPosts   Int     @default(0) @map("totalposts")
   bullishSummary String?
   bearishSummary String?
   neutralSummary String?
 }

Follow-ups:

  • Update usage sites (e.g., seed.ts) from totalpoststotalPosts.

69-71: Set explicit referential action to avoid manual child cleanup.

You’re already deleting Posts before PostGroups in seed due to RESTRICT. If the product semantics allow, switch to onDelete: Cascade so parent deletions remove children automatically.

-  postGroup     PostGroup @relation(fields: [postGroupId], references: [id])
+  postGroup     PostGroup @relation(fields: [postGroupId], references: [id], onDelete: Cascade)

If you must keep RESTRICT in prod, consider a seed-only flag or keep current order. Please confirm intended behavior.


61-73: Add an index on the foreign key for query performance.

Typical access patterns fetch Posts by postGroupId. Add an index to avoid seq scans.

 model Post {
   id            String    @id @default(uuid())
   content       String
   sentiment     Sentiment
   source        Source
   categories    String[]  @default([])
   subcategories String[]  @default([])
   link          String?
   postGroupId   String
   postGroup     PostGroup @relation(fields: [postGroupId], references: [id])
   createdAt     DateTime  @default(now())
   updatedAt     DateTime  @updatedAt
+
+  @@index([postGroupId])
 }
prisma/migrations/20250917075310_update_post_group_model/migration.sql (1)

8-9: Prefer column rename over drop+add to preserve data.

If this runs against a non-empty DB, current SQL discards counts. Use RENAME COLUMN to retain values.

-ALTER TABLE "public"."PostGroup" DROP COLUMN "totalSubposts",
-ADD COLUMN     "totalposts" INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE "public"."PostGroup" RENAME COLUMN "totalSubposts" TO "totalposts";

Confirm no prod/staging data would be lost if keeping the drop.

prisma/seed.ts (2)

11-15: Wrap destructive deletes in an ordered interactive transaction.

Ensures atomicity and preserves the required delete order under RESTRICT.

-  // Delete existing data to prevent stacking
-  await prisma.post.deleteMany({});
-  await prisma.postGroup.deleteMany({});
-  await prisma.user.deleteMany({});
+  // Delete existing data to prevent stacking (atomic & ordered)
+  await prisma.$transaction(async (tx) => {
+    await tx.post.deleteMany({});
+    await tx.postGroup.deleteMany({});
+    await tx.user.deleteMany({});
+  });

17-21: Clarify constant naming.

MAX_POSTGROUP suggests a single group; this is a count. Prefer plural or an explicit count name.

-  const MAX_POSTGROUP = 10;
+  const MAX_POST_GROUPS = 10;

Also update the loop to use MAX_POST_GROUPS.

src/components/PostComponents/PostGallery.tsx (5)

11-15: Avoid naming collision with Prisma’s Source enum.

Rename the UI helper type to reduce confusion with backend Source.

-type Source = {
+type SourceCount = {
   name: string;
   count: number;
 };

16-25: Align UI type with naming and future backend mapping.

  • Pluralize source since it’s an array.
  • Consider summary instead of content for group-level text (optional).
 type PostGroup = {
   title: string;
-  content: string;
+  content: string; // consider: summary
   sentiment: Sentiment;
   sentimentBar: { bullish: number; neutral: number; bearish: number };
   postCount: number;
-  source: Source[];
+  sources: SourceCount[];
   categories: string[];
   subcategories?: string[];
 };

Remember to update uses below.


28-29: Rename mock data to reflect groups and avoid drift with PR description.

The PR summary mentions mockPostGroup; since it’s an array, prefer mockPostGroups. Update default prop usage.

-const mockPostsData: PostGroup[] = [
+const mockPostGroups: PostGroup[] = [
 ...
-type PostsGalleryProps = {
-  posts?: PostGroup[];
-};
+type PostsGalleryProps = { posts?: PostGroup[] };
 ...
 export default function PostsGallery({
-  posts = mockPostsData,
+  posts = mockPostGroups,
 }: PostsGalleryProps) {

Also applies to: 95-97, 190-192


151-176: Keep naming consistent with the type change (sources).

If you adopt the plural rename, update usage here.

-          {post.source.map((src, idx) => {
+          {post.sources.map((src, idx) => {

195-197: Prefer a stable key over array index.

Use a deterministic key (e.g., group id or title). Index keys hurt reconciler behavior on reorder.

-        <SinglePostCard key={index} post={post} />
+        <SinglePostCard key={post.title} post={post} />

Ensure title is unique across the list; otherwise add a synthetic id in the mock/type.

prisma/migrations/20250917074232_rename_to_post_group_and_post_model/migration.sql (1)

51-52: Confirm referential action choice.

FK uses ON DELETE RESTRICT. If PostGroups are routinely pruned, consider CASCADE to simplify maintenance. Otherwise document the required deletion order.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30ada99 and 3052840.

📒 Files selected for processing (5)
  • prisma/migrations/20250917074232_rename_to_post_group_and_post_model/migration.sql (1 hunks)
  • prisma/migrations/20250917075310_update_post_group_model/migration.sql (1 hunks)
  • prisma/schema.prisma (1 hunks)
  • prisma/seed.ts (2 hunks)
  • src/components/PostComponents/PostGallery.tsx (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
prisma/seed.ts (1)
src/lib/prisma.ts (1)
  • prisma (13-14)
🔇 Additional comments (1)
prisma/seed.ts (1)

34-68: Seed: fix Faker array-generation; nested createMany is fine; keep totalposts or map it if you change the schema

  • faker.helpers.arrayElements accepts a number or a { min, max } range (v10) — your call is valid. (fakerjs.dev)
    However, you supply only two source words, so asking for { min: 2, max: 5 } will be capped at 2; generate variable-length arrays instead, e.g.:
    faker.helpers.multiple(() => faker.lorem.word(), { count: faker.number.int({ min: 2, max: 5 }) }). (v8.fakerjs.dev)

  • Prisma ^6.15.0 supports nested createMany for 1→N relations (so posts: { createMany: { data: [...] } } is supported), but createMany cannot nest additional relation writes per item and doesn’t return nested records — use nested create if you need deeper per-post relations. (prisma.io)

  • Schema currently defines totalposts (lowercase); keep using totalposts in the seed, or change the schema to totalPosts and add @Map("totalposts") if you prefer camelCase.

Likely an incorrect or invalid review comment.

Copy link

@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 (6)
src/components/PostComponents/PostGallery.tsx (6)

149-149: Tighten typing of sourceCounts (avoid stringly-typed keys).

Apply this diff:

-  }, {} as Record<string, number>);
+  }, {} as Partial<Record<Source, number>>);

171-176: Show the computed count; avoid desync with posts vs. totalposts.

Apply this diff:

-            {postGroup.totalposts} posts
+            {totalPosts} posts

If you need to display a server-aggregated count, consider postGroup.totalposts ?? totalPosts.


199-214: Reduce duplication in source pill styling/icon selection.

Apply this replacement inside the map:

-          {Object.entries(sourceCounts).map(([source, count]) => {
-            let pillClass =
-              "px-2 py-1 rounded-full flex items-center gap-1 border text-xs font-medium";
-            let icon = null;
-            if (source === "TWITTER") {
-              pillClass += " bg-blue-50 text-blue-600 border-blue-200";
-              icon = <FaTwitter className="w-3 h-3" />;
-            } else if (source === "LINKEDIN") {
-              pillClass += " bg-blue-50 text-blue-600 border-blue-200";
-              icon = <FaLinkedin className="w-3 h-3" />;
-            } else if (source === "REDDIT") {
-              pillClass += " bg-orange-50 text-orange-600 border-orange-200";
-              icon = <FaReddit className="w-3 h-3" />;
-            } else {
-              pillClass += " bg-gray-50 text-gray-600 border-gray-200";
-            }
-            return (
-              <span key={source} className={pillClass}>
+          {Object.entries(sourceCounts).map(([source, count]) => {
+            const { pillClass, icon } =
+              SOURCE_META[source as keyof typeof SOURCE_META] ?? SOURCE_META.DEFAULT;
+            return (
+              <span key={source} className={pillClass}>
                 {icon}
                 {source} <span className="font-semibold">({count})</span>
               </span>
             );
           })}

Add this helper once above the component (adjust classes as you prefer):

const BASE_PILL =
  "px-2 py-1 rounded-full flex items-center gap-1 border text-xs font-medium";
const SOURCE_META = {
  TWITTER: {
    pillClass: `${BASE_PILL} bg-blue-50 text-blue-600 border-blue-200`,
    icon: <FaTwitter className="w-3 h-3" />,
  },
  LINKEDIN: {
    pillClass: `${BASE_PILL} bg-blue-50 text-blue-600 border-blue-200`,
    icon: <FaLinkedin className="w-3 h-3" />,
  },
  REDDIT: {
    pillClass: `${BASE_PILL} bg-orange-50 text-orange-600 border-orange-200`,
    icon: <FaReddit className="w-3 h-3" />,
  },
  DEFAULT: {
    pillClass: `${BASE_PILL} bg-gray-50 text-gray-600 border-gray-200`,
    icon: null as JSX.Element | null,
  },
} as const;

240-240: Non-standard Tailwind spacing token.

Apply this diff (or use an existing scale token like ml-8):

-    <div className="flex flex-row justify-start ml-30 gap-6 p-2">
+    <div className="flex flex-row justify-start ml-[30px] gap-6 p-2">

241-243: Use a stable key.

Apply this diff:

-      {postGroups.map((postGroup, index) => (
-        <PostCard key={index} postGroup={postGroup} />
+      {postGroups.map((postGroup) => (
+        <PostCard key={postGroup.id} postGroup={postGroup} />
       ))}

123-124: Prop is required but you also provide a default. Make it optional or drop the default.

Apply this diff:

-type PostsGalleryProps = {
-  postGroups: PostGroup[];
-};
+type PostsGalleryProps = {
+  postGroups?: PostGroup[];
+};

Also applies to: 237-238

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ade745d and 03444ec.

📒 Files selected for processing (1)
  • src/components/PostComponents/PostGallery.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/PostComponents/PostGallery.tsx (1)
src/components/PostComponents/TagRenderer.tsx (1)
  • TagRenderer (13-49)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/components/PostComponents/PostGallery.tsx (2)

10-10: Don’t runtime‑import @prisma/client in a client component. Use type‑only.
This can pull Prisma into the browser bundle.

-import { Sentiment, Source } from "@prisma/client";
+import type { Sentiment, Source } from "@prisma/client";
#!/bin/bash
# Find "use client" TSX files that import @prisma/client at value level
rg -n --type=tsx '^\s*"use client";' -l \
| xargs -I{} rg -nP '^\s*import(?!\s+type).*@prisma/client' {}

139-144: Guard division by zero in sentiment bar (avoid NaN).
If a group has 0 posts, flex values become NaN and break layout.

-  const sentimentBar = {
-    bullish: (sentimentCounts.bullish / totalPosts) * 100,
-    neutral: (sentimentCounts.neutral / totalPosts) * 100,
-    bearish: (sentimentCounts.bearish / totalPosts) * 100,
-  };
+  const sentimentBar =
+    totalPosts > 0
+      ? {
+          bullish: (sentimentCounts.bullish / totalPosts) * 100,
+          neutral: (sentimentCounts.neutral / totalPosts) * 100,
+          bearish: (sentimentCounts.bearish / totalPosts) * 100,
+        }
+      : { bullish: 0, neutral: 0, bearish: 0 };
🧹 Nitpick comments (6)
src/components/PostComponents/TagRenderer.tsx (1)

43-45: Match “+N more” pill styling with tag pills.
For visual consistency, mirror the flex + padding used above.

-        <span className="px-1 py-0.5 text-[7px] font-medium text-gray-700 bg-gray-100 rounded-full">
+        <span className="flex items-center justify-center px-1 py-1 text-[7px] font-medium text-gray-700 bg-gray-100 rounded-full">
src/components/PostComponents/PostGallery.tsx (5)

221-224: Use the computed totalPosts to keep UI consistent with the bar.
Avoid mismatch between displayed count and computed distribution.

-            {postGroup.totalposts} posts
+            {totalPosts} posts

12-20: Verify naming: totalposts vs totalPosts.
CamelCase is typical in TS; confirm schema alignment or rename locally.

-  totalposts: number;
+  totalPosts: number;

If DB uses totalposts, map it at the boundary and keep UI types camelCase.


200-202: Fallback when group summary is null/undefined.
Prevents empty description blocks.

-          {dominantSentiment.description}
+          {dominantSentiment.description ?? "No summary available."}

288-288: Invalid Tailwind class ml-30.
Tailwind doesn’t ship ml-30 by default; use an arbitrary value or a supported token.

-    <div className="flex flex-row justify-start ml-30 gap-6 p-2">
+    <div className="flex flex-row justify-start gap-6 p-2 ml-[7.5rem]">

289-291: Avoid index keys; use stable IDs.
Prevents key churn and unnecessary re-renders.

-      {postGroups.map((postGroup, index) => (
-        <PostCard key={index} postGroup={postGroup} />
+      {postGroups.map((postGroup) => (
+        <PostCard key={postGroup.id} postGroup={postGroup} />
       ))}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03444ec and 93f6e56.

📒 Files selected for processing (2)
  • src/components/PostComponents/PostGallery.tsx (2 hunks)
  • src/components/PostComponents/TagRenderer.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/PostComponents/PostGallery.tsx (1)
src/components/PostComponents/TagRenderer.tsx (1)
  • TagRenderer (13-49)
🔇 Additional comments (1)
src/components/PostComponents/TagRenderer.tsx (1)

33-37: Centering pill content: LGTM.
Change to flex-centering is correct and safe.

@tasin2610 tasin2610 self-requested a review September 17, 2025 10:15
Copy link
Contributor

@tasin2610 tasin2610 left a comment

Choose a reason for hiding this comment

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

good work @ginzahatemi the code is really well written!

@tasin2610 tasin2610 merged commit 6827963 into main Sep 17, 2025
1 check was pending
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/components/PostComponents/PostGallery.tsx (1)

139-147: 0‑post guard on sentimentBar — good catch.

Prevents NaN/invalid flex when there are no posts.

🧹 Nitpick comments (12)
src/components/PostComponents/PostGallery.tsx (12)

10-10: Type-only Prisma import in a client component — good.

This avoids bundling Prisma into the browser. Optional: consider a small shared “domain-types” module (string unions mirroring Prisma enums) so client code doesn’t depend on @prisma/client at all.


224-227: Use posts.length for the displayed count (avoid drift with totalposts).

Rendering from the array ensures accuracy if a cached/denormalized total diverges.

Apply this diff:

-            {postGroup.totalposts} posts
+            {postGroup.posts.length} posts

16-16: Inconsistent casing: totalposts → totalPosts (or drop the field).

If you keep a denormalized count, prefer camelCase. Otherwise, remove it and rely on posts.length to avoid dual sources of truth.

Apply this diff to align casing:

-  totalposts: number;
+  totalPosts: number;

If the schema doesn’t expose this field (or it’s optional), consider removing it from the type entirely.


39-39: Denormalized count in mock data — consider removing.

If you switch the UI to posts.length, this becomes redundant and can be dropped.

Apply this diff:

-    totalposts: 3,
+    // totalPosts: 3, // (optional) keep only if present in API shape

88-88: Same as above for group2.

Apply this diff:

-    totalposts: 2,
+    // totalPosts: 2, // (optional) keep only if present in API shape

157-171: Tie-handling for dominant sentiment could be clearer.

Currently a 2‑way tie chooses the first branch (bullish) implicitly; consider making ties explicit (e.g., any tie on max → NEUTRAL) for predictability.

Apply this diff:

-  const getDominantSentiment = () => {
+  const getDominantSentiment = () => {
     const max = Math.max(
       sentimentCounts.bullish,
       sentimentCounts.neutral,
       sentimentCounts.bearish
     );
-
-    if (
-      sentimentCounts.bullish === sentimentCounts.neutral &&
-      sentimentCounts.neutral === sentimentCounts.bearish
-    ) {
-      return { sentiment: "NEUTRAL", description: postGroup.neutralSummary };
-    }
+    const maxTies =
+      (sentimentCounts.bullish === max ? 1 : 0) +
+      (sentimentCounts.neutral === max ? 1 : 0) +
+      (sentimentCounts.bearish === max ? 1 : 0);
+    if (maxTies > 1) {
+      return { sentiment: "NEUTRAL", description: postGroup.neutralSummary };
+    }

183-187: Tighten typing of sourceCounts.

Use the Source enum type for keys to catch unknown sources at compile time.

Apply this diff:

-  const sourceCounts = postGroup.posts.reduce((acc, post) => {
-    acc[post.source] = (acc[post.source] || 0) + 1;
-    return acc;
-  }, {} as Record<string, number>);
+  const sourceCounts = postGroup.posts.reduce((acc, post) => {
+    acc[post.source] = (acc[post.source] ?? 0) + 1;
+    return acc;
+  }, {} as Partial<Record<Source, number>>);

203-205: Fallback when no summary is available.

Prevents an empty description area if the chosen summary is null/undefined.

Apply this diff:

-          {dominantSentiment.description}
+          {dominantSentiment.description || "No summary available."}

213-220: Mark decorative sentiment icons as aria-hidden.

Screen readers already get the adjacent text; hide redundant SVGs.

Apply this diff:

-              <IoIosTrendingUp className="text-green-600 w-4 h-4" />
+              <IoIosTrendingUp aria-hidden className="text-green-600 w-4 h-4" />
@@
-              <IoIosTrendingDown className="text-red-600 w-4 h-4" />
+              <IoIosTrendingDown aria-hidden className="text-red-600 w-4 h-4" />
@@
-              <FiMinus className="text-gray-600 w-4 h-4" />
+              <FiMinus aria-hidden className="text-gray-600 w-4 h-4" />

291-291: Tailwind class ml-30 is not in the default spacing scale.

Use an arbitrary value or a valid token so it doesn’t get purged.

Apply this diff:

-    <div className="flex flex-row justify-start ml-30 gap-6 p-2">
+    <div className="flex flex-row justify-start ml-[30px] gap-6 p-2">

292-294: Use a stable key (postGroup.id) instead of the array index.

Avoids unnecessary remounts/reconciliation issues on reordering/insertions.

Apply this diff:

-      {postGroups.map((postGroup, index) => (
-        <PostCard key={index} postGroup={postGroup} />
+      {postGroups.map((postGroup) => (
+        <PostCard key={postGroup.id} postGroup={postGroup} />
       ))}

287-289: Defaulting to mock data in the exported component can leak to prod.

Prefer passing data from the parent; keep mocks in stories/dev‑only paths.

Apply this diff:

-export default function PostsGallery({
-  postGroups = mockPostGroup,
-}: PostsGalleryProps) {
+export default function PostsGallery({ postGroups }: PostsGalleryProps) {

If you need a local preview, add a Storybook story or a dev-only wrapper that injects mockPostGroup.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93f6e56 and ab028ec.

📒 Files selected for processing (1)
  • src/components/PostComponents/PostGallery.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/PostComponents/PostGallery.tsx (1)
src/components/PostComponents/TagRenderer.tsx (1)
  • TagRenderer (13-49)

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.

2 participants