import { MigrationInterface, QueryRunner } from "typeorm"; /** * Customer-support chat. A `support_conversations` row is the single ongoing * thread with a company; `support_messages` are its text messages. There is no * lifecycle column — a thread is opened by whichever side speaks first and * stays open. Enum-like columns are varchar (no PG enum churn). * * The unique index on `company_id` is load-bearing, not just an optimization: * the get-or-create path depends on it to settle concurrent first-messages. * It is partial on `deleted_at IS NULL` so a soft-deleted thread doesn't block * a fresh one. */ export class CreateSupportChat2310000000000 implements MigrationInterface { name = "CreateSupportChat2310000000000"; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.support_conversations ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), company_id uuid NOT NULL, company_name varchar(200), created_by_user_id uuid, last_message_at timestamptz, last_message_preview varchar(280), last_message_author_role varchar(12), customer_last_read_at timestamptz, agent_last_read_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz ) `); await queryRunner.query(` CREATE UNIQUE INDEX IF NOT EXISTS "IDX_SUPPORT_CONV_COMPANY" ON freight.support_conversations (company_id) WHERE deleted_at IS NULL `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS "IDX_SUPPORT_CONV_LASTMSG" ON freight.support_conversations (last_message_at) `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.support_messages ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), conversation_id uuid NOT NULL, author_user_id uuid NOT NULL, author_role varchar(12) NOT NULL, author_name varchar(200), body text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz ) `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS "IDX_SUPPORT_MSG_CONV_CREATED" ON freight.support_messages (conversation_id, created_at) `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( `DROP INDEX IF EXISTS freight."IDX_SUPPORT_MSG_CONV_CREATED"`, ); await queryRunner.query(`DROP TABLE IF EXISTS freight.support_messages`); await queryRunner.query( `DROP INDEX IF EXISTS freight."IDX_SUPPORT_CONV_LASTMSG"`, ); await queryRunner.query( `DROP INDEX IF EXISTS freight."IDX_SUPPORT_CONV_COMPANY"`, ); await queryRunner.query( `DROP TABLE IF EXISTS freight.support_conversations`, ); } }