frontend_nyla/src/components/TestSharepoint/testSharepointLogic.tsx

395 lines
12 KiB
TypeScript

import { useMemo, useState, useEffect } from 'react';
import { IoIosLink, IoIosCloudDownload } from 'react-icons/io';
import { ColumnConfig } from '../FormGenerator';
import { useSharePointTest } from '../../hooks/useSharePointTest';
import { useLanguage } from '../../contexts/LanguageContext';
import type {
TableAction,
SharePointConnection,
SharePointDocument,
TestSharepointLogicReturn
} from './testSharepointInterfaces';
export function useTestSharepointLogic(): TestSharepointLogicReturn {
const {
getConnections,
testConnection,
listDocuments,
discoverSites,
debugTokenDetails,
cleanupTokens,
isLoading
} = useSharePointTest();
const { t } = useLanguage();
// State management
const [connections, setConnections] = useState<SharePointConnection[]>([]);
const [selectedConnection, setSelectedConnection] = useState<SharePointConnection | null>(null);
const [connectionLoading, setConnectionLoading] = useState(false);
const [connectionError, setConnectionError] = useState<string | null>(null);
const [documents, setDocuments] = useState<SharePointDocument[]>([]);
const [documentsLoading, setDocumentsLoading] = useState(false);
const [documentsError, setDocumentsError] = useState<string | null>(null);
const [testingConnections, setTestingConnections] = useState<Set<string>>(new Set());
const [connectionTestResults, setConnectionTestResults] = useState<Record<string, any>>({});
const [discoveredSites, setDiscoveredSites] = useState<any[]>([]);
const [sitesDiscovered, setSitesDiscovered] = useState(false);
const [tokenDebugInfo, setTokenDebugInfo] = useState<any>(null);
// Load connections on mount
useEffect(() => {
loadConnections();
}, []);
const loadConnections = async () => {
setConnectionLoading(true);
setConnectionError(null);
try {
const conns = await getConnections();
setConnections(conns);
if (conns.length > 0 && !selectedConnection) {
setSelectedConnection(conns[0]);
}
} catch (error) {
console.error('Failed to load connections:', error);
setConnectionError(error instanceof Error ? error.message : 'Failed to load connections');
} finally {
setConnectionLoading(false);
}
};
// Configure columns for the SharePoint documents table
const columns: ColumnConfig[] = useMemo(() => [
{
key: 'documentName',
label: t('sharepoint.column.documentName', 'Document Name'),
type: 'string',
width: 300,
minWidth: 200,
maxWidth: 400,
sortable: true,
filterable: true,
searchable: true,
formatter: (value: string, row: any) => (
<span
style={{
color: 'var(--color-text)',
fontWeight: 500,
display: 'flex',
alignItems: 'center',
gap: '8px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
cursor: row?.type === 'folder' ? 'pointer' : 'default'
}}
title={value}
>
{row?.type === 'folder' ? '📁' : '📄'} {value}
</span>
)
},
{
key: 'mimeType',
label: t('sharepoint.column.mimeType', 'MIME Type'),
type: 'string',
width: 200,
minWidth: 150,
maxWidth: 300,
sortable: true,
filterable: true,
searchable: true,
},
{
key: 'size',
label: t('sharepoint.column.size', 'Size'),
type: 'number',
width: 140,
minWidth: 120,
maxWidth: 180,
sortable: true,
filterable: false,
formatter: (value: number | string | undefined) => {
if (!value || value === 0) return '-';
const sizeInBytes = typeof value === 'string' ? parseInt(value, 10) : value;
const units = ['Bytes', 'KB', 'MB', 'GB'];
let size = sizeInBytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return (
<span style={{ fontWeight: 500, color: 'var(--color-text)' }}>
{`${size.toFixed(1)} ${units[unitIndex]}`}
</span>
);
}
},
{
key: 'path',
label: t('sharepoint.column.path', 'Path'),
type: 'string',
width: 250,
minWidth: 200,
maxWidth: 400,
sortable: true,
filterable: true,
searchable: true,
},
], [t]);
// Handle connection selection
const handleSelectConnection = (connectionId: string) => {
const connection = connections.find(conn => conn.id === connectionId);
if (connection) {
setSelectedConnection(connection);
// Clear documents when changing connection
setDocuments([]);
setDocumentsError(null);
}
};
// Handle connection testing
const handleTestConnection = async (connectionId: string) => {
setTestingConnections(prev => new Set(prev).add(connectionId));
try {
const result = await testConnection(connectionId);
setConnectionTestResults(prev => ({ ...prev, [connectionId]: result }));
} catch (error) {
console.error('Connection test failed:', error);
setConnectionTestResults(prev => ({
...prev,
[connectionId]: {
success: false,
error: error instanceof Error ? error.message : 'Connection test failed'
}
}));
} finally {
setTestingConnections(prev => {
const newSet = new Set(prev);
newSet.delete(connectionId);
return newSet;
});
}
};
// Handle listing documents
const handleListDocuments = async (siteUrl?: string, folderPaths?: string[]) => {
if (!selectedConnection) {
setDocumentsError('No connection selected');
return;
}
setDocumentsLoading(true);
setDocumentsError(null);
try {
const connectionReference = `connection:${selectedConnection.authority}:${selectedConnection.externalUsername}:${selectedConnection.id}`;
const response = await listDocuments({
connectionReference,
siteUrl: siteUrl || 'https://your-tenant.sharepoint.com/sites/your-site',
folderPaths: folderPaths || ['/'],
includeSubfolders: false,
expectedDocumentFormats: [{ extension: '.json', mimeType: 'application/json' }]
});
console.log('SharePoint response:', response);
if (response.success && response.data?.documents) {
// Extract the actual files from the nested structure
const documents = response.data.documents;
if (documents.length > 0 && documents[0].documentData?.listResults) {
// Flatten all files from all folder results
const allFiles: SharePointDocument[] = [];
documents[0].documentData.listResults.forEach((folderResult: any) => {
if (folderResult.items && Array.isArray(folderResult.items)) {
folderResult.items.forEach((item: any) => {
// Convert SharePoint item to our document format
allFiles.push({
documentName: item.name || 'Unknown',
mimeType: item.file?.mimeType || (item.type === 'folder' ? 'folder' : 'unknown'),
size: item.size || 0,
path: folderResult.folderPath || '/',
type: item.type || (item.folder ? 'folder' : 'file'),
id: item.id,
documentData: item // Store the full item data
});
});
}
});
console.log('Extracted files:', allFiles);
console.log('Sample file structure:', allFiles[0]);
setDocuments(allFiles);
} else {
console.log('No listResults found in response');
setDocuments([]);
}
} else {
console.log('Response error or no documents:', response);
setDocumentsError(response.error || 'Failed to list documents');
setDocuments([]);
}
} catch (error) {
console.error('Failed to list documents:', error);
setDocumentsError(error instanceof Error ? error.message : 'Failed to list documents');
setDocuments([]);
} finally {
setDocumentsLoading(false);
}
};
// Handle site discovery
const handleDiscoverSites = async () => {
if (!selectedConnection) {
return;
}
try {
const result = await discoverSites();
if (result.success && result.data && result.data.sites) {
setDiscoveredSites(result.data.sites);
setSitesDiscovered(true);
setDocumentsError(null); // Clear any previous errors
} else {
console.error('Site discovery failed:', result);
setDiscoveredSites([]);
setSitesDiscovered(true); // Set to true so we show the "no sites" message
// Set error message to help user understand what went wrong
const errorMsg = result.error || result.message || 'Unknown error occurred';
setDocumentsError(`Site discovery failed: ${errorMsg}`);
}
} catch (error) {
console.error('Site discovery failed:', error);
setDiscoveredSites([]);
setSitesDiscovered(true);
setDocumentsError(`Site discovery error: ${error instanceof Error ? error.message : 'Network or authentication error'}`);
}
};
// Handle site selection
const handleSelectSite = (siteUrl: string) => {
// This will be used by the parent component
console.log('Site selected:', siteUrl);
};
// Handle token debug
const handleDebugTokens = async () => {
try {
const result = await debugTokenDetails();
setTokenDebugInfo(result);
console.log('Token debug info:', result);
} catch (error) {
console.error('Token debug failed:', error);
setTokenDebugInfo({ error: error instanceof Error ? error.message : 'Failed to get token info' });
}
};
// Handle token cleanup
const handleCleanupTokens = async () => {
try {
const result = await cleanupTokens();
console.log('Token cleanup result:', result);
// Clear the debug info to force refresh
setTokenDebugInfo(null);
// Show success message
setDocumentsError(null);
alert(`Success! Deleted ${result.data?.tokensDeleted || 0} tokens. Please reconnect your Microsoft account now.`);
} catch (error) {
console.error('Token cleanup failed:', error);
alert(`Token cleanup failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
// Handle folder navigation
const handleFolderNavigation = (document: SharePointDocument, currentPath: string) => {
if (document.type === 'folder') {
// Build the new path by combining current path with folder name
const newPath = currentPath === '/' ? `/${document.documentName}` : `${currentPath}/${document.documentName}`;
console.log('Navigating to folder:', newPath);
return newPath;
}
return currentPath;
};
// Handle document actions
const handleViewDocument = async (document: SharePointDocument) => {
console.log('View document:', document);
// TODO: Implement document viewing
};
const handleDownloadDocument = async (document: SharePointDocument) => {
console.log('Download document:', document);
// TODO: Implement document download
};
// Configure action buttons
const actions: TableAction[] = useMemo(() => [
{
label: t('sharepoint.action.view', 'View'),
icon: <IoIosLink />,
onClick: (document: SharePointDocument) => {
handleViewDocument(document);
}
},
{
label: t('sharepoint.action.download', 'Download'),
icon: <IoIosCloudDownload />,
onClick: (document: SharePointDocument) => {
handleDownloadDocument(document);
}
}
], [t]);
// Refetch connections
const refetchConnections = async () => {
await loadConnections();
};
return {
// Connection data
connections,
selectedConnection,
connectionLoading,
connectionError,
// Document data
documents,
documentsLoading: documentsLoading || isLoading,
documentsError,
// Table configuration
columns,
actions,
// Connection testing
testingConnections,
connectionTestResults,
// Site discovery
discoveredSites,
sitesDiscovered,
// Token debug
tokenDebugInfo,
// Handlers
handleSelectConnection,
handleTestConnection,
handleListDocuments,
handleDiscoverSites,
handleSelectSite,
handleDebugTokens,
handleCleanupTokens,
handleFolderNavigation,
refetchConnections
};
}