Files
sapiens-web2/shared/schema.ts
kimjaehyeon0101 6d81e0281a Add sorting options for media outlets by name or traffic
Introduce alphabetical and traffic score sorting for media outlets in both the main content and admin dashboard views, updating the schema to include a trafficScore field.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 069d4324-6c40-4355-955e-c714a50de1ea
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/069d4324-6c40-4355-955e-c714a50de1ea/jvFIdY3
2025-09-29 18:42:29 +00:00

203 lines
7.0 KiB
TypeScript

import { sql } from 'drizzle-orm';
import {
index,
jsonb,
pgTable,
timestamp,
varchar,
text,
integer,
boolean,
decimal,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
// Session storage table.
// (IMPORTANT) This table is mandatory for Replit Auth, don't drop it.
export const sessions = pgTable(
"sessions",
{
sid: varchar("sid").primaryKey(),
sess: jsonb("sess").notNull(),
expire: timestamp("expire").notNull(),
},
(table) => [index("IDX_session_expire").on(table.expire)],
);
// User storage table.
// (IMPORTANT) This table is mandatory for Replit Auth, don't drop it.
export const users = pgTable("users", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
email: varchar("email").unique(),
firstName: varchar("first_name"),
lastName: varchar("last_name"),
profileImageUrl: varchar("profile_image_url"),
role: varchar("role").default("user"), // user, admin, superadmin
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Media outlets (People, Topics, Companies)
export const mediaOutlets = pgTable("media_outlets", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
name: varchar("name").notNull(),
slug: varchar("slug").notNull().unique(),
category: varchar("category").notNull(), // people, topics, companies
description: text("description"),
imageUrl: varchar("image_url"),
tags: text("tags").array(),
trafficScore: integer("traffic_score").default(0), // For sorting by traffic
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Articles within media outlets
export const articles = pgTable("articles", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
mediaOutletId: varchar("media_outlet_id").notNull(),
title: varchar("title").notNull(),
slug: varchar("slug").notNull(),
excerpt: text("excerpt"),
content: text("content").notNull(),
imageUrl: varchar("image_url"),
authorId: varchar("author_id"),
tags: text("tags").array(),
isPinned: boolean("is_pinned").default(false),
isFeatured: boolean("is_featured").default(false),
publishedAt: timestamp("published_at").defaultNow(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Prediction markets
export const predictionMarkets = pgTable("prediction_markets", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
title: varchar("title").notNull(),
description: text("description"),
articleId: varchar("article_id"),
yesPrice: decimal("yes_price", { precision: 5, scale: 2 }),
noPrice: decimal("no_price", { precision: 5, scale: 2 }),
volume: decimal("volume", { precision: 12, scale: 2 }),
endDate: timestamp("end_date"),
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Auctions for media outlet management rights
export const auctions = pgTable("auctions", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
mediaOutletId: varchar("media_outlet_id").notNull(),
title: varchar("title").notNull(),
description: text("description"),
currentBid: decimal("current_bid", { precision: 10, scale: 2 }),
qualityScore: integer("quality_score"),
highestBidderId: varchar("highest_bidder_id"),
endDate: timestamp("end_date").notNull(),
duration: integer("duration"), // days
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Bids for auctions
export const bids = pgTable("bids", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
auctionId: varchar("auction_id").notNull(),
bidderId: varchar("bidder_id").notNull(),
amount: decimal("amount", { precision: 10, scale: 2 }).notNull(),
qualityScore: integer("quality_score"),
createdAt: timestamp("created_at").defaultNow(),
});
// Media outlet creation requests
export const mediaOutletRequests = pgTable("media_outlet_requests", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
name: varchar("name").notNull(),
category: varchar("category").notNull(),
description: text("description"),
requesterId: varchar("requester_id").notNull(),
status: varchar("status").default("pending"), // pending, approved, rejected
reviewedBy: varchar("reviewed_by"),
reviewedAt: timestamp("reviewed_at"),
createdAt: timestamp("created_at").defaultNow(),
});
// Comments on articles
export const comments = pgTable("comments", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
articleId: varchar("article_id").notNull(),
authorId: varchar("author_id").notNull(),
content: text("content").notNull(),
isPinned: boolean("is_pinned").default(false),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Insert schemas
export const insertUserSchema = createInsertSchema(users).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertMediaOutletSchema = createInsertSchema(mediaOutlets).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertArticleSchema = createInsertSchema(articles).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertPredictionMarketSchema = createInsertSchema(predictionMarkets).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertAuctionSchema = createInsertSchema(auctions).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertBidSchema = createInsertSchema(bids).omit({
id: true,
createdAt: true,
});
export const insertMediaOutletRequestSchema = createInsertSchema(mediaOutletRequests).omit({
id: true,
createdAt: true,
});
export const insertCommentSchema = createInsertSchema(comments).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Types
export type UpsertUser = typeof users.$inferInsert;
export type User = typeof users.$inferSelect;
export type InsertMediaOutlet = z.infer<typeof insertMediaOutletSchema>;
export type MediaOutlet = typeof mediaOutlets.$inferSelect;
export type InsertArticle = z.infer<typeof insertArticleSchema>;
export type Article = typeof articles.$inferSelect;
export type InsertPredictionMarket = z.infer<typeof insertPredictionMarketSchema>;
export type PredictionMarket = typeof predictionMarkets.$inferSelect;
export type InsertAuction = z.infer<typeof insertAuctionSchema>;
export type Auction = typeof auctions.$inferSelect;
export type InsertBid = z.infer<typeof insertBidSchema>;
export type Bid = typeof bids.$inferSelect;
export type InsertMediaOutletRequest = z.infer<typeof insertMediaOutletRequestSchema>;
export type MediaOutletRequest = typeof mediaOutletRequests.$inferSelect;
export type InsertComment = z.infer<typeof insertCommentSchema>;
export type Comment = typeof comments.$inferSelect;