Spaces:
Sleeping
Sleeping
| 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<typeof insertResourceSchema>; | |
| 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<Record<string, unknown>>(), | |
| 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<typeof insertJobSchema>; | |
| 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<typeof insertJobAssignmentSchema>; | |
| 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<typeof insertEditSessionSchema>; | |
| 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<typeof insertEditDiffSchema>; | |
| 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<Record<string, unknown>>(), | |
| 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], | |
| }), | |
| }) | |
| ); | |