import React, { useState } from 'react'; import { HiDocumentArrowDown, HiDocumentArrowUp, HiGlobeAlt, HiCodeBracket, HiCheckCircle, HiExclamationTriangle, HiInformationCircle, HiBookOpen, HiFolder } from 'react-icons/hi2'; import { Button } from '../../../ui/Button'; import Typography from '../../../ui/Typography'; import { useBlockTesterStore } from '../../../../stores/blockTesterStore'; import { cn } from '../../../../lib/utils'; import { Logger } from '@hashgraphonline/standards-sdk'; const logger = new Logger({ module: 'DevTools' }); interface DevToolsProps { className?: string; } /** * Development tools component for block testing, validation, export, and import * Integrates with desktop file operations and provides template library access */ const DevTools: React.FC = ({ className }) => { const { currentBlock, exportBlock, importBlock, validateBlock, errors, addError } = useBlockTesterStore(); const [isExporting, setIsExporting] = useState(false); const [isImporting, setIsImporting] = useState(false); const [showTemplateLibrary, setShowTemplateLibrary] = useState(false); const templateExamples = [ { id: 'card-basic', name: 'Basic Card', description: 'Simple card layout with title and content', category: 'layout', template: `
{{title}}

{{description}}

`, attributes: { title: { schema: { type: 'string', label: 'Card Title' }, value: 'Card Title' }, description: { schema: { type: 'textarea', label: 'Description' }, value: 'Card description goes here.' } } }, { id: 'button-cta', name: 'Call to Action Button', description: 'Styled button with hover effects', category: 'interactive', template: ``, attributes: { buttonText: { schema: { type: 'string', label: 'Button Text' }, value: 'Click Me' } } }, { id: 'alert-info', name: 'Information Alert', description: 'Informational alert with icon', category: 'ui', template: `

{{title}}

{{message}}

`, attributes: { title: { schema: { type: 'string', label: 'Alert Title' }, value: 'Information' }, message: { schema: { type: 'textarea', label: 'Alert Message' }, value: 'This is an informational alert message.' } } } ]; const handleExport = async (format: 'json' | 'html' | 'hcs-1') => { if (!currentBlock) { addError({ type: 'export', source: 'export', message: 'No block to export' }); return; } try { setIsExporting(true); if (typeof window !== 'undefined' && (window as any).electronAPI?.showSaveDialog) { const filters = { json: [{ name: 'JSON Files', extensions: ['json'] }], html: [{ name: 'HTML Files', extensions: ['html'] }], 'hcs-1': [{ name: 'HCS-1 Files', extensions: ['json'] }] }; const result = await (window as any).electronAPI.showSaveDialog({ filters: filters[format], defaultPath: `${currentBlock.name || 'block'}.${format === 'hcs-1' ? 'hcs1.json' : format}` }); if (!result.canceled && result.filePath) { const exportedContent = exportBlock(format); await (window as any).electronAPI.writeFile(result.filePath, exportedContent); if ((window as any).electronAPI?.showNotification) { (window as any).electronAPI.showNotification({ type: 'success', title: 'Export Successful', message: `Block exported to ${result.filePath}` }); } } } else { const exportedContent = exportBlock(format); const blob = new Blob([exportedContent], { type: format === 'html' ? 'text/html' : 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${currentBlock.name || 'block'}.${format === 'hcs-1' ? 'hcs1.json' : format}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } catch (error) { logger.error('Export failed', { error: error.message }); addError({ type: 'export', source: 'export', message: `Export failed: ${error.message}` }); } finally { setIsExporting(false); } }; const handleImport = async () => { try { setIsImporting(true); if (typeof window !== 'undefined' && (window as any).electronAPI?.showOpenDialog) { const result = await (window as any).electronAPI.showOpenDialog({ filters: [ { name: 'JSON Files', extensions: ['json'] }, { name: 'All Files', extensions: ['*'] } ], properties: ['openFile'] }); if (!result.canceled && result.filePaths.length > 0) { const fileContent = await (window as any).electronAPI.readFile(result.filePaths[0]); importBlock(fileContent); if ((window as any).electronAPI?.showNotification) { (window as any).electronAPI.showNotification({ type: 'success', title: 'Import Successful', message: 'Block imported successfully' }); } } } else { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { try { const content = e.target?.result as string; importBlock(content); } catch (error) { logger.error('Import failed', { error: error.message }); addError({ type: 'import', source: 'import', message: `Import failed: ${error.message}` }); } }; reader.readAsText(file); } }; input.click(); } } catch (error) { logger.error('Import failed', { error: error.message }); addError({ type: 'import', source: 'import', message: `Import failed: ${error.message}` }); } finally { setIsImporting(false); } }; const loadTemplateExample = (template: typeof templateExamples[0]) => { const blockAttributes: Record = {}; Object.entries(template.attributes).forEach(([key, attr]) => { blockAttributes[key] = attr.schema; }); const newBlock = { id: `example-${template.id}-${Date.now()}`, name: template.name, title: template.name, description: template.description, category: template.category as any, template: template.template, templateSource: { type: 'inline' as const, value: template.template, }, attributes: blockAttributes, actions: {}, keywords: [] as string[], created: new Date(), modified: new Date(), }; useBlockTesterStore.getState().setBlock(newBlock); useBlockTesterStore.getState().updateTemplate(template.template); useBlockTesterStore.getState().updateAttributes(template.attributes); }; const validation = validateBlock(); const hasErrors = validation.errors.length > 0; const hasWarnings = validation.warnings.length > 0; return (
{/* Export/Import Section */}
File Operations
{/* Export Options */}
Export Block
{/* Import Button */}
Import Block
{/* Validation Section */}
Block Validation
{!hasErrors && !hasWarnings ? (
Block is valid
) : (
{/* Errors */} {hasErrors && (
{validation.errors.length} Error{validation.errors.length !== 1 ? 's' : ''}
{validation.errors.slice(0, 3).map((error, index) => (
{error.field}: {error.message}
))} {validation.errors.length > 3 && ( +{validation.errors.length - 3} more )}
)} {/* Warnings */} {hasWarnings && (
{validation.warnings.length} Warning{validation.warnings.length !== 1 ? 's' : ''}
{validation.warnings.slice(0, 3).map((warning, index) => (
{warning.field}: {warning.message} {warning.suggestion && (
→ {warning.suggestion}
)}
))} {validation.warnings.length > 3 && ( +{validation.warnings.length - 3} more )}
)}
)}
{/* Template Library */}
Template Library
{showTemplateLibrary && (
{templateExamples.map((template) => ( ))}
)}
); }; export default DevTools;