frontend_nyla/src/components/FlowEditor/nodes/configs/EmailNodeConfig.tsx
2026-04-09 00:11:35 +02:00

242 lines
9 KiB
TypeScript

/**
* Email node config - connection selector, folder dropdown, query, subject, body.
*/
import React, { useEffect, useState } from 'react';
import type { NodeConfigRendererProps } from './types';
import { fetchConnections, fetchBrowse, type UserConnection, type BrowseEntry } from '../../../../api/workflowApi';
import { useLanguage } from '../../../../providers/language/LanguageContext';
export const EmailNodeConfig: React.FC<NodeConfigRendererProps> = ({ params,
updateParam,
instanceId,
request,
nodeType = 'email.checkEmail',
}) => {
const { t } = useLanguage();
const [connections, setConnections] = useState<UserConnection[]>([]);
const [folders, setFolders] = useState<BrowseEntry[]>([]);
const [loading, setLoading] = useState(false);
const [foldersLoading, setFoldersLoading] = useState(false);
useEffect(() => {
if (instanceId && request) {
setLoading(true);
fetchConnections(request, instanceId)
.then(setConnections)
.catch(() => setConnections([]))
.finally(() => setLoading(false));
}
}, [instanceId, request]);
const connectionId = (params.connectionId as string) ?? '';
const selectedConn = connections.find((c) => c.id === connectionId);
const mailService = selectedConn?.authority === 'google' ? 'gmail' : 'outlook';
useEffect(() => {
if (instanceId && request && connectionId) {
setFoldersLoading(true);
fetchBrowse(request, instanceId, connectionId, mailService, '/')
.then((r) => setFolders(r.items.filter((e) => e.isFolder)))
.catch(() => setFolders([]))
.finally(() => setFoldersLoading(false));
} else {
setFolders([]);
}
}, [instanceId, request, connectionId, mailService]);
const isDraft = nodeType === 'email.draftEmail';
const isSearch = nodeType === 'email.searchEmail';
const folderValue = (params.folder as string) ?? (isSearch ? 'All' : 'Inbox');
return (
<>
<div>
<label>Account</label>
<select
value={connectionId}
onChange={(e) => updateParam('connectionId', e.target.value)}
disabled={loading}
>
<option value="">{loading ? t('emailNodeConfig.loading') : t('emailNodeConfig.selectConnection')}</option>
{connections.map((c) => (
<option key={c.id} value={c.id}>
{c.externalEmail ?? c.externalUsername ?? c.id}
</option>
))}
</select>
</div>
{!isDraft && (
<div>
<label>Folder</label>
<select
value={folderValue}
onChange={(e) => updateParam('folder', e.target.value)}
disabled={foldersLoading || !connectionId}
>
<option value="">
{foldersLoading ? 'Loading folders...' : !connectionId ? t('emailNodeConfig.selectAccountFirst') : t('emailNodeConfig.selectFolder')}
</option>
{isSearch && <option value="All">All</option>}
{folders.length > 0
? folders.map((f) => {
const folderId = (f.path ?? '').replace(/^\//, '') || (f.metadata as { id?: string })?.id || '';
const value = folderId || f.name;
if (!value) return null;
return (
<option key={value} value={value}>
{f.name}
</option>
);
})
: !isSearch && (
<>
<option value="Inbox">Inbox</option>
<option value="Drafts">Drafts</option>
<option value="SentItems">{t('emailNodeConfig.sentItems')}</option>
<option value="DeletedItems">{t('emailNodeConfig.deletedItems')}</option>
<option value="JunkEmail">{t('emailNodeConfig.junkEmail')}</option>
</>
)}
{folderValue &&
!folders.some(
(f) =>
((f.path ?? '').replace(/^\//, '') || (f.metadata as { id?: string })?.id) === folderValue
) &&
folderValue !== 'All' && (
<option value={folderValue}>{folderValue}</option>
)}
</select>
</div>
)}
{isSearch && (
<>
<div>
<label>{t('emailNodeConfig.searchQueryOptional')}</label>
<input
value={(params.query as string) ?? ''}
onChange={(e) => updateParam('query', e.target.value)}
placeholder={t('emailNodeConfig.generalSearchTermSubjectBody')}
/>
</div>
<div>
<label>{t('emailNodeConfig.fromAddressOptional')}</label>
<input
value={(params.fromAddress as string) ?? ''}
onChange={(e) => updateParam('fromAddress', e.target.value)}
placeholder={t('emailNodeConfig.egSenderexamplecom')}
/>
</div>
<div>
<label>{t('emailNodeConfig.toAddressOptional')}</label>
<input
value={(params.toAddress as string) ?? ''}
onChange={(e) => updateParam('toAddress', e.target.value)}
placeholder={t('emailNodeConfig.egRecipientexamplecom')}
/>
</div>
<div>
<label>{t('emailNodeConfig.subjectContainsOptional')}</label>
<input
value={(params.subjectContains as string) ?? ''}
onChange={(e) => updateParam('subjectContains', e.target.value)}
placeholder={t('emailNodeConfig.wordOrPhraseInSubject')}
/>
</div>
<div>
<label>{t('emailNodeConfig.bodycontentContainsOptional')}</label>
<input
value={(params.bodyContains as string) ?? ''}
onChange={(e) => updateParam('bodyContains', e.target.value)}
placeholder={t('emailNodeConfig.wordOrPhraseInEmail')}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<input
type="checkbox"
id="searchHasAttachment"
checked={!!(params.hasAttachment as boolean)}
onChange={(e) => updateParam('hasAttachment', e.target.checked)}
/>
<label htmlFor="searchHasAttachment">{t('emailNodeConfig.onlyEmailsWithAttachment')}</label>
</div>
<div>
<label>Limit</label>
<input
type="number"
value={(params.limit as number) ?? 100}
onChange={(e) => updateParam('limit', parseInt(e.target.value, 10) || 100)}
/>
</div>
</>
)}
{nodeType === 'email.checkEmail' && (
<>
<div>
<label>{t('emailNodeConfig.fromAddressOptional')}</label>
<input
value={(params.fromAddress as string) ?? ''}
onChange={(e) => updateParam('fromAddress', e.target.value)}
placeholder={t('emailNodeConfig.egSenderexamplecom')}
/>
</div>
<div>
<label>{t('emailNodeConfig.subjectContainsOptional')}</label>
<input
value={(params.subjectContains as string) ?? ''}
onChange={(e) => updateParam('subjectContains', e.target.value)}
placeholder={t('emailNodeConfig.wordOrPhraseInSubject')}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<input
type="checkbox"
id="hasAttachment"
checked={!!(params.hasAttachment as boolean)}
onChange={(e) => updateParam('hasAttachment', e.target.checked)}
/>
<label htmlFor="hasAttachment">{t('emailNodeConfig.onlyEmailsWithAttachment')}</label>
</div>
<div>
<label>Limit</label>
<input
type="number"
value={(params.limit as number) ?? 100}
onChange={(e) => updateParam('limit', parseInt(e.target.value, 10) || 100)}
/>
</div>
</>
)}
{isDraft && (
<>
<div>
<label>Subject</label>
<input
value={(params.subject as string) ?? ''}
onChange={(e) => updateParam('subject', e.target.value)}
placeholder={t('emailNodeConfig.emailSubjectOrLeaveEmpty')}
/>
</div>
<div>
<label>Body</label>
<textarea
value={(params.body as string) ?? ''}
onChange={(e) => updateParam('body', e.target.value)}
placeholder={t('emailNodeConfig.emailBodyOrLeaveEmpty')}
rows={4}
/>
</div>
<div>
<label>{t('emailNodeConfig.toOptional')}</label>
<input
value={(params.to as string) ?? ''}
onChange={(e) => updateParam('to', e.target.value)}
placeholder={t('emailNodeConfig.recipientsOrFromAiWhen')}
/>
</div>
</>
)}
</>
);
};