From 3016806db9f1c231a336e189dffd66a04cce78f5 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Sun, 26 Apr 2026 23:59:14 +0200 Subject: [PATCH] added infomaniak --- src/api/connectionApi.ts | 4 +- src/components/UnifiedDataBar/SourcesTab.tsx | 11 +++ src/hooks/useConnections.ts | 91 +++++++++++++++++++- src/pages/basedata/ConnectionsPage.tsx | 27 +++++- 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/src/api/connectionApi.ts b/src/api/connectionApi.ts index 7263e95..192b29c 100644 --- a/src/api/connectionApi.ts +++ b/src/api/connectionApi.ts @@ -7,7 +7,7 @@ import { ApiRequestOptions } from '../hooks/useApi'; export interface Connection { id: string; userId: string; - authority: 'local' | 'google' | 'msft' | 'clickup'; + authority: 'local' | 'google' | 'msft' | 'clickup' | 'infomaniak'; externalId: string; externalUsername: string; externalEmail?: string; @@ -52,7 +52,7 @@ export interface PaginatedResponse { export interface CreateConnectionData { id?: string; userId?: string; - authority?: 'msft' | 'google' | 'clickup'; + authority?: 'msft' | 'google' | 'clickup' | 'infomaniak'; type?: 'msft' | 'google' | 'clickup'; // Backend maps type → authority externalId?: string; externalUsername?: string; diff --git a/src/components/UnifiedDataBar/SourcesTab.tsx b/src/components/UnifiedDataBar/SourcesTab.tsx index 6254f51..efdc366 100644 --- a/src/components/UnifiedDataBar/SourcesTab.tsx +++ b/src/components/UnifiedDataBar/SourcesTab.tsx @@ -126,6 +126,7 @@ const _AUTHORITY_ICONS: Record = { msft: '\uD83D\uDFE6', google: '\uD83D\uDFE9', clickup: '\uD83D\uDCCB', + infomaniak: '\uD83D\uDFE5', 'local:ftp': '\uD83D\uDD17', 'local:jira': '\uD83D\uDD27', }; @@ -138,6 +139,9 @@ const _SERVICE_ICONS: Record = { drive: '\uD83D\uDCC2', gmail: '\uD83D\uDCE8', files: '\uD83D\uDCC2', + clickup: '\uD83D\uDCCB', + kdrive: '\uD83D\uDCC2', + mail: '\uD83D\uDCE7', }; /* ─── Source colors & icons ──────────────────────────────────────────── */ @@ -158,6 +162,10 @@ const _SOURCE_COLORS: Record = { 'local:ftp': '#795548', 'local:jira': '#0052CC', clickup: '#7b68ee', + kdriveFolder: '#0098FF', + kdrive: '#0098FF', + mailFolder: '#0098FF', + mail: '#0098FF', }; function _getSourceColor(sourceType: string): string { @@ -188,6 +196,9 @@ const _SERVICE_TO_SOURCE_TYPE: Record = { drive: 'googleDriveFolder', gmail: 'gmailFolder', files: 'ftpFolder', + clickup: 'clickup', + kdrive: 'kdriveFolder', + mail: 'mailFolder', }; /* ─── Tree helpers ───────────────────────────────────────────────────── */ diff --git a/src/hooks/useConnections.ts b/src/hooks/useConnections.ts index 563f7a1..0902bef 100644 --- a/src/hooks/useConnections.ts +++ b/src/hooks/useConnections.ts @@ -295,7 +295,8 @@ export function useConnections() { if ( event.data.type === 'msft_connection_success' || event.data.type === 'google_connection_success' || - event.data.type === 'clickup_connection_success' + event.data.type === 'clickup_connection_success' || + event.data.type === 'infomaniak_connection_success' ) { // Clean up clearInterval(checkClosed); @@ -309,7 +310,8 @@ export function useConnections() { } else if ( event.data.type === 'msft_connection_error' || event.data.type === 'google_connection_error' || - event.data.type === 'clickup_connection_error' + event.data.type === 'clickup_connection_error' || + event.data.type === 'infomaniak_connection_error' ) { // Handle error clearInterval(checkClosed); @@ -495,6 +497,84 @@ export function useConnections() { } }; + // Create Infomaniak connection and open OAuth popup + const createInfomaniakConnectionAndAuth = async (): Promise => { + if (isConnecting) return; + setIsConnecting(true); + try { + const newConnection = await createConnection({ + type: 'infomaniak', + authority: 'infomaniak', + }); + + const connectResponse = await connectServiceApi(request, newConnection.id); + + if (!connectResponse.authUrl) { + throw new Error('No OAuth URL received from backend'); + } + + const apiBaseUrl = getApiBaseUrl(); + let authUrl = connectResponse.authUrl; + if (authUrl.startsWith('/')) { + authUrl = `${apiBaseUrl}${authUrl}`; + } + + return await new Promise((resolve, reject) => { + const popup = window.open( + authUrl, + 'infomaniak-connection', + 'width=500,height=600,scrollbars=yes,resizable=yes' + ); + + if (!popup) { + setIsConnecting(false); + reject(new Error('Popup was blocked. Please allow popups and try again.')); + return; + } + + const checkClosed = setInterval(() => { + if (popup.closed) { + clearInterval(checkClosed); + window.removeEventListener('message', messageListener); + setIsConnecting(false); + console.log('Infomaniak OAuth popup closed'); + fetchConnections(); + resolve(); + } + }, 1000); + + const messageListener = (event: MessageEvent) => { + const apiUrl = new URL(apiBaseUrl); + if (event.origin !== apiUrl.origin) { + return; + } + + if (event.data.type === 'infomaniak_connection_success') { + clearInterval(checkClosed); + window.removeEventListener('message', messageListener); + popup.close(); + setIsConnecting(false); + console.log('Infomaniak connection successful'); + fetchConnections(); + resolve(); + } else if (event.data.type === 'infomaniak_connection_error') { + clearInterval(checkClosed); + window.removeEventListener('message', messageListener); + popup.close(); + setIsConnecting(false); + reject(new Error(event.data.error || 'Infomaniak connection failed')); + } + }; + + window.addEventListener('message', messageListener); + }); + } catch (error) { + setIsConnecting(false); + console.error('Error creating Infomaniak connection:', error); + throw error; + } + }; + // Create Microsoft connection and open OAuth popup const createMicrosoftConnectionAndAuth = async (): Promise => { if (isConnecting) return; @@ -701,6 +781,7 @@ export function useConnections() { createGoogleConnectionAndAuth, createMicrosoftConnectionAndAuth, createClickupConnectionAndAuth, + createInfomaniakConnectionAndAuth, isLoading, loading: isLoading, // Alias for FormGenerator compatibility isConnecting, @@ -785,7 +866,8 @@ export function useOAuthConnect() { if ( event.data.type === 'msft_connection_success' || event.data.type === 'google_connection_success' || - event.data.type === 'clickup_connection_success' + event.data.type === 'clickup_connection_success' || + event.data.type === 'infomaniak_connection_success' ) { // Clean up - IMPORTANT: clear the checkClosed interval first clearInterval(checkClosed); @@ -799,7 +881,8 @@ export function useOAuthConnect() { } else if ( event.data.type === 'msft_connection_error' || event.data.type === 'google_connection_error' || - event.data.type === 'clickup_connection_error' + event.data.type === 'clickup_connection_error' || + event.data.type === 'infomaniak_connection_error' ) { // Handle error - also clear the checkClosed interval clearInterval(checkClosed); diff --git a/src/pages/basedata/ConnectionsPage.tsx b/src/pages/basedata/ConnectionsPage.tsx index 9636e82..7a9cae0 100644 --- a/src/pages/basedata/ConnectionsPage.tsx +++ b/src/pages/basedata/ConnectionsPage.tsx @@ -9,7 +9,7 @@ import React, { useState, useMemo, useEffect } from 'react'; import { useConnections, type Connection } from '../../hooks/useConnections'; import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable'; import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm'; -import { FaSync, FaGoogle, FaMicrosoft, FaLink, FaRedo, FaShieldAlt, FaTasks } from 'react-icons/fa'; +import { FaSync, FaGoogle, FaMicrosoft, FaLink, FaRedo, FaShieldAlt, FaTasks, FaCloud } from 'react-icons/fa'; import { getApiBaseUrl } from '../../../config/config'; import styles from '../admin/Admin.module.css'; @@ -35,6 +35,7 @@ export const ConnectionsPage: React.FC = () => { createGoogleConnectionAndAuth, createMicrosoftConnectionAndAuth, createClickupConnectionAndAuth, + createInfomaniakConnectionAndAuth, connectWithPopup, refreshMicrosoftToken, refreshGoogleToken, @@ -106,7 +107,8 @@ export const ConnectionsPage: React.FC = () => { data.authority === 'local' || data.authority === 'google' || data.authority === 'msft' || - data.authority === 'clickup' + data.authority === 'clickup' || + data.authority === 'infomaniak' ) { updateData.authority = data.authority; } else { @@ -202,6 +204,16 @@ export const ConnectionsPage: React.FC = () => { } }; + const handleCreateInfomaniak = async () => { + if (isConnecting) return; + try { + await createInfomaniakConnectionAndAuth(); + refetch(); + } catch (error) { + console.error('Error creating Infomaniak connection:', error); + } + }; + // Open Microsoft Admin Consent flow in a popup const handleAdminConsent = () => { setAdminConsentPending(true); @@ -252,7 +264,7 @@ export const ConnectionsPage: React.FC = () => {

{t('Verbindungen')}

- {t('Persönliche Datenanbindungen verwalten (OAuth: Google, Microsoft, ClickUp)')} + {t('Persönliche Datenanbindungen verwalten (OAuth: Google, Microsoft, ClickUp, Infomaniak)')}

@@ -296,6 +308,15 @@ export const ConnectionsPage: React.FC = () => { > ClickUp + )}