395 lines
12 KiB
TypeScript
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
|
|
};
|
|
}
|