Skip to content

Commit 289a673

Browse files
committed
feat: integrate Slack notifications for contact form submissions
- Added a new Slack notification feature to alert sales or support channels upon contact form submissions. - Implemented a `notifySlack` function to format and send messages to Slack using a webhook. - Enhanced error handling to ensure email notifications proceed even if Slack notifications fail.
1 parent 02f84c3 commit 289a673

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

apps/website/app/api/contact/route.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getHubSpotUTK, submitToHubSpot } from "@/lib/hubspot";
2+
import { notifySlack } from "@/lib/slack";
23
import type { NextRequest } from "next/server";
34
import { NextResponse } from "next/server";
45
import { Resend } from "resend";
@@ -70,6 +71,23 @@ export async function POST(request: NextRequest) {
7071
}
7172
}
7273

74+
// Send notification to Slack (sales or support channel)
75+
try {
76+
const slackSuccess = await notifySlack(body);
77+
if (slackSuccess) {
78+
console.log(
79+
`Successfully sent ${body.inquiryType} inquiry notification to Slack`,
80+
);
81+
} else {
82+
console.warn(
83+
`Failed to send ${body.inquiryType} inquiry notification to Slack, but continuing with email`,
84+
);
85+
}
86+
} catch (error) {
87+
console.error("Error sending to Slack:", error);
88+
// Continue with email even if Slack fails
89+
}
90+
7391
// Format email content
7492
const emailSubject = `[${body.inquiryType.toUpperCase()}] New contact form submission from ${body.firstName} ${body.lastName}`;
7593
const emailBody = `

apps/website/lib/slack.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
interface ContactFormData {
2+
inquiryType: "support" | "sales" | "other";
3+
firstName: string;
4+
lastName: string;
5+
email: string;
6+
company: string;
7+
message: string;
8+
}
9+
10+
interface SlackMessage {
11+
text?: string;
12+
blocks?: SlackBlock[];
13+
}
14+
15+
interface SlackBlock {
16+
type: string;
17+
text?: {
18+
type: string;
19+
text: string;
20+
};
21+
fields?: Array<{
22+
type: string;
23+
text: string;
24+
}>;
25+
elements?: Array<{
26+
type: string;
27+
text: string;
28+
}>;
29+
}
30+
31+
/**
32+
* Format contact form data as a Slack message with blocks
33+
*/
34+
function formatContactDataForSlack(
35+
contactData: ContactFormData,
36+
): SlackMessage {
37+
// Get emoji and label based on inquiry type
38+
let inquiryTypeEmoji: string;
39+
let inquiryTypeLabel: string;
40+
41+
switch (contactData.inquiryType) {
42+
case "sales":
43+
inquiryTypeEmoji = "💰";
44+
inquiryTypeLabel = "Sales";
45+
break;
46+
case "support":
47+
inquiryTypeEmoji = "🛟";
48+
inquiryTypeLabel = "Support";
49+
break;
50+
case "other":
51+
inquiryTypeEmoji = "📝";
52+
inquiryTypeLabel = "Other";
53+
break;
54+
default:
55+
inquiryTypeEmoji = "📧";
56+
inquiryTypeLabel = "Contact";
57+
}
58+
59+
return {
60+
text: `New ${contactData.inquiryType} inquiry from ${contactData.firstName} ${contactData.lastName}`,
61+
blocks: [
62+
{
63+
type: "header",
64+
text: {
65+
type: "plain_text",
66+
text: `${inquiryTypeEmoji} New ${inquiryTypeLabel} Inquiry`,
67+
},
68+
},
69+
{
70+
type: "section",
71+
fields: [
72+
{
73+
type: "mrkdwn",
74+
text: `*Name:*\n${contactData.firstName} ${contactData.lastName}`,
75+
},
76+
{
77+
type: "mrkdwn",
78+
text: `*Email:*\n${contactData.email}`,
79+
},
80+
{
81+
type: "mrkdwn",
82+
text: `*Company:*\n${contactData.company}`,
83+
},
84+
{
85+
type: "mrkdwn",
86+
text: `*Type:*\n${inquiryTypeLabel}`,
87+
},
88+
],
89+
},
90+
{
91+
type: "section",
92+
text: {
93+
type: "mrkdwn",
94+
text: `*Message:*\n${contactData.message}`,
95+
},
96+
},
97+
{
98+
type: "divider",
99+
},
100+
{
101+
type: "context",
102+
elements: [
103+
{
104+
type: "mrkdwn",
105+
text: "Sent from Dokploy website contact form",
106+
},
107+
],
108+
},
109+
],
110+
};
111+
}
112+
113+
/**
114+
* Send a message to Slack using a webhook URL
115+
*/
116+
export async function sendToSlack(
117+
contactData: ContactFormData,
118+
webhookUrl: string,
119+
): Promise<boolean> {
120+
try {
121+
if (!webhookUrl) {
122+
console.error("Slack webhook URL is not configured");
123+
return false;
124+
}
125+
126+
const message = formatContactDataForSlack(contactData);
127+
128+
const response = await fetch(webhookUrl, {
129+
method: "POST",
130+
headers: {
131+
"Content-Type": "application/json",
132+
},
133+
body: JSON.stringify(message),
134+
});
135+
136+
if (!response.ok) {
137+
const errorText = await response.text();
138+
console.error("Slack webhook error:", response.status, errorText);
139+
return false;
140+
}
141+
142+
console.log("Slack notification sent successfully");
143+
return true;
144+
} catch (error) {
145+
console.error("Error sending to Slack:", error);
146+
return false;
147+
}
148+
}
149+
150+
/**
151+
* Send contact form notification to Slack
152+
* All inquiry types (sales, support, other) are sent to the same Slack channel
153+
*/
154+
export async function notifySlack(
155+
contactData: ContactFormData,
156+
): Promise<boolean> {
157+
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
158+
159+
if (!webhookUrl) {
160+
console.warn(
161+
"Slack webhook URL is not configured (SLACK_WEBHOOK_URL)",
162+
);
163+
return false;
164+
}
165+
166+
return await sendToSlack(contactData, webhookUrl);
167+
}

0 commit comments

Comments
 (0)