- {/* Search Bar */}
+
-
- {/* Filters & Sorters Card */}
-
- {/* Sorters */}
-
-
- {/* Filters */}
-
-
-
-
-
-
-
-
-
-
-
-
);
}
diff --git a/src/components/PostComponents/PostGallery.tsx b/src/components/PostComponents/PostGallery.tsx
new file mode 100644
index 0000000..13fc099
--- /dev/null
+++ b/src/components/PostComponents/PostGallery.tsx
@@ -0,0 +1,200 @@
+"use client";
+
+import { IoIosTrendingUp } from "react-icons/io";
+import { CiChat1 } from "react-icons/ci";
+import { FaTwitter, FaLinkedin, FaReddit } from "react-icons/fa";
+
+import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
+import TagRenderer from "./TagRenderer";
+import { Sentiment } from "@prisma/client";
+
+type Source = {
+ name: string;
+ count: number;
+};
+
+type PostData = {
+ title: string;
+ description: string;
+ sentiment: Sentiment;
+ sentimentBar: { bullish: number; neutral: number; bearish: number };
+ postCount: number;
+ sources: Source[];
+ categories: string[];
+ subcategories?: string[];
+};
+
+// Mock data array
+const mockPostsData: PostData[] = [
+ {
+ title: "Bitcoin Institutional Adoption & Market Sentiment",
+ description:
+ "Major financial institutions showing increased interest in Bitcoin with positive analyst reports...",
+ sentiment: "BULLISH",
+ sentimentBar: { bullish: 60, neutral: 25, bearish: 15 },
+ postCount: 24,
+ sources: [
+ {
+ name: "Twitter",
+ count: 12,
+ },
+ {
+ name: "LinkedIn",
+ count: 8,
+ },
+ {
+ name: "Reddit",
+ count: 4,
+ },
+ ],
+ categories: ["Bitcoin", "Institutional"],
+ subcategories: ["ETF", "Regulation", "Spot", "Futures"],
+ },
+ {
+ title: "Ethereum DeFi Protocol Updates",
+ description:
+ "New developments in decentralized finance protocols showing promising growth patterns...",
+ sentiment: "NEUTRAL",
+ sentimentBar: { bullish: 30, neutral: 50, bearish: 20 },
+ postCount: 18,
+ sources: [
+ {
+ name: "Twitter",
+ count: 10,
+ },
+ {
+ name: "Reddit",
+ count: 8,
+ },
+ ],
+ categories: ["Ethereum"],
+ subcategories: ["Lending", "DEX", "Staking", "Yield", "Bridge"],
+ },
+ {
+ title: "Market Volatility Analysis",
+ description:
+ "Recent market downturn showing concerning trends across major cryptocurrencies...",
+ sentiment: "BEARISH",
+ sentimentBar: { bullish: 15, neutral: 25, bearish: 60 },
+ postCount: 32,
+ sources: [
+ {
+ name: "LinkedIn",
+ count: 15,
+ },
+ {
+ name: "Reddit",
+ count: 17,
+ },
+ ],
+ categories: ["Market", "Volatility"],
+ subcategories: ["Crash", "Recovery", "Bear", "Bull"],
+ },
+];
+
+type PostsGalleryProps = {
+ posts?: PostData[];
+};
+
+// Single card component
+export function SinglePostCard({ post }: { post: PostData }) {
+ const sentimentColor =
+ post.sentiment === "BULLISH"
+ ? "text-green-700 bg-green-100"
+ : post.sentiment === "BEARISH"
+ ? "text-red-700 bg-red-100"
+ : "text-gray-700 bg-gray-100";
+
+ return (
+
+
+
+ {post.title}
+
+
+
+ {post.description}
+
+
+ {/* Sentiment indicators */}
+
+
+
+ {post.sentiment}
+
+
+
+ {post.postCount}
+
+
+
+ {/* Sentiment Bar */}
+
+
+ {/* Source Tags */}
+
+ {post.sources.map((src, idx) => {
+ let pillClass =
+ "px-2 py-0.5 rounded-full flex items-center gap-1 border text-[9px] font-medium";
+ let icon = null;
+ if (src.name === "Twitter") {
+ pillClass += " bg-blue-50 text-blue-600 border-blue-200";
+ icon = ;
+ } else if (src.name === "LinkedIn") {
+ pillClass += " bg-blue-50 text-blue-600 border-blue-200";
+ icon = ;
+ } else if (src.name === "Reddit") {
+ pillClass += " bg-orange-50 text-orange-600 border-orange-200";
+ icon = ;
+ } else {
+ pillClass += " bg-gray-50 text-gray-600 border-gray-200";
+ }
+ return (
+
+ {icon}
+ {src.name} ({src.count})
+
+ );
+ })}
+
+
+ {/* Category/Subcategory Tags */}
+
+
+
+
+
+ );
+}
+
+export default function PostsGallery({
+ posts = mockPostsData,
+}: PostsGalleryProps) {
+ return (
+
+ {posts.map((post, index) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/PostComponents/PostsList.tsx b/src/components/PostComponents/PostsList.tsx
index d776ed6..ec1e560 100644
--- a/src/components/PostComponents/PostsList.tsx
+++ b/src/components/PostComponents/PostsList.tsx
@@ -1,11 +1,11 @@
"use client";
import { useEffect, useState, useRef, useCallback } from "react";
-import PostCard from "./PostCard";
import SentimentBar from "./SentimentBar";
import PostFilters from "./PostFilters";
import { SortField, SortOrder } from "./PostSortSelect";
import Spinner from "./Spinner";
import type { Post } from "@prisma/client";
+import PostGallery from "./PostGallery";
export default function PostsList() {
const [error, setError] = useState
(null);
@@ -96,69 +96,12 @@ export default function PostsList() {
}, [hasMore, loading, nextCursor, fetchPosts]);
return (
- <>
-
-
- {!loading && !error && info.length > 0 && (
-
- (sentimentFilter === "all" ||
- post.sentiment === sentimentFilter) &&
- (sourceFilter === "all" || post.source === sourceFilter)
- )}
- />
- )}
-
- {error && {error}
}
-
- {loading && (
-
-
-
- )}
-
- {!loading && !error && !info.length && (
- No Posts Found
- )}
-
- {!loading && !error && info.length > 0 && (
-
- {info
- .filter(
- (post) =>
- (sentimentFilter === "all" ||
- post.sentiment === sentimentFilter) &&
- (sourceFilter === "all" || post.source === sourceFilter)
- )
- .map((post: Post) => (
-
- ))}
- {/* Infinite scroll trigger */}
- {hasMore && (
-
- {loadingMore ? (
-
- ) : (
- Loading more...
- )}
-
- )}
-
- )}
- >
+
);
}
diff --git a/src/components/PostComponents/TagRenderer.tsx b/src/components/PostComponents/TagRenderer.tsx
new file mode 100644
index 0000000..03f76cf
--- /dev/null
+++ b/src/components/PostComponents/TagRenderer.tsx
@@ -0,0 +1,49 @@
+import React from "react";
+
+type Tag = {
+ type: "category" | "subcategory";
+ value: string;
+};
+
+type TagRendererProps = {
+ categories: string[];
+ subcategories?: string[];
+};
+
+export default function TagRenderer({
+ categories,
+ subcategories,
+}: TagRendererProps) {
+ const allTags: Tag[] = [
+ ...categories.map((cat) => ({ type: "category" as const, value: cat })),
+ ...(subcategories || []).map((subcat) => ({
+ type: "subcategory" as const,
+ value: subcat,
+ })),
+ ];
+
+ const visibleTags = allTags.slice(0, 5);
+ const remainingCount = allTags.length - 5;
+
+ return (
+ <>
+ {visibleTags.map((tag, idx) => (
+
+ #{tag.value}
+
+ ))}
+ {remainingCount > 0 && (
+
+ +{remainingCount} more
+
+ )}
+ >
+ );
+}