Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Agents from './agents';
import SharedAgentGate from './agents/SharedAgentGate';
import ActionButtons from './components/ActionButtons';
import Spinner from './components/Spinner';
import UploadToast from './components/UploadToast';
import Conversation from './conversation/Conversation';
import { SharedConversation } from './conversation/SharedConversation';
import { useDarkTheme, useMediaQuery } from './hooks';
Expand Down Expand Up @@ -45,6 +46,7 @@ function MainLayout() {
>
<Outlet />
</div>
<UploadToast />
</div>
);
}
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/check-circle-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/warn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 25 additions & 11 deletions frontend/src/components/ConnectorAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useRef } from 'react';
import { useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { useDarkTheme } from '../hooks';
import { selectToken } from '../preferences/preferenceSlice';

Expand All @@ -24,6 +25,7 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
onDisconnect,
errorMessage,
}) => {
const { t } = useTranslation();
const token = useSelector(selectToken);
const [isDarkTheme] = useDarkTheme();
const completedRef = useRef(false);
Expand All @@ -47,12 +49,16 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
cleanup();
onSuccess({
session_token: event.data.session_token,
user_email: event.data.user_email || 'Connected User',
user_email:
event.data.user_email ||
t('modals.uploadDoc.connectors.auth.connectedUser'),
});
} else if (errorProvider) {
completedRef.current = true;
cleanup();
onError(event.data.error || 'Authentication failed');
onError(
event.data.error || t('modals.uploadDoc.connectors.auth.authFailed'),
);
}
};

Expand All @@ -71,13 +77,15 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({

if (!authResponse.ok) {
throw new Error(
`Failed to get authorization URL: ${authResponse.status}`,
`${t('modals.uploadDoc.connectors.auth.authUrlFailed')}: ${authResponse.status}`,
);
}

const authData = await authResponse.json();
if (!authData.success || !authData.authorization_url) {
throw new Error(authData.error || 'Failed to get authorization URL');
throw new Error(
authData.error || t('modals.uploadDoc.connectors.auth.authUrlFailed'),
);
}

const authWindow = window.open(
Expand All @@ -86,9 +94,7 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
'width=500,height=600,scrollbars=yes,resizable=yes',
);
if (!authWindow) {
throw new Error(
'Failed to open authentication window. Please allow popups.',
);
throw new Error(t('modals.uploadDoc.connectors.auth.popupBlocked'));
}

window.addEventListener('message', handleAuthMessage as any);
Expand All @@ -98,13 +104,17 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
clearInterval(checkClosed);
window.removeEventListener('message', handleAuthMessage as any);
if (!completedRef.current) {
onError('Authentication was cancelled');
onError(t('modals.uploadDoc.connectors.auth.authCancelled'));
}
}
}, 1000);
intervalRef.current = checkClosed;
} catch (error) {
onError(error instanceof Error ? error.message : 'Authentication failed');
onError(
error instanceof Error
? error.message
: t('modals.uploadDoc.connectors.auth.authFailed'),
);
}
};

Expand Down Expand Up @@ -147,14 +157,18 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
/>
</svg>
<span>Connected as {userEmail}</span>
<span>
{t('modals.uploadDoc.connectors.auth.connectedAs', {
email: userEmail,
})}
</span>
</div>
{onDisconnect && (
<button
onClick={onDisconnect}
className="text-xs font-medium text-[#212121] underline hover:text-gray-700"
>
Disconnect
{t('modals.uploadDoc.connectors.auth.disconnect')}
</button>
)}
</div>
Expand Down
64 changes: 44 additions & 20 deletions frontend/src/components/GoogleDrivePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import useDrivePicker from 'react-google-drive-picker';

import ConnectorAuth from './ConnectorAuth';
Expand Down Expand Up @@ -26,6 +27,7 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
token,
onSelectionChange,
}) => {
const { t } = useTranslation();
const [selectedFiles, setSelectedFiles] = useState<PickerFile[]>([]);
const [selectedFolders, setSelectedFolders] = useState<PickerFile[]>([]);
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -66,14 +68,19 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({

if (!validateResponse.ok) {
setIsConnected(false);
setAuthError('Session expired. Please reconnect to Google Drive.');
setAuthError(
t('modals.uploadDoc.connectors.googleDrive.sessionExpired'),
);
setIsValidating(false);
return false;
}

const validateData = await validateResponse.json();
if (validateData.success) {
setUserEmail(validateData.user_email || 'Connected User');
setUserEmail(
validateData.user_email ||
t('modals.uploadDoc.connectors.auth.connectedUser'),
);
setIsConnected(true);
setAuthError('');
setAccessToken(validateData.access_token || null);
Expand All @@ -83,14 +90,14 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
setIsConnected(false);
setAuthError(
validateData.error ||
'Session expired. Please reconnect your account.',
t('modals.uploadDoc.connectors.googleDrive.sessionExpiredGeneric'),
);
setIsValidating(false);
return false;
}
} catch (error) {
console.error('Error validating session:', error);
setAuthError('Failed to validate session. Please reconnect.');
setAuthError(t('modals.uploadDoc.connectors.googleDrive.validateFailed'));
setIsConnected(false);
setIsValidating(false);
return false;
Expand All @@ -103,15 +110,13 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
const sessionToken = getSessionToken('google_drive');

if (!sessionToken) {
setAuthError('No valid session found. Please reconnect to Google Drive.');
setAuthError(t('modals.uploadDoc.connectors.googleDrive.noSession'));
setIsLoading(false);
return;
}

if (!accessToken) {
setAuthError(
'No access token available. Please reconnect to Google Drive.',
);
setAuthError(t('modals.uploadDoc.connectors.googleDrive.noAccessToken'));
setIsLoading(false);
return;
}
Expand Down Expand Up @@ -193,7 +198,7 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
});
} catch (error) {
console.error('Error opening picker:', error);
setAuthError('Failed to open file picker. Please try again.');
setAuthError(t('modals.uploadDoc.connectors.googleDrive.pickerFailed'));
setIsLoading(false);
}
};
Expand Down Expand Up @@ -264,9 +269,12 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
<>
<ConnectorAuth
provider="google_drive"
label="Connect to Google Drive"
label={t('modals.uploadDoc.connectors.googleDrive.connect')}
onSuccess={(data) => {
setUserEmail(data.user_email || 'Connected User');
setUserEmail(
data.user_email ||
t('modals.uploadDoc.connectors.auth.connectedUser'),
);
setIsConnected(true);
setAuthError('');

Expand All @@ -289,26 +297,34 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
<div className="rounded-lg border border-[#EEE6FF78] dark:border-[#6A6A6A]">
<div className="p-4">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-sm font-medium">Selected Files</h3>
<h3 className="text-sm font-medium">
{t('modals.uploadDoc.connectors.googleDrive.selectedFiles')}
</h3>
<button
onClick={() => handleOpenPicker()}
className="rounded-md bg-[#A076F6] px-3 py-1 text-sm text-white hover:bg-[#8A5FD4]"
disabled={isLoading}
>
{isLoading ? 'Loading...' : 'Select Files'}
{isLoading
? t('modals.uploadDoc.connectors.googleDrive.loading')
: t(
'modals.uploadDoc.connectors.googleDrive.selectFiles',
)}
</button>
</div>

{selectedFiles.length === 0 && selectedFolders.length === 0 ? (
<p className="text-sm text-gray-600 dark:text-gray-400">
No files or folders selected
{t(
'modals.uploadDoc.connectors.googleDrive.noFilesSelected',
)}
</p>
) : (
<div className="max-h-60 overflow-y-auto">
{selectedFolders.length > 0 && (
<div className="mb-2">
<h4 className="mb-1 text-xs font-medium text-gray-500">
Folders
{t('modals.uploadDoc.connectors.googleDrive.folders')}
</h4>
{selectedFolders.map((folder) => (
<div
Expand All @@ -317,7 +333,9 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
>
<img
src={folder.iconUrl}
alt="Folder"
alt={t(
'modals.uploadDoc.connectors.googleDrive.folderAlt',
)}
className="mr-2 h-5 w-5"
/>
<span className="flex-1 truncate text-sm">
Expand All @@ -337,7 +355,9 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
}}
className="ml-2 text-sm text-red-500 hover:text-red-700"
>
Remove
{t(
'modals.uploadDoc.connectors.googleDrive.remove',
)}
</button>
</div>
))}
Expand All @@ -347,7 +367,7 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
{selectedFiles.length > 0 && (
<div>
<h4 className="mb-1 text-xs font-medium text-gray-500">
Files
{t('modals.uploadDoc.connectors.googleDrive.files')}
</h4>
{selectedFiles.map((file) => (
<div
Expand All @@ -356,7 +376,9 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
>
<img
src={file.iconUrl}
alt="File"
alt={t(
'modals.uploadDoc.connectors.googleDrive.fileAlt',
)}
className="mr-2 h-5 w-5"
/>
<span className="flex-1 truncate text-sm">
Expand All @@ -375,7 +397,9 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
}}
className="ml-2 text-sm text-red-500 hover:text-red-700"
>
Remove
{t(
'modals.uploadDoc.connectors.googleDrive.remove',
)}
</button>
</div>
))}
Expand Down
Loading