diff --git a/apps/sim/app/api/auth/accounts/route.ts b/apps/sim/app/api/auth/accounts/route.ts index aebb5d6a288..67847afbfab 100644 --- a/apps/sim/app/api/auth/accounts/route.ts +++ b/apps/sim/app/api/auth/accounts/route.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { account } from '@sim/db/schema' +import { account, credential } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -28,16 +28,25 @@ export async function GET(request: NextRequest) { id: account.id, accountId: account.accountId, providerId: account.providerId, + credentialDisplayName: credential.displayName, }) .from(account) + .leftJoin(credential, eq(credential.accountId, account.id)) .where(and(...whereConditions)) .orderBy(desc(account.updatedAt)) - const accountsWithDisplayName = accounts.map((acc) => ({ + const seen = new Map() + for (const acc of accounts) { + if (!seen.has(acc.id)) { + seen.set(acc.id, acc) + } + } + + const accountsWithDisplayName = Array.from(seen.values()).map((acc) => ({ id: acc.id, accountId: acc.accountId, providerId: acc.providerId, - displayName: acc.accountId || acc.providerId, + displayName: acc.credentialDisplayName || acc.accountId || acc.providerId, })) return NextResponse.json({ accounts: accountsWithDisplayName }) diff --git a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts index ddc27300180..96acb82811d 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts @@ -8,13 +8,12 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { encryptSecret } from '@/lib/core/security/encryption' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' -import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types' import { MAX_EMAIL_RECIPIENTS, MAX_WORKFLOW_IDS } from '../constants' const logger = createLogger('WorkspaceNotificationAPI') const levelFilterSchema = z.array(z.enum(['info', 'error'])) -const triggerFilterSchema = z.array(z.enum(CORE_TRIGGER_TYPES)) +const triggerFilterSchema = z.array(z.string().min(1)) const alertRuleSchema = z.enum([ 'consecutive_failures', diff --git a/apps/sim/app/api/workspaces/[id]/notifications/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/route.ts index 6fc8f4866c7..6c46cef900a 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/route.ts @@ -9,14 +9,13 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { getSession } from '@/lib/auth' import { encryptSecret } from '@/lib/core/security/encryption' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' -import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types' import { MAX_EMAIL_RECIPIENTS, MAX_NOTIFICATIONS_PER_TYPE, MAX_WORKFLOW_IDS } from './constants' const logger = createLogger('WorkspaceNotificationsAPI') const notificationTypeSchema = z.enum(['webhook', 'email', 'slack']) const levelFilterSchema = z.array(z.enum(['info', 'error'])) -const triggerFilterSchema = z.array(z.enum(CORE_TRIGGER_TYPES)) +const triggerFilterSchema = z.array(z.string().min(1)) const alertRuleSchema = z.enum([ 'consecutive_failures', @@ -82,7 +81,7 @@ const createNotificationSchema = z workflowIds: z.array(z.string()).max(MAX_WORKFLOW_IDS).default([]), allWorkflows: z.boolean().default(false), levelFilter: levelFilterSchema.default(['info', 'error']), - triggerFilter: triggerFilterSchema.default([...CORE_TRIGGER_TYPES]), + triggerFilter: triggerFilterSchema.default([]), includeFinalOutput: z.boolean().default(false), includeTraceSpans: z.boolean().default(false), includeRateLimits: z.boolean().default(false), diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx index c551fd456c1..de9903c7cea 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx @@ -79,14 +79,6 @@ export function SlackChannelSelector({ const selectedChannel = channels.find((c) => c.id === value) - if (!accountId) { - return ( -
-

Select a Slack account first

-
- ) - } - const handleChange = (channelId: string) => { const channel = channels.find((c) => c.id === channelId) onChange(channelId, channel?.name || '') @@ -99,9 +91,13 @@ export function SlackChannelSelector({ value={value} onChange={handleChange} placeholder={ - channels.length === 0 && !isLoading ? 'No channels available' : 'Select channel...' + !accountId + ? 'Select an account first...' + : channels.length === 0 && !isLoading + ? 'No channels available' + : 'Select channel...' } - disabled={disabled || channels.length === 0} + disabled={disabled || !accountId || channels.length === 0} isLoading={isLoading} error={fetchError} searchable diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/workflow-selector/workflow-selector.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/workflow-selector/workflow-selector.tsx index 35f40657e08..1596e2f0882 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/workflow-selector/workflow-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/workflow-selector/workflow-selector.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react' import { X } from 'lucide-react' -import { Badge, Combobox, type ComboboxOption } from '@/components/emcn' +import { Badge, Combobox, type ComboboxOption, Label } from '@/components/emcn' import { Skeleton } from '@/components/ui' import { useWorkflows } from '@/hooks/queries/workflows' @@ -103,16 +103,16 @@ export function WorkflowSelector({ if (isLoading) { return ( -
- Workflows +
+
) } return ( -
- Workflows +
+ t.value) + type NotificationType = 'webhook' | 'email' | 'slack' type LogLevel = 'info' | 'error' type AlertRule = @@ -137,7 +140,7 @@ export const NotificationSettings = memo(function NotificationSettings({ workflowIds: [] as string[], allWorkflows: true, levelFilter: ['info', 'error'] as LogLevel[], - triggerFilter: [...CORE_TRIGGER_TYPES] as CoreTriggerType[], + triggerFilter: ALL_TRIGGER_VALUES, includeFinalOutput: false, includeTraceSpans: false, includeRateLimits: false, @@ -205,7 +208,7 @@ export const NotificationSettings = memo(function NotificationSettings({ workflowIds: [], allWorkflows: true, levelFilter: ['info', 'error'], - triggerFilter: [...CORE_TRIGGER_TYPES], + triggerFilter: ALL_TRIGGER_VALUES, includeFinalOutput: false, includeTraceSpans: false, includeRateLimits: false, @@ -477,7 +480,7 @@ export const NotificationSettings = memo(function NotificationSettings({ workflowIds: subscription.workflowIds || [], allWorkflows: subscription.allWorkflows, levelFilter: subscription.levelFilter as LogLevel[], - triggerFilter: subscription.triggerFilter as CoreTriggerType[], + triggerFilter: subscription.triggerFilter, includeFinalOutput: subscription.includeFinalOutput, includeTraceSpans: subscription.includeTraceSpans, includeRateLimits: subscription.includeRateLimits, @@ -626,7 +629,7 @@ export const NotificationSettings = memo(function NotificationSettings({ {activeTab === 'webhook' && ( <>
- +
- + - + addEmail(value)} @@ -673,7 +676,7 @@ export const NotificationSettings = memo(function NotificationSettings({ {activeTab === 'slack' && ( <>
- + {isLoadingSlackAccounts ? ( ) : slackAccounts.length === 0 ? ( @@ -719,7 +722,7 @@ export const NotificationSettings = memo(function NotificationSettings({
{slackAccounts.length > 0 && (
- + - + ({ label: level.charAt(0).toUpperCase() + level.slice(1), @@ -786,23 +789,23 @@ export const NotificationSettings = memo(function NotificationSettings({
- + ({ - label: trigger.charAt(0).toUpperCase() + trigger.slice(1), - value: trigger, + options={TRIGGER_OPTIONS.map((t) => ({ + label: t.label, + value: t.value, }))} multiSelect multiSelectValues={formData.triggerFilter} onMultiSelectChange={(values) => { - setFormData({ ...formData, triggerFilter: values as CoreTriggerType[] }) + setFormData({ ...formData, triggerFilter: values }) setFormErrors({ ...formErrors, triggerFilter: '' }) }} placeholder='Select trigger types...' overlayContent={ formData.triggerFilter.length > 0 ? (
- {formData.triggerFilter.map((trigger) => ( + {formData.triggerFilter.slice(0, 6).map((trigger) => ( ))} + {formData.triggerFilter.length > 6 && ( + + +{formData.triggerFilter.length - 6} + + )}
) : null } @@ -832,7 +843,7 @@ export const NotificationSettings = memo(function NotificationSettings({
- +
- + ({ value: rule.value, @@ -929,7 +940,7 @@ export const NotificationSettings = memo(function NotificationSettings({ {formData.alertRule === 'consecutive_failures' && (
- +
- +
- + - +
- +
- + - + - +
- +
- +
{isLoading ? ( -
- {[1, 2].map((i) => ( -
-
-
- -
- - -
-
-
- - - -
-
+
+ {[120, 80, 100, 90].map((labelWidth, i) => ( +
+ +
))}
@@ -1213,7 +1212,7 @@ export const NotificationSettings = memo(function NotificationSettings({ Slack - + {renderTabContent()} {renderTabContent()} {renderTabContent()} diff --git a/apps/sim/lib/logs/events.ts b/apps/sim/lib/logs/events.ts index 17ee7bd6782..bbf17b2320e 100644 --- a/apps/sim/lib/logs/events.ts +++ b/apps/sim/lib/logs/events.ts @@ -76,7 +76,8 @@ export async function emitWorkflowExecutionCompleted(log: WorkflowExecutionLog): for (const subscription of subscriptions) { const levelMatches = subscription.levelFilter.includes(log.level) - const triggerMatches = subscription.triggerFilter.includes(log.trigger) + const triggerMatches = + subscription.triggerFilter.length === 0 || subscription.triggerFilter.includes(log.trigger) if (!levelMatches || !triggerMatches) { logger.debug(`Skipping subscription ${subscription.id} due to filter mismatch`)