Skip to content

Commit 4012d65

Browse files
Feat: update front end for confluence connector (infiniflow#11747)
### What problem does this PR solve? Feat: update front end for confluence connector ### Type of change - [x] New Feature (non-breaking change which adds functionality)
1 parent e2bc1a3 commit 4012d65

3 files changed

Lines changed: 221 additions & 4 deletions

File tree

rag/svr/sync_data_source.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,23 @@ async def _generate(self, task: dict):
157157
from common.data_source.config import DocumentSource
158158
from common.data_source.interfaces import StaticCredentialsProvider
159159

160-
space = (self.conf.get("space") or "").strip()
161-
page_id = (self.conf.get("page_id") or "").strip()
162-
index_recursively = bool(self.conf.get("index_recursively", False))
160+
index_mode = (self.conf.get("index_mode") or "everything").lower()
161+
if index_mode not in {"everything", "space", "page"}:
162+
index_mode = "everything"
163+
164+
space = ""
165+
page_id = ""
166+
167+
index_recursively = False
168+
if index_mode == "space":
169+
space = (self.conf.get("space") or "").strip()
170+
if not space:
171+
raise ValueError("Space Key is required when indexing a specific Confluence space.")
172+
elif index_mode == "page":
173+
page_id = (self.conf.get("page_id") or "").strip()
174+
if not page_id:
175+
raise ValueError("Page ID is required when indexing a specific Confluence page.")
176+
index_recursively = bool(self.conf.get("index_recursively", False))
163177

164178
self.connector = ConfluenceConnector(
165179
wiki_base=self.conf["wiki_base"],
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import { useEffect, useMemo } from 'react';
2+
import { ControllerRenderProps, useFormContext } from 'react-hook-form';
3+
4+
import { Checkbox } from '@/components/ui/checkbox';
5+
import { Input } from '@/components/ui/input';
6+
import { cn } from '@/lib/utils';
7+
8+
/* ---------------- Token Field ---------------- */
9+
10+
export type ConfluenceTokenFieldProps = ControllerRenderProps & {
11+
fieldType: 'username' | 'token';
12+
placeholder?: string;
13+
disabled?: boolean;
14+
};
15+
16+
const ConfluenceTokenField = ({
17+
fieldType,
18+
value,
19+
onChange,
20+
placeholder,
21+
disabled,
22+
...rest
23+
}: ConfluenceTokenFieldProps) => {
24+
return (
25+
<div className="flex w-full flex-col gap-2">
26+
<Input
27+
className="w-full"
28+
type={fieldType === 'token' ? 'password' : 'text'}
29+
value={value ?? ''}
30+
onChange={(e) => onChange(e.target.value)}
31+
placeholder={
32+
placeholder ||
33+
(fieldType === 'token'
34+
? 'Enter your Confluence access token'
35+
: 'Confluence username or email')
36+
}
37+
disabled={disabled}
38+
{...rest}
39+
/>
40+
</div>
41+
);
42+
};
43+
44+
/* ---------------- Indexing Mode Field ---------------- */
45+
46+
type ConfluenceIndexingMode = 'everything' | 'space' | 'page';
47+
48+
export type ConfluenceIndexingModeFieldProps = ControllerRenderProps;
49+
50+
export const ConfluenceIndexingModeField = (
51+
fieldProps: ConfluenceIndexingModeFieldProps,
52+
) => {
53+
const { value, onChange, disabled } = fieldProps;
54+
const { watch, setValue } = useFormContext();
55+
56+
const mode = useMemo<ConfluenceIndexingMode>(
57+
() => (value as ConfluenceIndexingMode) || 'everything',
58+
[value],
59+
);
60+
61+
const spaceValue = watch('config.space');
62+
const pageIdValue = watch('config.page_id');
63+
const indexRecursively = watch('config.index_recursively');
64+
65+
useEffect(() => {
66+
if (!value) onChange('everything');
67+
}, [value, onChange]);
68+
69+
const handleModeChange = (nextMode?: string) => {
70+
const normalized = (nextMode || 'everything') as ConfluenceIndexingMode;
71+
onChange(normalized);
72+
73+
if (normalized === 'everything') {
74+
setValue('config.space', '', { shouldDirty: true, shouldTouch: true });
75+
setValue('config.page_id', '', { shouldDirty: true, shouldTouch: true });
76+
setValue('config.index_recursively', false, {
77+
shouldDirty: true,
78+
shouldTouch: true,
79+
});
80+
} else if (normalized === 'space') {
81+
setValue('config.page_id', '', { shouldDirty: true, shouldTouch: true });
82+
setValue('config.index_recursively', false, {
83+
shouldDirty: true,
84+
shouldTouch: true,
85+
});
86+
} else if (normalized === 'page') {
87+
setValue('config.space', '', { shouldDirty: true, shouldTouch: true });
88+
}
89+
};
90+
91+
return (
92+
<div className="w-full rounded-lg border border-border-button bg-bg-card p-4 space-y-4">
93+
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
94+
{INDEX_MODE_OPTIONS.map((option) => {
95+
const isActive = option.value === mode;
96+
return (
97+
<button
98+
key={option.value}
99+
type="button"
100+
disabled={disabled}
101+
onClick={() => handleModeChange(option.value)}
102+
className={cn(
103+
'flex-1 rounded-lg border px-3 py-2 transition-all',
104+
'border-transparent bg-transparent text-text-secondary hover:border-border-button hover:bg-bg-card-secondary',
105+
isActive &&
106+
'border-border-button bg-background text-primary shadow-sm',
107+
)}
108+
>
109+
{option.label}
110+
</button>
111+
);
112+
})}
113+
</div>
114+
115+
{mode === 'everything' && (
116+
<p className="text-sm text-text-secondary">
117+
This connector will index all pages the provided credentials have
118+
access to.
119+
</p>
120+
)}
121+
122+
{mode === 'space' && (
123+
<div className="space-y-2">
124+
<div className="text-sm font-semibold text-text-primary">
125+
Space Key
126+
</div>
127+
<Input
128+
className="w-full"
129+
value={spaceValue ?? ''}
130+
onChange={(e) =>
131+
setValue('config.space', e.target.value, {
132+
shouldDirty: true,
133+
shouldTouch: true,
134+
})
135+
}
136+
placeholder="e.g. KB"
137+
disabled={disabled}
138+
/>
139+
<p className="text-xs text-text-secondary">
140+
The Confluence space key to index.
141+
</p>
142+
</div>
143+
)}
144+
145+
{mode === 'page' && (
146+
<div className="space-y-2">
147+
<div className="text-sm font-semibold text-text-primary">Page ID</div>
148+
<Input
149+
className="w-full"
150+
value={pageIdValue ?? ''}
151+
onChange={(e) =>
152+
setValue('config.page_id', e.target.value, {
153+
shouldDirty: true,
154+
shouldTouch: true,
155+
})
156+
}
157+
placeholder="e.g. 123456"
158+
disabled={disabled}
159+
/>
160+
<p className="text-xs text-text-secondary">
161+
The Confluence page ID to index.
162+
</p>
163+
164+
<div className="flex items-center gap-2 pt-2">
165+
<Checkbox
166+
checked={Boolean(indexRecursively)}
167+
onCheckedChange={(checked) =>
168+
setValue('config.index_recursively', Boolean(checked), {
169+
shouldDirty: true,
170+
shouldTouch: true,
171+
})
172+
}
173+
disabled={disabled}
174+
/>
175+
<span className="text-sm text-text-secondary">
176+
Index child pages recursively
177+
</span>
178+
</div>
179+
</div>
180+
)}
181+
</div>
182+
);
183+
};
184+
185+
const INDEX_MODE_OPTIONS = [
186+
{ label: 'Everything', value: 'everything' },
187+
{ label: 'Space', value: 'space' },
188+
{ label: 'Page', value: 'page' },
189+
];
190+
191+
export default ConfluenceTokenField;

web/src/pages/user-setting/data-source/contant.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { FormFieldType } from '@/components/dynamic-form';
22
import SvgIcon from '@/components/svg-icon';
33
import { t } from 'i18next';
4+
import { ConfluenceIndexingModeField } from './component/confluence-token-field';
45
import GmailTokenField from './component/gmail-token-field';
56
import GoogleDriveTokenField from './component/google-drive-token-field';
6-
77
export enum DataSourceKey {
88
CONFLUENCE = 'confluence',
99
S3 = 's3',
@@ -230,23 +230,35 @@ export const DataSourceFormFields = {
230230
required: false,
231231
tooltip: t('setting.confluenceIsCloudTip'),
232232
},
233+
{
234+
label: 'Index Method',
235+
name: 'config.index_mode',
236+
type: FormFieldType.Text, // keep as text so RHF registers it
237+
required: false,
238+
horizontal: true,
239+
labelClassName: 'self-start pt-4',
240+
render: (fieldProps) => <ConfluenceIndexingModeField {...fieldProps} />,
241+
},
233242
{
234243
label: 'Space Key',
235244
name: 'config.space',
236245
type: FormFieldType.Text,
237246
required: false,
247+
hidden: true,
238248
},
239249
{
240250
label: 'Page ID',
241251
name: 'config.page_id',
242252
type: FormFieldType.Text,
243253
required: false,
254+
hidden: true,
244255
},
245256
{
246257
label: 'Index Recursively',
247258
name: 'config.index_recursively',
248259
type: FormFieldType.Checkbox,
249260
required: false,
261+
hidden: true,
250262
},
251263
],
252264
[DataSourceKey.GOOGLE_DRIVE]: [

0 commit comments

Comments
 (0)