diff --git a/apps/dashboard/src/modals/add-notification-rule.tsx b/apps/dashboard/src/modals/add-notification-rule.tsx
index 650d5f23..e5c124fe 100644
--- a/apps/dashboard/src/modals/add-notification-rule.tsx
+++ b/apps/dashboard/src/modals/add-notification-rule.tsx
@@ -15,6 +15,7 @@ import { PureFilterItem } from '@/components/report/sidebar/filters/FilterItem';
import { Button } from '@/components/ui/button';
import { Combobox } from '@/components/ui/combobox';
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
+import { Textarea } from '@/components/ui/textarea';
import { useAppParams } from '@/hooks/useAppParams';
import { useEventNames } from '@/hooks/useEventNames';
import { useEventProperties } from '@/hooks/useEventProperties';
@@ -51,6 +52,7 @@ export default function AddNotificationRule({ rule }: Props) {
integrations:
rule?.integrations.map((integration) => integration.id) ?? [],
projectId,
+ template: rule?.template ?? '',
config: rule?.config ?? {
type: 'events',
events: [
@@ -160,6 +162,16 @@ export default function AddNotificationRule({ rule }: Props) {
+
+
+
+
BASE_INTEGRATIONS.find((i) => i.id === id);
+export type INotificationRuleCached = Awaited<
+ ReturnType
+>[number];
export const getNotificationRulesByProjectId = cacheable(
function getNotificationRulesByProjectId(projectId: string) {
return db.notificationRule.findMany({
@@ -71,6 +74,7 @@ export const getNotificationRulesByProjectId = cacheable(
sendToApp: true,
sendToEmail: true,
config: true,
+ template: true,
integrations: {
select: {
id: true,
@@ -190,6 +194,32 @@ export function matchEvent(
return true;
}
+function notificationTemplateEvent({
+ payload,
+ rule,
+}: {
+ payload: IServiceCreateEventPayload;
+ rule: INotificationRuleCached;
+}) {
+ if (!rule.template) return `You received a new "${payload.name}" event`;
+ return rule.template
+ .replaceAll('$EVENT_NAME', payload.name)
+ .replaceAll('$RULE_NAME', rule.name);
+}
+
+function notificationTemplateFunnel({
+ events,
+ rule,
+}: {
+ events: IServiceEvent[];
+ rule: INotificationRuleCached;
+}) {
+ if (!rule.template) return `Funnel "${rule.name}" completed`;
+ return rule.template
+ .replaceAll('$EVENT_NAME', events.map((e) => e.name).join(' -> '))
+ .replaceAll('$RULE_NAME', rule.name);
+}
+
export async function checkNotificationRulesForEvent(
payload: IServiceCreateEventPayload,
) {
@@ -207,7 +237,10 @@ export async function checkNotificationRulesForEvent(
}
const notification = {
- title: `You received a new "${payload.name}" event`,
+ title: notificationTemplateEvent({
+ payload,
+ rule,
+ }),
message: project?.name ? `Project: ${project?.name}` : '',
projectId: payload.projectId,
payload: {
@@ -266,7 +299,7 @@ export async function checkNotificationRulesForSessionEnd(
const notificationPromises = funnelRules.flatMap((rule) => {
// Match funnel events
let funnelIndex = 0;
- const matchedEvents = [];
+ const matchedEvents: IServiceEvent[] = [];
for (const event of sortedEvents) {
if (matchEvent(event, rule.config.events[funnelIndex]!)) {
matchedEvents.push(event);
@@ -280,7 +313,10 @@ export async function checkNotificationRulesForSessionEnd(
// Create notification object
const notification = {
- title: `Funnel "${rule.name}" completed`,
+ title: notificationTemplateFunnel({
+ rule,
+ events: matchedEvents,
+ }),
message: project?.name ? `Project: ${project?.name}` : '',
projectId,
payload: { type: 'funnel', funnel: matchedEvents } as const,
diff --git a/packages/trpc/src/routers/notification.ts b/packages/trpc/src/routers/notification.ts
index 38b38e20..76fa53f6 100644
--- a/packages/trpc/src/routers/notification.ts
+++ b/packages/trpc/src/routers/notification.ts
@@ -100,6 +100,7 @@ export const notificationRouter = createTRPCRouter({
.map((id) => ({ id })),
},
config: input.config,
+ template: input.template || null,
},
});
}
diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts
index 1d8703ce..63095944 100644
--- a/packages/validation/src/index.ts
+++ b/packages/validation/src/index.ts
@@ -264,6 +264,7 @@ export type INotificationRuleConfig = z.infer;
export const zCreateNotificationRule = z.object({
id: z.string().optional(),
name: z.string().min(1),
+ template: z.string().optional(),
config: zNotificationRuleConfig,
integrations: z.array(z.string()),
sendToApp: z.boolean(),