Skip to main content
Glama
joelmnz

Article Manager MCP Server

by joelmnz
ImportFiles.tsx8.16 kB
import React, { useState, useEffect } from 'react'; import { apiClient } from '../utils/apiClient'; interface ImportStatus { phase: 'idle' | 'scanning' | 'validating' | 'importing' | 'completed' | 'error'; processed: number; total: number; currentFile?: string; dataDirAvailable: boolean; dataDir: string; result?: { imported?: number; skipped?: number; totalFiles?: number; conflicts?: any[]; errors?: any[]; }; error?: string; } interface ImportFilesProps { token: string; onNavigate: (path: string) => void; } export function ImportFiles({ token, onNavigate }: ImportFilesProps) { const [status, setStatus] = useState<ImportStatus | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { loadStatus(); const interval = setInterval(() => { if (status?.phase === 'importing' || status?.phase === 'validating') { loadStatus(false); } }, 1000); return () => clearInterval(interval); }, [status?.phase]); const loadStatus = async (showLoading = true) => { if (showLoading) setLoading(true); try { const response = await apiClient.get('/api/import/status', token); if (response.ok) { const data = await response.json(); setStatus(data); } else { setError('Failed to load import status'); } } catch (err) { setError('Failed to connect to server'); } finally { if (showLoading) setLoading(false); } }; const handleValidate = async () => { setLoading(true); try { const response = await apiClient.post('/api/import/validate', {}, token); if (response.ok) { await loadStatus(); } else { const data = await response.json(); setError(data.error || 'Validation failed'); } } catch (err) { setError('Failed to start validation'); } finally { setLoading(false); } }; const handleImport = async () => { try { const response = await apiClient.post('/api/import/start', {}, token); if (response.ok) { // Status update will be picked up by polling setStatus(prev => prev ? { ...prev, phase: 'importing' } : null); } else { const data = await response.json(); setError(data.error || 'Import failed to start'); } } catch (err) { setError('Failed to start import'); } }; if (loading && !status) { return <div className="loading">Loading import status...</div>; } if (error) { return ( <div className="error-container"> <h3>Error</h3> <p>{error}</p> <button onClick={() => window.location.reload()}>Retry</button> </div> ); } if (!status?.dataDirAvailable) { return ( <div className="import-container"> <h2>Import from Disk</h2> <div className="warning-box"> <p>Data directory not found at <code>{status?.dataDir}</code>.</p> <p>Please ensure you have mounted your data directory to the container.</p> </div> <button onClick={() => onNavigate('/')}>Back to Home</button> </div> ); } return ( <div className="import-container"> <div className="header-actions"> <button className="back-button" onClick={() => onNavigate('/rag-status')}> ← Back to Status </button> <h2>Import from Disk</h2> </div> <div className="status-card"> <p>Source Directory: <code>{status.dataDir}</code></p> <p>Status: <strong>{status.phase}</strong></p> </div> {status.phase === 'idle' && !status.result && ( <div className="action-card"> <p>Scan the directory to find markdown files available for import.</p> <button className="primary-button" onClick={handleValidate}> Scan Files </button> </div> )} {status.phase === 'validating' && ( <div className="progress-card"> <p>Scanning files...</p> <div className="spinner"></div> </div> )} {status.phase === 'idle' && status.result && ( <div className="results-card"> <h3>Scan Results</h3> <div className="stats-grid"> <div className="stat-item"> <label>Total Files</label> <span>{status.result.totalFiles}</span> </div> <div className="stat-item"> <label>Conflicts</label> <span>{status.result.conflicts?.length || 0}</span> </div> <div className="stat-item"> <label>Errors</label> <span>{status.result.errors?.length || 0}</span> </div> </div> {status.result.conflicts && status.result.conflicts.length > 0 && ( <div className="conflicts-list"> <h4>Conflicts (will be skipped)</h4> <ul> {status.result.conflicts.slice(0, 5).map((c: any, i: number) => ( <li key={i}>{c.sourceFilename} (Title: {c.existingTitle})</li> ))} {status.result.conflicts.length > 5 && ( <li>...and {status.result.conflicts.length - 5} more</li> )} </ul> </div> )} {status.result.errors && status.result.errors.length > 0 && ( <div className="conflicts-list" style={{ borderLeft: '4px solid var(--danger-color)' }}> <h4 style={{ color: 'var(--danger-color)' }}>Errors</h4> <ul> {status.result.errors.map((e: any, i: number) => ( <li key={i}> <strong>{e.sourceFilename}</strong>: {e.error} </li> ))} </ul> </div> )} <div className="actions"> <button className="secondary-button" onClick={handleValidate}> Rescan </button> <button className="primary-button" onClick={handleImport}> Start Import </button> </div> </div> )} {status.phase === 'importing' && ( <div className="progress-card"> <h3>Importing...</h3> <progress value={status.processed} max={status.total}></progress> <p>{status.processed} / {status.total} files processed</p> {status.currentFile && <p className="current-file">Processing: {status.currentFile}</p>} </div> )} {status.phase === 'completed' && status.result && ( <div className="completion-card"> <h3>Import Completed</h3> <div className="stats-grid"> <div className="stat-item"> <label>Imported</label> <span>{status.result.imported}</span> </div> <div className="stat-item"> <label>Skipped</label> <span>{status.result.skipped}</span> </div> <div className="stat-item"> <label>Errors</label> <span>{status.result.errors?.length || 0}</span> </div> </div> {status.result.errors && status.result.errors.length > 0 && ( <div className="conflicts-list" style={{ borderLeft: '4px solid var(--danger-color)' }}> <h4 style={{ color: 'var(--danger-color)' }}>Errors</h4> <ul> {status.result.errors.map((e: any, i: number) => ( <li key={i}> <strong>{e.sourceFilename}</strong>: {e.error} </li> ))} </ul> </div> )} <button className="primary-button" onClick={() => onNavigate('/')}> Go to Home </button> </div> )} {status.phase === 'error' && ( <div className="error-card"> <h3>Import Failed</h3> <p>{status.error}</p> <button onClick={handleValidate}>Try Again</button> </div> )} </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/joelmnz/mcp-markdown-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server