From 6ccc506b8c2aeb8d5f71f1ec75f3a45acd1e72be Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 18:40:19 +0100 Subject: [PATCH] Name the services Connect your data can sync The tile names up to three services this instance can sync (then "and more"), rather than services it may not offer, and spans the grid's width so its line doesn't wrap into a tall tile. --- frontend/src/locale/de.json | 3 ++- frontend/src/locale/en.json | 3 ++- frontend/src/locale/es.json | 3 ++- frontend/src/locale/jp.json | 3 ++- frontend/src/locale/ru.json | 3 ++- frontend/src/locale/zh-TW.json | 3 ++- frontend/src/locale/zh.json | 3 ++- frontend/src/upload/Upload.test.tsx | 29 ++++++++++++++++++++++-- frontend/src/upload/Upload.tsx | 34 +++++++++++++++++++++++------ 9 files changed, 68 insertions(+), 16 deletions(-) diff --git a/frontend/src/locale/de.json b/frontend/src/locale/de.json index 11d6fdc3..d7900af1 100644 --- a/frontend/src/locale/de.json +++ b/frontend/src/locale/de.json @@ -1815,7 +1815,8 @@ }, "connectData": { "title": "Deine Daten verbinden", - "description": "Google Drive, SharePoint, Confluence und mehr synchronisieren" + "description": "{{services}} synchronisieren", + "more": "mehr" } }, "createAPIKey": { diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index e6992055..4c03953c 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -1821,7 +1821,8 @@ }, "connectData": { "title": "Connect your data", - "description": "Sync Google Drive, SharePoint, Confluence and more" + "description": "Sync {{services}}", + "more": "more" } }, "createAPIKey": { diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index 2c0321c4..f2065bf4 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -1815,7 +1815,8 @@ }, "connectData": { "title": "Conecta tus datos", - "description": "Sincroniza Google Drive, SharePoint, Confluence y más" + "description": "Sincroniza {{services}}", + "more": "más" } }, "createAPIKey": { diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json index b224a4b1..e6ccba46 100644 --- a/frontend/src/locale/jp.json +++ b/frontend/src/locale/jp.json @@ -1804,7 +1804,8 @@ }, "connectData": { "title": "データを接続", - "description": "Google Drive、SharePoint、Confluence などを同期" + "description": "{{services}} を同期", + "more": "その他" } }, "createAPIKey": { diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json index 79d12254..4007f104 100644 --- a/frontend/src/locale/ru.json +++ b/frontend/src/locale/ru.json @@ -1899,7 +1899,8 @@ }, "connectData": { "title": "Подключите свои данные", - "description": "Синхронизируйте Google Drive, SharePoint, Confluence и другие сервисы" + "description": "Синхронизируйте {{services}}", + "more": "другие сервисы" } }, "createAPIKey": { diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json index 0392782e..4dcf1061 100644 --- a/frontend/src/locale/zh-TW.json +++ b/frontend/src/locale/zh-TW.json @@ -1804,7 +1804,8 @@ }, "connectData": { "title": "連接你的資料", - "description": "同步 Google Drive、SharePoint、Confluence 等" + "description": "同步 {{services}}", + "more": "更多服務" } }, "createAPIKey": { diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json index 78557c3e..020d5dce 100644 --- a/frontend/src/locale/zh.json +++ b/frontend/src/locale/zh.json @@ -1804,7 +1804,8 @@ }, "connectData": { "title": "连接你的数据", - "description": "同步 Google Drive、SharePoint、Confluence 等" + "description": "同步 {{services}}", + "more": "更多服务" } }, "createAPIKey": { diff --git a/frontend/src/upload/Upload.test.tsx b/frontend/src/upload/Upload.test.tsx index 3a5ac6cf..5af0f062 100644 --- a/frontend/src/upload/Upload.test.tsx +++ b/frontend/src/upload/Upload.test.tsx @@ -2,7 +2,11 @@ import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; vi.mock('react-i18next', () => ({ - useTranslation: () => ({ t: (key: string) => key }), + useTranslation: () => ({ + t: (key: string, opts?: { services?: string }) => + opts?.services ? `${key}(${opts.services})` : key, + i18n: { language: 'en' }, + }), })); const connectorsState = vi.hoisted(() => ({ @@ -136,6 +140,7 @@ describe('Upload source-type tiles', () => { const DRIVE = { key: 'google_drive', + name: 'Google Drive', icon: 'drive', sync_ingestor: 'google_drive', capabilities: ['sync'], @@ -168,11 +173,30 @@ describe('Upload source-type tiles', () => { false, ); expect(labels.at(-1)).toContain('modals.uploadDoc.connectData.title'); - expect(labels.at(-1)).toContain('modals.uploadDoc.connectData.description'); + // Named after what this instance can sync. + expect(labels.at(-1)).toContain( + 'modals.uploadDoc.connectData.description(Google Drive)', + ); expect(document.body.querySelector('h3')).toBeNull(); connectorsState.catalog = []; }); + it('names three syncing services, then the rest as more', async () => { + connectorsState.catalog = [ + DRIVE, + { ...DRIVE, key: 'share_point', name: 'SharePoint' }, + { ...DRIVE, key: 'confluence', name: 'Confluence' }, + { ...DRIVE, key: 's3', name: 'Amazon S3' }, + { ...DRIVE, key: 'off', name: 'Needs setup', available: false }, + { key: 'telegram', name: 'Telegram', capabilities: ['write'] }, + ]; + await render(); + expect(connectTile()!.textContent).toContain( + 'modals.uploadDoc.connectData.description(Google Drive, SharePoint, Confluence, and modals.uploadDoc.connectData.more)', + ); + connectorsState.catalog = []; + }); + it('opens the Connectors page on the syncing services', async () => { connectorsState.catalog = [DRIVE]; navigate.mockClear(); @@ -190,6 +214,7 @@ describe('Upload source-type tiles', () => { it('offers no Connect tile when no service can sync', async () => { connectorsState.catalog = [ { key: 'telegram', capabilities: ['write'], available: true }, + { ...DRIVE, available: false }, ]; await render(); expect(connectTile()).toBeUndefined(); diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index e431c95d..16b2042e 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -29,6 +29,8 @@ import { Separator } from '../components/ui/separator'; import { OptionCard } from '../components/ui/option-card'; import ConnectorSetupNotice from '../connectors/ConnectorSetupNotice'; import useConnectorLauncher from '../connectors/useConnectorLauncher'; +import { connectorName } from '../connectors/i18n'; +import { intlLocale } from '../utils/dateTimeUtils'; import { loadConnectors, selectConnections, @@ -474,7 +476,7 @@ function Upload({ })); const [nameTouched, setNameTouched] = useState(false); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const dispatch = useDispatch(); const store = useStore(); @@ -1152,10 +1154,27 @@ function Upload({ }; // Services that sync into Knowledge are connected on the Connectors page, - // so the picker offers one way there instead of a tile per service. - const offersConnect = - connectorsEnabled && - connectorCatalog.some((c) => c.capabilities?.includes('sync')); + // so the picker offers one way there instead of a tile per service, named + // after the first few this instance can sync. + const syncServices = connectorsEnabled + ? connectorCatalog + .filter((c) => c.available && c.capabilities?.includes('sync')) + .map((c) => connectorName(t, c)) + : []; + const SERVICES_NAMED = 3; + const connectDescription = t('modals.uploadDoc.connectData.description', { + services: new Intl.ListFormat(intlLocale(i18n.language), { + type: 'conjunction', + }).format( + syncServices.length > SERVICES_NAMED + ? [ + ...syncServices.slice(0, SERVICES_NAMED), + t('modals.uploadDoc.connectData.more'), + ] + : syncServices, + ), + interpolation: { escapeValue: false }, + }); const renderIngestorSelection = () => { const options = UPLOAD_AND_WEB_INGESTORS.map((type) => @@ -1173,11 +1192,12 @@ function Upload({ onClick={() => handleIngestorTypeChange(option.value)} /> ))} - {offersConnect && ( + {syncServices.length > 0 && ( } title={t('modals.uploadDoc.connectData.title')} - description={t('modals.uploadDoc.connectData.description')} + description={connectDescription} onClick={() => { handleClose(); navigate('/settings/connectors?capability=sync');