Add community features to media outlets and improve UI

Implement community section for each media outlet, including post creation, viewing, and replies, along with navigation and backend API routes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9a264234-c5d7-4dcc-adf3-a954b149b30d
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/9a264234-c5d7-4dcc-adf3-a954b149b30d/AX0T336
This commit is contained in:
kimjaehyeon0101
2025-10-14 09:37:47 +00:00
parent 2ea8ecb0ff
commit 4f0b4c7e1d
6 changed files with 986 additions and 1 deletions

View File

@ -8,6 +8,8 @@ import {
mediaOutletRequests,
comments,
predictionBets,
communityPosts,
communityReplies,
type User,
type UpsertUser,
type MediaOutlet,
@ -26,6 +28,10 @@ import {
type InsertComment,
type PredictionBet,
type InsertPredictionBet,
type CommunityPost,
type InsertCommunityPost,
type CommunityReply,
type InsertCommunityReply,
} from "@shared/schema";
import { db } from "./db";
import { eq, desc, asc, and, ilike, sql } from "drizzle-orm";
@ -77,6 +83,18 @@ export interface IStorage {
getCommentsByArticle(articleId: string): Promise<Comment[]>;
createComment(comment: InsertComment): Promise<Comment>;
// Community operations
getCommunityPostsByOutlet(mediaOutletId: string, sort?: string): Promise<CommunityPost[]>;
getCommunityPostById(id: string): Promise<CommunityPost | undefined>;
createCommunityPost(post: InsertCommunityPost): Promise<CommunityPost>;
updateCommunityPost(id: string, post: Partial<InsertCommunityPost>): Promise<CommunityPost>;
deleteCommunityPost(id: string): Promise<void>;
incrementPostViews(id: string): Promise<void>;
incrementPostLikes(id: string): Promise<void>;
getRepliesByPost(postId: string): Promise<CommunityReply[]>;
createCommunityReply(reply: InsertCommunityReply): Promise<CommunityReply>;
// Analytics operations
getAnalytics(): Promise<{
totalArticles: number;
@ -368,6 +386,81 @@ export class DatabaseStorage implements IStorage {
.where(eq(predictionBets.userId, userId))
.orderBy(desc(predictionBets.createdAt));
}
// Community operations
async getCommunityPostsByOutlet(mediaOutletId: string, sort: string = 'latest'): Promise<CommunityPost[]> {
const query = db
.select()
.from(communityPosts)
.where(eq(communityPosts.mediaOutletId, mediaOutletId));
if (sort === 'views') {
return await query.orderBy(desc(communityPosts.isPinned), desc(communityPosts.viewCount), desc(communityPosts.createdAt));
} else if (sort === 'likes') {
return await query.orderBy(desc(communityPosts.isPinned), desc(communityPosts.likeCount), desc(communityPosts.createdAt));
} else if (sort === 'replies') {
return await query.orderBy(desc(communityPosts.isPinned), desc(communityPosts.replyCount), desc(communityPosts.createdAt));
} else {
return await query.orderBy(desc(communityPosts.isPinned), desc(communityPosts.createdAt));
}
}
async getCommunityPostById(id: string): Promise<CommunityPost | undefined> {
const [post] = await db.select().from(communityPosts).where(eq(communityPosts.id, id));
return post;
}
async createCommunityPost(post: InsertCommunityPost): Promise<CommunityPost> {
const [newPost] = await db.insert(communityPosts).values(post).returning();
return newPost;
}
async updateCommunityPost(id: string, post: Partial<InsertCommunityPost>): Promise<CommunityPost> {
const [updated] = await db
.update(communityPosts)
.set({ ...post, updatedAt: new Date() })
.where(eq(communityPosts.id, id))
.returning();
return updated;
}
async deleteCommunityPost(id: string): Promise<void> {
await db.delete(communityPosts).where(eq(communityPosts.id, id));
}
async incrementPostViews(id: string): Promise<void> {
await db
.update(communityPosts)
.set({ viewCount: sql`view_count + 1` })
.where(eq(communityPosts.id, id));
}
async incrementPostLikes(id: string): Promise<void> {
await db
.update(communityPosts)
.set({ likeCount: sql`like_count + 1` })
.where(eq(communityPosts.id, id));
}
async getRepliesByPost(postId: string): Promise<CommunityReply[]> {
return await db
.select()
.from(communityReplies)
.where(eq(communityReplies.postId, postId))
.orderBy(asc(communityReplies.createdAt));
}
async createCommunityReply(reply: InsertCommunityReply): Promise<CommunityReply> {
const [newReply] = await db.insert(communityReplies).values(reply).returning();
// Increment reply count on the post
await db
.update(communityPosts)
.set({ replyCount: sql`reply_count + 1` })
.where(eq(communityPosts.id, reply.postId));
return newReply;
}
// Analytics operations
async getAnalytics(): Promise<{