Skip to content

Commit 2cf0f6f

Browse files
[Apps] Adding support app alpha and dogfooding (#1368)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Support app: inbox UI to create, view, reply, and manage conversations (status, priority, assignee, tags, internal notes). * Dashboard pages: Conversations and Support Settings; feedback can create managed conversations. * Public/internal APIs for listing, creating, updating, and fetching conversation details; client-side helpers. * **SLA** * Configurable first/next response targets, urgency classification, and timing logic. * **Data** * New conversation persistence (conversations, entry points, messages) and migration tests; preserves conversations on user/team deletion and anonymizes sender data. * **Tests** * Unit, migration, and end-to-end tests added. * **Documentation** * Updated docs describing conversation model and workflow rules. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 3385d6e commit 2cf0f6f

28 files changed

Lines changed: 8643 additions & 876 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
CREATE TABLE "Conversation" (
2+
"id" UUID NOT NULL,
3+
"tenancyId" UUID NOT NULL,
4+
"projectUserId" UUID,
5+
"teamId" UUID,
6+
"subject" TEXT NOT NULL,
7+
"status" TEXT NOT NULL,
8+
"priority" TEXT NOT NULL,
9+
"source" TEXT NOT NULL,
10+
"assignedToUserId" TEXT,
11+
"assignedToDisplayName" TEXT,
12+
"tags" JSONB,
13+
"firstResponseDueAt" TIMESTAMP(3),
14+
"firstResponseAt" TIMESTAMP(3),
15+
"nextResponseDueAt" TIMESTAMP(3),
16+
"lastCustomerReplyAt" TIMESTAMP(3),
17+
"lastAgentReplyAt" TIMESTAMP(3),
18+
"metadata" JSONB,
19+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
20+
"updatedAt" TIMESTAMP(3) NOT NULL,
21+
"lastMessageAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
22+
"lastInboundAt" TIMESTAMP(3),
23+
"lastOutboundAt" TIMESTAMP(3),
24+
"closedAt" TIMESTAMP(3),
25+
26+
CONSTRAINT "Conversation_pkey" PRIMARY KEY ("tenancyId","id"),
27+
CONSTRAINT "Conversation_status_check" CHECK ("status" IN ('open', 'pending', 'closed')),
28+
CONSTRAINT "Conversation_priority_check" CHECK ("priority" IN ('low', 'normal', 'high', 'urgent')),
29+
CONSTRAINT "Conversation_source_check" CHECK ("source" IN ('manual', 'chat', 'email', 'api')),
30+
CONSTRAINT "Conversation_tenancyId_fkey" FOREIGN KEY ("tenancyId") REFERENCES "Tenancy"("id") ON DELETE CASCADE ON UPDATE CASCADE
31+
);
32+
33+
CREATE TABLE "ConversationEntryPoint" (
34+
"id" UUID NOT NULL,
35+
"tenancyId" UUID NOT NULL,
36+
"conversationId" UUID NOT NULL,
37+
"channelType" TEXT NOT NULL,
38+
"adapterKey" TEXT NOT NULL,
39+
"externalChannelId" TEXT,
40+
"isEntryPoint" BOOLEAN NOT NULL DEFAULT FALSE,
41+
"metadata" JSONB,
42+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
43+
"updatedAt" TIMESTAMP(3) NOT NULL,
44+
45+
CONSTRAINT "ConversationEntryPoint_pkey" PRIMARY KEY ("tenancyId","id"),
46+
CONSTRAINT "ConversationEntryPoint_type_check" CHECK ("channelType" IN ('manual', 'chat', 'email', 'api')),
47+
CONSTRAINT "ConversationEntryPoint_tenancyId_fkey" FOREIGN KEY ("tenancyId") REFERENCES "Tenancy"("id") ON DELETE CASCADE ON UPDATE CASCADE,
48+
CONSTRAINT "ConversationEntryPoint_tenancyId_conversationId_fkey" FOREIGN KEY ("tenancyId", "conversationId") REFERENCES "Conversation"("tenancyId", "id") ON DELETE CASCADE ON UPDATE CASCADE
49+
);
50+
51+
CREATE TABLE "ConversationMessage" (
52+
"id" UUID NOT NULL,
53+
"tenancyId" UUID NOT NULL,
54+
"conversationId" UUID NOT NULL,
55+
"channelId" UUID,
56+
"messageType" TEXT NOT NULL,
57+
"senderType" TEXT NOT NULL,
58+
"senderId" TEXT,
59+
"senderDisplayName" TEXT,
60+
"senderPrimaryEmail" TEXT,
61+
"body" TEXT,
62+
"attachments" JSONB,
63+
"metadata" JSONB,
64+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
65+
66+
CONSTRAINT "ConversationMessage_pkey" PRIMARY KEY ("tenancyId","id"),
67+
CONSTRAINT "ConversationMessage_messageType_check" CHECK ("messageType" IN ('message', 'internal-note', 'status-change')),
68+
CONSTRAINT "ConversationMessage_senderType_check" CHECK ("senderType" IN ('user', 'agent', 'system')),
69+
CONSTRAINT "ConversationMessage_tenancyId_fkey" FOREIGN KEY ("tenancyId") REFERENCES "Tenancy"("id") ON DELETE CASCADE ON UPDATE CASCADE,
70+
CONSTRAINT "ConversationMessage_tenancyId_conversationId_fkey" FOREIGN KEY ("tenancyId", "conversationId") REFERENCES "Conversation"("tenancyId", "id") ON DELETE CASCADE ON UPDATE CASCADE,
71+
CONSTRAINT "ConversationMessage_tenancyId_channelId_fkey" FOREIGN KEY ("tenancyId", "channelId") REFERENCES "ConversationEntryPoint"("tenancyId", "id") ON DELETE NO ACTION ON UPDATE CASCADE
72+
);
73+
74+
CREATE INDEX "Conversation_user_lastMessageAt_idx" ON "Conversation"("tenancyId", "projectUserId", "lastMessageAt" DESC);
75+
CREATE INDEX "Conversation_status_lastMessageAt_idx" ON "Conversation"("tenancyId", "status", "lastMessageAt" DESC);
76+
CREATE INDEX "Conversation_team_lastMessageAt_idx" ON "Conversation"("tenancyId", "teamId", "lastMessageAt" DESC);
77+
CREATE INDEX "ConversationEntryPoint_conversation_createdAt_idx" ON "ConversationEntryPoint"("tenancyId", "conversationId", "createdAt");
78+
CREATE INDEX "ConversationEntryPoint_type_adapter_idx" ON "ConversationEntryPoint"("tenancyId", "channelType", "adapterKey");
79+
CREATE INDEX "ConversationMessage_conversation_createdAt_idx" ON "ConversationMessage"("tenancyId", "conversationId", "createdAt");
80+
CREATE INDEX "ConversationMessage_channel_createdAt_idx" ON "ConversationMessage"("tenancyId", "channelId", "createdAt");

0 commit comments

Comments
 (0)