Skip to content

Commit c598f5a

Browse files
committed
feat(webapp): attribute Plain support threads to their org tenant
1 parent 4af6f14 commit c598f5a

6 files changed

Lines changed: 51 additions & 1 deletion

File tree

apps/webapp/app/routes/resources.orgs.$organizationSlug.select-plan.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export const action = dashboardAction(
9595
email: user.email,
9696
name: user.name ?? "",
9797
title: "Plan cancelation feedback",
98+
organizationId: organization.id,
99+
organizationName: organization.title,
98100
components: [
99101
uiComponent.text({
100102
text: `${user.name} (${user.email}) just canceled their plan.`,

apps/webapp/app/utils/plain.server.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,20 @@ type Input = {
99
title: string;
1010
components: ReturnType<typeof uiComponent.text>[];
1111
labelTypeIds?: string[];
12+
organizationId?: string;
13+
organizationName?: string;
1214
};
1315

14-
export async function sendToPlain({ userId, email, name, title, components, labelTypeIds }: Input) {
16+
export async function sendToPlain({
17+
userId,
18+
email,
19+
name,
20+
title,
21+
components,
22+
labelTypeIds,
23+
organizationId,
24+
organizationName,
25+
}: Input) {
1526
if (!env.PLAIN_API_KEY) {
1627
return;
1728
}
@@ -53,6 +64,34 @@ export async function sendToPlain({ userId, email, name, title, components, labe
5364
return;
5465
}
5566

67+
// Attribute the thread to the org so support data can be rolled up per org: the tenant is
68+
// keyed by externalId = org_id. Isolated in its own try/catch, and the thread's
69+
// tenantIdentifier is gated on success — so a tenant failure (e.g. an API key without
70+
// tenant scope) downgrades to "no attribution" instead of dropping the thread. The
71+
// customer's own externalId (User.id, used by the customer cards + impersonation link) is
72+
// left untouched.
73+
let tenantLinked = false;
74+
if (organizationId) {
75+
try {
76+
await client.mutation.upsertTenant({
77+
input: {
78+
identifier: { externalId: organizationId },
79+
externalId: organizationId,
80+
name: organizationName ?? organizationId,
81+
},
82+
});
83+
await client.mutation.addCustomerToTenants({
84+
input: {
85+
customerIdentifier: { customerId },
86+
tenantIdentifiers: [{ externalId: organizationId }],
87+
},
88+
});
89+
tenantLinked = true;
90+
} catch (error) {
91+
console.error("Failed to link Plain customer to org tenant", error);
92+
}
93+
}
94+
5695
await client.mutation.createThread({
5796
input: {
5897
customerIdentifier: {
@@ -61,6 +100,7 @@ export async function sendToPlain({ userId, email, name, title, components, labe
61100
title: title,
62101
components: components,
63102
labelTypeIds,
103+
tenantIdentifier: tenantLinked ? { externalId: organizationId } : undefined,
64104
},
65105
});
66106
} catch (error) {

apps/webapp/app/v3/services/setBranchesAddOn.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export class SetBranchesAddOnService extends BaseService {
7474
email: user.email,
7575
name: user.name ?? user.displayName ?? user.email,
7676
title: `Preview branches quota request: ${amount}`,
77+
organizationId,
78+
organizationName: organization?.title,
7779
components: [
7880
uiComponent.text({
7981
text: `Org: ${organization?.title} (${organizationId})`,

apps/webapp/app/v3/services/setConcurrencyAddOn.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export class SetConcurrencyAddOnService extends BaseService {
106106
email: user.email,
107107
name: user.name ?? user.displayName ?? user.email,
108108
title: `Concurrency quota request: ${totalExtraConcurrency}`,
109+
organizationId,
110+
organizationName: organization?.title,
109111
components: [
110112
uiComponent.text({
111113
text: `Org: ${organization?.title} (${organizationId})`,

apps/webapp/app/v3/services/setSchedulesAddOn.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export class SetSchedulesAddOnService extends BaseService {
7474
email: user.email,
7575
name: user.name ?? user.displayName ?? user.email,
7676
title: `Schedules quota request: ${amount}`,
77+
organizationId,
78+
organizationName: organization?.title,
7779
components: [
7880
uiComponent.text({
7981
text: `Org: ${organization?.title} (${organizationId})`,

apps/webapp/app/v3/services/setSeatsAddOn.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export class SetSeatsAddOnService extends BaseService {
7474
email: user.email,
7575
name: user.name ?? user.displayName ?? user.email,
7676
title: `Seats quota request: ${amount}`,
77+
organizationId,
78+
organizationName: organization?.title,
7779
components: [
7880
uiComponent.text({
7981
text: `Org: ${organization?.title} (${organizationId})`,

0 commit comments

Comments
 (0)