added infomaniak

This commit is contained in:
ValueOn AG 2026-04-26 23:59:14 +02:00
parent 974c48e24d
commit 3016806db9
4 changed files with 124 additions and 9 deletions

View file

@ -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<T> {
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;

View file

@ -126,6 +126,7 @@ const _AUTHORITY_ICONS: Record<string, string> = {
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<string, string> = {
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<string, string> = {
'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<string, string> = {
drive: 'googleDriveFolder',
gmail: 'gmailFolder',
files: 'ftpFolder',
clickup: 'clickup',
kdrive: 'kdriveFolder',
mail: 'mailFolder',
};
/* ─── Tree helpers ───────────────────────────────────────────────────── */

View file

@ -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<void> => {
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<void>((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<void> => {
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);

View file

@ -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 = () => {
<div>
<h1 className={styles.pageTitle}>{t('Verbindungen')}</h1>
<p className={styles.pageSubtitle}>
{t('Persönliche Datenanbindungen verwalten (OAuth: Google, Microsoft, ClickUp)')}
{t('Persönliche Datenanbindungen verwalten (OAuth: Google, Microsoft, ClickUp, Infomaniak)')}
</p>
</div>
<div className={styles.headerActions}>
@ -296,6 +308,15 @@ export const ConnectionsPage: React.FC = () => {
>
<FaTasks /> ClickUp
</button>
<button
type="button"
className={styles.secondaryButton}
onClick={handleCreateInfomaniak}
disabled={isConnecting}
title={t('Infomaniak-Konto verbinden (kDrive + Mail)')}
>
<FaCloud /> Infomaniak
</button>
</>
)}
</div>