bugfix(CON-01, CON-02)
This commit is contained in:
parent
f09f43666a
commit
238dd6ae16
2 changed files with 43 additions and 12 deletions
|
|
@ -333,6 +333,8 @@ export function useConnections() {
|
||||||
|
|
||||||
// Create Google connection and open OAuth popup
|
// Create Google connection and open OAuth popup
|
||||||
const createGoogleConnectionAndAuth = async (): Promise<void> => {
|
const createGoogleConnectionAndAuth = async (): Promise<void> => {
|
||||||
|
if (isConnecting) return;
|
||||||
|
setIsConnecting(true);
|
||||||
try {
|
try {
|
||||||
// Step 1: Create a Google connection
|
// Step 1: Create a Google connection
|
||||||
const newConnection = await createConnection({
|
const newConnection = await createConnection({
|
||||||
|
|
@ -354,7 +356,7 @@ export function useConnections() {
|
||||||
authUrl = `${apiBaseUrl}${authUrl}`;
|
authUrl = `${apiBaseUrl}${authUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return await new Promise<void>((resolve, reject) => {
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
authUrl,
|
authUrl,
|
||||||
'google-connection',
|
'google-connection',
|
||||||
|
|
@ -362,6 +364,7 @@ export function useConnections() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!popup) {
|
if (!popup) {
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -371,6 +374,7 @@ export function useConnections() {
|
||||||
if (popup.closed) {
|
if (popup.closed) {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('Google OAuth popup closed');
|
console.log('Google OAuth popup closed');
|
||||||
// Refresh connections in case it succeeded
|
// Refresh connections in case it succeeded
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
|
|
@ -390,6 +394,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('Google connection successful');
|
console.log('Google connection successful');
|
||||||
// Refresh connections
|
// Refresh connections
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
|
|
@ -398,6 +403,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error(event.data.error || 'Google connection failed'));
|
reject(new Error(event.data.error || 'Google connection failed'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -405,6 +411,7 @@ export function useConnections() {
|
||||||
window.addEventListener('message', messageListener);
|
window.addEventListener('message', messageListener);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
setIsConnecting(false);
|
||||||
console.error('Error creating Google connection:', error);
|
console.error('Error creating Google connection:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -412,6 +419,8 @@ export function useConnections() {
|
||||||
|
|
||||||
// Create ClickUp connection and open OAuth popup
|
// Create ClickUp connection and open OAuth popup
|
||||||
const createClickupConnectionAndAuth = async (): Promise<void> => {
|
const createClickupConnectionAndAuth = async (): Promise<void> => {
|
||||||
|
if (isConnecting) return;
|
||||||
|
setIsConnecting(true);
|
||||||
try {
|
try {
|
||||||
const newConnection = await createConnection({
|
const newConnection = await createConnection({
|
||||||
type: 'clickup',
|
type: 'clickup',
|
||||||
|
|
@ -430,7 +439,7 @@ export function useConnections() {
|
||||||
authUrl = `${apiBaseUrl}${authUrl}`;
|
authUrl = `${apiBaseUrl}${authUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return await new Promise<void>((resolve, reject) => {
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
authUrl,
|
authUrl,
|
||||||
'clickup-connection',
|
'clickup-connection',
|
||||||
|
|
@ -438,6 +447,7 @@ export function useConnections() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!popup) {
|
if (!popup) {
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -446,6 +456,7 @@ export function useConnections() {
|
||||||
if (popup.closed) {
|
if (popup.closed) {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('ClickUp OAuth popup closed');
|
console.log('ClickUp OAuth popup closed');
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
resolve();
|
resolve();
|
||||||
|
|
@ -462,6 +473,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('ClickUp connection successful');
|
console.log('ClickUp connection successful');
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
resolve();
|
resolve();
|
||||||
|
|
@ -469,6 +481,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error(event.data.error || 'ClickUp connection failed'));
|
reject(new Error(event.data.error || 'ClickUp connection failed'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -476,6 +489,7 @@ export function useConnections() {
|
||||||
window.addEventListener('message', messageListener);
|
window.addEventListener('message', messageListener);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
setIsConnecting(false);
|
||||||
console.error('Error creating ClickUp connection:', error);
|
console.error('Error creating ClickUp connection:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -483,6 +497,8 @@ export function useConnections() {
|
||||||
|
|
||||||
// Create Microsoft connection and open OAuth popup
|
// Create Microsoft connection and open OAuth popup
|
||||||
const createMicrosoftConnectionAndAuth = async (): Promise<void> => {
|
const createMicrosoftConnectionAndAuth = async (): Promise<void> => {
|
||||||
|
if (isConnecting) return;
|
||||||
|
setIsConnecting(true);
|
||||||
try {
|
try {
|
||||||
// Step 1: Create a Microsoft connection
|
// Step 1: Create a Microsoft connection
|
||||||
const newConnection = await createConnection({
|
const newConnection = await createConnection({
|
||||||
|
|
@ -504,7 +520,7 @@ export function useConnections() {
|
||||||
authUrl = `${apiBaseUrl}${authUrl}`;
|
authUrl = `${apiBaseUrl}${authUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return await new Promise<void>((resolve, reject) => {
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
authUrl,
|
authUrl,
|
||||||
'msft-connection',
|
'msft-connection',
|
||||||
|
|
@ -512,6 +528,7 @@ export function useConnections() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!popup) {
|
if (!popup) {
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
reject(new Error('Popup was blocked. Please allow popups and try again.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -521,6 +538,7 @@ export function useConnections() {
|
||||||
if (popup.closed) {
|
if (popup.closed) {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('Microsoft OAuth popup closed');
|
console.log('Microsoft OAuth popup closed');
|
||||||
// Refresh connections in case it succeeded
|
// Refresh connections in case it succeeded
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
|
|
@ -540,6 +558,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
console.log('Microsoft connection successful');
|
console.log('Microsoft connection successful');
|
||||||
// Refresh connections
|
// Refresh connections
|
||||||
fetchConnections();
|
fetchConnections();
|
||||||
|
|
@ -548,6 +567,7 @@ export function useConnections() {
|
||||||
clearInterval(checkClosed);
|
clearInterval(checkClosed);
|
||||||
window.removeEventListener('message', messageListener);
|
window.removeEventListener('message', messageListener);
|
||||||
popup.close();
|
popup.close();
|
||||||
|
setIsConnecting(false);
|
||||||
reject(new Error(event.data.error || 'Microsoft connection failed'));
|
reject(new Error(event.data.error || 'Microsoft connection failed'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -555,6 +575,7 @@ export function useConnections() {
|
||||||
window.addEventListener('message', messageListener);
|
window.addEventListener('message', messageListener);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
setIsConnecting(false);
|
||||||
console.error('Error creating Microsoft connection:', error);
|
console.error('Error creating Microsoft connection:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,12 +97,15 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
// Handle edit submit
|
// Handle edit submit
|
||||||
const handleEditSubmit = async (data: Partial<Connection>) => {
|
const handleEditSubmit = async (data: Partial<Connection>) => {
|
||||||
if (!editingConnection) return;
|
if (!editingConnection) return;
|
||||||
// Note: updateConnection is handled through the hook
|
|
||||||
try {
|
try {
|
||||||
// Ensure authority is properly typed - filter and validate authority value
|
|
||||||
const updateData: Partial<import('../../api/connectionApi').Connection> = { ...data };
|
const updateData: Partial<import('../../api/connectionApi').Connection> = { ...data };
|
||||||
|
|
||||||
// Validate and set authority if present
|
// Strip computed/read-only fields the backend cannot write.
|
||||||
|
delete (updateData as any).connectionReference;
|
||||||
|
delete (updateData as any).displayLabel;
|
||||||
|
delete (updateData as any).tokenStatus;
|
||||||
|
delete (updateData as any).tokenExpiresAt;
|
||||||
|
|
||||||
if (data.authority) {
|
if (data.authority) {
|
||||||
if (
|
if (
|
||||||
data.authority === 'local' ||
|
data.authority === 'local' ||
|
||||||
|
|
@ -112,7 +115,6 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
) {
|
) {
|
||||||
updateData.authority = data.authority;
|
updateData.authority = data.authority;
|
||||||
} else {
|
} else {
|
||||||
// Remove invalid authority value
|
|
||||||
delete (updateData as any).authority;
|
delete (updateData as any).authority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,8 +175,10 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle create Google connection
|
// Guards prevent double-trigger while the OAuth popup is open, which would
|
||||||
|
// otherwise create additional orphan PENDING connections on every click.
|
||||||
const handleCreateGoogle = async () => {
|
const handleCreateGoogle = async () => {
|
||||||
|
if (isConnecting) return;
|
||||||
try {
|
try {
|
||||||
await createGoogleConnectionAndAuth();
|
await createGoogleConnectionAndAuth();
|
||||||
refetch();
|
refetch();
|
||||||
|
|
@ -183,8 +187,8 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle create Microsoft connection
|
|
||||||
const handleCreateMicrosoft = async () => {
|
const handleCreateMicrosoft = async () => {
|
||||||
|
if (isConnecting) return;
|
||||||
try {
|
try {
|
||||||
await createMicrosoftConnectionAndAuth();
|
await createMicrosoftConnectionAndAuth();
|
||||||
refetch();
|
refetch();
|
||||||
|
|
@ -194,6 +198,7 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateClickup = async () => {
|
const handleCreateClickup = async () => {
|
||||||
|
if (isConnecting) return;
|
||||||
try {
|
try {
|
||||||
await createClickupConnectionAndAuth();
|
await createClickupConnectionAndAuth();
|
||||||
refetch();
|
refetch();
|
||||||
|
|
@ -222,7 +227,12 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
|
|
||||||
// Form attributes for edit modal
|
// Form attributes for edit modal
|
||||||
const formAttributes = useMemo(() => {
|
const formAttributes = useMemo(() => {
|
||||||
const excludedFields = ['id', 'mandateId', 'userId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'connectedAt', 'lastChecked'];
|
const excludedFields = [
|
||||||
|
'id', 'mandateId', 'userId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt',
|
||||||
|
'connectedAt', 'lastChecked',
|
||||||
|
// computed/read-only fields the backend rejects on write
|
||||||
|
'connectionReference', 'displayLabel', 'tokenStatus', 'tokenExpiresAt',
|
||||||
|
];
|
||||||
return (attributes || [])
|
return (attributes || [])
|
||||||
.filter(attr => !excludedFields.includes(attr.name));
|
.filter(attr => !excludedFields.includes(attr.name));
|
||||||
}, [attributes]);
|
}, [attributes]);
|
||||||
|
|
@ -255,7 +265,7 @@ export const ConnectionsPage: React.FC = () => {
|
||||||
className={styles.secondaryButton}
|
className={styles.secondaryButton}
|
||||||
onClick={handleAdminConsent}
|
onClick={handleAdminConsent}
|
||||||
disabled={adminConsentPending}
|
disabled={adminConsentPending}
|
||||||
title={t('Microsoft Admin-Zustimmung erteilt der')}
|
title={t('Microsoft Admin-Zustimmung für die gesamte Organisation erteilen')}
|
||||||
>
|
>
|
||||||
<FaShieldAlt /> {t('Admin-Zustimmung')}
|
<FaShieldAlt /> {t('Admin-Zustimmung')}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue