import { pgTable, text, varchar, boolean, integer, timestamp, jsonb, uuid, pgEnum, real, index, } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; import { relations } from "drizzle-orm"; // ─── Enums ────────────────────────────────────────────────────────────────── export const jobStatusEnum = pgEnum("job_status", [ "pending", "assigned", "in_progress", "validating", "review", "completed", "cancelled", ]); export const assignmentTypeEnum = pgEnum("assignment_type", [ "auto", "manual", "reassigned", ]); export const acceptanceStatusEnum = pgEnum("acceptance_status", [ "pending", "accepted", "declined", ]); export const qcStatusEnum = pgEnum("qc_status", [ "none", "queued", "running", "completed", "failed", ]); export const changeCategoryEnum = pgEnum("change_category", [ "timing_fix", "translation_fix", "style_correction", "cpl_fix", "split_merge", "other", ]); export const errorTypeEnum = pgEnum("error_type", [ "input_error", "cicd_needed", "style_preference", "false_positive", ]); export const affectedInputEnum = pgEnum("affected_input", [ "neon", "alto_curated", "lsx_prompt", "lsx_logic", "arc_config", ]); export const reviewStatusEnum = pgEnum("review_status", [ "pending", "approved", "rejected", ]); // ─── General Settings (existing) ──────────────────────────────────────────── export const generalSettings = pgTable("general_settings", { id: varchar("id").primaryKey().default("general"), displayName: text("display_name").notNull().default("Studio Admin"), contactEmail: text("contact_email") .notNull() .default("admin@cpxstudio.com"), primaryRole: text("primary_role").default("Studio Manager"), twoFactorEnabled: boolean("two_factor_enabled").notNull().default(false), }); export const insertGeneralSettingsSchema = createInsertSchema( generalSettings ).omit({ id: true }); export const updateGeneralSettingsSchema = insertGeneralSettingsSchema.partial(); export type InsertGeneralSettings = z.infer< typeof insertGeneralSettingsSchema >; export type UpdateGeneralSettings = z.infer< typeof updateGeneralSettingsSchema >; export type GeneralSettings = typeof generalSettings.$inferSelect; // ─── Resources ────────────────────────────────────────────────────────────── export const resources = pgTable("resources", { id: uuid("id").primaryKey().defaultRandom(), name: text("name").notNull(), email: text("email").notNull().unique(), atlasResourceId: text("atlas_resource_id"), branchId: text("branch_id"), active: boolean("active").notNull().default(true), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at").notNull().defaultNow(), }); export const insertResourceSchema = createInsertSchema(resources).omit({ id: true, createdAt: true, updatedAt: true, }); export type InsertResource = z.infer; export type Resource = typeof resources.$inferSelect; // ─── Resource Skill Sets ──────────────────────────────────────────────────── export const resourceSkillSets = pgTable("resource_skill_sets", { id: uuid("id").primaryKey().defaultRandom(), resourceId: uuid("resource_id") .notNull() .references(() => resources.id, { onDelete: "cascade" }), platform: text("platform").notNull().default("GTS_X"), grade: text("grade"), sourceLanguageId: text("source_language_id"), targetLanguageId: text("target_language_id"), unitPrice: real("unit_price"), currencyId: text("currency_id"), active: boolean("active").notNull().default(true), }); // ─── Jobs ─────────────────────────────────────────────────────────────────── export const jobs = pgTable( "jobs", { id: uuid("id").primaryKey().defaultRandom(), arcJobId: text("arc_job_id"), atlasTaskId: text("atlas_task_id"), orderId: text("order_id"), sourceLanguage: text("source_language").notNull(), targetLanguage: text("target_language").notNull(), clientId: text("client_id"), status: jobStatusEnum("status").notNull().default("pending"), inputS3Key: text("input_s3_key"), currentS3Key: text("current_s3_key"), // Store the initial parsed document as JSON for diff comparison initialDocument: jsonb("initial_document"), // Store the current working document currentDocument: jsonb("current_document"), metadata: jsonb("metadata").$type>(), langfuseTraceId: text("langfuse_trace_id"), videoS3Key: text("video_s3_key"), qcStatus: qcStatusEnum("qc_status").notNull().default("none"), qcResults: jsonb("qc_results"), techQcPassed: boolean("tech_qc_passed").notNull().default(false), rationaleValidated: boolean("rationale_validated").notNull().default(false), qcReviewedAt: timestamp("qc_reviewed_at"), lockedAt: timestamp("locked_at"), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at").notNull().defaultNow(), }, (table) => [ index("idx_jobs_status").on(table.status), index("idx_jobs_arc_job_id").on(table.arcJobId), index("idx_jobs_client_id").on(table.clientId), ] ); export const insertJobSchema = createInsertSchema(jobs).omit({ id: true, createdAt: true, updatedAt: true, }); export type InsertJob = z.infer; export type Job = typeof jobs.$inferSelect; // ─── Job Assignments ──────────────────────────────────────────────────────── export const jobAssignments = pgTable( "job_assignments", { id: uuid("id").primaryKey().defaultRandom(), jobId: uuid("job_id") .notNull() .references(() => jobs.id, { onDelete: "cascade" }), resourceId: uuid("resource_id") .notNull() .references(() => resources.id), segmentIndex: integer("segment_index").notNull().default(0), assignmentType: assignmentTypeEnum("assignment_type") .notNull() .default("manual"), acceptanceStatus: acceptanceStatusEnum("acceptance_status") .notNull() .default("pending"), acceptanceToken: text("acceptance_token"), acceptanceExpiresAt: timestamp("acceptance_expires_at"), deadline: timestamp("deadline"), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").notNull().defaultNow(), }, (table) => [ index("idx_assignments_job").on(table.jobId), index("idx_assignments_resource").on(table.resourceId), index("idx_assignments_status").on(table.acceptanceStatus), ] ); export const insertJobAssignmentSchema = createInsertSchema( jobAssignments ).omit({ id: true, createdAt: true }); export type InsertJobAssignment = z.infer; export type JobAssignment = typeof jobAssignments.$inferSelect; // ─── Edit Sessions ────────────────────────────────────────────────────────── export const editSessions = pgTable( "edit_sessions", { id: uuid("id").primaryKey().defaultRandom(), jobId: uuid("job_id") .notNull() .references(() => jobs.id, { onDelete: "cascade" }), resourceId: uuid("resource_id") .notNull() .references(() => resources.id), startedAt: timestamp("started_at").notNull().defaultNow(), endedAt: timestamp("ended_at"), linesChanged: integer("lines_changed").notNull().default(0), timeSpentSeconds: integer("time_spent_seconds").notNull().default(0), }, (table) => [index("idx_sessions_job").on(table.jobId)] ); export const insertEditSessionSchema = createInsertSchema(editSessions).omit({ id: true, }); export type InsertEditSession = z.infer; export type EditSession = typeof editSessions.$inferSelect; // ─── Edit Diffs ───────────────────────────────────────────────────────────── export const editDiffs = pgTable( "edit_diffs", { id: uuid("id").primaryKey().defaultRandom(), jobId: uuid("job_id") .notNull() .references(() => jobs.id, { onDelete: "cascade" }), editSessionId: uuid("edit_session_id").references(() => editSessions.id), subtitleIndex: integer("subtitle_index").notNull(), field: text("field").notNull(), // text | tc_in | tc_out | position | style oldValue: text("old_value"), newValue: text("new_value"), rationale: text("rationale").notNull(), // WHY — required, drives CI/CD analysis changeCategory: changeCategoryEnum("change_category") .notNull() .default("other"), createdAt: timestamp("created_at").notNull().defaultNow(), }, (table) => [ index("idx_diffs_job").on(table.jobId), index("idx_diffs_session").on(table.editSessionId), index("idx_diffs_category").on(table.changeCategory), ] ); export const insertEditDiffSchema = createInsertSchema(editDiffs).omit({ id: true, createdAt: true, }); export type InsertEditDiff = z.infer; export type EditDiff = typeof editDiffs.$inferSelect; // ─── Feedback Analyses ────────────────────────────────────────────────────── export const feedbackAnalyses = pgTable( "feedback_analyses", { id: uuid("id").primaryKey().defaultRandom(), jobId: uuid("job_id") .notNull() .references(() => jobs.id, { onDelete: "cascade" }), errorType: errorTypeEnum("error_type").notNull(), confidenceScore: real("confidence_score").notNull(), description: text("description").notNull(), affectedInput: affectedInputEnum("affected_input"), reviewStatus: reviewStatusEnum("review_status") .notNull() .default("pending"), reviewerId: uuid("reviewer_id"), reviewedAt: timestamp("reviewed_at"), actionTaken: jsonb("action_taken").$type>(), createdAt: timestamp("created_at").notNull().defaultNow(), }, (table) => [ index("idx_feedback_job").on(table.jobId), index("idx_feedback_status").on(table.reviewStatus), index("idx_feedback_error_type").on(table.errorType), ] ); export const insertFeedbackAnalysisSchema = createInsertSchema( feedbackAnalyses ).omit({ id: true, createdAt: true }); export type InsertFeedbackAnalysis = z.infer< typeof insertFeedbackAnalysisSchema >; export type FeedbackAnalysis = typeof feedbackAnalyses.$inferSelect; // ─── Relations ────────────────────────────────────────────────────────────── export const resourcesRelations = relations(resources, ({ many }) => ({ skillSets: many(resourceSkillSets), assignments: many(jobAssignments), editSessions: many(editSessions), })); export const resourceSkillSetsRelations = relations( resourceSkillSets, ({ one }) => ({ resource: one(resources, { fields: [resourceSkillSets.resourceId], references: [resources.id], }), }) ); export const jobsRelations = relations(jobs, ({ many }) => ({ assignments: many(jobAssignments), editSessions: many(editSessions), editDiffs: many(editDiffs), feedbackAnalyses: many(feedbackAnalyses), })); export const jobAssignmentsRelations = relations( jobAssignments, ({ one }) => ({ job: one(jobs, { fields: [jobAssignments.jobId], references: [jobs.id], }), resource: one(resources, { fields: [jobAssignments.resourceId], references: [resources.id], }), }) ); export const editSessionsRelations = relations( editSessions, ({ one, many }) => ({ job: one(jobs, { fields: [editSessions.jobId], references: [jobs.id], }), resource: one(resources, { fields: [editSessions.resourceId], references: [resources.id], }), diffs: many(editDiffs), }) ); export const editDiffsRelations = relations(editDiffs, ({ one }) => ({ job: one(jobs, { fields: [editDiffs.jobId], references: [jobs.id], }), editSession: one(editSessions, { fields: [editDiffs.editSessionId], references: [editSessions.id], }), })); export const feedbackAnalysesRelations = relations( feedbackAnalyses, ({ one }) => ({ job: one(jobs, { fields: [feedbackAnalyses.jobId], references: [jobs.id], }), }) );