Change ticket IDs from UUID to sequential integers
Backend: - tickets.id: uuid → integer GENERATED ALWAYS AS IDENTITY - transactions.ticket_id, custom_field_values.ticket_id: uuid → integer - Routes convert string params to Number() for DB queries - ScripEngine.prepare takes ticketId: number - ActionPayload.ticketId: string → number Frontend: - Ticket.id: string → number, Transaction.ticket_id: string → number - API functions accept number params - formatTicketId() helper returns TKT-0001 format - Ticket rows display TKT-XXXX, detail page uses formatTicketId Migration: drops FKs, clears data, alters column types, re-adds FKs
This commit is contained in:
29
drizzle/migrations/0001_lovely_quentin_quire.sql
Normal file
29
drizzle/migrations/0001_lovely_quentin_quire.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Drop foreign key constraints referencing tickets.id
|
||||
ALTER TABLE "custom_field_values" DROP CONSTRAINT IF EXISTS "custom_field_values_ticket_id_tickets_id_fk";
|
||||
ALTER TABLE "transactions" DROP CONSTRAINT IF EXISTS "transactions_ticket_id_tickets_id_fk";
|
||||
|
||||
-- Drop dependent indexes
|
||||
DROP INDEX IF EXISTS "custom_field_values_ticket_id_idx";
|
||||
DROP INDEX IF EXISTS "transactions_ticket_id_idx";
|
||||
|
||||
-- Clear all data from affected tables (UUIDs cannot cast to integer)
|
||||
DELETE FROM "custom_field_values";
|
||||
DELETE FROM "transactions";
|
||||
DELETE FROM "tickets";
|
||||
|
||||
-- Alter column types with USING clause for empty tables
|
||||
ALTER TABLE "custom_field_values" ALTER COLUMN "ticket_id" SET DATA TYPE integer USING (0);
|
||||
ALTER TABLE "transactions" ALTER COLUMN "ticket_id" SET DATA TYPE integer USING (0);
|
||||
|
||||
-- Alter tickets.id to serial
|
||||
ALTER TABLE "tickets" ALTER COLUMN "id" DROP DEFAULT;
|
||||
ALTER TABLE "tickets" ALTER COLUMN "id" SET DATA TYPE integer USING (0);
|
||||
ALTER TABLE "tickets" ALTER COLUMN "id" ADD GENERATED ALWAYS AS IDENTITY (sequence name "tickets_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1);
|
||||
|
||||
-- Re-add foreign key constraints
|
||||
ALTER TABLE "custom_field_values" ADD CONSTRAINT "custom_field_values_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "transactions" ADD CONSTRAINT "transactions_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;
|
||||
|
||||
-- Re-create indexes
|
||||
CREATE INDEX "custom_field_values_ticket_id_idx" ON "custom_field_values" USING btree ("ticket_id");
|
||||
CREATE INDEX "transactions_ticket_id_idx" ON "transactions" USING btree ("ticket_id");
|
||||
916
drizzle/migrations/meta/0001_snapshot.json
Normal file
916
drizzle/migrations/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,916 @@
|
||||
{
|
||||
"id": "042752b4-e1ad-4b6d-96ed-81f836028826",
|
||||
"prevId": "981c2ca0-1a37-4fbd-8624-2e7f43cd8361",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.custom_field_values": {
|
||||
"name": "custom_field_values",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"custom_field_id": {
|
||||
"name": "custom_field_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"ticket_id": {
|
||||
"name": "ticket_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"custom_field_values_ticket_id_idx": {
|
||||
"name": "custom_field_values_ticket_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "ticket_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
},
|
||||
"custom_field_values_custom_field_id_idx": {
|
||||
"name": "custom_field_values_custom_field_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "custom_field_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"custom_field_values_custom_field_id_custom_fields_id_fk": {
|
||||
"name": "custom_field_values_custom_field_id_custom_fields_id_fk",
|
||||
"tableFrom": "custom_field_values",
|
||||
"tableTo": "custom_fields",
|
||||
"columnsFrom": [
|
||||
"custom_field_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"custom_field_values_ticket_id_tickets_id_fk": {
|
||||
"name": "custom_field_values_ticket_id_tickets_id_fk",
|
||||
"tableFrom": "custom_field_values",
|
||||
"tableTo": "tickets",
|
||||
"columnsFrom": [
|
||||
"ticket_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"custom_field_values_cf_id_ticket_id_value_unique": {
|
||||
"name": "custom_field_values_cf_id_ticket_id_value_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"custom_field_id",
|
||||
"ticket_id",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.custom_fields": {
|
||||
"name": "custom_fields",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"field_type": {
|
||||
"name": "field_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"values": {
|
||||
"name": "values",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"max_values": {
|
||||
"name": "max_values",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 1
|
||||
},
|
||||
"pattern": {
|
||||
"name": "pattern",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.lifecycles": {
|
||||
"name": "lifecycles",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"definition": {
|
||||
"name": "definition",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"lifecycles_name_unique": {
|
||||
"name": "lifecycles_name_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.queue_custom_fields": {
|
||||
"name": "queue_custom_fields",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"queue_id": {
|
||||
"name": "queue_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"custom_field_id": {
|
||||
"name": "custom_field_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"queue_custom_fields_queue_id_queues_id_fk": {
|
||||
"name": "queue_custom_fields_queue_id_queues_id_fk",
|
||||
"tableFrom": "queue_custom_fields",
|
||||
"tableTo": "queues",
|
||||
"columnsFrom": [
|
||||
"queue_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"queue_custom_fields_custom_field_id_custom_fields_id_fk": {
|
||||
"name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
|
||||
"tableFrom": "queue_custom_fields",
|
||||
"tableTo": "custom_fields",
|
||||
"columnsFrom": [
|
||||
"custom_field_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"queue_custom_fields_queue_id_custom_field_id_unique": {
|
||||
"name": "queue_custom_fields_queue_id_custom_field_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"queue_id",
|
||||
"custom_field_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.queues": {
|
||||
"name": "queues",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"lifecycle_id": {
|
||||
"name": "lifecycle_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"queues_lifecycle_id_lifecycles_id_fk": {
|
||||
"name": "queues_lifecycle_id_lifecycles_id_fk",
|
||||
"tableFrom": "queues",
|
||||
"tableTo": "lifecycles",
|
||||
"columnsFrom": [
|
||||
"lifecycle_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"queues_name_unique": {
|
||||
"name": "queues_name_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.scrips": {
|
||||
"name": "scrips",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"queue_id": {
|
||||
"name": "queue_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"condition_type": {
|
||||
"name": "condition_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"condition_config": {
|
||||
"name": "condition_config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"action_type": {
|
||||
"name": "action_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"action_config": {
|
||||
"name": "action_config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"template_id": {
|
||||
"name": "template_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"stage": {
|
||||
"name": "stage",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'TransactionCreate'"
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
},
|
||||
"disabled": {
|
||||
"name": "disabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"scrips_queue_id_idx": {
|
||||
"name": "scrips_queue_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "queue_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"scrips_queue_id_queues_id_fk": {
|
||||
"name": "scrips_queue_id_queues_id_fk",
|
||||
"tableFrom": "scrips",
|
||||
"tableTo": "queues",
|
||||
"columnsFrom": [
|
||||
"queue_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"scrips_template_id_templates_id_fk": {
|
||||
"name": "scrips_template_id_templates_id_fk",
|
||||
"tableFrom": "scrips",
|
||||
"tableTo": "templates",
|
||||
"columnsFrom": [
|
||||
"template_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.templates": {
|
||||
"name": "templates",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"queue_id": {
|
||||
"name": "queue_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"subject_template": {
|
||||
"name": "subject_template",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"body_template": {
|
||||
"name": "body_template",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"templates_queue_id_queues_id_fk": {
|
||||
"name": "templates_queue_id_queues_id_fk",
|
||||
"tableFrom": "templates",
|
||||
"tableTo": "queues",
|
||||
"columnsFrom": [
|
||||
"queue_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.tickets": {
|
||||
"name": "tickets",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "tickets_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
},
|
||||
"subject": {
|
||||
"name": "subject",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"queue_id": {
|
||||
"name": "queue_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"owner_id": {
|
||||
"name": "owner_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"creator_id": {
|
||||
"name": "creator_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"started_at": {
|
||||
"name": "started_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"resolved_at": {
|
||||
"name": "resolved_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"tickets_queue_id_idx": {
|
||||
"name": "tickets_queue_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "queue_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
},
|
||||
"tickets_status_idx": {
|
||||
"name": "tickets_status_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "status",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"tickets_queue_id_queues_id_fk": {
|
||||
"name": "tickets_queue_id_queues_id_fk",
|
||||
"tableFrom": "tickets",
|
||||
"tableTo": "queues",
|
||||
"columnsFrom": [
|
||||
"queue_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"tickets_owner_id_users_id_fk": {
|
||||
"name": "tickets_owner_id_users_id_fk",
|
||||
"tableFrom": "tickets",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"owner_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"tickets_creator_id_users_id_fk": {
|
||||
"name": "tickets_creator_id_users_id_fk",
|
||||
"tableFrom": "tickets",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"creator_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.transactions": {
|
||||
"name": "transactions",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"ticket_id": {
|
||||
"name": "ticket_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"transaction_type": {
|
||||
"name": "transaction_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"field": {
|
||||
"name": "field",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"old_value": {
|
||||
"name": "old_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"new_value": {
|
||||
"name": "new_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"data": {
|
||||
"name": "data",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"creator_id": {
|
||||
"name": "creator_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"transactions_ticket_id_idx": {
|
||||
"name": "transactions_ticket_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "ticket_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
},
|
||||
"transactions_created_at_idx": {
|
||||
"name": "transactions_created_at_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "created_at",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"transactions_ticket_id_tickets_id_fk": {
|
||||
"name": "transactions_ticket_id_tickets_id_fk",
|
||||
"tableFrom": "transactions",
|
||||
"tableTo": "tickets",
|
||||
"columnsFrom": [
|
||||
"ticket_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"transactions_creator_id_users_id_fk": {
|
||||
"name": "transactions_creator_id_users_id_fk",
|
||||
"tableFrom": "transactions",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"creator_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"users_username_unique": {
|
||||
"name": "users_username_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"username"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,13 @@
|
||||
"when": 1780859982396,
|
||||
"tag": "0000_acoustic_wendell_vaughn",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1780867177929,
|
||||
"tag": "0001_lovely_quentin_quire",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export const lifecycles = pgTable('lifecycles', {
|
||||
});
|
||||
|
||||
export const tickets = pgTable('tickets', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
|
||||
subject: text('subject').notNull(),
|
||||
queue_id: uuid('queue_id').notNull().references(() => queues.id),
|
||||
status: text('status').notNull(),
|
||||
@@ -41,7 +41,7 @@ export const tickets = pgTable('tickets', {
|
||||
|
||||
export const transactions = pgTable('transactions', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
ticket_id: uuid('ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
|
||||
ticket_id: integer('ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
|
||||
transaction_type: text('transaction_type').notNull(),
|
||||
field: text('field'),
|
||||
old_value: text('old_value'),
|
||||
@@ -103,7 +103,7 @@ export const queueCustomFields = pgTable('queue_custom_fields', {
|
||||
export const customFieldValues = pgTable('custom_field_values', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
custom_field_id: uuid('custom_field_id').notNull().references(() => customFields.id, { onDelete: 'cascade' }),
|
||||
ticket_id: uuid('ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
|
||||
ticket_id: integer('ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
|
||||
value: text('value').notNull(),
|
||||
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||||
}, (table) => ({
|
||||
|
||||
@@ -61,7 +61,7 @@ export function createTicketsRouter(db: Db): Hono {
|
||||
|
||||
// GET /:id — get ticket with custom field values
|
||||
router.get('/:id', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const id = Number(c.req.param('id'));
|
||||
|
||||
const ticket = await db.query.tickets.findFirst({
|
||||
where: eq(tickets.id, id),
|
||||
@@ -92,7 +92,7 @@ export function createTicketsRouter(db: Db): Hono {
|
||||
|
||||
// PATCH /:id — update ticket
|
||||
router.patch('/:id', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const id = Number(c.req.param('id'));
|
||||
const body = await c.req.json();
|
||||
const parsed = UpdateTicketSchema.parse(body);
|
||||
|
||||
@@ -186,7 +186,7 @@ export function createTicketsRouter(db: Db): Hono {
|
||||
|
||||
// POST /:id/preview — dry-run scrips
|
||||
router.post('/:id/preview', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const id = Number(c.req.param('id'));
|
||||
const body = await c.req.json();
|
||||
const parsed = UpdateTicketSchema.parse(body);
|
||||
|
||||
@@ -221,7 +221,7 @@ export function createTicketsRouter(db: Db): Hono {
|
||||
|
||||
// GET /:id/transactions — list transactions for ticket
|
||||
router.get('/:id/transactions', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const id = Number(c.req.param('id'));
|
||||
|
||||
const result = await db.query.transactions.findMany({
|
||||
where: eq(transactions.ticket_id, id),
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface ActionPayload {
|
||||
scripName: string;
|
||||
actionType: string;
|
||||
actionConfig: Record<string, unknown>;
|
||||
ticketId?: string;
|
||||
ticketId?: number;
|
||||
recipients?: string[];
|
||||
subject?: string;
|
||||
body?: string;
|
||||
@@ -97,7 +97,7 @@ export class SetCustomField implements ActionExecutor {
|
||||
async execute(payload: ActionPayload): Promise<{ success: boolean; message: string }> {
|
||||
const fieldId = payload.field_id ?? String(payload.actionConfig['field_id'] ?? '');
|
||||
const value = payload.value ?? String(payload.actionConfig['value'] ?? '');
|
||||
const ticketId = payload.ticketId ?? String(payload.actionConfig['ticket_id'] ?? '');
|
||||
const ticketId = payload.ticketId ?? Number(payload.actionConfig['ticket_id'] ?? 0);
|
||||
|
||||
if (!fieldId || !value || !ticketId) {
|
||||
return { success: false, message: 'SetCustomField: missing field_id, value, or ticket_id' };
|
||||
@@ -121,7 +121,7 @@ export class CreateTransaction implements ActionExecutor {
|
||||
constructor(private db: Db) {}
|
||||
|
||||
async execute(payload: ActionPayload): Promise<{ success: boolean; message: string }> {
|
||||
const ticketId = payload.ticketId ?? String(payload.actionConfig['ticket_id'] ?? '');
|
||||
const ticketId = payload.ticketId ?? Number(payload.actionConfig['ticket_id'] ?? 0);
|
||||
const transactionType = String(payload.actionConfig['transaction_type'] ?? '');
|
||||
const field = payload.actionConfig['field'] as string | undefined ?? null;
|
||||
const oldValue = payload.actionConfig['old_value'] as string | undefined ?? null;
|
||||
|
||||
@@ -35,7 +35,7 @@ export class ScripEngine {
|
||||
}
|
||||
|
||||
async prepare(
|
||||
ticketId: string,
|
||||
ticketId: number,
|
||||
transactions: Transaction[],
|
||||
): Promise<PreparedScrip[]> {
|
||||
const ticketRecord = await this.db.query.tickets.findFirst({
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, formatTicketId } from "@/lib/utils";
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
new: "#8a8f98",
|
||||
@@ -38,7 +38,7 @@ type FilterKey = (typeof FILTERS)[number]["key"];
|
||||
|
||||
function TicketRow({ ticket, onClick }: { ticket: Ticket; onClick: () => void }) {
|
||||
const statusColor = STATUS_COLORS[ticket.status] || STATUS_COLORS.new;
|
||||
const shortId = ticket.id.slice(0, 8);
|
||||
const shortId = formatTicketId(ticket.id);
|
||||
const timeAgo = formatDistanceToNow(new Date(ticket.updated_at), { addSuffix: true });
|
||||
|
||||
return (
|
||||
|
||||
@@ -20,7 +20,7 @@ import type {
|
||||
UpdateResult,
|
||||
} from "@/lib/types";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, formatTicketId } from "@/lib/utils";
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
new: "#8a8f98",
|
||||
@@ -151,7 +151,8 @@ export default function TicketDetailPage({
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const { id } = use(params);
|
||||
const { id: idParam } = use(params);
|
||||
const id = Number(idParam);
|
||||
const router = useRouter();
|
||||
|
||||
const [ticket, setTicket] = useState<Ticket | null>(null);
|
||||
@@ -328,7 +329,7 @@ export default function TicketDetailPage({
|
||||
{ticket.subject}
|
||||
</h1>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
<span className="font-mono">{ticket.id.slice(0, 8)}</span> · {queue?.name || ticket.queue_id}
|
||||
<span className="font-mono">{formatTicketId(ticket.id)}</span> · {queue?.name || ticket.queue_id}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function getTickets(params?: { queue_id?: string; status?: string }
|
||||
return request<Ticket[]>(`/tickets${qs ? `?${qs}` : ""}`);
|
||||
}
|
||||
|
||||
export async function getTicket(id: string): Promise<{ data: Ticket | null; error: string | null }> {
|
||||
export async function getTicket(id: number): Promise<{ data: Ticket | null; error: string | null }> {
|
||||
return request<Ticket>(`/tickets/${id}`);
|
||||
}
|
||||
|
||||
@@ -44,15 +44,15 @@ export async function createTicket(data: { subject: string; queue_id: string }):
|
||||
return request<Ticket>("/tickets", { method: "POST", body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
export async function updateTicket(id: string, data: { subject?: string; status?: string }): Promise<{ data: UpdateResult | null; error: string | null }> {
|
||||
export async function updateTicket(id: number, data: { subject?: string; status?: string }): Promise<{ data: UpdateResult | null; error: string | null }> {
|
||||
return request<UpdateResult>(`/tickets/${id}`, { method: "PATCH", body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
export async function previewTicket(id: string, data: { status?: string }): Promise<{ data: PreviewResult | null; error: string | null }> {
|
||||
export async function previewTicket(id: number, data: { status?: string }): Promise<{ data: PreviewResult | null; error: string | null }> {
|
||||
return request<PreviewResult>(`/tickets/${id}/preview`, { method: "POST", body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
export async function getTicketTransactions(id: string): Promise<{ data: Transaction[] | null; error: string | null }> {
|
||||
export async function getTicketTransactions(id: number): Promise<{ data: Transaction[] | null; error: string | null }> {
|
||||
return request<Transaction[]>(`/tickets/${id}/transactions`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface Ticket {
|
||||
id: string;
|
||||
id: number;
|
||||
subject: string;
|
||||
queue_id: string;
|
||||
status: string;
|
||||
@@ -21,7 +21,7 @@ export interface Queue {
|
||||
|
||||
export interface Transaction {
|
||||
id: string;
|
||||
ticket_id: string;
|
||||
ticket_id: number;
|
||||
transaction_type: string;
|
||||
field: string | null;
|
||||
old_value: string | null;
|
||||
|
||||
@@ -4,3 +4,7 @@ import { twMerge } from "tailwind-merge"
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
export function formatTicketId(id: number): string {
|
||||
return `TKT-${String(id).padStart(4, "0")}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user