tercul-frontend/shared/schema.ts
mukimovd 07e354075e Populate the platform with sample literary data and analysis features
Adds sample author data, text content, analysis results and annotation entities to JSON storage.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cbacfb18-842a-4116-a907-18c0105ad8ec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/39b5c689-6e8a-4d5a-9792-69cc81a56534/32264298-034a-445f-8b51-86b6180c466c.jpg
2025-05-08 00:13:10 +00:00

310 lines
10 KiB
TypeScript

import { pgTable, text, serial, integer, boolean, timestamp, pgEnum, primaryKey, json, unique } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
// Enums
export const roleEnum = pgEnum('role', ['user', 'contributor', 'reviewer', 'editor', 'admin']);
export const workTypeEnum = pgEnum('work_type', ['poem', 'story', 'novel', 'play', 'essay', 'other']);
// Users table
export const users = pgTable("users", {
id: serial("id").primaryKey(),
username: text("username").notNull().unique(),
password: text("password").notNull(),
email: text("email").notNull().unique(),
displayName: text("display_name"),
bio: text("bio"),
avatar: text("avatar"),
role: roleEnum("role").default('user').notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Authors table
export const authors = pgTable("authors", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
slug: text("slug").notNull().unique(),
birthYear: integer("birth_year"),
deathYear: integer("death_year"),
country: text("country"),
biography: text("biography"),
portrait: text("portrait"),
createdAt: timestamp("created_at").defaultNow().notNull(),
createdBy: integer("created_by").references(() => users.id),
});
// Works table (original literary works)
export const works = pgTable("works", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
slug: text("slug").notNull().unique(),
authorId: integer("author_id").references(() => authors.id).notNull(),
year: integer("year"),
type: workTypeEnum("type").default('other').notNull(),
language: text("language").notNull(),
content: text("content").notNull(),
description: text("description"),
createdAt: timestamp("created_at").defaultNow().notNull(),
createdBy: integer("created_by").references(() => users.id),
});
// Translations table
export const translations = pgTable("translations", {
id: serial("id").primaryKey(),
workId: integer("work_id").references(() => works.id).notNull(),
title: text("title").notNull(),
language: text("language").notNull(),
content: text("content").notNull(),
translatorId: integer("translator_id").references(() => users.id).notNull(),
year: integer("year"),
notes: text("notes"),
description: text("description"),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Tags table
export const tags = pgTable("tags", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
type: text("type").notNull(), // genre, period, theme, etc.
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Work-Tag relations (many-to-many)
export const workTags = pgTable("work_tags", {
workId: integer("work_id").references(() => works.id).notNull(),
tagId: integer("tag_id").references(() => tags.id).notNull(),
}, (t) => ({
pk: primaryKey(t.workId, t.tagId),
}));
// Bookmarks
export const bookmarks = pgTable("bookmarks", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id).notNull(),
workId: integer("work_id").references(() => works.id).notNull(),
note: text("note"),
createdAt: timestamp("created_at").defaultNow().notNull(),
}, (t) => ({
unq: unique().on(t.userId, t.workId),
}));
// Comments
export const comments = pgTable("comments", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id).notNull(),
workId: integer("work_id").references(() => works.id), // Optional for non-work comments
translationId: integer("translation_id").references(() => translations.id),
lineNumber: integer("line_number"),
content: text("content").notNull(),
parentId: integer("parent_id").references(() => comments.id),
// Adding support for blog posts and other content types
entityType: text("entity_type"), // 'work', 'translation', 'blogPost', etc.
entityId: integer("entity_id"), // ID of the entity
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Likes (polymorphic)
export const likes = pgTable("likes", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id).notNull(),
entityType: text("entity_type").notNull(), // 'work', 'translation', 'comment'
entityId: integer("entity_id").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
}, (t) => ({
unq: unique().on(t.userId, t.entityType, t.entityId),
}));
// Collections (curated sets of works)
export const collections = pgTable("collections", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
slug: text("slug").notNull().unique(),
description: text("description"),
userId: integer("user_id").references(() => users.id).notNull(),
isPublic: boolean("is_public").default(true).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Collection items
export const collectionItems = pgTable("collection_items", {
id: serial("id").primaryKey(),
collectionId: integer("collection_id").references(() => collections.id).notNull(),
workId: integer("work_id").references(() => works.id).notNull(),
order: integer("order").default(0).notNull(),
addedAt: timestamp("added_at").defaultNow().notNull(),
});
// Timeline events for authors
export const timelineEvents = pgTable("timeline_events", {
id: serial("id").primaryKey(),
authorId: integer("author_id").references(() => authors.id).notNull(),
year: integer("year").notNull(),
title: text("title").notNull(),
description: text("description"),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Blog posts
export const blogPosts = pgTable("blog_posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
slug: text("slug").notNull().unique(),
content: text("content").notNull(),
authorId: integer("author_id").references(() => users.id).notNull(),
excerpt: text("excerpt"),
publishedAt: timestamp("published_at"),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Blog post tags
export const blogPostTags = pgTable("blog_post_tags", {
postId: integer("post_id").references(() => blogPosts.id).notNull(),
tagId: integer("tag_id").references(() => tags.id).notNull(),
}, (t) => ({
pk: primaryKey(t.postId, t.tagId),
}));
// Reading progress
export const readingProgress = pgTable("reading_progress", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id).notNull(),
workId: integer("work_id").references(() => works.id).notNull(),
translationId: integer("translation_id").references(() => translations.id),
progress: integer("progress").default(0).notNull(), // percentage
lastReadAt: timestamp("last_read_at").defaultNow().notNull(),
});
// Insert schemas
export const insertUserSchema = createInsertSchema(users).omit({
id: true,
createdAt: true,
role: true
});
export const insertAuthorSchema = createInsertSchema(authors).omit({
id: true,
createdAt: true
});
export const insertWorkSchema = createInsertSchema(works).omit({
id: true,
createdAt: true
});
export const insertTranslationSchema = createInsertSchema(translations).omit({
id: true,
createdAt: true
});
export const insertTagSchema = createInsertSchema(tags).omit({
id: true,
createdAt: true
});
export const insertBookmarkSchema = createInsertSchema(bookmarks).omit({
id: true,
createdAt: true
});
export const insertCommentSchema = createInsertSchema(comments).omit({
id: true,
createdAt: true
});
export const insertLikeSchema = createInsertSchema(likes).omit({
id: true,
createdAt: true
});
export const insertCollectionSchema = createInsertSchema(collections).omit({
id: true,
createdAt: true
});
export const insertCollectionItemSchema = createInsertSchema(collectionItems).omit({
id: true,
addedAt: true
});
export const insertTimelineEventSchema = createInsertSchema(timelineEvents).omit({
id: true,
createdAt: true
});
export const insertBlogPostSchema = createInsertSchema(blogPosts).omit({
id: true,
createdAt: true
});
export const insertReadingProgressSchema = createInsertSchema(readingProgress).omit({
id: true,
lastReadAt: true
});
// Types
export type InsertUser = z.infer<typeof insertUserSchema>;
export type User = typeof users.$inferSelect;
export type InsertAuthor = z.infer<typeof insertAuthorSchema>;
export type Author = typeof authors.$inferSelect;
export type InsertWork = z.infer<typeof insertWorkSchema>;
export type Work = typeof works.$inferSelect;
export type InsertTranslation = z.infer<typeof insertTranslationSchema>;
export type Translation = typeof translations.$inferSelect;
export type InsertTag = z.infer<typeof insertTagSchema>;
export type Tag = typeof tags.$inferSelect;
export type InsertBookmark = z.infer<typeof insertBookmarkSchema>;
export type Bookmark = typeof bookmarks.$inferSelect;
export type InsertComment = z.infer<typeof insertCommentSchema>;
export type Comment = typeof comments.$inferSelect;
export type InsertLike = z.infer<typeof insertLikeSchema>;
export type Like = typeof likes.$inferSelect;
export type InsertCollection = z.infer<typeof insertCollectionSchema>;
export type Collection = typeof collections.$inferSelect;
export type InsertCollectionItem = z.infer<typeof insertCollectionItemSchema>;
export type CollectionItem = typeof collectionItems.$inferSelect;
export type InsertTimelineEvent = z.infer<typeof insertTimelineEventSchema>;
export type TimelineEvent = typeof timelineEvents.$inferSelect;
export type InsertBlogPost = z.infer<typeof insertBlogPostSchema>;
export type BlogPost = typeof blogPosts.$inferSelect;
export type InsertReadingProgress = z.infer<typeof insertReadingProgressSchema>;
export type ReadingProgress = typeof readingProgress.$inferSelect;
// Analysis results interface for linguistic/sentiment analysis and other analytics
export interface AnalysisResult {
id: number;
workId: number;
type: string; // 'sentiment', 'linguistics', 'themes', 'poetic', etc.
data: any;
createdAt: Date;
}
export type InsertAnalysisResult = Omit<AnalysisResult, 'id' | 'createdAt'>;
// Annotations interface for line-by-line notes, context, and analysis
export interface Annotation {
id: number;
workId: number;
translationId: number | null;
lineNumber: number | null;
content: string;
type: string; // 'context', 'analysis', 'translation', etc.
userId: number;
isOfficial: boolean;
createdAt: Date;
}
export type InsertAnnotation = Omit<Annotation, 'id' | 'createdAt'>;