diff --git a/frontend/src/components/settings/SettingsFanoutSection.tsx b/frontend/src/components/settings/SettingsFanoutSection.tsx index bac330c..1631d44 100644 --- a/frontend/src/components/settings/SettingsFanoutSection.tsx +++ b/frontend/src/components/settings/SettingsFanoutSection.tsx @@ -48,6 +48,18 @@ function formatPrivateTopicSummary(config: Record) { return `${prefix}/dm:, ${prefix}/gm:, ${prefix}/raw/...`; } +function formatAppriseTargets(urls: string | undefined, maxLength = 80) { + const targets = (urls || '') + .split('\n') + .map((line) => line.trim()) + .filter(Boolean); + if (targets.length === 0) return 'No targets configured'; + + const joined = targets.join(', '); + if (joined.length <= maxLength) return joined; + return `${joined.slice(0, maxLength - 3)}...`; +} + const DEFAULT_BOT_CODE = `def bot( sender_name: str | None, sender_key: str | None, @@ -1334,6 +1346,30 @@ export function SettingsFanoutSection({ )} + + {cfg.type === 'webhook' && ( +
+
+ URL:{' '} + + {((cfg.config as Record).url as string) || 'Not set'} + +
+
+ )} + + {cfg.type === 'apprise' && ( +
+
+ Targets:{' '} + + {formatAppriseTargets( + (cfg.config as Record).urls as string | undefined + )} + +
+
+ )} ); })} diff --git a/frontend/src/test/fanoutSection.test.tsx b/frontend/src/test/fanoutSection.test.tsx index 5137090..0a2ac39 100644 --- a/frontend/src/test/fanoutSection.test.tsx +++ b/frontend/src/test/fanoutSection.test.tsx @@ -461,4 +461,44 @@ describe('SettingsFanoutSection', () => { screen.getByText('meshcore/dm:, meshcore/gm:, meshcore/raw/...') ).toBeInTheDocument(); }); + + it('webhook list shows destination URL', async () => { + const config: FanoutConfig = { + id: 'wh-1', + type: 'webhook', + name: 'Webhook', + enabled: true, + config: { url: 'https://example.com/hook', method: 'POST', headers: {} }, + scope: { messages: 'all', raw_packets: 'none' }, + sort_order: 0, + created_at: 1000, + }; + mockedApi.getFanoutConfigs.mockResolvedValue([config]); + renderSection(); + + await waitFor(() => expect(screen.getByText('https://example.com/hook')).toBeInTheDocument()); + }); + + it('apprise list shows compact target summary', async () => { + const config: FanoutConfig = { + id: 'ap-1', + type: 'apprise', + name: 'Apprise', + enabled: true, + config: { + urls: 'discord://abc\nmailto://one@example.com\nmailto://two@example.com', + preserve_identity: true, + include_path: true, + }, + scope: { messages: 'all', raw_packets: 'none' }, + sort_order: 0, + created_at: 1000, + }; + mockedApi.getFanoutConfigs.mockResolvedValue([config]); + renderSection(); + + await waitFor(() => + expect(screen.getByText(/discord:\/\/abc, mailto:\/\/one@example.com/)).toBeInTheDocument() + ); + }); });